Penn Electric Racing / Mbed 2 deprecated SystemManagement

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers inCommands.cpp Source File

inCommands.cpp

00001 #include "inCommands.h"
00002 #include "runTime.h"
00003 #include "inMacros.h"
00004 
00005 // Compare string to a word in the serial input, shorter to type
00006 #define CMP(w, string)                                              if (!strcasecmp(word[w-1], string))
00007 
00008 // Serial input
00009 int serviceSerial()
00010 {
00011     static int end = 0;                              // End of string position
00012 
00013     int c=0;
00014     if (pc.readable()) c = pc.getc();
00015     if (c == -1 || c == 0) return 0;
00016 
00017     char b = c;                                      // Casted to char type
00018     bool process = false;                            // Is string complete (ready to parse)?
00019 
00020     if (b == '\n' || b == '\r') {                    // Enter key was pressed, dump for processing
00021         tempData.inputStr[end] = 0;                  // Null terminate
00022         end = 0;                                     // Reset to start
00023         process = true;                              // Flag for processing
00024 
00025     } else if (b == '\b' || b == 127) {              // Backspace or delete
00026         if (end > 0) end--;                          // Move back one char
00027         tempData.inputStr[end] = 0;                  // Erase char
00028 
00029     } else if (b > 31 && b < 127) {                  // New valid displayable char
00030         tempData.inputStr[end++] = b;                // Add to buffer
00031         tempData.inputStr[end] = 0;                  // Add null terminator
00032         if (end >= RX_SIZE) {
00033             end = 0;                                 // Reset end location
00034             process = true;                          // Flag for processing
00035         }
00036     }
00037     // Continue to parsing section only if flagged as complete and string not empty
00038     if (!process || strlen((char*)tempData.inputStr) == 0) return 0;
00039 
00040     static char word[3][RX_SIZE+1];                  // Hold 3 words
00041     int pieces = sscanf(tempData.inputStr, "%s %s %s", word[0], word[1], word[2]);      // Populate words
00042     tempData.inputStr[0] = 0;                        // Empty the string displayed on screen
00043     char *next;                                      // Used by strtof and strtoul
00044 
00045     // One word commands
00046     if (pieces == 1) {
00047         // Reset the microcontroller
00048         CMP(1, "reset") {
00049             NVIC_SystemReset();
00050             return 1;
00051         }
00052         // Clear non-volatile fault flags, then reset the microcontroller
00053         CMP(1, "resetClear") {
00054             runTime::clearFaults();
00055             NVIC_SystemReset();
00056             return 1;
00057         }
00058         return -1;
00059     }
00060     // Two word commands
00061     if (pieces == 2) {
00062         // Change the serial dashboard display mode
00063         CMP(1, "display") {
00064             CMP(2, "compact") {
00065                 if (param->change_extendedSerial(0)) {
00066                     op->profileModded=true;
00067                     return 1;  // Change function defined by macro
00068                 }
00069             }
00070             CMP(2, "extended") {
00071                 if (param->change_extendedSerial(1)) {
00072                     op->profileModded=true;
00073                     return 1;  // Change function defined by macro
00074                 }
00075             }
00076         }
00077         // Change ACK setting for CAN (noAck allows testing of CAN without other nodes)
00078         CMP(1, "CANack") {
00079             CMP(2, "noack") {                           // NoAck test mode
00080                 if (param->change_CANnoAck(1)) {
00081                     op->profileModded=true;
00082                     return 1;
00083                 }
00084             }
00085             CMP(2, "ack") {                             // Normal CAN protocol
00086                 if (param->change_CANnoAck(0)) {
00087                     op->profileModded=true;
00088                     return 1;
00089                 }
00090             }
00091             return -1;
00092         }
00093         // Artificially capture a freeze frame, save to flash
00094         CMP(1, "capture") {
00095             CMP(2, "freeze") {
00096                 if (!FreezeFrame::getError()) {         // Only allow capture if spot available (last error frame was cleared manually)
00097                     if (FreezeFrame::writeFrame()) {    // Copy RAM frame to freezree sector in flash
00098                         return 1;
00099                     }
00100                 }
00101             }
00102             return -1;
00103         }
00104         // Clear fault conditions
00105         CMP(1, "clear") {
00106             CMP(2, "freeze") {                          // Clear the freeze frame (mark as read)
00107                 FreezeFrame::clearError();              // Clear the non-volatile error marker
00108                 return 1;
00109             }
00110             CMP(2, "faults") {                          // Clear everything
00111                 runTime::clearFaults();
00112                 return 1;
00113             }
00114             return -1;
00115         }
00116         // Change the display contents
00117         CMP(1, "view") {
00118             CMP(2, "freeze") {                          // View the last stored freeze frame
00119                 if (FreezeFrame::getFrame(&tempData.freeze)) {  // Fetch the pointer from flash
00120                     return 1;
00121                 }
00122             }
00123             CMP(2, "live") {                            // View live data from RAM
00124                 tempData.freeze = NULL;                 // Zero the pointer
00125                 return 1;
00126             }
00127             return -1;
00128         }
00129         // Artificially update the SOC (battery life %)
00130         CMP(1, "SOC") {
00131             CMP(2, "Reset") {                           // Command was "SOC reset" - reset to 100%, do this after a full charge
00132                 glvBat.resetToSOC(1);
00133                 return 1;
00134             }
00135             float soc = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
00136             if (*next == 0) {
00137                 if (glvBat.resetToSOC(soc)) return 1;
00138             }
00139             return -1;
00140         }
00141         // Artificially update the AmpHours count (linked with SOC)
00142         CMP(1, "Ah") {
00143             CMP(2, "Reset") {                           // Command was "Amphours reset", equivalent to "SOC reset"
00144                 glvBat.resetToSOC(1);
00145                 return 1;
00146             }
00147             float ah = strtod(word[1], &next);          // Command was "Amphours xxx" where xxx is a float in amphours
00148             if (*next == 0) {
00149                 if (glvBat.resetToAh(ah)) return 1;
00150             }
00151             return -1;
00152         }
00153         // Change the battery capacity setting for calculating Amphours
00154         CMP(1, "Capacity") {
00155             float cap = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
00156             if (*next == 0) {
00157                 if (glvBat.changeCapacity(cap)) return 1;
00158             }
00159             return -1;
00160         }
00161 
00162         bool parsed=false;
00163 
00164         CHANGE_VAR("GLVchar",       chargeCurrent)
00165         CHANGE_VAR("GLVdisch",      dischargeCurrent)
00166         CHANGE_VAR("GLVnomCap",     nominalCapacity)
00167         CHANGE_VAR("GLVtaps",       glvBat_taps)
00168         CHANGE_VAR("dcdcThres",     dcdcThreshold)
00169         CHANGE_VAR("dcdcOver",      dcdcOverCurrent)
00170         CHANGE_VAR("dcdcStart",     dcdcStartDelay)
00171         CHANGE_VAR("dcdcStop",      dcdcStopDelay)
00172         CHANGE_VAR("dcdcTaps",      dcdc_taps)
00173         CHANGE_VAR("imdStart",      imdStartDelay)
00174         CHANGE_VAR("amsStart",      amsStartDelay)
00175         CHANGE_VAR("IntOverT",      internalOverTemp)
00176         CHANGE_VAR("CANtxSize",     CANtxSize)
00177         CHANGE_VAR("CANrxSize",     CANrxSize)
00178         CHANGE_VAR("SerialBaud",    SerialBaud)
00179         CHANGE_VAR("SerialTx",      SerialTxSize)
00180         CHANGE_VAR("CANack",        CANnoAck)
00181 
00182         if (!parsed) return -1;
00183 
00184         CMP(1, "GLVnomCap")             return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
00185         CMP(1, "GLVtaps")               return glvBat.size(param->glvBat_taps)?1:-1;
00186         CMP(1, "dcdcTaps")              return dcdc.size(param->dcdc_taps)?1:-1;
00187         CMP(1, "CANtxSize")             return can.txSize(param->CANtxSize)?1:-1;
00188         CMP(1, "CANrxSize")             return can.rxSize(param->CANrxSize)?1:-1;
00189         CMP(1, "SerialBaud")            pc.baud(param->SerialBaud);
00190         CMP(1, "SerialTx")              return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
00191 
00192         return 1;
00193     }
00194     // Three word commands
00195     if (pieces == 3) {
00196         // Fan Duty
00197         CMP(1, "Fan") {
00198             float val1 = strtod(word[1], &next);
00199             if (*next == 0) {
00200                 float val2 = strtod(word[2], &next);
00201                 if (*next == 0) {
00202                     op->dcdc.request.fan1 = val1;
00203                     op->dcdc.request.fan2 = val2;
00204                     return 1;
00205                 }
00206             }
00207             return -1;
00208         }
00209 
00210         // Pump Duty
00211         CMP(1, "Pump") {
00212             float val1 = strtod(word[1], &next);
00213             if (*next == 0) {
00214                 float val2 = strtod(word[2], &next);
00215                 if (*next == 0) {
00216                     op->dcdc.request.pump1 = val1;
00217                     op->dcdc.request.pump2 = val2;
00218                     return 1;
00219                 }
00220             }
00221             return -1;
00222         }
00223         // Set the system time (RTC)
00224         CMP(1, "Time") {
00225             struct tm t;    // Time & date struct
00226             int ret = sscanf(word[1], "%d/%d/%d", &t.tm_mon, &t.tm_mday, &t.tm_year);   // Populate date
00227             t.tm_year = t.tm_year - 1900;       // Year mod to fix 0 index
00228             t.tm_mon = t.tm_mon - 1;            // Month mod to fix 0 index
00229             if (ret == 3) {                     // All 3 items found
00230                 ret = sscanf(word[2], "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);    // Populate time
00231                 if (ret == 3) {                 // All 3 items found
00232                     set_time(mktime(&t));       // Set the RTC
00233                     time_t diff = time(NULL) - op->SysTime;     // Get change in time from old to new
00234                     op->startTime += diff;                      // Shift the startTime to new timebase
00235                     return 1;
00236                 }
00237             }
00238             return -1;
00239         }
00240 
00241         // Profile manipulations between RAM and flash
00242         CMP(1, "Profile") {
00243             // Write, copy RAM to a flash sector
00244             CMP(2, "Write") {
00245                 unsigned int index = strtoul(word[2], &next, 10);               // Get index from command "profile write xxx"
00246                 if (index <= NUM_STORED_PROFILES && index > 0 && *next == 0) {  // Check within bounds
00247                     bool s = Profile::saveProfile(index);                       // Write to flash
00248                     if (s) {                                                    // Successful?
00249                         op->profileModded = false;                              // Mark it as a fresh, unmodified profile
00250                         op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
00251                         return 1;
00252                     }
00253                 }
00254                 return -1;
00255             }
00256             // Load, read from flash to RAM
00257             CMP(2, "Load") {
00258                 CMP(3, "default") {                                             // The hard-coded flash profile (found in FreezeFrame.cpp)
00259                     Profile::loadProfile(0);                                    // Copy default to RAM
00260                     op->profileIndex = Profile::usingProfile();                 // Change the currently loaded profile marker
00261                     op->profileModded = false;                                  // Mark it as a fresh, unmodified profile
00262                     return 1;
00263                 }
00264                 CMP(3, "freeze") {                                              // Get the profile portion of the last freeze frame captured
00265                     if(Profile::loadProfile(-1)) {                              // Attemp to retrieve and copy to RAM
00266                         op->profileModded = false;                              // Mark it as a fresh, unmodified profile
00267                         op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
00268                         return 1;
00269                     }
00270                 }
00271                 int index = strtol(word[2], &next, 10);                          // Command was "profile load xxx"
00272                 if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
00273                     if (Profile::loadProfile(index)) {                           // Attempt to retrieve and copy to RAM
00274                         op->profileModded = false;                               // Mark it as a fresh, unmodified profile
00275                         op->profileIndex = Profile::usingProfile();              // Change the currently loaded profile marker
00276                         return 1;
00277                     }
00278                 }
00279                 return -1;
00280             }
00281             // View the profile only (NOT loaded to RAM, just display changed)
00282             CMP(2, "view") {
00283                 CMP(3, "default") {                                             // View the hard-coded flash profile
00284                     if (Profile::getProfile(&tempData.viewProfile, 0)) {        // Attempt to fetch pointer
00285                         tempData.viewProfileNum = 0;                            // Mark the index of the fetched profile
00286                         return 1;
00287                     }
00288                 }
00289                 CMP(3, "freeze") {                                              // View the profile portion only of the last captured freeze frame
00290                     if (Profile::getProfile(&tempData.viewProfile, -1)) {       // Attempt to fetch pointer
00291                         tempData.viewProfileNum = -1;                           // Mark the index of the fetched profile
00292                         return 1;
00293                     }
00294                 }
00295                 CMP(3, "live") {                                                // Revert to normal, live view
00296                     tempData.viewProfileNum = -2;                               // Mark live
00297                     tempData.viewProfile = NULL;                                // Clear the pointer
00298                     return 1;
00299                 }
00300 
00301                 int index = strtol(word[2], &next, 10);                          // Command was "profile view xxx"
00302                 if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
00303                     if (Profile::getProfile(&tempData.viewProfile, index)) {     // Attempt to fetch pointer
00304                         tempData.viewProfileNum = index;                         // Mark the index of the fetched profile
00305                         return 1;
00306                     }
00307                 }
00308                 return -1;
00309             }
00310         }
00311     }
00312     return -1;
00313 }
00314 // Called when AMS AIRs Mode message stops coming in
00315 Timeout timer_AIRS_CLOSED;
00316 void timeout_AIRS_CLOSED()
00317 {
00318     op->signals  &= ~AIRS_CLOSED;
00319 }
00320 
00321 // Called when Charger CAN messages stop coming in
00322 Timeout timer_CHARGER_DET;
00323 void timeout_CHARGER_DET()
00324 {
00325     op->signals  &= ~CHARGER_DET;
00326 }
00327 
00328 // Called when PCM messages stop coming in
00329 Timeout timer_FANS;
00330 void timeout_FANS()
00331 {
00332     op->dcdc.request.fan1  = 0;
00333     op->dcdc.request.fan2  = 0;
00334 }
00335 Timeout timer_PUMPS;
00336 void timeout_PUMPS()
00337 {
00338     op->dcdc.request.pump1  = 0;
00339     op->dcdc.request.pump2  = 0;
00340 }
00341 
00342 #define REFRESH_TIMEOUT(NAME)           \
00343 timer_##NAME.detach();                  \
00344 timer_##NAME.attach(&timeout_##NAME, CAN_DEVICE_TIMEOUT);
00345 
00346 bool serviceCAN(CANMessage* fromXbee)
00347 {
00348     CANMessage msg;
00349     if (fromXbee != NULL) {
00350         memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
00351     } else {
00352         if (!can.rxRead(msg)) return false;
00353     }
00354     xbee.receive(msg);
00355 
00356     // Redirect global car reset
00357     if (msg.id == GLOBAL_CAR_RESET_RX_ID) msg.id = RESETCLEAR_RX_ID;
00358 
00359     switch (msg.id) {
00360             // Reset microntroller
00361         case (RESET_RX_ID):
00362             if (msg.len == 0) {     // Length must = 0
00363                 NVIC_SystemReset();
00364                 CAN_SUCCESS
00365             }
00366             CAN_FAIL
00367 
00368 
00369             // Clear non-volatile fault flags, then reset microcontroller
00370         case (RESETCLEAR_RX_ID):
00371             if (msg.len == 0) {                 // Length must = 0
00372                 FreezeFrame::clearError();
00373                 NVIC_SystemReset();
00374                 CAN_SUCCESS
00375             }
00376             CAN_FAIL
00377             
00378             // Clear fault conditions
00379         case (CLEAR_RX_ID):
00380             if (msg.len == 0) {                         // No data byte
00381                 runTime::clearFaults();
00382                 op->faultCode = 0;
00383                 CAN_SUCCESS
00384             }
00385             CAN_FAIL
00386 
00387             // Set the time (RTC)
00388         case (TIME_RX_ID):
00389             if (msg.len == 6*sizeof(char)) {                    // 6 Bytes
00390                 struct tm t;                                    // Time & date struct
00391                 t.tm_mon  = msg.data[0];                        // Month in byte[0]
00392                 t.tm_mday = msg.data[1];                        // Day
00393                 t.tm_year = msg.data[2];                        // Year (offset from 2000)
00394                 t.tm_year = t.tm_year - 1900 + 2000;            // Apply year index mod and offset
00395                 t.tm_mon  = t.tm_mon - 1;                       // Month index mod
00396                 t.tm_hour = msg.data[3];                        // Get hour of time in byte[3] (24 hr format)
00397                 t.tm_min  = msg.data[4];                        // Minutes
00398                 t.tm_sec  = msg.data[5];                        // Seconds
00399                 set_time(mktime(&t));                           // Set RTC
00400                 time_t diff = time(NULL) - op->SysTime;         // Old time to new time change
00401                 op->startTime += diff;                          // Shift the startTime to new timebase
00402                 CAN_SUCCESS
00403             }
00404             CAN_FAIL
00405 
00406             // RAM and flash profile manipulations
00407         case (PROFILE_RX_ID):
00408             if (msg.len == 2*sizeof(char)) {                    // 2 command bytes
00409                 int index=-2;
00410                 for (int i = 0; i < NUM_STORED_PROFILES+1; i++) {   // Get the profile number
00411                     if (msg.data[1] == (1<<i))  index=i;
00412                 }
00413                 if (msg.data[1] == 1<<6)   index=-1;                // Special case for Freeze
00414                 if (index == -2) {                              // Not matched to anything, fail
00415                     CAN_FAIL
00416                 }
00417                 if (msg.data[0] == 0) {                         // Load profile from a flash location to RAM
00418                     if (Profile::loadProfile(index)) {          // Attempt to load (copy flash to RAM)
00419                         op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
00420                         op->profileModded = false;                      // Mark it as a fresh, unmodified profile
00421                         CAN_SUCCESS
00422                     }
00423                 }
00424                 if (msg.data[0] == 1) {                         // Write profile to flash from RAM
00425                     bool s = Profile::saveProfile(index);       // Write profile to flash slot
00426                     if (s) {
00427                         op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
00428                         op->profileModded = false;                      // Mark it as a fresh, unmodified profile
00429                         CAN_SUCCESS
00430                     }
00431                 }
00432             }
00433             CAN_FAIL
00434 
00435         case FAN_CONTROL_RX_ID:
00436             if (msg.len != 2*sizeof(float)) return false;
00437             REFRESH_TIMEOUT(FANS)
00438             op->dcdc.request.fan1 = *((float*)((void*)(&msg.data[0])));
00439             op->dcdc.request.fan2 = *((float*)((void*)(&msg.data[4])));
00440             return true;
00441 
00442         case PUMP_CONTROL_RX_ID:
00443             if (msg.len != 2*sizeof(float)) return false;
00444             REFRESH_TIMEOUT(PUMPS)
00445             op->dcdc.request.pump1 = *((float*)((void*)(&msg.data[0])));
00446             op->dcdc.request.pump2 = *((float*)((void*)(&msg.data[4])));
00447             return true;
00448 
00449         case AMS_MODE_RX_ID:
00450             if (msg.len != sizeof(char)) return false;
00451             REFRESH_TIMEOUT(AIRS_CLOSED)
00452             if (msg.data[0] & 1<<3) {     // AIRs closed?
00453                 op->signals |= AIRS_CLOSED;
00454             } else {
00455                 op->signals &= ~AIRS_CLOSED;
00456             }
00457             return true;
00458         case CHARGER_ERR_RX_ID:
00459             REFRESH_TIMEOUT(CHARGER_DET)
00460             op->signals |= CHARGER_DET;
00461             return true;
00462         default:
00463             break;
00464     }
00465     bool parsed=false;
00466 
00467     CAN_CHANGE(chargeCurrent,           PROFILE_CHARGECURRENT_RX_ID     )
00468     CAN_CHANGE(dischargeCurrent,        PROFILE_DISCHARGECURRENT_RX_ID  )
00469     CAN_CHANGE(nominalCapacity,         PROFILE_NOMINALCAPACITY_RX_ID   )
00470     CAN_CHANGE(glvBat_taps,             PROFILE_GLVBATTAPS_RX_ID        )
00471     CAN_CHANGE(dcdcThreshold,           PROFILE_DCDCONTHRESHOLD_RX_ID   )
00472     CAN_CHANGE(dcdcOverCurrent,         PROFILE_DCDCOVERCURRENT_RX_ID   )
00473     CAN_CHANGE(dcdcStartDelay,          PROFILE_DCDCSTARTDELAY_RX_ID    )
00474     CAN_CHANGE(dcdcStopDelay,           PROFILE_DCDCSTOPDELAY_RX_ID     )
00475     CAN_CHANGE(dcdc_taps,               PROFILE_DCDC_TAPS_RX_ID         )
00476     CAN_CHANGE(imdStartDelay,           PROFILE_IMDSTARTDELAY_RX_ID     )
00477     CAN_CHANGE(amsStartDelay,           PROFILE_AMSSTARTDELAY_RX_ID     )
00478     CAN_CHANGE(internalOverTemp,        PROFILE_INTERNALOVERTEMP_RX_ID  )
00479     CAN_CHANGE(CANnoAck,                PROFILE_CANNOACK_RX_ID          )
00480     CAN_CHANGE(extendedSerial,          PROFILE_EXTENDSERIAL_RX_ID      )
00481     CAN_CHANGE(CANtxSize,               PROFILE_CANTXSIZE_RX_ID         )
00482     CAN_CHANGE(CANrxSize,               PROFILE_CANRXSIZE_RX_ID         )
00483     CAN_CHANGE(SerialBaud,              PROFILE_SERIALBAUD_RX_ID        )
00484     CAN_CHANGE(SerialTxSize,            PROFILE_SERIALTXSIZE_RX_ID      )
00485 
00486     if (!parsed) return false;
00487 
00488     if (msg.id == PROFILE_NOMINALCAPACITY_RX_ID )   return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
00489     if (msg.id == PROFILE_GLVBATTAPS_RX_ID      )   return glvBat.size(param->glvBat_taps)?1:-1;
00490     if (msg.id == PROFILE_DCDC_TAPS_RX_ID       )   return dcdc.size(param->dcdc_taps)?1:-1;
00491     if (msg.id == PROFILE_CANTXSIZE_RX_ID       )   return can.txSize(param->CANtxSize)?1:-1;
00492     if (msg.id == PROFILE_CANRXSIZE_RX_ID       )   return can.rxSize(param->CANrxSize)?1:-1;
00493     if (msg.id == PROFILE_SERIALBAUD_RX_ID      )   pc.baud(param->SerialBaud);
00494     if (msg.id == PROFILE_SERIALTXSIZE_RX_ID    )   return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
00495 
00496     return true;
00497 }
00498 // Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
00499 /*
00500 bool receiveMsgXbee()
00501 {
00502     CANMessage msg;
00503     if (xbeeRelay.receive(msg)) {                       // Incoming CAN message string received
00504         if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT;    // Send it out on the CAN bus
00505         serviceCAN(&msg);                               // Send it into the local serviceCAN routine
00506         return true;
00507     } else return false;
00508 }*/
00509 
00510 void inCommands::thread_getInputs(void const* args)
00511 {
00512     while(1) {
00513         serviceCAN(0);
00514         //receiveMsgXbee();
00515 
00516         int ret = serviceSerial();
00517         if (ret == -1) tempData.parseGoodChar = 'x';
00518         if (ret == 1)  tempData.parseGoodChar = 251;
00519         osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<2);      // Signal watchdog thread
00520     }
00521 }