System Management code

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

Files at this revision

API Documentation at this revision

Comitter:
pspatel321
Date:
Wed Jan 07 03:25:50 2015 +0000
Parent:
33:6bc82b6b62e5
Child:
35:9337ac9f4e1b
Commit message:
Added serial input. Updated glvBat coulomb counter to match AMS, brought in changes to outDiag and inCommands from AMS.

Changed in this revision

IOobjects/IOobjects.cpp Show annotated file Show diff for this revision Revisions of this file
IOobjects/IOobjects.h Show annotated file Show diff for this revision Revisions of this file
Libs/CANBuffer.lib Show annotated file Show diff for this revision Revisions of this file
Libs/CoulombCounter/CoulombCounter.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/CoulombCounter/CoulombCounter.h Show annotated file Show diff for this revision Revisions of this file
Libs/DC_DC/DC_DC.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/XbeeManager/CAN-xbee/CAN-xbee.h Show annotated file Show diff for this revision Revisions of this file
Libs/XbeeManager/XbeeManager.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/XbeeManager/XbeeManager.h Show annotated file Show diff for this revision Revisions of this file
inCommands/inCommands.cpp Show annotated file Show diff for this revision Revisions of this file
inCommands/inCommands.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
outDiagnostics/outDiagnostics.cpp Show annotated file Show diff for this revision Revisions of this file
outDiagnostics/outDiagnostics.h Show diff for this revision Revisions of this file
serviceCAN/serviceCAN.cpp Show diff for this revision Revisions of this file
serviceCAN/serviceCAN.h Show diff for this revision Revisions of this file
--- a/IOobjects/IOobjects.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/IOobjects/IOobjects.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -25,11 +25,10 @@
 Temperature internalTmp(&NXFT15XH103_TABLE, p15);    // Temperature conversion look-up table for internal temperature on the GLV bat charger FET (TABLE PTR, PIN)
 PollSwitch switches(sw, sizeof(sw)/sizeof(sw[0]));   // Shutdown switch sense pins (SWITCH PIN ARRAY, NUM PINS)
 
-CANxbee xbee1(p9,  p10, XBEE_BAUD, XBEE_TX_SIZE, XBEE_RX_SIZE);
-CANxbee xbee2(p13, p14, XBEE_BAUD, XBEE_TX_SIZE, XBEE_RX_SIZE);
-XbeeManager xbeeRelay(&xbee1, &xbee2);
+XbeeManager xbeeRelay(p9, p10, p13, p14, XBEE_BAUD, XBEE_TX_SIZE, XBEE_RX_SIZE);
 //XbeeRelay xbee;
 
 DigitalOut extras[] = {(p16), (p17)};                // Unused analog pins driven low
 Inputs data;
-CANinputs CANdata;
\ No newline at end of file
+CANinputs CANdata;
+TempData tempData;
\ No newline at end of file
--- a/IOobjects/IOobjects.h	Tue Jan 06 20:45:26 2015 +0000
+++ b/IOobjects/IOobjects.h	Wed Jan 07 03:25:50 2015 +0000
@@ -77,8 +77,13 @@
     float dcdcPump1Duty;
     float dcdcPump2Duty;
 };
+class TempData {
+public:
+    char inputStr[RX_SIZE+1];
+    char parseGoodChar;
+};
 
 extern CANinputs CANdata;
 extern Inputs data;
-
+extern TempData tempData;
 #endif
\ No newline at end of file
--- a/Libs/CANBuffer.lib	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/CANBuffer.lib	Wed Jan 07 03:25:50 2015 +0000
@@ -1,1 +1,1 @@
-http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#4baa7251c6c0
+http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#533722a1a6cf
--- a/Libs/CoulombCounter/CoulombCounter.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -5,9 +5,6 @@
 const float BAT_ISENSE_OFFSET = -0.5*BAT_ISENSE_MULTIPLIER; // Offset to convert float to amps
 const float BAT_ISENSE_LIMS = 3.0;                          // Over-current limit = +/- 3A
 
-const int rtcGPREG_counter = 0;     // RTC GPREG offset for the coulomb counter
-const int rtcGPREG_capacity = 1;    // RTC GPREG offset for the capacity spec
-
 CoulombCounter::CoulombCounter(PinName _pin, int _mSec) : BatISense(_pin) {
     mSec = _mSec;
     
@@ -54,15 +51,7 @@
     tracker++;
     if (tracker >= CC_FILTER_TAPS) tracker = 0;
 }
-void CoulombCounter::resetToSOC(float SOC) { 
-    store.write(SOC*capacity(), rtcGPREG_counter);
-}
-void CoulombCounter::resetToAh(float Ah) {
-    store.write(Ah, rtcGPREG_counter);
-}
-void CoulombCounter::changeCapacity(float capAh) {
-    store.write(capAh, rtcGPREG_capacity);
-}
+
 float CoulombCounter::current() { 
     float avg = 0;
     for (int i = 0; i < CC_FILTER_TAPS; i++) {
@@ -72,15 +61,7 @@
     if (abs(avg) > BAT_ISENSE_LIMS) overCurrent = true;
     return avg;
 }
-float CoulombCounter::ampHours() {
-    return store.read(rtcGPREG_counter);
-}
-float CoulombCounter::capacity() {
-    return store.read(rtcGPREG_capacity);
-}
-float CoulombCounter::SOC() {
-    return ampHours()/capacity();
-}
+
 bool CoulombCounter::overCurrentDetected() {
-    return overCurrent;    
+    return overCurrent;
 }
\ No newline at end of file
--- a/Libs/CoulombCounter/CoulombCounter.h	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.h	Wed Jan 07 03:25:50 2015 +0000
@@ -5,31 +5,43 @@
 #include "CANBuffer.h"
 #include "RTCStore.h"
 
-const float defaultAh = 1.5;        // Default amphours of battery, in case RTC read is bad/empty
+const float defaultAh = 1.5;        // Default amphours of battery, in case store read is bad/empty
 const float defaultSOC = 0.5;
 const int CC_FILTER_TAPS = 10;
+const int rtcGPREG_counter = 0;                             // rtc GPREG offset for the coulomb counter
+const int rtcGPREG_capacity = 1;                            // rtc GPREG offset for the capacity spec
+const float MIN_CAPACITY_SETTING = 0.5;                       // Lowest allowable capacity setting
+const float MAX_CAPACITY_SETTING = 10;                        // Largest allowable capacity setting
 
 class CoulombCounter {
 public:
 
-    // Configures for a certain pin, millisecond sample period, and which GPREG in RTC to use to store the ampHours
+    // Configures for a certain pin, millisecond sample period, and which GPREG in store to use to store the ampHours
     CoulombCounter(PinName _pin, int _mSec);
     
-    // Allow zeroing the SOC when the battery is fully charged/dead, SOC in % from 0 to 1
-    void resetToSOC(float SOC);
-   
-   // Allow zeroing the SOC (via zeroing the Ah) when the battery is fully charged/dead           
-    void resetToAh(float Ah);
-    
-    // Allow change of capacity spec (changes SOC)
-    void changeCapacity(float capAh);
-    
     bool overCurrentDetected();                                 // Sensor above range
     float current();                                            // Last current reading in Amps
-    float ampHours();
-    float capacity();
-    float SOC();
     void sample();
+    
+    float capacity() { return store.read(rtcGPREG_capacity); }
+    float SOC() { return ampHours()/capacity(); }
+    float ampHours() { return store.read(rtcGPREG_counter); }
+    
+    bool changeCapacity(float _ampHours) {
+        if (_ampHours < MIN_CAPACITY_SETTING || _ampHours > MAX_CAPACITY_SETTING) return false;
+        store.write(_ampHours, rtcGPREG_capacity);
+        return true;
+    }
+    bool resetToSOC(float _SOC) {
+        if (_SOC < 0 || _SOC > 1) return false;
+        store.write(_SOC*capacity(), rtcGPREG_counter);
+        return true;
+    }
+    bool resetToAh(float _ampHours) {
+        if (_ampHours < 0 || _ampHours > capacity()) return false; 
+        store.write(_ampHours, rtcGPREG_counter);
+        return true;
+    }
 
 private:
     //Ticker sampler;         // Used to capture next sample and coulomb count
--- a/Libs/DC_DC/DC_DC.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/DC_DC/DC_DC.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -58,11 +58,8 @@
     // Do nothing if already on
     if (on && dcdcControl == ON) return;
     
-    // Do nothing if error present
-    if (critError) return;
-    
-    // If start requested
-    if (on) {
+    // If start requested and no error
+    if (on && !critError) {
         dcdcControl = ON;
         starting = true;
         startTimer.reset();
@@ -70,6 +67,7 @@
         stopTimer.stop();
         stopTimer.reset();
         stopping = false;
+        
     // If stop requested
     } else {
         stopping=true;
@@ -166,8 +164,8 @@
     if (critError || starting || stopping || !(status & 1) || dcdcControl == OFF) return;
     
     else {
-        if (chan == FAN1) fan1.write(duty);
-        if (chan == FAN2) fan2.write(duty);
+        if (chan == FAN1)  fan1.write(duty);
+        if (chan == FAN2)  fan2.write(duty);
         if (chan == PUMP1) pump1.write(duty);
         if (chan == PUMP2) pump2.write(duty);   
     }
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -1,56 +1,93 @@
 #include "CAN-xbee.h"
 
+// Creates stringified version of CANMessage msg in char* buff (must be at least 15 chars)
+// Returns -1 for bad message, or else returns size of char* buff including the terminating '\n'
+int convert2array(CANMessage &msg, char* buff) {
+    
+    // Check message integrity
+    if (msg.len > 8) return -1;
+    if ((msg.format == CANStandard) && ((msg.id & 0x7FF) != msg.id)) return -1;
+    if ((msg.format == CANExtended) && ((msg.id & 0x1FFFFFFF) != msg.id)) return -1;
+    
+    int i = 0;
+    buff[i] = 'C';      // Start of message
+    i++;
+   /* buff[i] = (msg.format << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2);     // Header byte, message info on ID size, RTR, # data bytes
+    i++;
+    if (msg.format == CANStandard) {
+        buff[i++] = (msg.id & 0x0FF);               // Lower byte of ID
+        buff[i++] = (msg.id & 0x700) >> 8;          // Upper byte of ID
+    } else {
+        buff[i++] = (msg.id & 0x000000FF);          // Lowest byte of ID
+        buff[i++] = (msg.id & 0x0000FF00) >> 8;
+        buff[i++] = (msg.id & 0x00FF0000) >> 16;
+        buff[i++] = (msg.id & 0x1F000000) >> 24;    // Highest byte of ID
+    }
+    for (int j = 0; j < msg.len; j++) {
+        buff[i++] = msg.data[j];
+    }
+    buff[i++] = '\n';
+    */return i;
+}
+
+bool convert2msg(CANMessage &msg, char* buff) {
+    // Check for 'D' for done
+    if (buff[0] != 'D') return -1;
+    
+    bool extended = buff[1] & 1;
+    bool rtr = buff[1] & 2;
+    char DLC = buff[1] >> 2;
+    if (DLC > 8) return false;     // Bad length
+    
+    // Standard frame, get id and data
+    if (!extended) {
+        if (buff[3] & 0x7 != buff[3]) return false;                 // Last byte of ID bad
+        msg.id = buff[2] | (int)(buff[3] << 8);                     // Build the ID
+        for (int i = 0; i < DLC; i++) msg.data[i] = buff[i+4];      // Build the data array
+        
+    // Extended frame, get id and data
+    } else {
+        if (buff[5] & 0x1F != buff[5]) return false;                            // Last byte of ID bad
+        msg.id = buff[2] | (buff[3] << 8) | (buff[4] << 16) | (buff[5] << 24);  // Build the ID
+        for (int i = 0; i < DLC; i++) msg.data[i] = buff[i+6];                  // Build the data array
+    }
+    msg.len = DLC;
+    if (rtr) msg.type = CANRemote;
+    else msg.type = CANData;
+    if (extended) msg.format = CANExtended;
+    else msg.format = CANStandard;
+    return true;                        // Successfully parsed, passed all checks, arguement was updated
+}
+
 CANxbee::CANxbee(PinName tx, PinName rx, int _baud, int txSize, int rxSize) : serial(tx, rx, txSize, rxSize) {
     serial.baud(_baud);
     rx_i = 0;
     rxBuff[0] = 0;
-    building = false;
+    getChars = -1;
 }
 
 // Send a CAN message, first reformat it into a char array, then send over Xbees
 bool CANxbee::send(CANMessage &msg) {
-    char buff[14];      // Build the string-ified CAN message here
-    int size;
-    
-    buff[0] = 'C';
-    int i;
-    if (msg.format == 0) {              // Standard, 11-bit ID
-        buff[1] = (0 << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2);
-        buff[2] = msg.id & 0xFF;        // Lower byte of ID
-        buff[3] = (msg.id & 0x7FF) >> 8;
-        for (i = 0; (i < msg.len) && (i < 8); i++) {
-            buff[i+4] = msg.data[i];    // Get data bytes
-        }
-        buff[i+4] = '\n';               // Terminate message
-        size = i+4+1;
-        
-    } else if (msg.format == 1) {       // Extended, 29-bit ID
-        buff[1] = (1 << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2);
-        buff[2] = msg.id & 0x000000FF;  // Lower byte of ID
-        buff[3] = msg.id & 0x0000FF00 >> 8;
-        buff[4] = msg.id & 0x00FF0000 >> 16;
-        buff[5] = msg.id & 0x1F000000 >> 24;
-        for (i = 0; (i < msg.len) && (i < 8); i++) {
-            buff[i+6] = msg.data[i];    // Get data bytes
-        }
-        buff[i+6] = '\n';               // Terminate message
-        size = i+6+1;
-    } else return false;                // Bad message
-
+    char buff[15];                      // Build the string-ified CAN message here
+    int size = convert2array(msg, buff);
+    if (size == -1) return false;       // Bad message, string not formed
     bool success=false;
     
-    // Begin thread-safe section
-    __disable_irq();
+    size=1;
+    int bytesLeft = serial.txBufferGetSize(0) - serial.txBufferGetCount();
+    Timer t;
     
-    // Check if enough spaces in buffer
-    if ((serial.txBufferGetSize(0) - serial.txBufferGetCount()) >= size) {
+    // Begin thread-safe section
+    NVIC_DisableIRQ(TIMER3_IRQn);       // Timer3-->RTOS tick
+
+    if (bytesLeft >= size) {
         for (int i = 0; i < size; i++) serial.putc(buff[i]);    // Send the message out
         success = true;
     }
     
     // End thread-safe section
-    __enable_irq();
-    
+    NVIC_EnableIRQ(TIMER3_IRQn);       // Timer3-->RTOS tick
+
     return success;
 }
 
@@ -59,62 +96,40 @@
     int newChar = serial.getcNb();
     if (newChar == -1) return false;        // No new char
     char c = newChar & 0xFF;                // Cast to char
-    bool process=false;
     
-    char len=0;
-    // Listen for a 'C', start of new message
-    if (c == 'C' && !building) {
-        rxBuff[rx_i] = 'C';
-        rx_i++;
-        building = true;    // Now building a string
+    // Listen for a 'C', start of new message as long as not already building message
+    if (c == 'C' && (rxBuff[0] != 'C')) {
+        rx_i = 0;               // Reset to start of message
+        rxBuff[rx_i++] = 'C';   // Add the 'C', increment
         return false;
     }
-    // Keep building a new message until too big or encounter '\n'
-    if (building) {
-        rxBuff[rx_i] = c;
-        rx_i++;
-        if (c == '\n') {        // Newline! process this string
-            process = true;
-            building = false;   // Reset to keep capturing on next call
-            len = rx_i;
-            rx_i = 0;
-        }
-        else if (rx_i >= sizeof(rxBuff)) {   // Too big, bad string
-            rx_i = 0;
-            building = false;
-            return false;
+    
+    // 'C' already found, now filling in contents of message
+    if (rxBuff[0] == 'C') {
+        if (getChars == -1) {           // Get the header byte
+            rxBuff[rx_i++] = c;         // Add to string, increment
+            bool extended = c & 1;      // Is this an extended message? (4 ID bytes)
+            char DLC = c >> 2;          // How long is the data section?
+            if (DLC > 8) {              // Bad DLC, reset variables
+                getChars = -1;
+                rxBuff[0] = 0;
+                rx_i = 0;
+                return false;
+            }
+            getChars = DLC + extended?4:2;  // Need to obtain getChars more characters to complete this message
+        } else {                            // This is not a header byte, add to contents of message
+            rxBuff[rx_i++] = c;
+            getChars--;
+            if (getChars <= 0) {            // Just added the last char, done message, reset variables
+                rxBuff[0] = 'D';            // Mark 'D' for done
+                rx_i = 0;
+                getChars = -1;
+            }
         }
     }
-    if (!process) return false;
+    if (rxBuff[0] != 'D') return false;
     
     // A string is ready, process it here
-    if (rxBuff[0] != 'C') return false;     // No start char, not valid
-    
-    // Extract the data from the header byte
-    bool extended = rxBuff[1] & 1;
-    bool rtr = rxBuff[1] & 2;
-    char DLC = rxBuff[1] >> 2;
-    int id=0;
-    if (DLC > 8) return false;                  // Bad DLC
-    if (!extended) {                            // Standard ID
-        if (len > 12 || len < 5) return false;  // Too big/too small for standard size
-        if (rxBuff[3] & 0x7 != rxBuff[3]) return false;     // Last byte of ID bad
-        if (len != 5 + DLC) return false;       // Improper number of bytes
-        id = rxBuff[2] | (rxBuff[3] << 8);      // Build the ID
-        for (int i = 0; i < DLC; i++) msg.data[i] = rxBuff[i+4];        // Build the data array
-
-    } else {                                    // Extended ID
-        if (len > 14 || len < 7) return false;  // Too big/too small for extended size
-        if (rxBuff[5] & 0x1F != rxBuff[5]) return false;                                // Last byte of ID bad
-        if (len != 7 + DLC) return false;       // Improper number of bytes
-        id = rxBuff[2] | (rxBuff[3] << 8) | (rxBuff[4] << 16) | (rxBuff[5] << 24);      // Build the ID
-        for (int i = 0; i < DLC; i++) msg.data[i] = rxBuff[i+6];        // Build the data array
-    }
-    msg.id = id;
-    msg.len = DLC;
-    if (rtr) msg.type = CANRemote;
-    else msg.type = CANData;
-    if (extended) msg.format = CANExtended;
-    else msg.format = CANStandard;
-    return true;            // Successfully parsed, passed all checks, arguement was updated
+    if (convert2msg(msg, rxBuff)) return true;
+    else return false;
 }
\ No newline at end of file
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.h	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.h	Wed Jan 07 03:25:50 2015 +0000
@@ -24,9 +24,9 @@
     
 private:
     MODSERIAL serial;
-    char rxBuff[14];
+    char rxBuff[15];
     int rx_i;
-    bool building;
+    int getChars;
 };
 
 #endif
\ No newline at end of file
--- a/Libs/XbeeManager/XbeeManager.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -1,9 +1,9 @@
 #include "XbeeManager.h"
 
-XbeeManager::XbeeManager(CANxbee* _x1, CANxbee* _x2)
+XbeeManager::XbeeManager(PinName tx1, PinName rx1, PinName tx2, PinName rx2, int baud, int txSize, int rxSize) :
+                        x1(tx1, rx1, baud, txSize, rxSize), x2(tx2, rx2, baud, txSize, rxSize)
 {
-    x1 = _x1;
-    x2 = _x2;
+    
     alternate = false;
     extraAvail = false;
     counterX1in = 0;
@@ -16,12 +16,12 @@
 {
     alternate = !alternate;
     if (alternate) {
-        if (x1->send(msg)) {
+        if (x1.send(msg)) {
             counterX1out++;
             return true;   
         }
     } else {
-        if (x2->send(msg)) {
+        if (x2.send(msg)) {
             counterX2out++;
             return true;   
         }
@@ -37,14 +37,14 @@
         counterX2in++;
         return true;
     }
-    if (x1->receive(msg)) {
-        if (x2->receive(extra)) {
+    if (x1.receive(msg)) {
+        if (x2.receive(extra)) {
             extraAvail = true;
         }
         counterX1in++;
         return true;
     } else {
-        if (x2->receive(msg)) {
+        if (x2.receive(msg)) {
             counterX2in++;
             return true;   
         }
--- a/Libs/XbeeManager/XbeeManager.h	Tue Jan 06 20:45:26 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.h	Wed Jan 07 03:25:50 2015 +0000
@@ -6,7 +6,7 @@
 
 class XbeeManager {
 public:
-    XbeeManager(CANxbee* x1, CANxbee* x2);
+    XbeeManager(PinName tx1, PinName rx1, PinName tx2, PinName rx2, int baud, int txSize, int rxSize);
     bool send(CANMessage &msg);
     bool receive(CANMessage &msg); 
     unsigned int counterX1in;
@@ -16,8 +16,8 @@
     
 private:
     bool alternate;
-    CANxbee* x1;
-    CANxbee* x2;
+    CANxbee x1;
+    CANxbee x2;
     CANMessage extra;
     bool extraAvail;
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inCommands/inCommands.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -0,0 +1,206 @@
+#include "inCommands.h"
+
+bool inCommands::serviceCAN(CANMessage* fromXbee)
+{
+    CANMessage msg;
+    if (fromXbee != NULL) {
+        memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
+    } else {
+        if (!can.rxRead(msg)) return false;
+    }
+
+    switch (msg.id) {
+        case FAN_CONTROL_ID:
+            if (msg.len != 2*sizeof(float)) break;
+            memcpy((void*)&CANdata.dcdcFan1Duty, &msg.data[0], sizeof(float));
+            memcpy((void*)&CANdata.dcdcFan2Duty, &msg.data[4], sizeof(float));
+            dcdc.setPwm(FAN1, CANdata.dcdcFan1Duty);
+            dcdc.setPwm(FAN2, CANdata.dcdcFan2Duty);
+            break;
+
+        case PUMP_CONTROL_ID:
+            if (msg.len != 2*sizeof(float)) break;
+            memcpy((void*)&CANdata.dcdcPump1Duty, &msg.data[0], sizeof(float));
+            memcpy((void*)&CANdata.dcdcPump2Duty, &msg.data[4], sizeof(float));
+            dcdc.setPwm(PUMP1, CANdata.dcdcPump1Duty);
+            dcdc.setPwm(PUMP2, CANdata.dcdcPump2Duty);
+            break;
+
+        case DCDC_CONTROL_ID:
+            if (msg.len != sizeof(char)) break;
+            if (msg.data[0] == 1) dcdc.set(1);
+            else dcdc.set(0);
+            break;
+
+        case AMS_RELAYS_ID:
+            if (msg.len != sizeof(char)) break;
+            if ((msg.data[0] & (1<<3|1<<4|1<<5)) == (1<<3|1<<4|1<<5)) {     // AIRs closed? 1<<3=posAIR, 1<<4=negAIR, 1<<5=tractiveEnable signal
+                CANdata.airsClosed = true;
+                dcdc.set(1);
+            } else {
+                CANdata.airsClosed = false;
+                dcdc.set(0);
+            }
+            break;
+
+        case GLVBAT_CLEARSOC_ID:
+            if (msg.len != sizeof(float)) break;
+            glvBat.resetToSOC(*((float*)(&msg.data[0])));
+            break;
+
+        case GLVBAT_CLEARAH_ID:
+            if (msg.len != sizeof(float)) break;
+            glvBat.resetToAh(*((float*)(&msg.data[0])));
+            break;
+
+        case GLVBAT_SETCAPAC_ID:
+            if (msg.len != sizeof(float)) break;
+            glvBat.changeCapacity(*((float*)(&msg.data[0])));
+            break;
+        case STEERING_RESET_ID:
+            NVIC_SystemReset();
+            break;
+        default:
+            break;
+    }
+
+    return true;
+
+}
+// Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
+bool inCommands::receiveMsgXbee()
+{
+    CANMessage msg;
+    if (xbeeRelay.receive(msg)) {                       // Incoming CAN message string received
+        if (!can.txWrite(msg)) data.canFault = true;    // Send it out on the CAN bus
+        serviceCAN(&msg);                               // Send it into the local serviceCAN routine
+        return true;
+    } else return false;
+}
+
+// Compare string to a word in the serial input, shorter to type
+#define CMP(w, string)                                              if (!strcasecmp(word[w-1], string))
+static char word[3][RX_SIZE+1];
+// Serial input
+int inCommands::serviceSerial()
+{
+
+    static int end = 0;                              // End of string position
+    int c = pc.getcNb();                             // Get char from RX buffer (returns an int)
+    if (c == -1) return -2;                          // Invalid char, no char available
+    char b = c;                                      // Casted to char type
+    bool process = false;                            // Is string complete (ready to parse)?
+
+    if (b == '\n' || b == '\r') {                    // Enter key was pressed, dump for processing
+        tempData.inputStr[end] = 0;                  // Null terminate
+        end = 0;                                     // Reset to start
+        process = true;                              // Flag for processing
+
+    } else if (b == '\b' || b == 127) {              // Backspace or delete
+        if (end > 0) end--;                          // Move back one char
+        tempData.inputStr[end] = 0;                  // Erase char
+
+    } else if (b > 31 && b < 127) {                  // New valid displayable char
+        tempData.inputStr[end] = b;                  // Add to buffer
+        end++;                                       // Increment end
+        tempData.inputStr[end] = 0;                  // Add null terminator
+        if (end >= RX_SIZE) {
+            end = 0;                                 // Reset end location
+            process = true;                          // Flag for processing
+        }
+    }
+
+    // Continue to parsing section only if flagged as complete and string not empty
+    if (!process || strlen(tempData.inputStr) == 0) return 0;
+
+    int pieces = sscanf(tempData.inputStr, "%s %s %s", word[0], word[1], word[2]);      // Populate words
+    tempData.inputStr[0] = 0;                        // Empty the string displayed on screen
+
+    char *next;                                      // Used by strtod and strtoul
+
+    // One word commands
+    if (pieces == 1) {
+        // Reset the microcontroller
+        CMP(1, "reset") {
+            NVIC_SystemReset();
+            return 1;
+        }
+        // Two word commands
+    }
+    if (pieces == 2) {
+        // Manual DC-DC on/off control
+        CMP(1, "dcdc") {
+            CMP(2, "on") {
+                dcdc.set(1);
+                return 1;
+            }
+            CMP(2, "off") {
+                dcdc.set(0);
+                return 1;
+            }
+            return -1;
+        }
+        // Artificially update the SOC (battery life %)
+        CMP(1, "SOC") {
+            CMP(2, "Reset") {                           // Command was "SOC reset" - reset to 100%, do this after a full charge
+                glvBat.resetToSOC(1);
+                return 1;
+            }
+            float soc = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
+            if (*next == 0) {
+                if (glvBat.resetToSOC(soc)) return 1;
+            }
+            return -1;
+        }
+        // Artificially update the AmpHours count (linked with SOC)
+        CMP(1, "Ah") {
+            CMP(2, "Reset") {                           // Command was "Amphours reset", equivalent to "SOC reset"
+                glvBat.resetToSOC(1);
+                return 1;
+            }
+            float ah = strtod(word[1], &next);          // Command was "Amphours xxx" where xxx is a float in amphours
+            if (*next == 0) {
+                if (glvBat.resetToAh(ah)) return 1;
+            }
+            return -1;
+        }
+        // Change the battery capacity setting for calculating Amphours
+        CMP(1, "Capacity") {
+            float cap = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
+            if (*next == 0) {
+                if (glvBat.changeCapacity(cap)) return 1;
+            }
+            return -1;   
+        }
+    }
+    if (pieces == 3) {
+        // Fan Duty
+        CMP(1, "Fan") {
+            float val1 = strtod(word[1], &next);
+            if (*next == 0) {
+                float val2 = strtod(word[2], &next);
+                if (*next == 0) {
+                    dcdc.setPwm(FAN1, val1);
+                    dcdc.setPwm(FAN2, val2);
+                    return 1;
+                }
+            }
+            return -1;
+        }
+
+        // Pump Duty
+        CMP(1, "Pump") {
+            float val1 = strtod(word[1], &next);
+            if (*next == 0) {
+                float val2 = strtod(word[2], &next);
+                if (*next == 0) {
+                    dcdc.setPwm(FAN1, val1);
+                    dcdc.setPwm(FAN2, val2);
+                    return 1;
+                }
+            }
+            return -1;
+        }
+    }
+    return -1;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inCommands/inCommands.h	Wed Jan 07 03:25:50 2015 +0000
@@ -0,0 +1,14 @@
+#ifndef _SERVICE_CAN_H
+#define _SERVICE_CAN_H
+
+#include "IOobjects.h"
+
+const int DEVICE_CAN_TIMEOUT = 0.25;      // Comms. lost to external device if a message is not received within 200 ms
+
+namespace inCommands {
+    bool serviceCAN(CANMessage* fromXbee=0);
+    bool receiveMsgXbee();
+    int serviceSerial();
+}
+
+#endif
\ No newline at end of file
--- a/main.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/main.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -1,7 +1,7 @@
 #include "IOobjects.h"
 #include "runTime.h"
 #include "outDiagnostics.h"
-#include "serviceCAN.h"
+#include "inCommands.h"
 
 int main() {
     wdt.kick();                         // Kick the watchdog timer
@@ -9,7 +9,11 @@
     pc.format(8, SerialBase::None, 2);  // 2 Stop bits, reduce bad serial packets
     
     can.mode(FIFO);
-    DigitalOut led1(LED1);
+    NVIC_SetPriority(TIMER3_IRQn, 4);
+    NVIC_SetPriority(UART0_IRQn, 0);
+    NVIC_SetPriority(CAN_IRQn, 3);
+    NVIC_SetPriority(TIMER0_IRQn, 1);
+    NVIC_SetPriority(PWM1_IRQn, 2);
     
     bool normalReset = true;
     // Did a watchdog reset occur since last power cycle?
@@ -17,7 +21,6 @@
         wdt.clearFlag();                    // Clear flag
         data.watchdogReset = true;
         pc.printf("Sys Mgmt Watchdog Reset\r\n");
-        led1=1;
         normalReset=false;
     }
     // Did a brownout reset occur since last power cycle?
@@ -30,23 +33,26 @@
     if (normalReset) pc.printf("Sys Mgmt Reset\r\n");
     
     // Start the 10Hz data thread
-    // Thread gather(runTime::thread_gather, 0, osPriorityHigh);
+    Thread gather(runTime::thread_gather, 0, osPriorityHigh, 512);
     
     // Start the 100Hz data timer (more time critical than thread)
-    //RtosTimer sample(runTime::thread_sample, osTimerPeriodic);
-    //sample.start(FAST_LOOP*1000);
+    RtosTimer sample(runTime::thread_sample, osTimerPeriodic);
+    sample.start(FAST_LOOP*1000);
     
     // Start the serial, CAN threads
-    Thread serial_out(outDiagnostics::thread_serialOut, 0, osPriorityAboveNormal, 6000);     // Allocate 6kB RAM stack
-    //Thread can_out(outDiagnostics::thread_canOut, 0, osPriorityAboveNormal);            // Allocate 256B RAM stack
+    Thread serial_out(outDiagnostics::thread_serialOut, 0, osPriorityAboveNormal, 2000);     // Allocate 6kB RAM stack
+    Thread can_out(outDiagnostics::thread_canOut, 0, osPriorityAboveNormal, 512);            // Allocate 256B RAM stack
     
-    wdt.kick(WDT_TIME);     // Startup complete, normal timeout
-    
+    wdt.kick(1);     // Startup complete, normal timeout
     // Background task
     while(1) {
         // Service CAN and Xbee logic
-        //if (canbus::serviceCAN());
-        //canbus::receiveMsgXbee();
+        inCommands::serviceCAN();
+        inCommands::receiveMsgXbee();
+        
+        int ret = inCommands::serviceSerial();
+        if (ret == -1) tempData.parseGoodChar = 'x';
+        if (ret == 1)  tempData.parseGoodChar = 251;
         wdt.kick();
     }
 }
\ No newline at end of file
--- a/outDiagnostics/outDiagnostics.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ b/outDiagnostics/outDiagnostics.cpp	Wed Jan 07 03:25:50 2015 +0000
@@ -1,42 +1,117 @@
 #include "outDiagnostics.h"
 
-// Macros for working with the string
-#define ADD_LINE                  len+=sprintf(buff+len,"%s\r\n",line);                                                    // Add newlines, add it to the working buffer 
-#define ADD_SPRINTF_LINE          padCenter(line,max_charsPerLine-2,temp,' '); len+=sprintf(buff+len,"%s\r\n",line);       // Add newlines, add it to the working buffer 
-#define BOOL(VAR)                 (VAR)?"ERR":"OK"
+osThreadId Serialthread_id = 0;                         // RTOS thread ID of thread_serialOut
+const int max_charsPerLine = 80;                        // Max chars per line of printed information
+const int max_lines = TX_SIZE / (max_charsPerLine+2);   // Lines per printing chunk (write chunk to MODSERIAL Buffer every x lines)
+
+// Interrupt function called when the MODSERIAL TX buffer is empty
+// The functions signals RTOS to unblock to serial thread and allow it
+// to produce the chunk of serial output data
+void empty(MODSERIAL_IRQ_INFO *q)
+{
+    osSignalSet(Serialthread_id, 1);                    // Set signal 1 for serial thread
+}
 
-// Print to a string buffer, pad to maxLen chars and center it with char pad, str must be null terminated!
-void padCenter(char *buff, int LineLen, char *str, char pad) {
-    int len = strlen(str);
-    int padL = (LineLen-len)/2;                              // -1 to save room for the null terminator
-    for (int i=0; i<padL; i++) buff[i] = pad;                // Fill in the left padding chars
-    strcpy(buff+padL, str);
-    for (int i = padL+len; i<LineLen; i++) buff[i] = pad;    // Fill remaining with padding chars
-    buff[LineLen-1] = '\0';                                  // Add null terminator
+// Add a line of text to the printing queue.
+// Actually gets added to another local buffer, which gets copied into the MODSERIAL TX buffer when it
+// empties.  Provides double-buffered output stream so inflow and outflow rates can be different.
+// Char *str NEEDS to be NULL terminated string! bool newline determines whether newline characters \r\n are appended to the line
+void AddToChunk (char *str, bool newline=true)
+{
+    static char chunk[(max_charsPerLine+5)*max_lines];          // The chunk buffer, holds one chunk= max_charsPerLine*max_lines characters
+    static int pos = 0;                                         // The position to load next character
+    static int lines = 0;                                       // Number of lines currently stored in the chunk
+    static int wait = pc.txBufferGetSize(0) * (CHAR_TIME*1000); // Max wait time to empty the tx buffer = max time to send out all chars
+
+    pos += sprintf(chunk+pos, "%s%s", str, newline?"\r\n":"");  // Append to working array
+    lines++;                                                    // New line added to array
+
+    if (lines >= max_lines) {                                   // Time to print the block
+        // Is buffer free enough to accept the block?
+        if (pc.txBufferGetCount() - pc.txBufferGetSize(0) > pos+5) {
+            for (int i = 0; i < pos; i++) pc.putc(chunk[i]);    // Add the string to the buffer
+        } else {
+            // No, it is not free enough to hold the string
+            osSignalClear(Serialthread_id, 1);                  // Clear signal if it was set
+            Thread::signal_wait(1, wait);                       // RTOS wait for empty signal, max wait if it never occurs
+            for (int i = 0; i < pos; i++) pc.putc(chunk[i]);    // Add into the output buffer
+        }
+        lines = 0;                                              // Chunk added, reset line counter
+        pos = 0;                                                // Chunk added, reset position
+    }
 }
 
-// Generates the serial dashboard, uses MODDMA MODSERIAL to speed up printing
-void outDiagnostics::thread_serialOut(void const *args) {
-    const int max_charsPerLine = 81;                 // Max chars per line
-    const int max_lines = 35;                        // Max lines that the layout prints out
+// Print to internal string buffer, pad to maxLen chars and center it with char pad, str must be null terminated!
+void padCenter(int LineLen, char *str, char pad)
+{
+    static char line[max_charsPerLine+5];                    // String buffer to work with one line at a time
+
+    int len = strlen(str);                                   // Length of input string
+    int padL = (LineLen-len)/2;                              // How many pad chars needed on left side?
+    for (int i=0; i<padL; i++) line[i] = pad;                // Fill in the left padding chars
+    strcpy(line+padL, str);                                  // Copy to line string
+    for (int i = padL+len; i<LineLen; i++) line[i] = pad;    // Fill remaining with padding chars
+    line[LineLen-1] = '\0';                                  // Add null terminator
+
+    AddToChunk(line);
+}
+
+
+// Macros for working with the strings
+#define ADD_SPRINTF_LINE    padCenter(max_charsPerLine, temp, ' ');   // Cetner the string, then add newlines, and add to chunk
+#define DIVIDER_LINE        padCenter(max_charsPerLine, "", 196);     // Generate a line full of divider char 196, add to chunk
+#define TITLE(string)       padCenter(max_charsPerLine, string, 196); // Generate a title line (centered, surrounded by char 196), add to chunk
+#define BLANK_LINE          padCenter(max_charsPerLine, "", ' ');     // Generate a line full of spaces (blank), add to chunk
+#define BOOL(VAR)           (VAR)?"ERR":"OK"
+
+// Generates the serial dashboard, uses MODSERIAL, self-paced (thread yields when buffer is full, resumes when empty)
+void outDiagnostics::thread_serialOut(void const *args)
+{
+    Serialthread_id = Thread::gettid();              // Record thread ID so empty() can signal this thread
+    char temp[max_charsPerLine+5];                   // String buffer to sprintf into, max 1 line
+
+    pc.attach(&empty, MODSERIAL::TxEmpty);           // Attach the tx empty interrupt which paces this thread
     pc.printf("\033[2J");                            // Clear the screen to get rid of reset message
 
-    char buff[max_charsPerLine*max_lines];           // Giant string to store the printout
-    char line[max_charsPerLine];                     // String buffer to work with one line at a time
-    char temp[max_charsPerLine];                     // String buffer to sprintf into
+    tempData.parseGoodChar = ' ';
+    tempData.inputStr[0] = 0;
+    
+    Timer serialLoop;                                // Timer to track the serial loop time, since this thread paces itself
+    int serialTime_ms = 0;
+    serialLoop.reset();
+    serialLoop.start();                              // Start the counter for tracking serial loop time
+
+    //const char barSpace[4] = { ' ', 179, ' ', 0 };   // Commonly used string with char 179
+
     while(1) {
 
-        int len = 0;
-        len += sprintf(buff+len, "\033[0;0H");                      // Home the cursor
-        padCenter(line, max_charsPerLine-2, "-", '-'); ADD_LINE     // Generate a line full of -'s
-        padCenter(line, max_charsPerLine-2, " Penn Electric Racing - REV0 System Management Controller Serial Dashboard ", '-'); ADD_LINE
-        padCenter(line, max_charsPerLine-2, "-", '-'); ADD_LINE     // Generate a line full of -'s
+        serialTime_ms = serialLoop.read_ms();        // Update loop timer, reset for next loop
+        serialLoop.reset();
 
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " GLV Battery ", '*'); ADD_LINE
-        sprintf(temp, "Current: %4.3fA Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity); ADD_SPRINTF_LINE
-        sprintf(temp, "Amphours: %4.3fAh SOC: %5.1f%% Overcurrent: %s", data.glvAmphours, data.glvSOC*100.0, BOOL(data.glvOverCurrent)); ADD_SPRINTF_LINE
+        sprintf(temp, "\033[0;0H\033[0;0H");
+        AddToChunk(temp, false);                     // Move to 0,0, do not append newline
+        
+        DIVIDER_LINE
+        TITLE(" Penn Electric Racing - REV0 System Management Controller Dashboard ")
+        DIVIDER_LINE
         
+        int tempLength=0;
+        tempLength += sprintf(temp, "Command Input:%c %s%c", tempData.parseGoodChar, tempData.inputStr, 176);  // Command input: print header, reply, input string, and cursor marker
+        for (int i = 0; i < max_charsPerLine - strlen(temp)- 1; i++) {      // Fill in the rest of the line with blanks
+            tempLength += sprintf(temp+tempLength, " ");                    // Append spaces
+        }
+        AddToChunk(temp);        // Add this to the chunk
+        
+        TITLE(" GLV Battery ")
+        BLANK_LINE
+        sprintf(temp, "Current: %4.3fA          Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity);
+        ADD_SPRINTF_LINE
+        sprintf(temp, "Amphours: %4.3fAh    SOC: %5.3f    Overcurrent: %s", data.glvAmphours, data.glvSOC, BOOL(data.glvOverCurrent));
+        ADD_SPRINTF_LINE
+
+        BLANK_LINE
+        TITLE(" DC-DC Converter ")
+        BLANK_LINE
         char DCDC = data.dcdcStatus;
         char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","ON","OFF"};
         int dcdcMode = 0;
@@ -44,133 +119,120 @@
         else if (DCDC & PowerDown) dcdcMode = 2;
         else if (DCDC & SetOn)     dcdcMode = 3;
         else if (!(DCDC & SetOn))  dcdcMode = 4;
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " DC-DC Converter ", '*'); ADD_LINE
-        sprintf(temp, "Current: %5.3fA Overcurrent: %s SensorFault: %s", data.dcdcCurrent, BOOL(DCDC & OverCurrent), BOOL(DCDC & SensorFault)); ADD_SPRINTF_LINE
-        sprintf(temp, "Active: %s Mode: %s AIRS: %s StatusByte: 0x%x", (DCDC & ConvOn)?"YES":"NO", dcdcModesStr[dcdcMode], CANdata.airsClosed?"CLOSED":"OPEN", DCDC); ADD_SPRINTF_LINE
-        sprintf(temp, "StartFault: %s StopFault: %s CritErrors: %s", BOOL(DCDC & FailStart), BOOL(DCDC & FailStop), BOOL(data.dcdcError)); ADD_SPRINTF_LINE
+        sprintf(temp, "Active: %3s     Mode: %10s     AIRS: %6s     StatusByte: 0x%02x", (DCDC & ConvOn)?"YES":"NO", dcdcModesStr[dcdcMode], CANdata.airsClosed?"CLOSED":"OPEN", DCDC);
+        ADD_SPRINTF_LINE
+        sprintf(temp, "Current: %5.3fA     Overcurrent: %3s     SensorFault: %3s", data.dcdcCurrent, BOOL(DCDC & OverCurrent), BOOL(DCDC & SensorFault));
+        ADD_SPRINTF_LINE
+        sprintf(temp, "StartFault: %3s     StopFault:   %3s     CritErrors:  %3s", BOOL(DCDC & FailStart), BOOL(DCDC & FailStop), BOOL(data.dcdcError));
+        ADD_SPRINTF_LINE
 
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " PWM Channels ", '*'); ADD_LINE
-        sprintf(temp, "Actual:   FAN1: %5.1f%% FAN2: %5.1f%% PUMP1: %5.1f%% PUMP2: %5.1f%%", data.dcdcFan1Duty*100.0, data.dcdcFan2Duty*100.0, data.dcdcPump1Duty*100.0, data.dcdcPump2Duty*100.0); ADD_SPRINTF_LINE
-        sprintf(temp, "Requestd: FAN1: %5.1f%% FAN2: %5.1f%% PUMP1: %5.1f%% PUMP2: %5.1f%%", CANdata.dcdcFan1Duty*100.0, CANdata.dcdcFan2Duty*100.0, CANdata.dcdcPump1Duty*100.0, CANdata.dcdcPump2Duty*100.0); ADD_SPRINTF_LINE
+        BLANK_LINE
+        TITLE(" PWM Channels ")
+        BLANK_LINE
+        sprintf(temp, "Actual:     FAN1: %5.3f   FAN2: %5.3f   PUMP1: %5.3f   PUMP2: %5.3f", data.dcdcFan1Duty, data.dcdcFan2Duty, data.dcdcPump1Duty, data.dcdcPump2Duty);
+        ADD_SPRINTF_LINE
+        sprintf(temp, "Requestd:   FAN1: %5.3f   FAN2: %5.3f   PUMP1: %5.3f   PUMP2: %5.3f", CANdata.dcdcFan1Duty, CANdata.dcdcFan2Duty, CANdata.dcdcPump1Duty, CANdata.dcdcPump2Duty);
+        ADD_SPRINTF_LINE
 
+        BLANK_LINE
+        TITLE(" IMD ")
+        BLANK_LINE
         const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " IMD ", '*'); ADD_LINE
-        sprintf(temp, "Status: %s Resistance: %7.0fkohm CritError: %s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError)); ADD_SPRINTF_LINE
-        
+        sprintf(temp, "Status: %10s Resistance: %7.0fkohm CritError: %3s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError));
+        ADD_SPRINTF_LINE
+
+        BLANK_LINE
+        TITLE(" Latch Circuit Monitors ")
+        BLANK_LINE
         char AMSerr = data.AMSlatchError;
         char IMDerr = data.IMDlatchError;
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " Latch Circuits ", '*'); ADD_LINE
-        sprintf(temp, "AMS: OK: %s Latch: %s SoftFault: %s HardFault: %s", (AMSerr & 1)?"LOW":"HIGH", (AMSerr & 2)?"OPEN":"OK", BOOL(AMSerr & 4), BOOL(AMSerr & 8)); ADD_SPRINTF_LINE
-        sprintf(temp, "IMD: OK: %s Latch: %s SoftFault: %s HardFault: %s", (IMDerr & 1)?"LOW":"HIGH", (IMDerr & 2)?"OPEN":"OK", BOOL(IMDerr & 4), BOOL(IMDerr & 8)); ADD_SPRINTF_LINE
+        sprintf(temp, "AMS: OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (AMSerr & 1)?"LOW":"HIGH", BOOL(AMSerr & 2), BOOL(AMSerr & 4), BOOL(AMSerr & 8));
+        ADD_SPRINTF_LINE
+        sprintf(temp, "IMD: OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (IMDerr & 1)?"LOW":"HIGH", BOOL(IMDerr & 2), BOOL(IMDerr & 4), BOOL(IMDerr & 8));
+        ADD_SPRINTF_LINE
 
+        BLANK_LINE
+        TITLE(" Shutdown Switches ")
+        BLANK_LINE
         char switches = data.switchState;
         const char switchNames[12][26] = {"FUSE","AMS LATCH","IMD LATCH","PCM RELAY","BRAKE PLAUSIBILITY RELAY","LEFT E-STOP","INERTIA SWITCH","BRAKE OVER-TRAVEL SWITCH","COCKPIT E-STOP","RIGHT E-STOP","HVD","TSMS"};
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line
-        padCenter(line, max_charsPerLine-2, " Shutdown Switches ", '*'); ADD_LINE
         if (switches == 0) sprintf(temp, "All switches are CLOSED.");
         else sprintf(temp, "%s is OPEN.", switchNames[switches-1]);
         ADD_SPRINTF_LINE
-        
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE   // Generate blank line
-        padCenter(line, max_charsPerLine-2, " Telemetry ", '*'); ADD_LINE
-        sprintf(temp, "Channel 1: MessagesIn: %d MessagesOut: %d", xbeeRelay.counterX1in, xbeeRelay.counterX1out); ADD_SPRINTF_LINE
-        sprintf(temp, "Channel 2: MessagesIn: %d MessagesOut: %d", xbeeRelay.counterX2in, xbeeRelay.counterX2out); ADD_SPRINTF_LINE
 
-        padCenter(line, max_charsPerLine-2, " ", ' '); ADD_LINE     // Generate blank line 
-        padCenter(line, max_charsPerLine-2, " Miscellaneous ", '*'); ADD_LINE
-        sprintf(temp, "Temp: %5.1fC OverTemp: %s canFault: %s WatchdogReset: %s ErrorFrame: 0x%x", data.internalTemp, BOOL(data.internalOverTemp), BOOL(data.canFault), BOOL(data.watchdogReset), data.errorFrame); ADD_SPRINTF_LINE
+        BLANK_LINE
+        TITLE(" Telemetry ")
+        BLANK_LINE
+        sprintf(temp, "Channel 1: MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX1in, xbeeRelay.counterX1out);
+        ADD_SPRINTF_LINE
+        sprintf(temp, "Channel 2: MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX2in, xbeeRelay.counterX2out);
+        ADD_SPRINTF_LINE
 
-        // Write it all at once to output tx buffer
-        for (int i = 0; i < strlen(buff); i++) {
-            pc.putc(buff[i]);   
-        }
+        BLANK_LINE
+        TITLE(" Miscellaneous ")
+        BLANK_LINE
+        sprintf(temp, "Temp: %5.1fC %3s canFault: %3s Watchdog: %3s ErrorCode: 0x%02x SerailTime: %3dms", data.internalTemp, BOOL(data.internalOverTemp), BOOL(data.canFault), BOOL(data.watchdogReset), data.errorFrame, serialTime_ms);
+        ADD_SPRINTF_LINE
+
         // Erase screen every 5sec to remove glitches
         static int count=0;
         if (count%50==0) {
             pc.printf("\033[2J");        // Clear the screen
         }
         count++;
-        Thread::wait(100);
     }
 }
 
-template <typename T>
-void sendCAN(int ID, T dat) {
+// Macros to streamline can bus message formatting and sending
+#define SEND_CAN(LEN, ID)               msg.len=LEN; msg.id=ID; msg.type = CANData; msg.format = CANStandard; if (!can.txWrite(msg)) data.canFault = true; xbeeRelay.send(msg);
+#define FLOAT(DATA, ID)                 *((float*)((void*)&msg.data[0]))    = DATA;    SEND_CAN(sizeof(float), ID)
+#define UINT(DATA, ID)                  *((uint32_t*)((void*)&msg.data[0])) = DATA;    SEND_CAN(sizeof(uint32_t), ID)
+#define FLOAT_PAIR(DATA1, DATA2, ID)    *((float*)((void*)&msg.data[0]))    = DATA1;   *((float*)((void*)&msg.data[4]))    = DATA2;   SEND_CAN(2*sizeof(float), ID)
+#define UINT_PAIR(DATA1, DATA2, ID)     *((uint32_t*)((void*)&msg.data[0])) = DATA1;   *((uint32_t*)((void*)&msg.data[4])) = DATA2;   SEND_CAN(2*sizeof(uint32_t), ID)
+#define CHAR(DATA, ID)                  msg.data[0]                         = DATA;    SEND_CAN(sizeof(char), ID)
+
+void outDiagnostics::thread_canOut(void const *args)
+{
     CANMessage msg;
-    msg.id = ID;
-    msg.len = sizeof(T);
-    memcpy(&msg.data[0], (void*)&dat, sizeof(T));
-    if (!can.txWrite(msg)) data.canFault = true;
-    xbeeRelay.send(msg);
-}
-
-void outDiagnostics::thread_canOut(void const *args) {
-
     while(1) {
-        CANMessage msg;
 
         // Sys Mgmt Error Frame
-        sendCAN(SYS_ERROR_ID, data.errorFrame);
-        
+        CHAR(data.errorFrame, SYS_ERROR_ID)
+
         // Xbee1 Counter
-        msg.id = SYS_XBEE1_ID;
-        msg.len = 2*sizeof(int);
-        memcpy(&msg.data[0], (void*)&xbeeRelay.counterX1in, sizeof(int));
-        memcpy(&msg.data[4], (void*)&xbeeRelay.counterX1out, sizeof(int));
-        if (!can.txWrite(msg)) data.canFault = true;
-        xbeeRelay.send(msg);
+        UINT_PAIR(xbeeRelay.counterX1in, xbeeRelay.counterX1out, SYS_XBEE1_ID)
 
         // Xbee2 Counter
-        msg.id = SYS_XBEE2_ID;
-        msg.len = 2*sizeof(int);
-        memcpy(&msg.data[0], (void*)&xbeeRelay.counterX2in, sizeof(int));
-        memcpy(&msg.data[4], (void*)&xbeeRelay.counterX2out, sizeof(int));
-        if (!can.txWrite(msg)) data.canFault = true;
-        xbeeRelay.send(msg);
-        
+        UINT_PAIR(xbeeRelay.counterX2in, xbeeRelay.counterX2out, SYS_XBEE2_ID)
+
         // Internal temperature
-        sendCAN(SYS_TEMP_ID, data.internalTemp);
+        FLOAT(data.internalTemp, SYS_TEMP_ID)
 
         // GLV Battery
-        sendCAN(SYS_GLV_CURRENT_ID, data.glvCurrent);
-        sendCAN(SYS_GLV_CAPACITY_ID, data.glvCapacity);
-        sendCAN(SYS_GLV_AH_ID, data.glvAmphours);
-        sendCAN(SYS_GLV_SOC_ID, data.glvSOC);
-
+        FLOAT(data.glvCurrent, SYS_GLV_CURRENT_ID)
+        FLOAT(data.glvCapacity, SYS_GLV_CAPACITY_ID)
+        FLOAT(data.glvAmphours, SYS_GLV_AH_ID)
+        FLOAT(data.glvSOC, SYS_GLV_SOC_ID)
+        
         // DC-DC Converter
-        sendCAN(SYS_DCDC_CURRENT_ID, data.dcdcCurrent);
-        sendCAN(SYS_DCDC_STATUS_ID, data.dcdcStatus);
+        FLOAT(data.dcdcCurrent, SYS_DCDC_CURRENT_ID)
+        CHAR(data.dcdcStatus, SYS_DCDC_STATUS_ID)
 
-        // PWM Channels        
-        msg.id = SYS_PWM_FAN_ID;
-        msg.len = 2*sizeof(float);
-        memcpy(&msg.data[0], (void*)&data.dcdcFan1Duty, sizeof(float));
-        memcpy(&msg.data[4], (void*)&data.dcdcFan2Duty, sizeof(float));
-        if (!can.txWrite(msg)) data.canFault = true;
-        xbeeRelay.send(msg);
+        // PWM Channels
+        FLOAT_PAIR(data.dcdcFan1Duty, data.dcdcFan2Duty, SYS_PWM_FAN_ID)
+        FLOAT_PAIR(data.dcdcPump1Duty, data.dcdcPump2Duty, SYS_PWM_PUMP_ID)
 
-        msg.id = SYS_PWM_PUMP_ID;
-        msg.len = 2*sizeof(float);
-        memcpy(&msg.data[0], (void*)&data.dcdcPump1Duty, sizeof(float));
-        memcpy(&msg.data[4], (void*)&data.dcdcPump2Duty, sizeof(float));
-        if (!can.txWrite(msg)) data.canFault = true;
-        xbeeRelay.send(msg);
-        
         // IMD
-        sendCAN(SYS_IMD_STATUS_ID, data.imdStatus);
-        sendCAN(SYS_IMD_RESIST_ID, data.imdResistance);
+        FLOAT(data.imdStatus, SYS_IMD_STATUS_ID)
+        CHAR(data.imdResistance, SYS_IMD_RESIST_ID)
 
         // Latches
-        sendCAN(SYS_IMD_LATCH_ID, data.IMDlatchError);
-        sendCAN(SYS_AMS_LATCH_ID, data.AMSlatchError);
+        CHAR(data.IMDlatchError, SYS_IMD_LATCH_ID)
+        CHAR(data.AMSlatchError, SYS_AMS_LATCH_ID)
 
         // Shutdown Switches
-        sendCAN(SYS_SWITCHES_ID, data.switchState);
+        CHAR(data.switchState, SYS_SWITCHES_ID)
 
         Thread::wait(CAN_LOOP*1000);
-    }   
+    }
 }
\ No newline at end of file
--- a/outDiagnostics/outDiagnostics.h	Tue Jan 06 20:45:26 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef  _OUT_DIAGNOSTICS_H
-#define _OUT_DIAGNOSTICS_H
-
-#include "IOobjects.h"
-
-namespace outDiagnostics {
-    
-    void thread_canOut(void const *args);
-    void thread_serialOut(void const *args);
-    
-}
-
-#endif
\ No newline at end of file
--- a/serviceCAN/serviceCAN.cpp	Tue Jan 06 20:45:26 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-#include "serviceCAN.h"
-#include "CAN_RxIDs.h"
-
-bool canbus::serviceCAN(CANMessage* fromXbee)
-{
-    CANMessage msg;
-    if (fromXbee != NULL) {
-        memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
-    } else {
-        if (!can.rxRead(msg)) return false;
-    }
-
-    switch (msg.id) {
-        case FAN_CONTROL_ID:
-            if (msg.len != 2*sizeof(float)) break;
-            memcpy((void*)&CANdata.dcdcFan1Duty, &msg.data[0], sizeof(float));
-            memcpy((void*)&CANdata.dcdcFan2Duty, &msg.data[4], sizeof(float));
-            dcdc.setPwm(FAN1, CANdata.dcdcFan1Duty);
-            dcdc.setPwm(FAN2, CANdata.dcdcFan2Duty);
-            break;
-
-        case PUMP_CONTROL_ID:
-            if (msg.len != 2*sizeof(float)) break;
-            memcpy((void*)&CANdata.dcdcPump1Duty, &msg.data[0], sizeof(float));
-            memcpy((void*)&CANdata.dcdcPump2Duty, &msg.data[4], sizeof(float));
-            dcdc.setPwm(PUMP1, CANdata.dcdcPump1Duty);
-            dcdc.setPwm(PUMP2, CANdata.dcdcPump2Duty);
-            break;
-
-        case DCDC_CONTROL_ID:
-            if (msg.len != sizeof(char)) break;
-            if (msg.data[0] == 1) dcdc.set(1);
-            else dcdc.set(0);
-            break;
-
-        case AMS_RELAYS_ID:
-            if (msg.len != sizeof(char)) break;
-            if ((msg.data[0] & (1<<3|1<<4|1<<5)) == (1<<3|1<<4|1<<5)) {     // AIRs closed? 1<<3=posAIR, 1<<4=negAIR, 1<<5=tractiveEnable signal
-                CANdata.airsClosed = true;
-                dcdc.set(1);
-            } else {
-                CANdata.airsClosed = false;
-                dcdc.set(0);
-            }
-            break;
-
-        case GLVBAT_CLEARSOC_ID:
-            if (msg.len != sizeof(float)) break;
-            glvBat.resetToSOC(*((float*)(&msg.data[0])));
-            break;
-
-        case GLVBAT_CLEARAH_ID:
-            if (msg.len != sizeof(float)) break;
-            glvBat.resetToAh(*((float*)(&msg.data[0])));
-            break;
-
-        case GLVBAT_SETCAPAC_ID:
-            if (msg.len != sizeof(float)) break;
-            glvBat.changeCapacity(*((float*)(&msg.data[0])));
-            break;
-        case STEERING_RESET_ID:
-            NVIC_SystemReset();
-            break;
-        default:
-            break;
-    }
-
-    return true;
-
-}
-// Check for incoming messages from the xbees
-bool canbus::receiveMsgXbee()
-{
-    CANMessage msg;
-    if (xbeeRelay.receive(msg)) {                       // Incoming CAN message string received
-        if (!can.txWrite(msg)) data.canFault = true;    // Send it out on the CAN bus
-        serviceCAN(&msg);                               // Send it into the local serviceCAN routine
-        return true;
-    } else return false;
-}
\ No newline at end of file
--- a/serviceCAN/serviceCAN.h	Tue Jan 06 20:45:26 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef _SERVICE_CAN_H
-#define _SERVICE_CAN_H
-
-#include "IOobjects.h"
-
-const int DEVICE_CAN_TIMEOUT = 0.25;      // Comms. lost to external device if a message is not received within 200 ms
-
-namespace canbus {
-    bool serviceCAN(CANMessage* fromXbee=0);
-    bool receiveMsgXbee();
-}
-
-#endif
\ No newline at end of file