Saturday 21 May 2016

Part 3 - Arduino Software for Controlling the SPOT GPS Gen 2

Arduino Software for Controlling the SPOT GPS Gen 2

This post provides the actual software used in the Arduino Nano for controlling the SPOT GPS Gen 2.
The whole system has had extensive testing for about 6 months on land before commencing trials at sea.

/*
 SPOT GPS Gen 2 Controller
 This controller is designed to activate the SPT GPS Gen 2 at predetermined intervals.
 It is intended to operate for many months on battery power.

 It uses a state machine to control transitions into different states.

 Alternate between pressing the ok button and the custom message button.
 This is intended to improve overall reliability. on the basis that if one message function fails, may be the other one will work.

 Power is minimized by using a sleep state, and using an 8 second watch dog timer.

Wiring Details
 --------------             ------------------------------
 | Arduino     |           |      SPOT GPS Gen 2          |
 | Nano        |           |                              |
 |  D9  Pin xx |----47kR-->|  Power Switch                |
 |             |           |                              |
 |  D12 Pin xx |<---47kR---| 3Vdc Switched (power sense)  |
 |             |           |  TP(unmarked)                |
 |             |           |                              |
 |  D10 Pin xx |----47kR-->| Ok Message Switch            |
 |             |           |                              |
 |  D11 Pin xx |----47kR-->| Custom Message Switch        |
 |             |           |                              |
 |             |           |                              |
 |             |           |                              |
 |         Gnd |-----------| Gnd                          |
 --------------             ------------------------------

 V1.0 16/8/2015 John Semmens Initial Version
 V1.1 30/8/2015 Near final release.  Adjusting timing constants.
 V1.2 3/9/2015 timing adjustment for 4 hours.

 */

#include <avr/sleep.h>
#include <avr/wdt.h>

const byte LED = 13;
int counter8s; // global variable as counter.

const byte SpotOnOffButton = 9;
const byte SpotOn3VSense = 12;
const byte SpotOkMsgButton = 10;
const byte SpotCustomMsgButton = 11;

const int kSpotOffTime = 1720; // x 8 seconds // 830 = 2 hours
// 1740 = 4 hours + 3 min
// 1720 = 4 hours-3minutes  --- this must be wrong... it must be about 4 hours. 8am 4/9/2015

const int kSpotOnTime = 38;    // x 8 seconds // 38 = about 5 min

enum SpotGPSState_Type
{
    Power_Off,
    Power_On,
    Sending_Ok,
    Power_Off_2,
    Power_On_2,
    Sending_Custom
};

SpotGPSState_Type SpotGPSState;
int q8SecondBlocks;

// watchdog interrupt
ISR(WDT_vect)
{
    wdt_disable();  // disable watchdog

void setup() {
    counter8s = 0;
    SpotGPSState = Power_Off;
    q8SecondBlocks = 1;
}

void loop()
{
    EightSeconds();

    // disable ADC
    ADCSRA = 0;

    // clear various "reset" flags
    MCUSR = 0;
    // allow changes, disable reset
    WDTCSR = bit(WDCE) | bit(WDE);
    // set interrupt mode and an interval
    WDTCSR = bit(WDIE) | bit(WDP3) | bit(WDP0);    // set WDIE, and 8 seconds delay
    wdt_reset();  // pat the dog

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    noInterrupts();           // timed sequence follows
    sleep_enable();

    // turn off brown-out enable in software
    MCUCR = bit(BODS) | bit(BODSE);
    MCUCR = bit(BODS);
    interrupts();             // guarantees next instruction executed
    sleep_cpu();

    // cancel sleep as a precaution
    sleep_disable();
}

void EightSeconds()
{
    // We arrive here every 8 seconds approx
    // Perform the "action()" at intervals of aprroximately 8 seconds x k8SecondBlocks
    // Typical values:
    //          7 = 1 minute
    //        106 = 15 minutes
    //        424 = 1 hour
    //        833 = 2 hours
    //        1000 = 2 hours 22 minutes (measured)
    //       1700 = 4 hours

    counter8s++;
    if (counter8s >= q8SecondBlocks)
    {
        counter8s = 0;
        action();
    }
}

void action() {
    // Process the state machine

    switch (SpotGPSState)
    {
    case Power_Off:
        Spot_Turn_On();
        SpotGPSState = Power_On;
        q8SecondBlocks = 1; // "on" time before moving to next state, about 8 sec
        break;

    case Power_On:
        Spot_Message_OK();
        SpotGPSState = Sending_Ok;
        q8SecondBlocks = kSpotOnTime; // "send ok message" time before turning off. 38 = about 5 min
        break;

    case Sending_Ok:
        Spot_Turn_Off();
        SpotGPSState = Power_Off_2;
        q8SecondBlocks = kSpotOffTime; // "off time" before turning on again.
        break;

    case Power_Off_2:
        Spot_Turn_On();
        SpotGPSState = Power_On_2;
        q8SecondBlocks = 1; // "on" time before moving to next state, about 8 sec
        break;

    case Power_On_2:
        Spot_Message_Custom();
        SpotGPSState = Sending_Custom;
        q8SecondBlocks = kSpotOnTime; // "send Custom message" time before turning off. 38 = about 5 min
        break;

    case Sending_Custom:
        Spot_Turn_Off();
        SpotGPSState = Power_Off;
        q8SecondBlocks = kSpotOffTime; // "off time" before turning on again.
        break;

    default: // should not get here.
        Spot_Turn_Off();
        SpotGPSState = Power_Off;
        q8SecondBlocks = kSpotOffTime; // "off time" before turning on again.
    }
}

void Spot_Turn_On()
{
    // Procedure to simulate pressing the power on button on the spot - to turn it on.
    // it waits for the feedback from the Spot device in the form of a switched 3Volt signal.

    // check if its off by looking for 3V sense signal low
    pinMode(SpotOn3VSense, INPUT);
    if (digitalRead(SpotOn3VSense) == LOW)
    {
        // press the on/off button.
        pinMode(SpotOnOffButton, OUTPUT);
        digitalWrite(SpotOnOffButton, LOW);

        // turn the led on
        pinMode(LED, OUTPUT);
        digitalWrite(LED, HIGH);

        // wait for power sense 3V signal
        pinMode(SpotOn3VSense, INPUT);
        while (digitalRead(SpotOn3VSense) == LOW)
        {
            delay(100);
            wdt_reset();
        }

        // release on/off button.
        digitalWrite(SpotOnOffButton, HIGH);
        pinMode(SpotOnOffButton, INPUT);

        // turn led off
        digitalWrite(LED, LOW);
        pinMode(LED, INPUT);
    }
}

void Spot_Turn_Off()
{
    // Procedure to simulate pressing the power on button on the spot - to turn it off
    // it waits for the feedback from the Spot device in the form of a switched 3Volt signal.

    // check if its on by looking for 3V sense signal
    pinMode(SpotOn3VSense, INPUT);
    if (digitalRead(SpotOn3VSense) == HIGH)
    {
        // only turn it off if its on already

        // press the on/off button.
        pinMode(SpotOnOffButton, OUTPUT);
        digitalWrite(SpotOnOffButton, LOW);

        // LED on
        pinMode(LED, OUTPUT);
        digitalWrite(LED, HIGH);

        // wait for power sense 3V to drop to zero
        pinMode(SpotOn3VSense, INPUT);
        while (digitalRead(SpotOn3VSense) == HIGH)
        {
            delay(100);
            wdt_reset();
        }

        // release on/off button.
        digitalWrite(SpotOnOffButton, HIGH);
        pinMode(SpotOnOffButton, INPUT);

        // turn led off
        digitalWrite(LED, LOW);
        pinMode(LED, INPUT);
    }
}

void Spot_Message_OK()
{
    // Procedure to simulate pressing the "I'm OK" button on the Spot Device.
    // It uses a simple 4 second delay before releasing.

    // press the OK Message button.
    pinMode(SpotOkMsgButton, OUTPUT);
    digitalWrite(SpotOkMsgButton, LOW);

    // LED on
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);

    // hold down OK button for 4 seconds
    delay(4000);

    // release OK Message button.
    digitalWrite(SpotOkMsgButton, HIGH);
    pinMode(SpotOkMsgButton, INPUT);

    // turn led off
    digitalWrite(LED, LOW);
    pinMode(LED, INPUT);
}


void Spot_Message_Custom()
{
    // Procedure to simulate pressing the "Custom Message" button on the Spot Device.
    // It uses a simple 4 second delay before releasing.

    // press the "Custom" Message button.
    pinMode(SpotCustomMsgButton, OUTPUT);
    digitalWrite(SpotCustomMsgButton, LOW);

    // LED on
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);

    // hold down OK button for 4 seconds
    delay(4000);

    // release "Custom" Message button.
    digitalWrite(SpotCustomMsgButton, HIGH);
    pinMode(SpotCustomMsgButton, INPUT);

    // turn led off
    digitalWrite(LED, LOW);
    pinMode(LED, INPUT);
}


No comments:

Post a Comment