Monitor for central heating system (e.g. 2zones+hw) Supports up to 15 temp probes (DS18B20/DS18S20) 3 valve monitors Gas pulse meter recording Use stand-alone or with nodeEnergyServer See http://robdobson.com/2015/09/central-heating-monitor

Dependencies:   EthernetInterfacePlusHostname NTPClient Onewire RdWebServer SDFileSystem-RTOS mbed-rtos mbed-src

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 // Gas usage monitor
00002 // Counts pulses from a gas meter
00003 // Monitors temperature sensors
00004 // Monitors valve/pump activity
00005 // Web interface and UDP broadcast
00006 // Rob Dobson 2015
00007 
00008 #include "mbed.h"
00009 #include "EthernetInterface.h"
00010 #include "NTPClient.h"
00011 #include "RdWebServer.h"
00012 #include "GasUseCounter.h"
00013 #include "Thermometers.h"
00014 #include "VoltAlerter.h"
00015 #include "Watchdog.h"
00016 #include "Logger.h"
00017 
00018 // System name (used for hostname)
00019 char systemName[20] = "RdGasUseMonitor";
00020 
00021 // Web and UDB ports 
00022 const int WEBPORT = 80; // Port for web server
00023 const int BROADCAST_PORT = 42853; // Arbitrarily chosen port number
00024 
00025 // Main loop delay between data collection passes
00026 const int LOOP_DELAY_IN_MS = 250;
00027 
00028 // Debugging and status
00029 RawSerial pc(USBTX, USBRX);
00030 DigitalOut led1(LED1); //ticking (flashes)
00031 DigitalOut led2(LED2); //state of the 1st voltage alerter
00032 DigitalOut led3(LED3); //socket connecting status
00033 DigitalOut led4(LED4); //server status
00034 
00035 // Web server
00036 UDPSocket sendUDPSocket;
00037 Endpoint broadcastEndpoint;
00038 
00039 // Network Time Protocol (NTP)
00040 NTPClient ntp;
00041 const int NTP_REFRESH_INTERVAL_HOURS = 1;
00042 
00043 // Mutex for SD card access
00044 Mutex sdCardMutex;
00045 
00046 // File system for SD card
00047 SDFileSystem sd(p5, p6, p7, p8, "sd");
00048 
00049 // Base folder for web file system
00050 char* baseWebFolder = "/sd/";
00051 
00052 // Log file names
00053 const char* gasPulseFileName1 = "/sd/curPulse.txt";
00054 const char* gasPulseFileName2 = "/sd/curPulse2.txt";
00055 const char* eventLogFileName = "/sd/log.txt";
00056 const char* dataLogFileBase = "/sd/";
00057 
00058 // Logger
00059 Logger logger(eventLogFileName, dataLogFileBase, sdCardMutex);
00060 
00061 // Gas use counter
00062 DigitalIn gasPulsePin(p21);
00063 GasUseCounter gasUseCounter(gasPulseFileName1, gasPulseFileName2, gasPulsePin, logger, sdCardMutex);
00064 
00065 // Thermometers - DS18B20 OneWire Thermometer connections
00066 const PinName tempSensorPins[] = { p22 };
00067 Thermometers thermometers(sizeof(tempSensorPins)/sizeof(PinName), tempSensorPins, LOOP_DELAY_IN_MS, logger);
00068 
00069 // Voltage Sensors / Alerters
00070 const int NUM_VOLT_ALERTERS = 3;
00071 VoltAlerter voltAlerter1(p23);
00072 VoltAlerter voltAlerter2(p24);
00073 VoltAlerter voltAlerter3(p25);
00074 
00075 // Watchdog
00076 Watchdog watchdog;
00077 
00078 // Broadcast message format
00079 // Data format of the broadcast message - senml - https://tools.ietf.org/html/draft-jennings-senml-08
00080 // {
00081 //     "e": [
00082 //              {"n":"gasCount","v":%d}, 
00083 //              {"n":"gasPulseRateMs","v":%d,"u":"ms"},
00084 //              {"n":"temp_%s","v":%0.1f,"u":"degC"},
00085 //              ...
00086 //              {"n":"pump_%d","v":%d},
00087 //              ...
00088 //          ],
00089 //      "bt": %d
00090 // }
00091 const char broadcastMsgPrefix[] = "{\"e\":[";
00092 const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d,\"u\":\"count\"},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}";
00093 const char broadcastTemperatureFormat[] = "{\"n\":\"temp_%s_%s\",\"v\":%0.1f,\"u\":\"degC\"}";
00094 const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d_%s\",\"bv\":%d}";
00095 const char broadcastMsgSuffix[] = "],\"bt\":%d}";
00096 
00097 // Broadcast message length and buffer
00098 const int MAX_DEVICE_NAME_LEN = 20;
00099 const int broadcastMsgLen = sizeof(broadcastMsgPrefix) + 
00100             sizeof(broadcastMsgGasFormat) + 
00101             ((sizeof(broadcastTemperatureFormat)+MAX_DEVICE_NAME_LEN)*Thermometers::MAX_THERMOMETERS) + 
00102             ((sizeof(broadcastVoltAlerterFormat)+MAX_DEVICE_NAME_LEN)*NUM_VOLT_ALERTERS) + 
00103             sizeof(broadcastMsgSuffix) + 
00104             60;
00105 char broadcastMsgBuffer[broadcastMsgLen];
00106 
00107 // Handling of SD card file delete and file upload commands
00108 const int MAX_FNAME_LENGTH = 127;
00109 char fileNameForWrite[MAX_FNAME_LENGTH+1] = "";
00110 int fileRemainingForWrite = 0;
00111 
00112 // Thermometer and pump names
00113 char thermometerAddrs[][MAX_DEVICE_NAME_LEN] =
00114 {
00115     "28b1b1e0050000d0",
00116     "289dd7c705000060",
00117     "28b3b0c60500000a"
00118 };
00119 char thermometerNames[][MAX_DEVICE_NAME_LEN] =
00120 {
00121     "HWCylinder",
00122     "Inflow2",
00123     "Outflow2"
00124 };
00125 int numNamedThermometers = 3;
00126 
00127 char voltAlerterNames[][MAX_DEVICE_NAME_LEN] =
00128 {
00129     "PumpUpper",
00130     "PumpLower",
00131     "PumpHW"
00132 };
00133 
00134 // Get names of thermometers
00135 char* getThermometerName(char* thermAddr)
00136 {
00137     for (int i = 0; i < numNamedThermometers; i++)
00138     {
00139         if (strcmp(thermometerAddrs[i], thermAddr) == 0)
00140             return thermometerNames[i];
00141     }
00142     return "Unknown";
00143 }
00144 
00145 // Get names of volt alerters
00146 char* getVoltAlerterName(int idx)
00147 {
00148     if (idx >= 0 && idx < NUM_VOLT_ALERTERS)
00149     {
00150         return voltAlerterNames[idx];
00151     }
00152     return "Unknown";
00153 }
00154 
00155 // Format broadcast message
00156 void GenBroadcastMessage()
00157 {
00158     // Get temperature values
00159     TemperatureValue tempValues[Thermometers::MAX_THERMOMETERS];
00160     int numTempValues = thermometers.GetTemperatureValues(Thermometers::MAX_THERMOMETERS, tempValues, 100);
00161 //    for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
00162 //    {
00163 //        printf("Temp: %.1f, Addr: %s, Time: %d\r\n", tempValues[tempIdx].tempInCentigrade, tempValues[tempIdx].address, tempValues[tempIdx].timeStamp);
00164 //    }
00165 
00166     // Format the broadcast message
00167     time_t timeNow = time(NULL);
00168     strcpy(broadcastMsgBuffer, broadcastMsgPrefix);
00169     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgGasFormat, gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs());
00170     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
00171     for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
00172     {
00173         sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastTemperatureFormat, tempValues[tempIdx].address, 
00174                         getThermometerName(tempValues[tempIdx].address),
00175                         tempValues[tempIdx].tempInCentigrade);
00176         strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
00177     }
00178     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 1, getVoltAlerterName(0), voltAlerter1.GetState());
00179     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
00180     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 2, getVoltAlerterName(1), voltAlerter2.GetState());
00181     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
00182     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, getVoltAlerterName(2), voltAlerter3.GetState());
00183     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgSuffix, timeNow);
00184 }
00185 
00186 // Send broadcast message with current data
00187 void SendInfoBroadcast()
00188 {
00189     led3 = true;
00190     
00191     // Init the sending socket
00192     sendUDPSocket.init();
00193     sendUDPSocket.set_broadcasting();
00194     broadcastEndpoint.set_address("255.255.255.255", BROADCAST_PORT);
00195         
00196     // Format the message
00197     GenBroadcastMessage();
00198             
00199     // Send
00200     int bytesToSend = strlen(broadcastMsgBuffer);
00201     int rslt = sendUDPSocket.sendTo(broadcastEndpoint, broadcastMsgBuffer, bytesToSend);
00202     if (rslt == bytesToSend)
00203     {
00204         logger.LogDebug("Broadcast (len %d) Sent ok %s", bytesToSend, broadcastMsgBuffer);
00205     }
00206     else if (rslt == -1)
00207     {
00208         logger.LogDebug("Broadcast Failed to send %s", broadcastMsgBuffer);
00209     }
00210     else
00211     {
00212         logger.LogDebug("Broadcast Didn't send all of %s", broadcastMsgBuffer);
00213     }
00214     
00215     // Log the data
00216     logger.LogData(broadcastMsgBuffer);
00217     
00218     led3 = false;
00219 }
00220 
00221 char* getCurDataCallback(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
00222                 int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
00223 {
00224     // Format message
00225     GenBroadcastMessage();
00226     return broadcastMsgBuffer;
00227 }
00228 
00229 char* setGasUseCallback(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
00230                 int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
00231 {
00232     logger.LogDebug("Setting gas use count %s", argStr);
00233     int newGasUse = 0;
00234     char* eqStr = strchr(argStr, '=');
00235     if (eqStr == NULL)
00236         return "SetGasValue FAILED";
00237     sscanf(eqStr+1, "%d", &newGasUse);
00238     gasUseCounter.SetCount(newGasUse);
00239     return "SetGasValue OK";
00240 }
00241 
00242 // Handle SD Card File Delete
00243 char* sdCardDelete(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
00244                 int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
00245 {
00246     printf("Delete file %s\r\n", argStr);
00247     sdCardMutex.lock();
00248     char* baseWebFolder = "/sd/";
00249     char filename[MAX_FNAME_LENGTH+1];
00250     sprintf(filename, "%s%s", baseWebFolder, argStr);
00251     printf("Full filename for delete is %s\r\n", filename);
00252     bool delOk = (remove(filename) == 0);
00253     sdCardMutex.unlock();
00254     if (delOk)
00255         return "Delete OK";
00256     return "Delete Failed";
00257 }
00258 
00259 // Handle SD Card File Upload
00260 char* sdCardUpload(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
00261                 int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
00262 {
00263     printf("Upload file %s PayloadLen %d ContentLen %d MsgLen %d\r\n", argStr, payloadLen, contentLen, msgLen);
00264     if (payloadLen == 0)
00265     {
00266         printf("Upload file - payload len == 0 - quitting\r\n");
00267         return "OK";
00268     }
00269     
00270     bool writeSuccess = false;
00271     if (splitPayloadPos == 0)
00272     {
00273         // Get the filename
00274         fileNameForWrite[0] = '\0';
00275         fileRemainingForWrite = 0;
00276         char* filenameText = " filename=\"";
00277         char* pFilename = strstr((char*)pPayload, filenameText);
00278         if ((pFilename != NULL) && (pFilename - ((char*)pPayload) < 200))
00279         {
00280             pFilename += strlen(filenameText);
00281             char* pEndFileName = strstr(pFilename, "\"");
00282             if (pEndFileName != NULL)
00283             {
00284                 int fileNameLen = pEndFileName - pFilename;
00285                 if (fileNameLen + strlen(baseWebFolder) < MAX_FNAME_LENGTH)
00286                 {
00287                     strcpy(fileNameForWrite, baseWebFolder);
00288                     strncpy(fileNameForWrite+strlen(baseWebFolder), pFilename, fileNameLen);
00289                     fileNameForWrite[fileNameLen+strlen(baseWebFolder)] = '\0';
00290                     printf("Upload file - filename %s\r\n", fileNameForWrite);
00291                 }
00292                 else
00293                 {
00294                     printf("Upload file - filename too long - quitting\r\n");
00295                 }
00296             }
00297             else
00298             {
00299                 printf("Upload file - end of filename not found - quitting\r\n");
00300             }
00301         }
00302         else
00303         {
00304             printf("Upload file - filename not found (or not near start of message) - quitting\r\n");
00305         }
00306         // Write first chunk to file
00307         if (strlen(fileNameForWrite) > 0)
00308         {
00309             // Find the chunk start
00310             char* chunkStartText = "\r\n\r\n";
00311             char* pChunk = strstr((char*)pPayload, chunkStartText);
00312             if ((pChunk != NULL) && (pChunk - ((char*)pPayload) < 300))
00313             {
00314                 pChunk += strlen(chunkStartText);
00315                 // Find chunk len
00316                 int headerLen = pChunk - ((char*)pPayload);
00317                 int chunkLen = payloadLen - headerLen;
00318                 fileRemainingForWrite = contentLen - headerLen;
00319                 int numBytesToWrite = chunkLen;
00320                 if (fileRemainingForWrite > 0)
00321                 {
00322                     // Check for end boundary of data
00323                     if ((fileRemainingForWrite - chunkLen < 100) && (payloadLen > 50))
00324                     {
00325                         char* pEndForm = strstr(((char*)pPayload) + payloadLen - 50, "\r\n------");
00326                         if (pEndForm != NULL)
00327                         {
00328                             numBytesToWrite = pEndForm - pChunk;
00329                             printf("Upload file - payload end found writing %d bytes\r\n", numBytesToWrite);
00330                         }
00331                     }
00332                     // Write chunk to file
00333                     sdCardMutex.lock();    
00334                     FILE* fp = fopen(fileNameForWrite, "w+");
00335                     if (fp == NULL)
00336                     {
00337                         printf("Upload file - Failed to open output file\r\n");
00338                     }
00339                     else
00340                     {
00341                         fwrite(pChunk, sizeof(char), numBytesToWrite, fp);
00342                         fclose(fp);
00343                         printf("Upload file - written %d bytes\r\n", numBytesToWrite);
00344                         writeSuccess = true;
00345                     }
00346                     sdCardMutex.unlock();
00347                     // Reduce remaining bytes
00348                     fileRemainingForWrite -= chunkLen;
00349                 }
00350                 else
00351                 {
00352                     printf("Upload file - file remaining for write <= 0 - quitting\r\n");
00353                 }
00354             }
00355             else
00356             {
00357                 printf("Upload file - can't find chunk start - quitting\r\n");
00358             }
00359         }
00360         else
00361         {
00362             printf("Upload file - filename blank - quitting\r\n");
00363         }
00364 
00365     }
00366     else
00367     {
00368         if (strlen(fileNameForWrite) != 0)
00369         {
00370             if (fileRemainingForWrite > 0)
00371             {
00372                 int numBytesToWrite = payloadLen;
00373                 // Check for end boundary of data
00374                 if ((fileRemainingForWrite - payloadLen < 100) && (payloadLen > 50))
00375                 {
00376                     char* pEndForm = strstr(((char*)pPayload) + payloadLen - 50, "\r\n------");
00377                     if (pEndForm != NULL)
00378                     {
00379                         numBytesToWrite = pEndForm - ((char*)pPayload);
00380                         printf("Upload file - payload end found writing %d bytes\r\n", numBytesToWrite);
00381                     }
00382                 }
00383                 sdCardMutex.lock();    
00384                 FILE* fp = fopen(fileNameForWrite, "a");
00385                 if (fp == NULL)
00386                 {
00387                     printf("Failed to open output file\r\n");
00388                 }
00389                 else
00390                 {
00391                     fwrite(pPayload, sizeof(char), numBytesToWrite, fp);
00392                     fclose(fp);
00393                     writeSuccess = true;
00394                     printf("Upload file - written %d bytes\r\n", numBytesToWrite);
00395                 }
00396                 sdCardMutex.unlock();
00397                 // Reduce remaining bytes
00398                 fileRemainingForWrite -= payloadLen;
00399             }
00400             else
00401             {
00402                 printf("Upload file - file remaining for write <= 0 - quitting\r\n");
00403             }
00404         }
00405         else
00406         {
00407             printf("Upload file - filename blank - quitting\r\n");
00408         }
00409     }
00410     
00411     // Return results
00412     if (writeSuccess)
00413         return "Write OK";
00414     return "Write Failed";
00415 }
00416 
00417 // Create, configure and run the web server
00418 void http_thread(void const* arg)
00419 {
00420     RdWebServer webServer(&sdCardMutex);
00421     webServer.addCommand("", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "index.htm", false);
00422     webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, NULL, true);
00423     webServer.addCommand("listfiles", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "/", false);
00424     webServer.addCommand("getcurdata", RdWebServerCmdDef::CMD_CALLBACK, &getCurDataCallback);
00425     webServer.addCommand("setgascount", RdWebServerCmdDef::CMD_CALLBACK, &setGasUseCallback);
00426     webServer.addCommand("delete", RdWebServerCmdDef::CMD_CALLBACK, &sdCardDelete);
00427     webServer.addCommand("upload", RdWebServerCmdDef::CMD_CALLBACK, &sdCardUpload);
00428     webServer.init(WEBPORT, &led4, baseWebFolder);
00429     webServer.run();
00430 }
00431 
00432 // Network time protocol (NTP) thread to get time from internet
00433 void ntp_thread(void const* arg)
00434 {
00435     while (1)
00436     {
00437         logger.LogDebug("Trying to update time...");
00438         if (ntp.setTime("0.pool.ntp.org") == NTP_OK)
00439         {
00440             osDelay(1000);  // This delay is simply to try to improve printf output
00441             logger.LogDebug("Set time successfully");
00442             time_t ctTime;
00443             ctTime = time(NULL);
00444             logger.LogDebug("Time is set to (UTC): %s", ctime(&ctTime));
00445         }
00446         else
00447         {
00448             logger.LogDebug("Cannot set from NTP");
00449         }
00450         
00451         // Refresh time every K hours
00452         for (int k = 0; k < NTP_REFRESH_INTERVAL_HOURS; k++)
00453         {
00454             // 1 hour
00455             for (int i = 0; i < 60; i++)
00456             {
00457                 for (int j = 0; j < 60; j++)
00458                 {
00459                     osDelay(1000);
00460                 }
00461                 logger.LogDebug("%d mins to next NTP time refresh", (NTP_REFRESH_INTERVAL_HOURS-k-1)*60 + (59-i));
00462             }
00463         }
00464     }
00465 }
00466 
00467 // #define TEST_WATCHDOG 1
00468 #ifdef TEST_WATCHDOG
00469 int watchdogTestLoopCount = 0;
00470 #endif
00471 
00472 // Main
00473 int main()
00474 {
00475     pc.baud(115200);
00476     logger.SetDebugDest(true, true);
00477     logger.LogDebug("\r\n\r\nGas Monitor V2 - Rob Dobson 2015");
00478 
00479     // Initialise thermometers
00480     thermometers.Init();
00481     
00482     // Get the current count from the SD Card
00483     gasUseCounter.Init();
00484     
00485     // Setup ethernet interface
00486     char macAddr[6];
00487     mbed_mac_address(macAddr);
00488     logger.LogDebug("Ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]); 
00489     logger.LogDebug("Connecting to ethernet ...");
00490 
00491     // Init ethernet
00492     EthernetInterface::init();
00493 
00494     // Using code described here https://developer.mbed.org/questions/1602/How-to-set-the-TCPIP-stack-s-hostname-pr/
00495     // to setName on the ethernet interface
00496     EthernetInterface::setName(systemName);
00497 
00498     // Connect ethernet
00499     EthernetInterface::connect();
00500     logger.LogDebug("IP Address: %s HostName %s", EthernetInterface::getIPAddress(), EthernetInterface::getName());
00501     
00502     // NTP Time setter
00503     Thread ntpTimeSetter(&ntp_thread);
00504     
00505     // Web Server
00506     Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3));
00507 
00508     // Store reason for restart
00509     bool watchdogCausedRestart = watchdog.WatchdogCausedRestart();
00510     bool restartCauseRecorded = false;
00511     
00512     // Setup the watchdog for reset
00513     watchdog.SetTimeoutSecs(300);
00514     
00515     // Time of last broadcast
00516     time_t timeOfLastBroadcast = time(NULL);
00517     const int TIME_BETWEEN_BROADCASTS_IN_SECS = 60;
00518     while(true)
00519     {
00520         // Check if we can record the reason for restart (i.e. if time is now set)
00521         if (!restartCauseRecorded)
00522         {
00523             time_t nowTime = time(NULL);
00524             if (nowTime > 1000000000)
00525             {
00526                 // Record the reason for restarting in the log file
00527                 if (watchdogCausedRestart)
00528                 {
00529                     logger.LogEvent("Watchdog Restart");
00530                     logger.LogDebug("Watchdog Restart");
00531                 }
00532                 else
00533                 {
00534                     logger.LogEvent("Normal Restart");
00535                     logger.LogDebug("Normal Restart");
00536                 }
00537                 restartCauseRecorded = true;
00538             }
00539         }
00540         
00541         // Loop delay
00542         osDelay(LOOP_DELAY_IN_MS);
00543         
00544         // Feed the watchdog and show the flashing LED
00545         led1 = !led1;
00546         watchdog.Feed();
00547 
00548         // Service gas count
00549         if (gasUseCounter.Service())
00550         {
00551             SendInfoBroadcast();
00552             timeOfLastBroadcast = time(NULL);
00553         }
00554         
00555         // Service thermometers
00556         thermometers.Service();
00557         
00558         // Check if ready for a broadcast
00559         if ((time(NULL) - timeOfLastBroadcast) >= TIME_BETWEEN_BROADCASTS_IN_SECS)
00560         {
00561             SendInfoBroadcast();
00562             timeOfLastBroadcast = time(NULL);
00563         }
00564         
00565         // Service volt alerters
00566         voltAlerter1.Service();
00567         voltAlerter2.Service();
00568         voltAlerter3.Service();
00569         
00570         // Set LED2 to the state of the first volt alerter
00571         led2 = voltAlerter1.GetState();
00572         
00573 #ifdef TEST_WATCHDOG
00574         // After about 20 seconds of operation we'll hang to test the watchdog
00575         if (watchdogTestLoopCount++ > 80)
00576         {
00577             // This should cause watchdog to kick in and reset
00578             osDelay(20000);
00579         }
00580 #endif
00581     }
00582 }
00583