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

Dependencies:   XBEE_900HP_SPI mbed-rtos mbed

Files at this revision

API Documentation at this revision

Comitter:
ottaviano3
Date:
Tue Aug 18 00:06:08 2015 +0000
Parent:
0:1e026f2c8707
Commit message:
Major bug fixes and reliability updates. Fixed erroneous gps serial code. Added neglected mutex code where necessary.

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/main.cpp	Fri May 01 15:57:01 2015 +0000
+++ b/main.cpp	Tue Aug 18 00:06:08 2015 +0000
@@ -1,86 +1,184 @@
+/*
+/ 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"
 
-// Serial output to PC
+/*===========================================================/
+/ 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);
 
-// GPS to pc
+// Connected serial GPS pins
 Serial gps(p28,p27);
 
-// setup xbee
+// 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];
-
-// Predefine scanner thread
-void xbeeScanner(void const *args);
-void hostScanner(void const *args);
-void getline();
-
+// 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 Area
-    */
+    /*===========================/
+    / Configuration Section      /
+    /===========================*/
 
-    // Set pc baud rate to pretty high for some speed
+    // 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);
-    
-    // Configure gps with default values.
+    // 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';
-    gps.printf("$PSRF100,1,4800,8,1,0*%i\r\n",gpsxor);
+    // 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);
 
-    // Enable GGA
-    gpsxor = 'P'^'S'^'R'^'F'^'1'^'0'^'3'^','^'0'^','^'0'^','^'1'^','^'1';
-    gps.printf("$PSRF103,0,0,1,1*%i\r\n",gpsxor);
+    // 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';
-    gps.printf("$PSRF103,1,0,0,1*%i\r\n",gpsxor);
+    // 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';
-    gps.printf("$PSRF103,2,0,0,1*%i\r\n",gpsxor);
+    // 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';
-    gps.printf("$PSRF103,3,0,0,1*%i\r\n",gpsxor);
+    // 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';
-    gps.printf("$PSRF103,4,0,0,1*%i\r\n",gpsxor);
+    // 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';
-    gps.printf("$PSRF103,5,0,0,1*%i\r\n",gpsxor);
+    // 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);
 
-    /*End Configuration Area
-    */
+    // 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 buffer[30];
+        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",buffer);
+                pc.scanf("%s",cmdbuff);
             }
-
-            led1 = 1;
-        } while (strcmp( startkey, buffer ) != 0);
+            wdt.kick();
+        } while (strcmp( startkey, cmdbuff ) != 0);
+        // Clear string in buffer
+        cmdbuff[0] = '\0';
+        // LED 1 off as we enter next segment
         led1 = 0;
 
-        // Code to respond saying its started.
+        // Let host know that we are starting.
         pc.printf("SLON\r\n");
 
         xbee.clearBuff();
@@ -90,15 +188,20 @@
         Thread hostscan(hostScanner);
 
         // Main runloop
+        led2 = 1;
         do {
+            serial_mutex.lock();
             if (pc.readable() == true) {
                 // Get bytes from computer
-                pc.scanf("%s",buffer);
+                pc.scanf("%s",cmdbuff);
             }
+            serial_mutex.unlock();
+            wdt.kick();
+        } while (strcmp( endkey, cmdbuff ) != 0);
+        led2 = 0;
 
-            led2 = 1;
-        } while (strcmp( endkey, buffer ) != 0);
-        led2 = 0;
+        // Clear string in buffer
+        cmdbuff[0] = '\0';
 
         xbscan.terminate();
         hostscan.terminate();
@@ -108,39 +211,79 @@
 // Thread to update host position
 void hostScanner(void const *args)
 {
-    char nodeid[4] = "0";
+    // 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;
-    int satsused;
-    float hdop;
-    float latitude, longitude;
-    float altitude;
-    float time;
+    double satsused;
+    double hdop;
+    double latitude, longitude;
+    double altitude;
+    double time;
+    char altunits;
+    double geoidsep;
+    char geoidunits;
+    int difrefstationid;
+    unsigned int gpschecksum;
     
-    while (1) {
-        // update.
-        getline();
-        if(sscanf(gpsmsg, "GPGGA,%f,%f,%c,%f,%c,%i,%i,%f,%f", &time, &latitude, &ns, &longitude, &ew, &lock, &satsused, &hdop, &altitude) >= 1) {
-            if((lock != 1) && (lock != 2) && (lock != 6)) {
-                pc.printf("DNLK,%s,NOLOCK\n",nodeid, latitude, longitude, altitude);
+    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 {
-                double degrees;
-                double minutes = modf(latitude/100.0f, &degrees);
-                minutes = (minutes*100.0f)/60.0f;
-                latitude = degrees + minutes;
-
-                minutes = modf(longitude/100.0f, &degrees);
-                minutes = (minutes*100.0f)/60.0f;
-                longitude = degrees + minutes;
-
-                if(ns == 'S') {
-                    latitude  *= -1.0;
-                }
-                if(ew == 'W') {
-                    longitude *= -1.0;
-                }
-                pc.printf("DLIN,%s,%f,%f,%f\n",nodeid, latitude, longitude, altitude);
+                // 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);
         }
     }
 }
@@ -154,9 +297,13 @@
         while(xbee.attn() == 0) {
             // Read in data and run all background checksum and validation stuff
             if (xbee.readPacket(buffer) == 0) {
-                pc.printf("%s", buffer);
+                serial_mutex.lock();
+                pc.printf("%s\r\n", buffer);
+                serial_mutex.unlock();
             } else {
-                pc.printf("Packet Skipped\n\r");
+                serial_mutex.lock();
+                pc.printf("Packet Failed Validation\r\n");
+                serial_mutex.unlock();
                 xbee.clearBuff();
             }
         }
@@ -165,15 +312,68 @@
     }
 }
 
-void getline()
+// Function to get the gps message and make sure its valid.
+int getGPS(char* data)
 {
-    while(gps.getc() != '$');    // wait for the start of a line
-    for(int i=0; i<256; i++) {
-        gpsmsg[i] = gps.getc();
-        if(gpsmsg[i] == '\r') {
-            gpsmsg[i] = 0;
-            return ;
+    // 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;
         }
     }
-    error("Overflowed message limit");
+
+    // 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;
 }
\ No newline at end of file