The software that runs on the Sentinel base station. This code is for the LPC1768.
Dependencies: XBEE_900HP_SPI mbed-rtos mbed
Revision 1:dd8228f2263c, committed 2015-08-18
- 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, °rees); + minutes = (minutes*100.0f)/60.0f; + latitude = degrees + minutes; + // Convert longitude + minutes = modf(longitude/100.0f, °rees); + 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, °rees); - minutes = (minutes*100.0f)/60.0f; - latitude = degrees + minutes; - - minutes = modf(longitude/100.0f, °rees); - 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