The software that runs on the Sentinel base station. This code is for the LPC1768.

Dependencies:   XBEE_900HP_SPI mbed-rtos mbed

main.cpp

Committer:
ottaviano3
Date:
2015-08-18
Revision:
1:dd8228f2263c
Parent:
0:1e026f2c8707

File content as of revision 1:dd8228f2263c:

/*
/ Sentinel software for the base station node.
/
/ Design work done by:
/ Dominic Ottaviano
/ Sheldon Fernandes
/ Thien L. Nguyen
*/

#include "mbed.h"
#include "xbee900hp.h"
#include "rtos.h"

/*===========================================================/
/ Primary Configuration Area                                 /
/ Global Variables and Pin Assigments Declared Within        /
/===========================================================*/

// Serial Port Mutex to prevent multiple transmissions
Mutex serial_mutex;

// Declare serial output to the host PC over USB
Serial pc(USBTX,USBRX);

// Connected serial GPS pins
Serial gps(p28,p27);

// Declare pins of connected xbee module using spi (EXPERIMENTAL DRIVER)
xbee900hp xbee(p11,p12,p13, p8, p9);

// Led outputs on the MBED
DigitalOut led1(LED1);
DigitalOut led2(LED2);

// Global Variables
// Buffer for reading in from xbee
char buffer[256];
// Buffer for storing GPS data
char gpsmsg[256];

// Predefine threads running here
// Thread to handle data inputs and outputs to the XBee
void xbeeScanner(void const *args);
// Thread to handle data inputs from the GPS module
void hostScanner(void const *args);

// Function prototypes
// Function to query the GPS
int getGPS(char* data);

/*===========================================================/
/ END OF Primary Configuration Area                          /
/===========================================================*/

/*===========================================================/
/ Code to setup the watchdog timer on the MBED to handle-    /
/ auto resets                                                /
/===========================================================*/

class Watchdog
{
public:
    // Load timeout value in watchdog timer and enable
    void kick(float s) {
        LPC_WDT->WDCLKSEL = 0x1;                // Set CLK src to PCLK
        uint32_t clk = SystemCoreClock / 16;    // WD has a fixed /4 prescaler, PCLK default is /4
        LPC_WDT->WDTC = s * (float)clk;
        LPC_WDT->WDMOD = 0x3;                   // Enabled and Reset
        kick();
    }
    // "kick" or "feed" the dog - reset the watchdog timer
    // by writing this required bit pattern
    void kick() {
        LPC_WDT->WDFEED = 0xAA;
        LPC_WDT->WDFEED = 0x55;
    }
};

// Declare watchdog timer
Watchdog wdt;

/*===========================================================/
/ End of watchdog timer code                                 /
/===========================================================*/

/*===========================================================/
/ Begin main program code                                    /
/===========================================================*/

int main()
{
    /*===========================/
    / Configuration Section      /
    /===========================*/

    // Set PC parameters
    // Baud rate
    pc.baud(57600);
    // Other params (8 data bits, no parity, 1 stop bit)
    pc.format(8,SerialBase::None,1);

    // Set GPS paramters
    // Baud rate
    gps.baud(4800);
    // Other params (8 data bits, no parity, 1 stop bit)
    gps.format(8,SerialBase::None,1);

    // Configure gps with these parameters:
    // NMEA, 4800 Baud, 8 Data bits, 1 Stop bit, No Parity
    unsigned int gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'0'^','^'1'^','^'4'^'8'^'0'^'0'^','^'8'^','^'1'^','^'0';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    // Send command to the GPS module
    gps.printf("$PSRF100,1,4800,8,1,0*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable automatic broadcast of all messages. We are going to poll manually.
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'0'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,0,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable GLL
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'1'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,1,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable GSA
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'2'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,2,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable GSV
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'3'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,3,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable RMC
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'4'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,4,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Disable VTG
    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'5'^','^'0'^','^'0'^','^'1';
    // Trim to 8 bits just in case
    gpsxor = gpsxor & 0xFF;
    gps.printf("$PSRF103,5,0,0,1*%u%u\r\n",(gpsxor & 0xF0) >> 4,gpsxor & 0x0F);

    // Enable watchdog with a reasonable half second timeout
    wdt.kick(1000);

    /*===========================/
    / END Configuration Section  /
    /===========================*/
    

    while (true) {
        // Define Keys
        char startkey[12] = "SentinelOn";
        char endkey[13] = "SentinelOff";
        char cmdbuff[30];

        // LED 1 on shows ready status.
        led1 = 1;
        // Start loop waiting for start message
        do {
            if (pc.readable() == true) {
                // Get bytes from computer
                pc.scanf("%s",cmdbuff);
            }
            wdt.kick();
        } while (strcmp( startkey, cmdbuff ) != 0);
        // Clear string in buffer
        cmdbuff[0] = '\0';
        // LED 1 off as we enter next segment
        led1 = 0;

        // Let host know that we are starting.
        pc.printf("SLON\r\n");

        xbee.clearBuff();

        // Start xbeeScanner Thread
        Thread xbscan(xbeeScanner);
        Thread hostscan(hostScanner);

        // Main runloop
        led2 = 1;
        do {
            serial_mutex.lock();
            if (pc.readable() == true) {
                // Get bytes from computer
                pc.scanf("%s",cmdbuff);
            }
            serial_mutex.unlock();
            wdt.kick();
        } while (strcmp( endkey, cmdbuff ) != 0);
        led2 = 0;

        // Clear string in buffer
        cmdbuff[0] = '\0';

        xbscan.terminate();
        hostscan.terminate();
    }
}

// Thread to update host position
void hostScanner(void const *args)
{
    // Set variables, there are more than we need here but it would be useful in the future if we did need any.
    char nodeid[5] = "BASE";
    char ns, ew;
    int lock;
    double satsused;
    double hdop;
    double latitude, longitude;
    double altitude;
    double time;
    char altunits;
    double geoidsep;
    char geoidunits;
    int difrefstationid;
    unsigned int gpschecksum;
    
    serial_mutex.lock();
    // Announce base node to computer application
    pc.printf("ANCE,%s,\r\n",nodeid);
    serial_mutex.unlock();

    // Main run loop
    while (true) {
        // Get new data from the GPS and if data is read successfully continue
        if (!(getGPS(gpsmsg))) {
            // Parse the recieved data and check if its valid (HINT: lf = double since pointers aren't promoted)
            if(sscanf(gpsmsg, "$GPGGA,%lf,%lf,%c,%lf,%c,%i,%lf,%lf,%lf,%c,%lf,%c,,%i*%x", &time, &latitude, &ns, &longitude, &ew, &lock, &satsused, &hdop, &altitude, &altunits, &geoidsep, &geoidunits, &difrefstationid, &gpschecksum) == 14) {
                // Check if the lock is valid
                if((lock != 1) && (lock != 2) && (lock != 6)) {
                    // Lock is not valid, let host know.
                    serial_mutex.lock();
                    pc.printf("DNLK,%s,NOLOCK\r\n",nodeid);
                    serial_mutex.unlock();
                } else {
                    // Convert latitude into proper form for display on a map.
                    double degrees;
                    double minutes = modf(latitude/100.0f, &degrees);
                    minutes = (minutes*100.0f)/60.0f;
                    latitude = degrees + minutes;
                    // Convert longitude
                    minutes = modf(longitude/100.0f, &degrees);
                    minutes = (minutes*100.0f)/60.0f;
                    longitude = degrees + minutes;

                    // Correct for south and west.
                    if(ns == 'S') {
                        latitude  *= -1.0;
                    }
                    if(ew == 'W') {
                        longitude *= -1.0;
                    }
                    
                    serial_mutex.lock();
                    // Send formatted info to the host
                    pc.printf("DLIN,%s,%f,%f,%f,%f,\r\n",nodeid, latitude, longitude, altitude, satsused);
                    serial_mutex.unlock();
                }
                // Wait a second for GPS data to be fresh again and not waste cycles.
                Thread::wait(1000);
            } else {
                // GPS hasn't found a good enough lock
                serial_mutex.lock();
                pc.printf("DNLK,%s,NOLOCK\r\n",nodeid);
                serial_mutex.unlock();
                // If this check fails we give up cpu time to another thread but only momentarily, we need to get this data successfully.
                Thread::wait(500);
            }
        } else {
            // GPS hasn't found lock or sent a corrupted message
            serial_mutex.lock();
            pc.printf("DNLK,%s,NOLOCK\r\n",nodeid);
            serial_mutex.unlock();
            // If this check fails we give up cpu time to another thread but only momentarily, we need to get this data successfully.
            Thread::wait(500);
        }
    }
}

// Thread to scan the xbee
void xbeeScanner(void const *args)
{
    // Main runloop
    while (true) {
        // check if xbee is flagging data to be read
        while(xbee.attn() == 0) {
            // Read in data and run all background checksum and validation stuff
            if (xbee.readPacket(buffer) == 0) {
                serial_mutex.lock();
                pc.printf("%s\r\n", buffer);
                serial_mutex.unlock();
            } else {
                serial_mutex.lock();
                pc.printf("Packet Failed Validation\r\n");
                serial_mutex.unlock();
                xbee.clearBuff();
            }
        }
        // Give up rest of timeslice to boost that performance
        Thread::yield();
    }
}

// Function to get the gps message and make sure its valid.
int getGPS(char* data)
{
    // Request a query of GGA
    // Precomputed checksum to save cpu cycles
    serial_mutex.lock();
    gps.printf("$PSRF103,0,1,0,1*%u%u\r\n",0x2,0x5);
    serial_mutex.unlock();

    // Timer to prevent hangs if gps doesn't respond.
    Timer gpsTO;
    gpsTO.start();

    // Wait for gps to becom readable.
    while (gps.readable() == false) {
        // Timeout
        if (gpsTO.read() > 2) {
            return 1;
        }
    }

    // Wait a tiny bit to allow the gps to send the whole line.
    Thread::wait(50);
    // Get data from gps
    serial_mutex.lock();
    gps.scanf("%s",data);
    serial_mutex.unlock();

    // Compute checksum of recieved gps data
    int i = 0;
    unsigned int calcgpschecksum = 0;
    // Checksum is calculated between and not including the $ and *
    while ((data[i] != '\0') && (data[i] != '*')) {
        // Skip the $
        if (data[i] != '$') {
            calcgpschecksum = calcgpschecksum ^ data[i];
        }
        i++;
    }
    // Shift the checksum to match the format we recieve from the gps
    calcgpschecksum = calcgpschecksum & 0xFF;

    // Get checksum sent by gps out of string
    unsigned int realgpschecksum = 0;
    char checksumarray[3];
    for (int i = 0; i < 256; i++) {
        if (data[i] == '*') {
            // Create little array with ascii hex values.
            checksumarray[0] = data[i+1];
            checksumarray[1] = data[i+2];
            checksumarray[2] = '\0';
            // Convert ascii values to regular integer
            sscanf(checksumarray,"%x",&realgpschecksum);
            break;
        }
    }

    // Check for checksum match
    if( calcgpschecksum == realgpschecksum ) {
        return 0;
    }

    // No checksum match
    return 1;
}