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:
Sat Feb 07 08:54:51 2015 +0000
Parent:
37:2207b58b9a7f
Child:
39:ddf38df9699e
Commit message:
Updated with profiles, operating info, etc. Just like the other programs. Awaiting test in car.

Changed in this revision

Constants.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/DataStructures.cpp Show annotated file Show diff for this revision Revisions of this file
DataStructures/DataStructures.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/Headers/DefaultProfile.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/Headers/FreezeFrame.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/Headers/OperatingInfo.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/Headers/Profile.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/Headers/TemporaryData.h Show annotated file Show diff for this revision Revisions of this file
DataStructures/IAP.lib Show annotated file Show diff for this revision Revisions of this file
IOobjects/CAN_RxIDs.h Show annotated file Show diff for this revision Revisions of this file
IOobjects/CAN_TxIDs.h Show annotated file Show diff for this revision Revisions of this file
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/DC_DC/DC_DC.h Show annotated file Show diff for this revision Revisions of this file
Libs/IMD/IMD.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/IMD/IMD.h Show annotated file Show diff for this revision Revisions of this file
Libs/LatchMonitor/LatchMonitor.cpp Show annotated file Show diff for this revision Revisions of this file
Libs/LatchMonitor/LatchMonitor.h 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
Libs/mbed-rtos.lib 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
inCommands/inMacros.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/outMacros.h Show annotated file Show diff for this revision Revisions of this file
runTime/runTime.cpp Show annotated file Show diff for this revision Revisions of this file
runTime/runTime.h Show annotated file Show diff for this revision Revisions of this file
--- a/Constants.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Constants.h	Sat Feb 07 08:54:51 2015 +0000
@@ -1,10 +1,14 @@
 #ifndef CONSTANTS_H
 #define CONSTATNS_H
 
-#define BAUD            230400  // Serial port baud rate
+#define NUM_STORED_PROFILES 3
+#define CAN_DEVICE_TIMEOUT  0.2
+#define SERIAL_BAUD     230400  // Serial port baud rate
 #define CHAR_TIME       0.00005 // Time to send 1 char @ above baud
 #define TX_SIZE         1000    // Serial buffer TX size
 #define RX_SIZE         60      // Serial buffer RX size
+#define CAN_TX_SIZE     128
+#define CAN_RX_SIZE     64
 
 #define XBEE_BAUD       250000  // 250k baud serial for xbees
 #define XBEE_TX_SIZE    1000    // Serial buffer TX size for xbees
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/DataStructures.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,277 @@
+#include "DataStructures.h"
+#include "DefaultProfile.h"
+#include "IAP.h"
+
+#define HEADER_SECTOR           27                                                                  // Where the NV_Data header information is stored
+#define HEADER_PTR              (NV_Data*)((uint32_t*)(sector_start_adress[HEADER_SECTOR]))
+#define ID_MARKER               0x5a5a5a5a                                                          // Marker that identifies whether the flash was erased from programming a new .bin file
+#define FRAME_SECTOR            28                                                                  // First sector for freeze frame storage (swapspace)
+#define FRAME_PTR               (FreezeFrame*)((uint32_t*)(sector_start_adress[FRAME_SECTOR]))
+#define PROFILE_SECTOR          29                                                                  // The flash sector where profiles are stored, 29 is the last 32kB size sector in memory
+#define PROFILE_START_PTR       (Profile_checkSum*)((uint32_t*)(sector_start_adress[PROFILE_SECTOR]))        // Pointer to the first profile object in flash
+
+// OPERATING DATA OBJECTS
+FreezeFrame frame;                            // Current working object in RAM, contains both param and freeze frame
+OperatingInfo *op = &(frame.op.op);           // Handle to just the op section
+Profile *param = &(frame.param.param);        // Handle to just the param section
+TemporaryData tempData;                       // Temporary data object in RAM
+
+IAP iap;                                      // Access to In-Application-Programming --> flash memory programming routines
+Profile_checkSum copyBuffer[NUM_STORED_PROFILES];      // Scratch-space in RAM for saving profiles, since flash writes must erase the entire sector and only RAM is usable during flash write
+
+// Non-volatile data holder class
+class NV_Data
+{
+public:
+    NV_Data() {
+        id = ID_MARKER;
+        frameStoredFlag = 0;
+        usingProfile = 0;
+    }
+    int frameStoredFlag;
+    int usingProfile;
+    
+    int id;
+};
+
+// Construct new freeze frames with empty OperatingInfo and default Profile
+FreezeFrame::FreezeFrame()
+{
+    memcpy(&(this->param), &defaultProfile, sizeof(Profile));
+    memset(&(this->op), 0, sizeof(OperatingInfo));
+}
+
+// Helper function to compute checksums using the BSD checksum algorithim for checking validity of flash contents
+// Arguments:  start location of the block, size in bytes     Returns:  checksum
+uint32_t checksum(const void* ptr, unsigned int size)
+{
+    const char* start = (char*)(ptr);
+    uint32_t checksum = 0;
+    for (uint32_t i = 0; i < size; i++) {   // Skip last 4 bytes which holds the checksum itsel
+        // BSD checksum algorithm
+        checksum = (checksum >> 1) + ((checksum & 1) << 15);
+        checksum += start[i];   // Accumulate
+        checksum &= 0xffff;     // Time to 16 bits
+    }
+    return checksum;
+}
+
+// Helper function to streamline flash memory prep-erase-prep-write
+// Takes start pointer (RAM), size of object to copy, start pointer (flash), and sector number
+int flashWrite(void* start, int size, void* flash_start, int sector)
+{
+    for (int i = 256; i <= 8192; i *= 2) {  // Count by powers of 2
+        if (size <= i) {                    // Stop when oversize or equal
+            size = i;
+            break;
+        }
+    }
+    if (size > 4096) return -1;           // Chunk too big to copy
+    if (size == 2048) size = 4096;  // 2048 does not exist, next size up
+
+    __disable_irq();                      // Flash operations are non-interruptable
+
+    // Flash prepare, erase, prepare, write - (r == 0) means successful, non-zero = error code
+    int r = iap.prepare(sector, sector);
+    if (r == 0) {
+        r = iap.erase(sector, sector);
+        if (r == 0) {
+            r = iap.prepare(sector, sector);
+            if (r == 0) {
+                r = iap.write((char*)start, (char*)flash_start, size);
+            }
+        }
+    }
+    __enable_irq();     // Re-enable interrupts
+    return r;           // 0 if success, otherwise see fault codes in LPC1768 User Manual
+}
+bool setProfile(int index)
+{
+    bool passed = false;
+    if (index == 0) passed = true;
+    else if (index == -1) passed = true;
+    else if (index <= NUM_STORED_PROFILES && index > 0) passed = true;
+
+    if (!passed) return false;
+    NV_Data temp;
+    NV_Data* header = HEADER_PTR;
+    
+    __disable_irq();
+    memcpy(&temp, header, sizeof(NV_Data));    // Atomic copy to RAM
+    __enable_irq();
+    
+    temp.usingProfile = index;
+
+    // Write it back to flash
+    int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+    return (i == 0);
+}
+
+// Retrieve the index number of the currently active profile (last profile loaded/saved)
+int Profile::usingProfile()
+{
+    NV_Data* header = HEADER_PTR;
+    return header->usingProfile;
+}
+
+// Startup procedure run on chip reset to fetch last used profile
+bool Profile::loadStartUp()
+{
+    // Check health of the header
+    NV_Data *header = HEADER_PTR;
+    bool passed=true;
+    if (header->id != ID_MARKER) passed = false;
+    if (header->usingProfile > NUM_STORED_PROFILES || header->usingProfile < -1) passed = false;
+    if (header->frameStoredFlag != 0 && header->frameStoredFlag != ~0) passed = false;
+
+    // Write a new, blank header    
+    if (!passed) {
+        NV_Data temp;
+        int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+    }
+    
+    if (loadProfile(usingProfile())) {   // Try to fetch the profile that is currently marked
+        return true;
+    } else {                            // If fail, revert to default
+        loadProfile(0);
+        return false;
+    }
+}
+
+// Attempt to copy profile from flash to RAM object as indicated by int index
+bool Profile::loadProfile(int index)
+{
+    Profile *p;
+    if (!getProfile(&p, index)) return false;
+    
+    __disable_irq();
+    memcpy(param, p, sizeof(Profile));
+    __enable_irq();
+
+    if (setProfile(index)) return true;
+    return false;                        // Bad sum, no copy was done
+}
+
+// Attempt to fetch the profile pointer only, no copy to RAM
+bool Profile::getProfile(Profile **ptr, int index)
+{
+    Profile_checkSum *p;
+
+    // If default profile requested
+    if (index == 0) {
+        *ptr = &defaultProfile;                 // Set pointer to default, return
+        return true;
+
+        // If freeze profile requested
+    } else if (index == -1) {
+        FreezeFrame *freeze;
+        if (!FreezeFrame::getFrame(&freeze)) {  // Attempt to fetch frame
+            *ptr = NULL;                        // Not available, set NULL
+            return false;
+        }
+        p = &(freeze->param);
+
+        // Profile from flash storage requested
+    } else if (index <= NUM_STORED_PROFILES && index > 0) {
+        p = PROFILE_START_PTR + (index - 1);  // Set a pointer to the profile
+
+        // Bad command, invalid index
+    } else {
+        *ptr = NULL;
+        return false;
+    }
+    // Validate contents by computing checksum
+    if (p->BSDchecksum == checksum(&(p->param), sizeof(Profile))) {         // Check checksum
+        *ptr = &(p->param);           // Fill out the pointer, checksum passed
+        return true;
+    }
+    *ptr = NULL;
+    return false;    // Bad checksum, ptr set to NULL
+}
+// Take the current RAM object contents and save it to flash at location given by index
+// Flash write requires the sector to be erased first, so the function will copy out and re-wrtie the profiles
+bool Profile::saveProfile(int index)
+{
+    if (index > NUM_STORED_PROFILES || index <= 0) return false;            // Index invalid
+    frame.param.BSDchecksum = checksum(param, sizeof(Profile));             // Add the checksum
+
+    // Copy out all of the old profiles
+    for (int i = 0; i < NUM_STORED_PROFILES; i++) {
+        if (i == (index - 1)) {     // Exception, replace this slot with the RAM profile
+            __disable_irq();
+            memcpy(&copyBuffer[i], &(frame.param), sizeof(Profile_checkSum));
+            __enable_irq();
+            continue;               // Skip to next
+        }
+        __disable_irq();
+        memcpy(&copyBuffer[i], PROFILE_START_PTR+i, sizeof(Profile_checkSum));   // Copy profile from flash to RAM
+        __enable_irq();
+    }
+    int r = flashWrite(copyBuffer, sizeof(copyBuffer), sector_start_adress[PROFILE_SECTOR], PROFILE_SECTOR);
+
+    if (r == 0) {
+        return setProfile(index);
+    }
+    return false;
+}
+
+// Fetch pointer to the last written freeze frame object if available, do not copy contents to RAM
+bool FreezeFrame::getFrame(FreezeFrame **frame)
+{    
+    if (FreezeFrame::getError() == false) {
+        *frame = NULL;    // No frame available, return
+        return false;
+    }
+    
+    FreezeFrame* ptr = FRAME_PTR;                                                               // Set pointer to frame
+    uint32_t checksumOp = checksum(&(ptr->op.op), sizeof(OperatingInfo));                       // Compute checksum for operatingInfo section
+    uint32_t checksumParam = checksum(&(ptr->param.param), sizeof(Profile));                    // Compute checksum for profile section
+
+    if (checksumOp == ptr->op.BSDchecksum && checksumParam == ptr->param.BSDchecksum) {         // Compare to stored checksum
+        *frame = ptr;       // Checksum passed, pass out the ptr
+        return true;
+    }
+    *frame = NULL;          // Checksum failed, write NULL
+    return false;
+}
+
+// Copy contents of RAM to flash, usually called on error to record the internal state upon enetering fault conditions
+bool FreezeFrame::writeFrame()
+{
+    // Compute and record the checksums
+    frame.param.BSDchecksum = checksum(&(frame.param.param), sizeof(Profile));
+    frame.op.BSDchecksum = checksum(&(frame.op.op), sizeof(OperatingInfo));
+
+    int r = flashWrite(&frame, sizeof(FreezeFrame), sector_start_adress[FRAME_SECTOR], FRAME_SECTOR);
+    if (r == 0) {
+        // Set the error flag in the header
+        NV_Data temp;
+        NV_Data* header = HEADER_PTR;
+        __disable_irq();
+        memcpy(&temp, header, sizeof(NV_Data));
+        __enable_irq();
+        temp.frameStoredFlag = ~0;
+        int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+        return (i == 0);
+    }
+    return false;                               // False if flash operations failed
+}
+
+// Get the non-volatile freeze Frame recorded flag that indicates whether a freeze frame is available
+bool FreezeFrame::getError()
+{
+    NV_Data* header = HEADER_PTR;
+    return header->frameStoredFlag;
+}
+// Clear the frame error, for ex. when the frame is read by user
+bool FreezeFrame::clearError()
+{
+    NV_Data temp;
+    NV_Data* header = HEADER_PTR;
+    __disable_irq();
+    memcpy(&temp, header, sizeof(NV_Data));
+    __enable_irq();
+    temp.frameStoredFlag = 0;
+    int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+    return (i == 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/DataStructures.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,13 @@
+#ifndef DATA_STRUCTURES_H
+#define DATA_STRUCTURES_H
+
+#include "FreezeFrame.h"
+#include "TemporaryData.h"
+
+extern FreezeFrame frame;       // Allow global access to the current RAM frame as well
+extern Profile *param;          // Allow global access to the current RAM profile inside of FreezeFrame frame
+extern OperatingInfo *op;       // Allow global access to the current RAM operating info inside of FreezeFrame frame
+
+extern TemporaryData tempData;  // Allow global access to the RAM temporary data object for stuff that does not belong in a profile nor is it relevant to operating info in a freeze frame
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/DefaultProfile.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,35 @@
+#ifndef DEFAULT_PROFILE_H
+#define DEFAULT_PROFILE_H
+
+#include "Profile.h"
+
+// The default flash configuration object, used as backup/failsafe in case other profiles are corrupted
+Profile defaultProfile = {
+    -2,         // GLV charging current limit
+    5,          // GLV discharging current limit
+    1.4,        // GLV battery nominal capacity (Ah)
+    50,         // GLV battery current filter taps
+    
+    1.0,        // DC-DC turn-on threshold current
+    20,         // DC-DC over current limit
+    1,          // DC-DC start-up delay
+    1,          // DC-DC stop delay
+    50,         // DC-DC current filter taps
+    
+    10,         // IMD Latch soft-delay on startup
+    10,         // AMS Latch soft-delay on startup
+    60,         // Interal over-temp
+    
+    true,       // CAN noack
+    true,       // Extended serial display
+    
+    CAN_TX_SIZE,        // CAN tx size
+    CAN_RX_SIZE,        // CAN rx size
+    SERIAL_BAUD,        // Serial port baudrate
+    TX_SIZE,            // Serial tx buffer size
+    XBEE_BAUD,          // Xbee baudrate
+    XBEE_TX_SIZE,       // Xbee tx buffer size
+    XBEE_RX_SIZE,       // Xbee rx buffer size
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/FreezeFrame.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,24 @@
+#ifndef FREEZE_FRAME_H
+#define FREEZE_FRAME_H
+
+#include "OperatingInfo.h"
+#include "Profile.h"
+
+// Freeze frame class, containing everything that describes the current operation of the AMS
+class FreezeFrame
+{
+public:
+    FreezeFrame();
+    Profile_checkSum param;              // Profile - contains parameters and configurable settings
+    OperatingInfo_checkSum op;           // OperatingInfo - contains all the data gathered from sensors and processed outputs
+
+    static bool getFrame(FreezeFrame **frame);  // Fetch pointer to the last stored freeze frame if available
+    static bool writeFrame();                   // Write the current RAM frame to flash as a freeze frame
+    static bool prepare();                      // Call on microcontroller startup, pre-erase a flash sector so flash write occurs very fast upon error
+
+    static bool getError();     // Get last marked error state
+    static bool setError();     // Mark error
+    static bool clearError();   // Reset (clear) the freeze frame error flag
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/OperatingInfo.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,102 @@
+#ifndef OPERATING_INFO_H
+#define OPERATING_INFO_H
+
+#include "Constants.h"
+#include <stdint.h>
+#include <time.h>
+
+enum OperatingMode {
+    // MODES
+    FAULT=0,
+    OKAY=1,
+};
+
+// Items here will prevent moving from STANDBY to IDLE
+enum TopLevelFaults {
+    WATCHDOG            = 1<<0,     // Last reset caused by WDT
+    BROWNOUT            = 1<<1,     // Last reset caused by Brownout
+    CAN_FAULT           = 1<<2,     // CAN buffer overflow
+    
+    INT_OVER_TEMP       = 1<<3,     // Temp. sensor on Charge FET is over limit 
+    IMD_LATCH           = 1<<4,     // Fault detected on the IMD latch circuit
+    AMS_LATCH           = 1<<5,     // Fault detected on the AMS latch circuit
+    IMD_FAULT           = 1<<6,     // IMD not okay or not running
+    DCDC_FAULT          = 1<<7,
+    GLVBAT_FAULT        = 1<<8,
+    FREEZE_FRAME        = 1<<9,    // There is an error freeze frame captured, waiting to be inspected by user
+};
+
+enum SignalFlags {
+    SHUTDOWN_CLOSED     = 1<<0,     // Shutdown circuit ready (closed)
+    AIRS_CLOSED         = 1<<1,     // AIRs Closed (CAN)
+    CHARGER_DET         = 1<<2,     // Charger detected (CAN)
+};
+
+// Contains all of the relevant operating data gathered from sensors and the prcoessed outputs
+// NOTE there are no mutexes/protection mechanisms here!  Ensure single producer, single consumer for every item
+class OperatingInfo
+{
+public:
+     // Diagnostic data
+    char mode;
+    char signals;
+    uint16_t faultCode;
+    time_t SysTime;
+    time_t startTime;
+    signed char profileIndex;
+    bool profileModded;
+        
+    struct {            // GLV-Battery related
+        float current;
+        float SOC;
+        float Ah;
+        float capacity;
+        char error;
+    } glvBat;
+    
+    struct {            // DC-DC converter and PWM channels
+        char status;
+        float current;
+        struct {
+            float pump1;
+            float pump2;
+            float fan1;
+            float fan2;
+        } request;
+        struct {
+            float pump1;
+            float pump2;
+            float fan1;
+            float fan2;
+        } actual;
+    } dcdc;
+
+    struct {                // IMD
+        char status;
+        float resistance;
+        char error;
+    } imd;
+    
+    struct {                // Latch circuit supervisor
+        char imd;
+        char ams;
+    } latch;
+    
+    struct MsgCounter {     // Telemetry tracking
+        unsigned int msgIn;
+        unsigned int msgOut;
+        float rateOut;
+    };
+    struct MsgCounter xbee1;
+    struct MsgCounter xbee2;
+    
+    char switchState;
+    float internalTemp;
+};
+class OperatingInfo_checkSum
+{
+public:
+    OperatingInfo op;
+    uint32_t BSDchecksum;
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/Profile.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,88 @@
+#ifndef _Profile_H
+#define _Profile_H
+
+// The defines below are used to create change functions
+// The change functions allow changing things in the Profile with limits on allowed values
+#define CHANGE_FUNC(NAME, LLIM, ULIM)               \
+template<class T>                                   \
+bool change_##NAME(T data) {                        \
+    if (data > ULIM || data < LLIM) return false;   \
+    if (data == NAME) return false;                 \
+    NAME = data;                                    \
+    return true;                                    \
+}
+
+// Stores configurable parameters like limits, constants, etc.
+// NOTE there are no mutexes/protection mechanisms! Ensure single producer (inCommands.cpp - main thread), single consumer
+// To change a member of class Profile, use the change functions defined.  They will enforce limits on the changes.
+class Profile
+{
+public:
+    // GLV Battery
+    float chargeCurrent;
+    float dischargeCurrent;
+    float nominalCapacity;
+    unsigned int glvBat_taps;
+    CHANGE_FUNC(chargeCurrent,    -10, 0)
+    CHANGE_FUNC(dischargeCurrent,   0, 10)
+    CHANGE_FUNC(nominalCapacity,    0, 10)
+    CHANGE_FUNC(glvBat_taps,        1, 1000)
+
+    // DC-DC converter
+    float dcdcThreshold;
+    float dcdcOverCurrent;
+    float dcdcStartDelay;
+    float dcdcStopDelay;
+    unsigned int dcdc_taps;
+    CHANGE_FUNC(dcdcThreshold,      0, 10)
+    CHANGE_FUNC(dcdcOverCurrent,    0, 100)
+    CHANGE_FUNC(dcdcStartDelay,     0, 10)
+    CHANGE_FUNC(dcdcStopDelay,      0, 10)
+    CHANGE_FUNC(dcdc_taps,          1, 1000)
+
+    // Latch Circuits
+    float imdStartDelay;
+    float amsStartDelay;
+    CHANGE_FUNC(imdStartDelay,      0, 100)
+    CHANGE_FUNC(amsStartDelay,      0, 100)
+
+    // Over-temp
+    float internalOverTemp;
+    CHANGE_FUNC(internalOverTemp,   0, 100)
+
+    // Boolean states / modes
+    bool CANnoAck;                      // Is noAck mode on?
+    bool extendedSerial;
+    CHANGE_FUNC(CANnoAck,           0, 1)
+    CHANGE_FUNC(extendedSerial,     0, 1)
+
+    // Buffer sizes
+    unsigned int CANtxSize;             // Size of CAN TX buffer
+    unsigned int CANrxSize;             // Size of CAN RX buffer
+    unsigned int SerialBaud;            // Serial port baudrate
+    unsigned int SerialTxSize;          // Serial TX buffer size
+    unsigned int XbeeBaud;              // Serial port baudrate
+    unsigned int XbeeTxSize;            // Serial TX buffer size
+    unsigned int XbeeRxSize;            // Serial TX buffer size
+    CHANGE_FUNC(CANtxSize,          1,    1000)
+    CHANGE_FUNC(CANrxSize,          1,    1000)
+    CHANGE_FUNC(SerialBaud,         9600, 921600)
+    CHANGE_FUNC(SerialTxSize,       1,    10000)
+    CHANGE_FUNC(XbeeBaud,           9600, 921600)
+    CHANGE_FUNC(XbeeTxSize,         1,    10000)
+    CHANGE_FUNC(XbeeRxSize,         1,    10000)
+
+    // Load / store / fetch functions
+    static bool loadProfile(int index);                     // Load profile from flash into the current RAM object, 0=default, -1=freeze frame
+    static bool saveProfile(int index);                     // Save the current RAM profile into flash to index, 0=default
+    static bool getProfile(Profile **ptr, int index);       // Retrieve the handle to a profile
+    static bool loadStartUp();                              // Load the profile on startup
+    static int  usingProfile();                             // Return the last profile loaded into RAM, 0=default, -1=freeze frame
+};
+class Profile_checkSum
+{
+public:
+    Profile param;
+    uint32_t BSDchecksum;
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/TemporaryData.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,29 @@
+#ifndef TEMPORARY_DATA_H
+#define TEMPORARY_DATA_H
+
+#include "FreezeFrame.h"
+#include <string.h>
+
+// For temporary data that changes frequently in operation for inter-task communication.
+// NOTE there are no mutexes/protection mechanisms.  Ensure single producer, single consumer.
+class TemporaryData
+{
+public:
+    TemporaryData() {
+        memset(this, 0, sizeof(TemporaryData));
+        parseGoodChar = ' ';    // Start with a blank after the "Command Input:" line
+        viewProfileNum = -2;    // -2 = display live RAM profile
+    }
+    char inputStr[RX_SIZE+1];   // Stores the user-typed command input string for serial interface
+    char parseGoodChar;         // Character to display for string parsed and successful operation
+
+    Profile* viewProfile;       // Used by "profile view xxx" command, switch the profile display to this profile if not NULL, otherwise view the current RAM frame
+    int viewProfileNum;         // Index of the profile being viewed
+
+    FreezeFrame* freeze;        // Used by "view freeze" command, display this freezeFrame over serial if not NULL, otherwise view the current RAM frame
+
+    bool timeSynced;            // Used to check whether or not RTC time has been synchronized with the AMS (master clock source)
+    void* wdtThreadId;          // ID to access the watchdog check-in thread
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/IAP.lib	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/okano/code/IAP/#cee1a2a734c9
--- a/IOobjects/CAN_RxIDs.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/CAN_RxIDs.h	Sat Feb 07 08:54:51 2015 +0000
@@ -1,18 +1,49 @@
 #ifndef CAN_RXIDS_H
 #define CAN_RXIDS_H
 
-#define BASE_ID             0x500              // Start at 0x500 for this device
+#define RX_ID_BASE             0x580              // Start at 0x500 for this device
 
-// Receive IDs
-#define FAN_CONTROL_ID      BASE_ID + 0x80
-#define PUMP_CONTROL_ID     BASE_ID + 0x81
-#define DCDC_CONTROL_ID     BASE_ID + 0x82
+// Receive Command IDs
+// NOTE after each command is parsed, a message will return back with the same ID of length 1 with data[0]=1 for success and data[0]=0 for fail
+#define RESET_RX_ID         RX_ID_BASE + 0x01    // Reset over CAN
+#define RESETCLEAR_RX_ID    RX_ID_BASE + 0x02    // Clear all errors and then reset, clean slate startup
+#define CAPTURE_RX_ID       RX_ID_BASE + 0x03    // Artificially capture freeze frame
+#define CLEAR_RX_ID         RX_ID_BASE + 0x04    // Clear a freeze frame error (0), clear every fault (1)
+#define TIME_RX_ID          RX_ID_BASE + 0x05    // Change the time (MM DD YY HH MM SS - 6 bytes)
+#define PROFILE_RX_ID       RX_ID_BASE + 0x06    // Byte[0] = load/write Byte[1] = profile #
+#define SOC_RX_ID           RX_ID_BASE + 0x07    // Change the current SOC to some float from 0 to 1
+#define AH_RX_ID            RX_ID_BASE + 0x08    // Change the current ampHours to some float from 0 to capacity
+
+#define FAN_CONTROL_ID      RX_ID_BASE + 0x10
+#define PUMP_CONTROL_ID     RX_ID_BASE + 0x11
 
-#define GLVBAT_SETSOC_ID    BASE_ID + 0x90
-#define GLVBAT_SETAH_ID     BASE_ID + 0x91
-#define GLVBAT_SETCAPAC_ID  BASE_ID + 0x92
+// Profile Data Change Requests
+#define PROFILE_CHARGECURRENT_RX_ID     RX_ID_BASE + 0x20
+#define PROFILE_DISCHARGECURRENT_RX_ID  RX_ID_BASE + 0x21
+#define PROFILE_NOMINALCAPACITY_RX_ID   RX_ID_BASE + 0x22
+#define PROFILE_GLVBATTAPS_RX_ID        RX_ID_BASE + 0x23
+#define PROFILE_DCDCONTHRESHOLD_RX_ID   RX_ID_BASE + 0x24
+#define PROFILE_DCDCOVERCURRENT_RX_ID   RX_ID_BASE + 0x25
+#define PROFILE_DCDCSTARTDELAY_RX_ID    RX_ID_BASE + 0x26
+#define PROFILE_DCDCSTOPDELAY_RX_ID     RX_ID_BASE + 0x27
+#define PROFILE_DCDC_TAPS_RX_ID         RX_ID_BASE + 0x28
+#define PROFILE_IMDSTARTDELAY_RX_ID     RX_ID_BASE + 0x29
+#define PROFILE_INTERNALOVERTEMP_RX_ID  RX_ID_BASE + 0x2A
+#define PROFILE_CANNOACK_RX_ID          RX_ID_BASE + 0x2B
+#define PROFILE_EXTENDSERIAL_RX_ID      RX_ID_BASE + 0x2C
+#define PROFILE_CANTXSIZE_RX_ID         RX_ID_BASE + 0x2D
+#define PROFILE_CANRXSIZE_RX_ID         RX_ID_BASE + 0x2E
+#define PROFILE_SERIALBAUD_RX_ID        RX_ID_BASE + 0x2F
+#define PROFILE_SERIALTXSIZE_RX_ID      RX_ID_BASE + 0x30
+#define PROFILE_XBEEBAUD_RX_ID          RX_ID_BASE + 0x31
+#define PROFILE_XBEETXSIZE_RX_ID        RX_ID_BASE + 0x32
+#define PROFILE_XBEERXSIZE_RX_ID        RX_ID_BASE + 0x33
 
-#define AMS_MODE_ID         0x301
-#define STEERING_RESET_ID   0x602
+#define RX_ID_END                       0x5FF           // End of SYS MGMT receive block
+
+// Messages from other devices in the car
+#define CHARGER_ERR_ID          0x700       // Is the charger connected?
+#define AMS_MODE_ID             0x301       // Contains AIRs data
+#define GLOBAL_CAR_RESET_RX_ID  0x602       // Reset button from steering wheel
 
 #endif
\ No newline at end of file
--- a/IOobjects/CAN_TxIDs.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/CAN_TxIDs.h	Sat Feb 07 08:54:51 2015 +0000
@@ -1,20 +1,27 @@
 #ifndef CAN_TXIDS_H
 #define CAN_TXIDS_H
 
-#define BASE_ID                 0x500              // Start at 0x500 for this device
+#define BASE_ID                 0x500                // Start at 0x500 for this device
 
 // Transmit IDs - System Mgmt Specific
 // Operating diagnostics
 #define SYS_ERROR_ID            BASE_ID + 0x00       // Error frame - critical errors that require shutdown
-#define SYS_XBEE1_ID            BASE_ID + 0x01       // Message in/out counter for xbee1
-#define SYS_XBEE2_ID            BASE_ID + 0x02       // Message in/out counter for xbee2
-#define SYS_TEMP_ID             BASE_ID + 0x03       // Internal temperature of the glv battery chargerFET
+#define SYS_MODE_ID             BASE_ID + 0x01       // Operating mode
+#define SYS_FLAGS_ID            BASE_ID + 0x02       // Signals
+#define SYS_PROFILE_ID          BASE_ID + 0x03       // Profile being used
+#define SYS_TIME_ID             BASE_ID + 0x04       // SysTime and startup time
+#define SYS_XBEE1_MSG_ID        BASE_ID + 0x05       // Message in/out counter for xbee1
+#define SYS_XBEE1_RATE_ID       BASE_ID + 0x06       // Message in/out counter for xbee1
+#define SYS_XBEE2_MSG_ID        BASE_ID + 0x07       // Message in/out counter for xbee2
+#define SYS_XBEE2_RATE_ID       BASE_ID + 0x08       // Message in/out counter for xbee2
+#define SYS_TEMP_ID             BASE_ID + 0x09       // Internal temperature of the glv battery chargerFET
 
 // GLV Battery
 #define SYS_GLV_CURRENT_ID      BASE_ID + 0x10       // GLV battery current
 #define SYS_GLV_CAPACITY_ID     BASE_ID + 0x11       // GLV battery capacity setting
 #define SYS_GLV_AH_ID           BASE_ID + 0x12       // GLV battery amphours
 #define SYS_GLV_SOC_ID          BASE_ID + 0x13       // GLV battery SOC
+#define SYS_GLV_ERROR_ID        BASE_ID + 0x14       // GLV battery error byte
 
 // DC-DC Converter
 #define SYS_DCDC_CURRENT_ID     BASE_ID + 0x20       // DC-DC current 
--- a/IOobjects/IOobjects.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/IOobjects.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -16,19 +16,14 @@
     DigitalIn(P1_28, PullDown),    // Sense11 - TSMS/tractive enable
 };
 PollSwitch switches(sw, sizeof(sw)/sizeof(sw[0]));   // Shutdown switch sense pins (SWITCH PIN ARRAY, NUM PINS)
-CANBuffer can(CAN2, MEDIUM, P4_28);                  // Buffered CAN interface (PORT, SPEED, SILENT PIN)
-CoulombCounter glvBat(p19, FAST_LOOP*1000);          // Coulomb counter battery monitor for GLV Battery (CURRENT SENSE PIN, INTEGRATION TIME)
+CANBuffer can(CAN2, MEDIUM, P4_28, 1, 1);            // Buffered CAN interface (PORT, SPEED, SILENT PIN)
+CoulombCounter glvBat(p19, FAST_LOOP*1000, 1);       // Coulomb counter battery monitor for GLV Battery (CURRENT SENSE PIN, INTEGRATION TIME)
 IMD imd(P1_26);                                      // IMD PWM sense channel to read status and resistance (IMD PWM PIN)
-LatchMonitor AMSlatch(P0_18, P0_22, START_DELAY*1000);  // Supervisor for AMS hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
-LatchMonitor IMDlatch(P0_17, P0_21, START_DELAY*1000);  // Supervisor for IMD hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
-MODSERIAL pc(USBTX, USBRX, TX_SIZE, RX_SIZE);        // Serial to computer for diagnostics, 3kB output buffer, 256 byte input buffer
-DC_DC dcdc(p18, p20, p26, p25, p24, p23, 0.01, 1);   // DC-DC converter & high-current load control (CONTROL PIN, CURRENT SENSE PIN, FAN1 PIN, FAN2 PIN, PUMP1 PIN, PUMP2 PIN, PWM PERIOD (sec), FULL-SCALE SLEW (sec))
+LatchMonitor AMSlatch(P0_18, P0_22);                 // Supervisor for AMS hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
+LatchMonitor IMDlatch(P0_17, P0_21);                 // Supervisor for IMD hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
+MODSERIAL pc(USBTX, USBRX, 1, RX_SIZE);              // Serial to computer for diagnostics, 3kB output buffer, 256 byte input buffer
+DC_DC dcdc(p18, p20, p26, p25, p24, p23, 0.01, 5, 1);// DC-DC converter & high-current load control (CONTROL PIN, CURRENT SENSE PIN, FAN1 PIN, FAN2 PIN, PUMP1 PIN, PUMP2 PIN, PWM PERIOD (sec), FULL-SCALE SLEW (sec))
 Temperature internalTmp(&NXFT15XH103_TABLE, p15);    // Temperature conversion look-up table for internal temperature on the GLV bat charger FET (TABLE PTR, PIN)
-
-XbeeManager xbeeRelay(p9, p10, p13, p14, XBEE_BAUD, XBEE_TX_SIZE, XBEE_RX_SIZE);
+XbeeManager xbeeRelay(p9, p10, p13, p14, XBEE_BAUD, 1, 1);
 //XbeeRelay xbee;
-
 DigitalOut extras[] = {(p16), (p17)};                // Unused analog pins driven low
-Inputs data;
-CANinputs CANdata;
-TempData tempData;
\ No newline at end of file
--- a/IOobjects/IOobjects.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/IOobjects.h	Sat Feb 07 08:54:51 2015 +0000
@@ -16,6 +16,7 @@
 #include "Temperature.h"
 #include "Watchdog.h"
 #include "XbeeManager.h"
+#include "DataStructures.h"
 //#include "XbeeRelay.h"
 
 extern CANBuffer can;
@@ -31,58 +32,4 @@
 //extern XbeeRelay xbee;
 extern XbeeManager xbeeRelay;
 
-// Shared data updated by producer threads
-class Inputs {
-public:
-    float glvCurrent; 
-    float glvAmphours;
-    float glvSOC;
-    float glvCapacity;
-    bool  glvOverCurrent;       // Error bit, turn off car
-    
-    char  dcdcStatus;
-    bool  dcdcError;            // Error bit, turn off car
-    float dcdcCurrent;
-    float dcdcFan1Duty;
-    float dcdcFan2Duty;
-    float dcdcPump1Duty;
-    float dcdcPump2Duty;
-    
-    char  imdStatus;
-    float imdResistance;
-    bool  imdError;            // Error bit, turn off car
-
-    char AMSlatchError;        // Error bit, turn off car
-    char IMDlatchError;        // Error bit, turn off car
-
-    char switchState;
-    
-    float internalTemp;
-    bool  internalOverTemp;    // Error bit, turn off car
-        
-    bool watchdogReset;        // Error bit, turn off car
-    bool canFault;             // Error bit, turn off car
-
-    char errorFrame;
-};
-
-// Shared data updated by CAN messeges
-class CANinputs {
-public:
-    bool  airsClosed;
-    float dcdcFan1Duty;
-    float dcdcFan2Duty;
-    float dcdcPump1Duty;
-    float dcdcPump2Duty;
-};
-class TempData {
-public:
-    char inputStr[RX_SIZE+1];
-    char parseGoodChar;
-    void* wdtThreadId;
-};
-
-extern CANinputs CANdata;
-extern Inputs data;
-extern TempData tempData;
-#endif
\ No newline at end of file
+#endif
--- a/Libs/CANBuffer.lib	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/CANBuffer.lib	Sat Feb 07 08:54:51 2015 +0000
@@ -1,1 +1,1 @@
-http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#b2b886bdb080
+http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#bf7adaf90781
--- a/Libs/CoulombCounter/CoulombCounter.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -1,65 +1,109 @@
 #include "CoulombCounter.h"
 
 const float MSEC_HRS = 2.77778e-7;                          // Multiplier to convert milliseconds to hours
-const float BAT_ISENSE_MULTIPLIER = /*6.2299*/6.640114;     // Multiplier to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
-const float BAT_ISENSE_OFFSET = /*-0.5*BAT_ISENSE_MULTIPLIER*/-3.344968; // Offset to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
-const float BAT_ISENSE_LIMS = 3.0;                          // Over-current limit = +/- 3A
+const float BAT_ISENSE_MULTIPLIER = 6.640114;               // Multiplier to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
+const float BAT_ISENSE_OFFSET = -3.344968;                  // Offset to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
 
-CoulombCounter::CoulombCounter(PinName _pin, int _mSec) : BatISense(_pin) {
+CoulombCounter::CoulombCounter(PinName _pin, int _mSec, unsigned int size) : BatISense(_pin)
+{
     mSec = _mSec;
+    _size = 0;
+    errorPacket = 0;
+    overChargeCurrent = 0;
+    overDischargeCurrent = 0;
     
     // Default capacity if blank or corrupted
-    float capReg = store.read(rtcGPREG_capacity);
-    if (capReg < 1.0 || capReg > 5.0) {             // Bad, write default
-        store.write(defaultAh, rtcGPREG_capacity);
+    float capReg = store.read(GPREG_AH_CAPACITY);
+    if (capReg < MIN_CAPACITY_SETTING || capReg > MAX_CAPACITY_SETTING) {   // Bad, write default
+        store.write(DEFAULT_AH, GPREG_AH_CAPACITY);
+        errorPacket |= CAP_INIT;
     }
-    
+
     // Default SOC if blank or corrupted
-    float Ah = store.read(rtcGPREG_counter);
-    if (Ah < 0 || Ah > defaultAh) {                    // Bad, write default
-        store.write(defaultSOC*defaultAh, rtcGPREG_counter);
+    float Ah = store.read(GPREG_AH_COUNTER);
+    if (Ah < 0 || Ah > MAX_CAPACITY_SETTING) {                              // Bad, write default
+        store.write(DEFAULT_SOC*DEFAULT_AH, GPREG_AH_COUNTER);
+        errorPacket |= SOC_INIT;
     }
-    
+
+    // Allocate the buffer
+    buffArr = new float [size];
+    if (buffArr == 0) {
+        error("Coulomb Counter failed to allocate memory");
+    }
+    _size = size;
+    tracker = 0;
+
     // Take the initial readings, fill the buffer
-    for (int i = 0; i < CC_FILTER_TAPS; i++) {
+    for (int i = 0; i < _size; i++) {
         buffArr[i] = BatISense.read()*BAT_ISENSE_MULTIPLIER+BAT_ISENSE_OFFSET;
     }
     updateAvg();      // Get avg and fill out overCurrent flag
-    tracker=0;
+}
+void CoulombCounter::setup(float *charge, float *discharge) {
+    overChargeCurrent = charge;
+    overDischargeCurrent = discharge;
 }
+bool CoulombCounter::size(unsigned int size)
+{
+    if (_size == size) return false;
 
-void CoulombCounter::sample() {
+    // Get a current sample, this will be used to fill the new buffer
+    updateAvg();
+    float avg = current();
+
+    float* newArr = new float [size];
+    if (newArr == 0) return false;
+    else {
+        delete[] buffArr;   // Free old
+        __disable_irq();
+
+        buffArr = newArr;   // Transfer
+        _size = size;       // Set new size
+        for (int i = 0; i < _size; i++) buffArr[i] = avg;   // Fill with old avg
+
+        __enable_irq();
+        return true;
+    }
+}
+void CoulombCounter::sample()
+{
     // Take the reading
     float currentSample = BatISense.read()*BAT_ISENSE_MULTIPLIER+BAT_ISENSE_OFFSET;
-        
+
     // Integrate
     float f = ampHours()-currentSample*mSec*MSEC_HRS;
-    
+
     // Bound to valid range
     float cap = capacity();
     if (f > cap) f = cap;
     if (f < 0) f = 0;
-    
+
     // Write
-    store.write(f, rtcGPREG_counter);
+    store.write(f, GPREG_AH_COUNTER);
+
+    // Add to filter
+    buffArr[tracker++] = currentSample;
+    tracker %= _size;
     
-    // Add to filter
-    buffArr[tracker] = currentSample;
-    tracker++;
-    if (tracker >= CC_FILTER_TAPS) tracker = 0;
+    // Update the avg, check for over-current
     updateAvg();
 }
 
-void CoulombCounter::updateAvg() {
+void CoulombCounter::updateAvg()
+{
     float avg = 0;
-    for (int i = 0; i < CC_FILTER_TAPS; i++) {
-        avg += buffArr[i];   
+    for (int i = 0; i < _size; i++) {
+        avg += buffArr[i];
     }
-    avg /= CC_FILTER_TAPS;
-    if (abs(avg) > BAT_ISENSE_LIMS) overCurrent = true;
+    avg /= _size;
     currentFiltered = avg;
-}
+    
+    errorPacket = 0;
+    if (overChargeCurrent == 0) return;     // setup() not run yet
 
-bool CoulombCounter::overCurrentDetected() {
-    return overCurrent;
+    if (avg > *overDischargeCurrent)    errorPacket |= OVER_DISCHARGE_I;
+    else                                errorPacket &= ~OVER_DISCHARGE_I;
+    if (avg < *overChargeCurrent)       errorPacket |= OVER_CHARGE_I;
+    else                                errorPacket &= ~OVER_CHARGE_I;
 }
\ No newline at end of file
--- a/Libs/CoulombCounter/CoulombCounter.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.h	Sat Feb 07 08:54:51 2015 +0000
@@ -2,58 +2,81 @@
 #define _FILE_CURRENTMONITOR_H
 
 #include "mbed.h"
-#include "CANBuffer.h"
 #include "RTCStore.h"
 
-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 = 50;                              // Nominal current draw with only Sys.Mgmt plugeed in should be 0.182A for calibration
-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
+#define GPREG_AH_COUNTER    0                                // rtc GPREG offset for the coulomb counter
+#define GPREG_AH_CAPACITY   1                                // rtc GPREG offset for the capacity spec
+
+const float DEFAULT_AH = 1.5;                                // Default amphours of battery, in case store read is bad/empty
+const float DEFAULT_SOC = 0.5;                               // Defualt SOC, in case of bad read/store
+
+const float MIN_CAPACITY_SETTING = 0.5;                      // Lowest allowable capacity setting
+const float MAX_CAPACITY_SETTING = 10;                       // Largest allowable capacity setting
 
-class CoulombCounter {
-    
+enum GLV_BAT_ERROR {
+    OVER_CHARGE_I       = 1<<0,     // Charge current exceeded (after filter)
+    OVER_DISCHARGE_I    = 1<<1,     // Discharge current exceeded (after filter)
+    CAP_INIT            = 1<<2,     // Capacity RTC register was invalid on startup
+    SOC_INIT            = 1<<3,     // SOC RTC register was invalid on startup
+};
+
+class CoulombCounter
+{
+
 public:
 
     // Configures for a certain pin, millisecond sample period, and which GPREG in store to use to store the ampHours
-    CoulombCounter(PinName _pin, int _mSec);
-    
-    bool overCurrentDetected();                                 // Sensor above range
-    float current() { return currentFiltered; }                 // Last current reading in Amps
+    CoulombCounter(PinName _pin, int _mSec, unsigned int size=50);
+    void setup(float* overChargeCurrent, float* overDischargeCurrent);
+    bool size(unsigned int size);
+    char readError() {
+        return errorPacket;
+    }
+    void clearError() {
+        errorPacket = 0;
+    }
+    float current() {
+        return currentFiltered;    // Last current reading in Amps
+    }
     void sample();
-    
-    float capacity() { return store.read(rtcGPREG_capacity); }
-    float SOC() { return ampHours()/capacity(); }
-    float ampHours() { return store.read(rtcGPREG_counter); }
-    
+
+    float capacity() {
+        return store.read(GPREG_AH_CAPACITY);
+    }
+    float SOC() {
+        return ampHours()/capacity();
+    }
+    float ampHours() {
+        return store.read(GPREG_AH_COUNTER);
+    }
     bool changeCapacity(float _ampHours) {
         if (_ampHours < MIN_CAPACITY_SETTING || _ampHours > MAX_CAPACITY_SETTING) return false;
-        store.write(_ampHours, rtcGPREG_capacity);
+        store.write(_ampHours, GPREG_AH_CAPACITY);
         return true;
     }
     bool resetToSOC(float _SOC) {
         if (_SOC < 0 || _SOC > 1) return false;
-        store.write(_SOC*capacity(), rtcGPREG_counter);
+        store.write(_SOC*capacity(), GPREG_AH_COUNTER);
         return true;
     }
     bool resetToAh(float _ampHours) {
-        if (_ampHours < 0 || _ampHours > capacity()) return false; 
-        store.write(_ampHours, rtcGPREG_counter);
+        if (_ampHours < 0 || _ampHours > capacity()) return false;
+        store.write(_ampHours, GPREG_AH_COUNTER);
         return true;
     }
 
 private:
-    //Ticker sampler;         // Used to capture next sample and coulomb count
-    RTCStore store;
-    volatile bool overCurrent;
-    void updateAvg();         // Used to get average current and update flags
-    int mSec;
-    volatile float currentFiltered;
-    AnalogIn BatISense;     // Analog input pin
+    RTCStore store;                 // Access to the RTC registers (battery-backed)
+    unsigned int _size;             // Size of buffer
+    void updateAvg();               // Used to get average current and update flags
+    int mSec;                       // Integration time
+    float currentFiltered;          // Filtered current, last result of updateAvg()
+    AnalogIn BatISense;             // Analog input pin
+    char errorPacket;               // Errors (over-current)
+    float *buffArr;                 // The buffer itself
+    unsigned int tracker;           // Position to load next sample
     
-    volatile float buffArr[CC_FILTER_TAPS];
-    volatile unsigned int tracker;
+    float* overChargeCurrent;       // Charge current limit
+    float* overDischargeCurrent;    // Discharge current limit
 };
 #endif
\ No newline at end of file
--- a/Libs/DC_DC/DC_DC.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/DC_DC/DC_DC.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -4,78 +4,118 @@
 #define TURN_OFF dcdcControl = 1; isControlPinOn = false;
 #define TURN_ON  dcdcControl = 0; isControlPinOn = true;
 
-const float CURRENT_MULTIPLIER_DEFAULT = 41.35338345864663;      // Full scale amps
-const float CURRENT_OFFSET_DEFAULT = 0.0909090909090909;         // Float adc reading for 0 amps
+const float CURRENT_MULTIPLIER_DEFAULT = 41.35338345864663;     // Full scale amps
+const float CURRENT_OFFSET_DEFAULT = 0.0909090909090909;        // Float adc reading for 0 amps
 
-const float DC_DC_ON_THRESHOLD     = 0.5;                    // Current draw required in order to declare it as on
-const int   STARTUP_DELAY_MS       = 1000;                   // DC-DC converter startup time in milliseconds
-const float OVER_CURRENT_THRESHOLD = 25;                     // Overcurrent threshold
-const float CURRENT_SENSOR_LLIM    = -1.0;                   // Lowest allowable reading before declaring sensor broken
-const float CURRENT_SENSOR_ULIM    = 33;                     // Sensor is at 5V rail, broken
-const int   STOP_DELAY_MS          = 1000;                   // Amount of time given to turn-off before flagging error
+const float CURRENT_SENSOR_LLIM    = -1.0;                      // Lowest allowable reading before declaring sensor broken
+const float CURRENT_SENSOR_ULIM    = 33;                        // Sensor is at 5V rail, broken
 
-DC_DC::DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew) :
+DC_DC::DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew, unsigned int size) :
     dcdcControl(_dcdcPin, 1), dcdcCurrent(_dcdcCurrent), fan1(_fan1, period, slew), fan2(_fan2, period, slew), pump1(_pump1, period, slew), pump2(_pump2, period, slew)
 {
     TURN_OFF
     currentOffset = CURRENT_OFFSET_DEFAULT;
-    starting = false;
-    stopping = false;
     buffTracker = 0;
     wait_ms(10);            // Make sure Hall effect IC is ready
     startTimer.reset();
     stopTimer.reset();
     status=0;
+    _size = 0;
+    onThreshold = 0;
+    startDelay = 0;
+    stopDelay = 0;
+    overCurrent = 0;
+    
+    filterBuff = new float[size];
+    if (filterBuff == 0) {
+        error("DC-DC failed to allocate memory");   
+    }
+    _size = size;
     
     // Fill up the buffer
-    for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+    for (int i = 0; i < _size; i++) {
         filterBuff[i] = (dcdcCurrent - currentOffset) * CURRENT_MULTIPLIER_DEFAULT;
         wait_ms(10);
     }
-    updateCurrent();                // Compute avg
-    sample();                       // Use sample() function to check for errors
     
-    if (!critError) {               // If no errors, zero the sensor
-        currentOffset = (current / CURRENT_MULTIPLIER_DEFAULT) + CURRENT_OFFSET_DEFAULT;
-    }
+    // Compute the zero
+    currentOffset = (current / CURRENT_MULTIPLIER_DEFAULT) + CURRENT_OFFSET_DEFAULT;
     
     // Correct the buffer for the new zero
-    for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+    for (int i = 0; i < _size; i++) {
        filterBuff[i] = ((CURRENT_OFFSET_DEFAULT - currentOffset) * CURRENT_MULTIPLIER_DEFAULT) + filterBuff[i];
     }
 }
 
+void DC_DC::setup(float* _onThreshold, float* _overCurrent, float* _startDelay, float* _stopDelay) {
+    onThreshold = _onThreshold;
+    overCurrent = _overCurrent;
+    startDelay  = _startDelay;
+    startDelay  = _startDelay;
+}
+
+bool DC_DC::size(unsigned int size) {
+    if (_size == size) return false;
+    
+    // Get recent
+    updateCurrent();
+    float avg = getCurrent();
+    
+    // Allocate new
+    float* newBuff = new float[size];
+    if (newBuff == 0) return false;     // Not found, nothing has been changed
+    else {
+        delete[] filterBuff;            // Free old
+        __disable_irq();
+        
+        _size = size;                   // Set size
+        filterBuff = newBuff;           // Transfer to new
+        for (int i = 0; i < size; i++) filterBuff[i] = avg;     // Pre-fill with old average
+        
+        __enable_irq();
+        return true;
+    }   
+}
 void DC_DC::updateCurrent()
 {
     float avg=0;
-    for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+    for (int i = 0; i < _size; i++) {
         avg += filterBuff[i];
     }
-    avg /= DC_DC_FILTER_TAPS;
+    avg /= _size;
     current = avg;
 }
 
 void DC_DC::set(bool on)
 {
+    if (onThreshold == 0) return;                               // Not setup yet
+
     // Do nothing if already on
     if (on && isControlPinOn) return;
     
-    // Do nothing if already off
-    if (!on && !isControlPinOn) return;
+    // Double check if already off
+    if (!on && !isControlPinOn) {
+        fan1.directOff();
+        fan2.directOff();
+        pump1.directOff();
+        pump2.directOff();
+        TURN_OFF
+    }
 
     // If start requested and no error
-    if (on && !critError) {
-        starting = true;
+    if (on && !critError && !(status & POWER_DOWN)) {
+        status |= POWER_UP;
+        status &= ~POWER_DOWN;
         TURN_ON
         startTimer.reset();
         startTimer.start();
         stopTimer.stop();
         stopTimer.reset();
-        stopping = false;
 
-        // If stop requested
+    // If stop requested
     } else {
-        stopping = true;
+        status &= ~POWER_UP;
+        status |= POWER_DOWN;
         fan1.directOff();
         fan2.directOff();
         pump1.directOff();
@@ -85,7 +125,6 @@
         stopTimer.start();
         startTimer.stop();
         startTimer.reset();
-        starting = false;
     }
 }
 
@@ -94,61 +133,44 @@
     // Get next current sample
     float curr = (dcdcCurrent.read() - currentOffset) * CURRENT_MULTIPLIER_DEFAULT;    
 
-    filterBuff[buffTracker] = curr;                             // Add to buffer filter
-    buffTracker++;
-    if (buffTracker >= DC_DC_FILTER_TAPS) buffTracker = 0;
+    filterBuff[buffTracker++] = curr;                           // Add to buffer filter
+    buffTracker %= _size;
     updateCurrent();                                            // Compute average
     
+    if (onThreshold == 0) return;                               // Not setup yet
+    
     // Update status
-    register char stat = status & 0xF0;                         // The upper 4 bits (locking errors)
-    if (current >= DC_DC_ON_THRESHOLD) stat |= ConvOn;          // The converter is actually on (positive current)
-    if (isControlPinOn) stat |= SetOn;                          // The converter is set on
+    char stat = 0;
+    if (current >= *onThreshold)    stat |= CONV_ON;            // The converter is actually on (positive current)
+    if (isControlPinOn)             stat |= SET_ON;             // The converter is set on
 
     // During Timed Startup Phase
-    if (starting) {
-        stat |= PowerUp;                                        // Indicate state in status byte
-        if (startTimer.read_ms() >= STARTUP_DELAY_MS) {         // Start time elapsed
-            if (stat & ConvOn) {                                // Positive current detected
-                startTimer.stop();      // Stop timer
-                startTimer.reset();     // Reset to zero
-                starting = false;       // Indicate running
-            } else {
-                startTimer.stop();
+    if (stat & POWER_UP) {
+        if (startTimer.read() >= *startDelay) {                 // Start time elapsed
+            if (stat & CONV_ON) {                               // Positive current detected
+                startTimer.stop();                              // Stop timer
                 startTimer.reset();
-                stat |= FailStart;      // Failed to start
+                stat &= ~POWER_UP;                              // Done power-up
             }
         }
-    } else stat &= ~PowerUp;
+    }
 
     // During Timed Stopping Phase
-    if (stopping) {
-        stat |= PowerDown;                                      // Indicate state in status byte
-        if (stopTimer.read_ms() >= STOP_DELAY_MS) {             // Stop time elapsed
-            if (stat & ConvOn) {                                // Converter still on
-                stopTimer.stop();
+    if (stat & POWER_DOWN) {
+        if (stopTimer.read() >= *stopDelay) {                   // Stop time elapsed
+            if (!(stat & CONV_ON)) {                            // Current dropped, its off
+                stopTimer.stop();                               // Stop timer
                 stopTimer.reset();
-                stat |= FailStop;         // It didn't turn off even after timer expired!
-            } else {                      // Its actually off
-                stopping = false;
-                stopTimer.stop();
-                stopTimer.reset();
+                stat &= ~POWER_DOWN;                            // Done power-down
             }
         }
-    } else stat &= ~PowerDown;
-
-    // While in steady state
-    if (!stopping && !starting) {
-        if ((stat & SetOn) && !(stat & ConvOn)) stat |= FailStart;    // Should be running but its not
-        else                                    stat &= ~FailStart;
-        if (!(stat & SetOn) && (stat & ConvOn)) stat |= FailStop;     // Should be stopped but its not
-        else                                    stat &= ~FailStop;
     }
     
-    if (current < CURRENT_SENSOR_LLIM || current > CURRENT_SENSOR_ULIM) stat |= SensorFault;    // Sensor out of range
-    else if (current > OVER_CURRENT_THRESHOLD) stat |= OverCurrent;                             // Over current
+    if (current < CURRENT_SENSOR_LLIM || current > CURRENT_SENSOR_ULIM) stat |= SENSOR_FAULT;   // Sensor out of range
+    else if (current > *overCurrent)                                    stat |= OVER_CURRENT;   // Over current
 
     // Process critical error conditions
-    if (stat & (SensorFault | OverCurrent)) critError = true;
+    if (stat & (SENSOR_FAULT | OVER_CURRENT)) critError = true;
     else critError =  false;
     status = stat;
 
@@ -161,16 +183,26 @@
         TURN_OFF
         startTimer.stop();
         startTimer.reset();
-        starting = false;
+    }
+    
+    // Don't allow to run if still powering on or off
+    if (stat & (POWER_UP | POWER_DOWN)) {
+        fan1.directOff();
+        fan2.directOff();
+        pump1.directOff();
+        pump2.directOff();
     }
 }
 
 void DC_DC::setPwm(enum Channel_T chan, float duty)
 {
-    // Do nothing if error present, starting in startup, or DC-DC not actually on, or not set on
-    if (critError || starting || stopping || !(status & ConvOn) || !isControlPinOn) return;
-
-    else {
+    // Off if error present, starting in startup, or DC-DC not actually on, or not set on
+    if (!onThreshold || critError || !(status & CONV_ON) || !isControlPinOn || (status & (POWER_UP | POWER_DOWN))) {
+        fan1.directOff();
+        fan2.directOff();
+        pump1.directOff();
+        pump2.directOff();
+    } else {
         if (chan == FAN1)  fan1.write(duty);
         if (chan == FAN2)  fan2.write(duty);
         if (chan == PUMP1) pump1.write(duty);
@@ -179,9 +211,9 @@
 }
 float DC_DC::readPwm(enum Channel_T chan)
 {
-    if (chan == FAN1) return fan1.readRaw();
-    if (chan == FAN2) return fan2.readRaw();
-    if (chan == PUMP1) return pump1.readRaw();
-    if (chan == PUMP2) return pump2.readRaw();
+    if (chan == FAN1)   return fan1.readRaw();
+    if (chan == FAN2)   return fan2.readRaw();
+    if (chan == PUMP1)  return pump1.readRaw();
+    if (chan == PUMP2)  return pump2.readRaw();
     else return 0;
 }
\ No newline at end of file
--- a/Libs/DC_DC/DC_DC.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/DC_DC/DC_DC.h	Sat Feb 07 08:54:51 2015 +0000
@@ -5,26 +5,26 @@
 #include "FanPump.h"
 
 enum Channel_T {
-    FAN1=0,
-    FAN2,
-    PUMP1,
-    PUMP2,  
+    FAN1            = 0,
+    FAN2            = 1,
+    PUMP1           = 2,
+    PUMP2           = 3,  
 };
+
 enum Status_Bits_T {  
-    ConvOn=1,
-    SetOn=2,
-    PowerUp=4,
-    PowerDown=8,
-    OverCurrent=16,
-    SensorFault=32,
-    FailStart=64,
-    FailStop=128,
+    CONV_ON         = 1<<0,
+    SET_ON          = 1<<1,
+    POWER_UP        = 1<<2,
+    POWER_DOWN      = 1<<3,
+    OVER_CURRENT    = 1<<4,
+    SENSOR_FAULT    = 1<<5,
 };
-const int DC_DC_FILTER_TAPS = 50;
 
 class DC_DC{
 public:
-    DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew);
+    DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew, unsigned int size=50);
+    void setup(float* onThreshold, float* overCurrent, float* startDelay, float* stopDelay);
+    bool size(unsigned int size);
     float getCurrent() { return current; }
     void set(bool on); 
     void setPwm(enum Channel_T chan, float duty);
@@ -37,26 +37,25 @@
         bit3 - 1 = DC-DC Power-down, its use is currently blocked until ready
         bit4 - 1 = DC-DC over current (drawing more current than allowed according to sensor)
         bit5 - 1 = DC-DC current sensor out of range (broken)
-        bit6 - 1 = DC-DC failed to start
-        bit7 - 1 = DC-DC failed to stop
     */
     char getStatus() { return status; }
     bool hasCritError() { return critError; }         // An error exists according to status above
     void sample();                   // Attach this function in a 10ms RTOS timer
 
 private:
+    
     Timer startTimer;
     Timer stopTimer;
-    volatile bool starting;
-    volatile bool stopping;
     volatile bool isControlPinOn;
     DigitalOut dcdcControl;
     AnalogIn dcdcCurrent;
-    volatile float currentOffset;
-    volatile float filterBuff[DC_DC_FILTER_TAPS];
-    volatile int buffTracker;
+    
+    float currentOffset;
+    float* filterBuff;
+    int buffTracker;
+    unsigned int _size;
+    
     void updateCurrent();
-
     volatile float current;
     volatile char status;
     volatile bool critError;
@@ -65,6 +64,11 @@
     FanPump fan2;
     FanPump pump1;
     FanPump pump2;
+    
+    float* onThreshold;
+    float* overCurrent;
+    float* startDelay;
+    float* stopDelay;
 };
 
 #endif
\ No newline at end of file
--- a/Libs/IMD/IMD.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/IMD/IMD.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -2,11 +2,14 @@
 #include <math.h>
 #include "pinmap.h"
 
-static IMD* instance[4] = { 0 };    // Access member from static frame, one IMD permitted per timer module (4 total IMD objects)
+
+const float ZERO_HZ_TIMEOUT = 0.21;     // Time (sec) that must pass without an edge to call it 0 Hz, set to greater than the longest expected pulse width
+const float EXTRA = 0.02;               // Margin on the IMD PWM limits
 
-const uint32_t PCLK = 24000000;     // Timer counting clock = 24Mhz
+const uint32_t PCLK = 24000000;         // Timer counting clock = 24Mhz
 const uint32_t TIMEOUT_TICKS = PCLK*ZERO_HZ_TIMEOUT;    // Zeroing timeout in clock ticks = seconds * PCLK
-const float EXTRA = 0.01;           // Margin on the IMD PWM limits
+
+static IMD* instance[4] = { 0 };        // Access member from static frame, one IMD permitted per timer module (4 total IMD objects)
 
 // Interrupt functions, must be static context
 void tim0_IRQ() {    
--- a/Libs/IMD/IMD.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/IMD/IMD.h	Sat Feb 07 08:54:51 2015 +0000
@@ -5,16 +5,14 @@
 
 #include "mbed.h"
 
-const float ZERO_HZ_TIMEOUT = 0.15;     // Time (sec) that must pass without an edge to call it 0 Hz, set to greater than the longest expected pulse width
-
 enum IMDstatus  {
-    OFF=0,
-    NORMAL=1,
-    UNDERVOLT=2,
-    SPEEDSTART=3,
-    ERROR=4,
-    GROUNDERR=5,
-    INVALID=6,  
+    OFF         = 0,
+    NORMAL      = 1,
+    UNDERVOLT   = 2,
+    SPEEDSTART  = 3,
+    ERROR       = 4,
+    GROUNDERR   = 5,
+    INVALID     = 6,  
 };
 
 class IMD{
--- a/Libs/LatchMonitor/LatchMonitor.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/LatchMonitor/LatchMonitor.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -1,30 +1,47 @@
 #include "LatchMonitor.h"
 
-LatchMonitor::LatchMonitor(PinName _ok, PinName _fault, unsigned int startupDelay_ms):okPin(_ok, PullDown), faultPin(_fault, PullDown) {
-    
+LatchMonitor::LatchMonitor(PinName _ok, PinName _fault) : okPin(_ok, PullDown), faultPin(_fault, PullDown)
+{
+    started = false;
+    lastStat = 0;
+    startDelay = 0;
+}
+void LatchMonitor::setup(float* _startDelay)
+{
+    startDelay = _startDelay;
+}
+void LatchMonitor::delayStart()
+{
+    lastStat = 0;
+    update();
+    if (startDelay == 0) return;    // Not setup yet
+
     // Power-on reset detected
     if (LPC_SC->RSID & 1) {
-        LPC_SC->RSID = 1;    // Clear POR flag
-        started = false;     // Use the blocking startup timer
-        startup.attach_us(this, &LatchMonitor::startupDelay, startupDelay_ms*1000);
+        LPC_SC->RSID = 1;           // Clear POR flag
+        started = false;            // Use the blocking startup timer
+        startup.attach(this, &LatchMonitor::startupDelay, *startDelay);
     } else {
-        started = true;      // Not a power-cycle, do not use the timer
+        started = true;             // Not a power-cycle, do not use the timer
     }
 }
 
-void LatchMonitor::startupDelay() {
-    started = true;   
+void LatchMonitor::startupDelay()
+{
+    started = true;
 }
-char LatchMonitor::update() {
+char LatchMonitor::update()
+{
     char ret = 0;
     ret |= (!okPin << 0);       // Mirror the ok pin
     ret |= (faultPin << 1);     // Copy the fault pin
-    
+
     if (started) {
         ret |= !okPin << 2;                 // Mirror the ok pin when started only
         if (!okPin && !faultPin) {          // If started && okFault but not caught in hardware
-            ret |= 1 << 3;   
+            ret |= HARD_FAULT;
         }
     }
+    lastStat = ret;
     return ret;
 }
--- a/Libs/LatchMonitor/LatchMonitor.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/LatchMonitor/LatchMonitor.h	Sat Feb 07 08:54:51 2015 +0000
@@ -4,26 +4,25 @@
 #include "mbed.h"
 
 enum LatchMon_Status_Bits {
-    okPinF=1,
-    faultPinF=2,
-    softwareF=4,
-    hardwareF=8,  
+    OK_FAULT        = 1<<0,     // Device OK pin is low
+    LATCHED_HARD    = 1<<1,     // Circuit is in the tripped state
+    LATCHED_SOFT    = 1<<2,     // Software-timer has expired and decided to tag the fault
+    HARD_FAULT      = 1<<3,     // The circuit should have latched open, but it did not
 };
 
-class LatchMonitor {
+class LatchMonitor
+{
 
 public:
     // Make this startup delay longer than the actual hardware circuit delay so that it can catch errors in the circuit
-    LatchMonitor(PinName _ok, PinName _fault, unsigned int startupDelay_ms);
-    /*
-        char update() returns status encoded in a char
-        bit0 = okPinFault - equals 1 when the OK pin from the monitored device is LOW indicating device fault
-        bit1 = faultPinFault - equals 1 when the sys. mgmt. latch circuit is faulted with startup timer in hardware
-        bit2 = softwareFault - equals 1 when the LatchMonitor fault occurs with startup timer in software
-        bit3 = hardwareFault - the circuit is supposed to signal fault on the hardware pin but its not doing so!
-    */
+    LatchMonitor(PinName _ok, PinName _fault);
+    void setup(float* startDelay);
+    void delayStart();
+    
     char update();
 private:
+    char lastStat;
+    float* startDelay;
     Timeout startup;
     void startupDelay();
     bool started;
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -10,10 +10,8 @@
     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++;
+    buff[i++] = 'C';      // Start of message
+    buff[i++] = (msg.format << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2);     // Header byte, message info on ID size, RTR, # data bytes
     if (msg.format == CANStandard) {
         buff[i++] = (msg.id & 0x0FF);               // Lower byte of ID
         buff[i++] = (msg.id & 0x700) >> 8;          // Upper byte of ID
@@ -27,7 +25,7 @@
         buff[i++] = msg.data[j];
     }
     buff[i++] = '\n';
-    */return i;
+    return i;
 }
 
 bool convert2msg(CANMessage &msg, char* buff) {
@@ -67,28 +65,20 @@
 }
 
 // Send a CAN message, first reformat it into a char array, then send over Xbees
-bool CANxbee::send(CANMessage &msg) {
+bool CANxbee::send(CANMessage &msg, unsigned int* length) {
     char buff[15];                      // Build the string-ified CAN message here
     int size = convert2array(msg, buff);
+    *length = 0;
     if (size == -1) return false;       // Bad message, string not formed
-    bool success=false;
     
-    size=1;
     int bytesLeft = serial.txBufferGetSize(0) - serial.txBufferGetCount();
-    Timer t;
     
-    // 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;
+        *length = size;
+        return true;
     }
-    
-    // End thread-safe section
-    NVIC_EnableIRQ(TIMER3_IRQn);       // Timer3-->RTOS tick
-
-    return success;
+    return false;
 }
 
 // Continuously call this function in main program, when it returns true, it holds a newly received CAN message that came via Xbee
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.h	Sat Feb 07 08:54:51 2015 +0000
@@ -19,9 +19,17 @@
 public:
     CANxbee(PinName tx, PinName rx, int baud, int txSize, int rxSize);
     
-    bool send(CANMessage &msg);         // Send a CANMessage object over the xbee (first converts to serial)
+    bool send(CANMessage &msg, unsigned int* length);         // Send a CANMessage object over the xbee (first converts to serial)
     bool receive(CANMessage &msg);      // Receive a CANMessage object over the xbee (converts from serial message to CAN)
-    
+    void baud(int baudrate) {
+        serial.baud(baudrate);   
+    }
+    bool txSize(unsigned int size) {
+        return (serial.txBufferSetSize(size) == 0);   
+    }
+    bool rxSize(unsigned int size) {
+        return (serial.rxBufferSetSize(size) == 0);   
+    }   
 private:
     MODSERIAL serial;
     char rxBuff[15];
--- a/Libs/XbeeManager/XbeeManager.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -3,27 +3,31 @@
 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)
 {
-    
     alternate = false;
     extraAvail = false;
     counterX1in = 0;
     counterX2in = 0;
     counterX1out = 0;
     counterX2out = 0;
+    bytesX1out = 0;
+    bytesX2out = 0;
 }
 
 bool XbeeManager::send(CANMessage &msg)
 {
     alternate = !alternate;
+    unsigned int length;
     if (alternate) {
-        if (x1.send(msg)) {
+        if (x1.send(msg, &length)) {
             counterX1out++;
+            bytesX1out += length;
             return true;   
         }
     } else {
-        if (x2.send(msg)) {
+        if (x2.send(msg, &length)) {
             counterX2out++;
-            return true;   
+            bytesX2out += length;
+            return true;
         }
     }
     return false;
--- a/Libs/XbeeManager/XbeeManager.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.h	Sat Feb 07 08:54:51 2015 +0000
@@ -7,12 +7,25 @@
 class XbeeManager {
 public:
     XbeeManager(PinName tx1, PinName rx1, PinName tx2, PinName rx2, int baud, int txSize, int rxSize);
+    void baud(int baudrate) {
+        x1.baud(baudrate);
+        x2.baud(baudrate);   
+    }
+    bool txSize(unsigned int size) {
+        return (x1.txSize(size) && x2.txSize(size));   
+    }
+    bool rxSize(unsigned int size) {
+        return (x1.rxSize(size) && x2.rxSize(size));          
+    }
+    
     bool send(CANMessage &msg);
     bool receive(CANMessage &msg); 
     unsigned int counterX1in;
     unsigned int counterX2in;
     unsigned int counterX1out;
     unsigned int counterX2out;
+    unsigned int bytesX1out;
+    unsigned int bytesX2out;
     
 private:
     bool alternate;
--- a/Libs/mbed-rtos.lib	Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/mbed-rtos.lib	Sat Feb 07 08:54:51 2015 +0000
@@ -1,1 +1,1 @@
-http://developer.mbed.org/users/mbed_official/code/mbed-rtos/#28712e303960
+http://developer.mbed.org/users/mbed_official/code/mbed-rtos/#5448826aa700
--- a/inCommands/inCommands.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/inCommands/inCommands.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -1,95 +1,19 @@
 #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_MODE_ID:
-            if (msg.len != sizeof(char)) break;
-            if (msg.data[0] & 1<<2) {     // AIRs closed?
-                CANdata.airsClosed = true;
-                dcdc.set(1);
-            } else {
-                CANdata.airsClosed = false;
-                dcdc.set(0);
-            }
-            break;
-
-        case GLVBAT_SETSOC_ID:
-            if (msg.len != sizeof(float)) break;
-            glvBat.resetToSOC(*((float*)(&msg.data[0])));
-            break;
-
-        case GLVBAT_SETAH_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;
-}
+#include "runTime.h"
+#include "inMacros.h"
 
 // Compare string to a word in the serial input, shorter to type
 #define CMP(w, string)                                              if (!strcasecmp(word[w-1], string))
 
 // Serial input
-int inCommands::serviceSerial()
+int serviceSerial()
 {
- static int end = 0;                                 // End of string position
+    static int end = 0;                              // End of string position
 
     int c=0;
     if (pc.readable()) c = pc.getc();
-    if (c == -1) return -2;                          // Invalid char, no char available
-    
+    if (c == -1 || c == 0) return 0;
+
     char b = c;                                      // Casted to char type
     bool process = false;                            // Is string complete (ready to parse)?
 
@@ -112,7 +36,7 @@
     }
     // Continue to parsing section only if flagged as complete and string not empty
     if (!process || strlen((char*)tempData.inputStr) == 0) return 0;
-    
+
     static char word[3][RX_SIZE+1];                  // Hold 3 words
     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
@@ -125,17 +49,79 @@
             NVIC_SystemReset();
             return 1;
         }
+        // Clear non-volatile fault flags, then reset the microcontroller
+        CMP(1, "resetClear") {
+            runTime::clearFaults();
+            NVIC_SystemReset();
+            return 1;
+        }
+        return -1;
     }
     // Two word commands
     if (pieces == 2) {
-        // Manual DC-DC on/off control
-        CMP(1, "dcdc") {
-            CMP(2, "on") {
-                dcdc.set(1);
+        // Change the serial dashboard display mode
+        CMP(1, "display") {
+            CMP(2, "compact") {
+                if (param->change_extendedSerial(0)) {
+                    op->profileModded=true;
+                    return 1;  // Change function defined by macro
+                }
+            }
+            CMP(2, "extended") {
+                if (param->change_extendedSerial(1)) {
+                    op->profileModded=true;
+                    return 1;  // Change function defined by macro
+                }
+            }
+        }
+        // Change ACK setting for CAN (noAck allows testing of CAN without other nodes)
+        CMP(1, "CANack") {
+            CMP(2, "noack") {                           // NoAck test mode
+                if (param->change_CANnoAck(1)) {
+                    op->profileModded=true;
+                    return 1;
+                }
+            }
+            CMP(2, "ack") {                             // Normal CAN protocol
+                if (param->change_CANnoAck(0)) {
+                    op->profileModded=true;
+                    return 1;
+                }
+            }
+            return -1;
+        }
+        // Artificially capture a freeze frame, save to flash
+        CMP(1, "capture") {
+            CMP(2, "freeze") {
+                if (!FreezeFrame::getError()) {         // Only allow capture if spot available (last error frame was cleared manually)
+                    if (FreezeFrame::writeFrame()) {    // Copy RAM frame to freezree sector in flash
+                        return 1;
+                    }
+                }
+            }
+            return -1;
+        }
+        // Clear fault conditions
+        CMP(1, "clear") {
+            CMP(2, "freeze") {                          // Clear the freeze frame (mark as read)
+                FreezeFrame::clearError();              // Clear the non-volatile error marker
                 return 1;
             }
-            CMP(2, "off") {
-                dcdc.set(0);
+            CMP(2, "faults") {                          // Clear everything
+                runTime::clearFaults();
+                return 1;
+            }
+            return -1;
+        }
+        // Change the display contents
+        CMP(1, "view") {
+            CMP(2, "freeze") {                          // View the last stored freeze frame
+                if (FreezeFrame::getFrame(&tempData.freeze)) {  // Fetch the pointer from flash
+                    return 1;
+                }
+            }
+            CMP(2, "live") {                            // View live data from RAM
+                tempData.freeze = NULL;                 // Zero the pointer
                 return 1;
             }
             return -1;
@@ -172,6 +158,44 @@
             }
             return -1;
         }
+
+        bool parsed=false;
+
+        CHANGE_VAR("GLVchar",       chargeCurrent)
+        CHANGE_VAR("GLVdisch",      dischargeCurrent)
+        CHANGE_VAR("GLVnomCap",     nominalCapacity)
+        CHANGE_VAR("GLVtaps",       glvBat_taps)
+        CHANGE_VAR("dcdcThres",     dcdcThreshold)
+        CHANGE_VAR("dcdcOver",      dcdcOverCurrent)
+        CHANGE_VAR("dcdcStart",     dcdcStartDelay)
+        CHANGE_VAR("dcdcStop",      dcdcStopDelay)
+        CHANGE_VAR("dcdcTaps",      dcdc_taps)
+        CHANGE_VAR("imdStart",      imdStartDelay)
+        CHANGE_VAR("amsStart",      amsStartDelay)
+        CHANGE_VAR("IntOverT",      internalOverTemp)
+        CHANGE_VAR("CANtxSize",     CANtxSize)
+        CHANGE_VAR("CANrxSize",     CANrxSize)
+        CHANGE_VAR("SerialBaud",    SerialBaud)
+        CHANGE_VAR("SerialTx",      SerialTxSize)
+        CHANGE_VAR("XbeeBaud",      XbeeBaud)
+        CHANGE_VAR("XbeeTxSize",    XbeeTxSize)
+        CHANGE_VAR("XbeeRxSize",    XbeeRxSize)
+        CHANGE_VAR("CANack",        CANnoAck)
+
+        if (!parsed) return -1;
+
+        CMP(1, "GLVnomCap")             return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
+        CMP(1, "GLVtaps")               return glvBat.size(param->glvBat_taps)?1:-1;
+        CMP(1, "dcdcTaps")              return dcdc.size(param->dcdc_taps)?1:-1;
+        CMP(1, "CANtxSize")             return can.txSize(param->CANtxSize)?1:-1;
+        CMP(1, "CANrxSize")             return can.rxSize(param->CANrxSize)?1:-1;
+        CMP(1, "SerialBaud")            pc.baud(param->SerialBaud);
+        CMP(1, "SerialTx")              return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
+        CMP(1, "XbeeBaud")              xbeeRelay.baud(param->XbeeBaud);
+        CMP(1, "XbeeTxSize")            return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
+        CMP(1, "XbeeRxSize")            return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;
+
+        return 1;
     }
     // Three word commands
     if (pieces == 3) {
@@ -181,8 +205,8 @@
             if (*next == 0) {
                 float val2 = strtod(word[2], &next);
                 if (*next == 0) {
-                    dcdc.setPwm(FAN1, val1);
-                    dcdc.setPwm(FAN2, val2);
+                    op->dcdc.request.fan1 = val1;
+                    op->dcdc.request.fan2 = val2;
                     return 1;
                 }
             }
@@ -195,24 +219,323 @@
             if (*next == 0) {
                 float val2 = strtod(word[2], &next);
                 if (*next == 0) {
-                    dcdc.setPwm(FAN1, val1);
-                    dcdc.setPwm(FAN2, val2);
+                    op->dcdc.request.pump1 = val1;
+                    op->dcdc.request.pump2 = val2;
+                    return 1;
+                }
+            }
+            return -1;
+        }
+        // Set the system time (RTC)
+        CMP(1, "Time") {
+            struct tm t;    // Time & date struct
+            int ret = sscanf(word[1], "%d/%d/%d", &t.tm_mon, &t.tm_mday, &t.tm_year);   // Populate date
+            t.tm_year = t.tm_year - 1900;       // Year mod to fix 0 index
+            t.tm_mon = t.tm_mon - 1;            // Month mod to fix 0 index
+            if (ret == 3) {                     // All 3 items found
+                ret = sscanf(word[2], "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);    // Populate time
+                if (ret == 3) {                 // All 3 items found
+                    set_time(mktime(&t));       // Set the RTC
+                    time_t diff = time(NULL) - op->SysTime;     // Get change in time from old to new
+                    op->startTime += diff;                      // Shift the startTime to new timebase
                     return 1;
                 }
             }
             return -1;
         }
+
+        // Profile manipulations between RAM and flash
+        CMP(1, "Profile") {
+            // Write, copy RAM to a flash sector
+            CMP(2, "Write") {
+                unsigned int index = strtoul(word[2], &next, 10);               // Get index from command "profile write xxx"
+                if (index <= NUM_STORED_PROFILES && index > 0 && *next == 0) {  // Check within bounds
+                    bool s = Profile::saveProfile(index);                       // Write to flash
+                    if (s) {                                                    // Successful?
+                        op->profileModded = false;                              // Mark it as a fresh, unmodified profile
+                        op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
+                        return 1;
+                    }
+                }
+                return -1;
+            }
+            // Load, read from flash to RAM
+            CMP(2, "Load") {
+                CMP(3, "default") {                                             // The hard-coded flash profile (found in FreezeFrame.cpp)
+                    Profile::loadProfile(0);                                    // Copy default to RAM
+                    op->profileIndex = Profile::usingProfile();                 // Change the currently loaded profile marker
+                    op->profileModded = false;                                  // Mark it as a fresh, unmodified profile
+                    return 1;
+                }
+                CMP(3, "freeze") {                                              // Get the profile portion of the last freeze frame captured
+                    if(Profile::loadProfile(-1)) {                              // Attemp to retrieve and copy to RAM
+                        op->profileModded = false;                              // Mark it as a fresh, unmodified profile
+                        op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
+                        return 1;
+                    }
+                }
+                int index = strtol(word[2], &next, 10);                          // Command was "profile load xxx"
+                if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
+                    if (Profile::loadProfile(index)) {                           // Attempt to retrieve and copy to RAM
+                        op->profileModded = false;                               // Mark it as a fresh, unmodified profile
+                        op->profileIndex = Profile::usingProfile();              // Change the currently loaded profile marker
+                        return 1;
+                    }
+                }
+                return -1;
+            }
+            // View the profile only (NOT loaded to RAM, just display changed)
+            CMP(2, "view") {
+                CMP(3, "default") {                                             // View the hard-coded flash profile
+                    if (Profile::getProfile(&tempData.viewProfile, 0)) {        // Attempt to fetch pointer
+                        tempData.viewProfileNum = 0;                            // Mark the index of the fetched profile
+                        return 1;
+                    }
+                }
+                CMP(3, "freeze") {                                              // View the profile portion only of the last captured freeze frame
+                    if (Profile::getProfile(&tempData.viewProfile, -1)) {       // Attempt to fetch pointer
+                        tempData.viewProfileNum = -1;                           // Mark the index of the fetched profile
+                        return 1;
+                    }
+                }
+                CMP(3, "live") {                                                // Revert to normal, live view
+                    tempData.viewProfileNum = -2;                               // Mark live
+                    tempData.viewProfile = NULL;                                // Clear the pointer
+                    return 1;
+                }
+
+                int index = strtol(word[2], &next, 10);                          // Command was "profile view xxx"
+                if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
+                    if (Profile::getProfile(&tempData.viewProfile, index)) {     // Attempt to fetch pointer
+                        tempData.viewProfileNum = index;                         // Mark the index of the fetched profile
+                        return 1;
+                    }
+                }
+                return -1;
+            }
+        }
     }
     return -1;
 }
+// Called when AMS AIRs Mode message stops coming in
+Timeout timer_AIRS_CLOSED;
+void timeout_AIRS_CLOSED()
+{
+    op->signals  &= ~AIRS_CLOSED;
+}
+
+// Called when Charger CAN messages stop coming in
+Timeout timer_CHARGER_DET;
+void timeout_CHARGER_DET()
+{
+    op->signals  &= ~CHARGER_DET;
+}
+
+// Called when PCM messages stop coming in
+Timeout timer_FANS;
+void timeout_FANS()
+{
+    op->dcdc.request.fan1  = 0;
+    op->dcdc.request.fan2  = 0;
+}
+Timeout timer_PUMPS;
+void timeout_PUMPS()
+{
+    op->dcdc.request.pump1  = 0;
+    op->dcdc.request.pump2  = 0;
+}
+
+#define REFRESH_TIMEOUT(NAME)           \
+timer_##NAME.detach();                  \
+timer_##NAME.attach(&timeout_##NAME, CAN_DEVICE_TIMEOUT);
+
+bool serviceCAN(CANMessage* fromXbee)
+{
+    CANMessage msg;
+    if (fromXbee != NULL) {
+        memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
+    } else {
+        if (!can.rxRead(msg)) return false;
+    }
+    // Redirect global car reset
+    if (msg.id == GLOBAL_CAR_RESET_RX_ID) msg.id = RESETCLEAR_RX_ID;
+
+    switch (msg.id) {
+            // Reset microntroller
+        case (RESET_RX_ID):
+            if (msg.len == 0) {     // Length must = 0
+                NVIC_SystemReset();
+                CAN_SUCCESS
+            }
+            CAN_FAIL
+
+        
+        // Clear non-volatile fault flags, then reset microcontroller
+        case (RESETCLEAR_RX_ID):
+            if (msg.len == 0) {                 // Length must = 0
+                FreezeFrame::clearError();
+                NVIC_SystemReset();
+                CAN_SUCCESS
+            }
+            CAN_FAIL
+
+        // Artificially capture a freeze frame
+        case (CAPTURE_RX_ID):
+            if (msg.len == 0) {                         // Length must = 0
+                if (!FreezeFrame::getError()) {         // Only allow capture if freeze frame from the last error was read
+                    if (FreezeFrame::writeFrame()) {    // Capture the RAM contents to flash
+                        CAN_SUCCESS
+                    }
+                }
+            }
+            CAN_FAIL
+
+        // Clear fault conditions
+        case (CLEAR_RX_ID):
+            if (msg.len == 1) {                         // One data byte
+                if (msg.data[0] == 0) {                 // Clear only freeze frame error if = 0
+                    FreezeFrame::clearError();          // Clear non-volatile freeze frame marker
+                    CAN_SUCCESS
+                }
+                if (msg.data[0] == 1) {                 // Clear everything if = 1
+                    runTime::clearFaults();
+                    op->faultCode = 0;
+                    CAN_SUCCESS
+                }
+            }
+            CAN_FAIL
+
+            // Set the time (RTC)
+        case (TIME_RX_ID):
+            if (msg.len == 6*sizeof(char)) {                    // 6 Bytes
+                struct tm t;                                    // Time & date struct
+                t.tm_mon  = msg.data[0];                        // Month in byte[0]
+                t.tm_mday = msg.data[1];                        // Day
+                t.tm_year = msg.data[2];                        // Year (offset from 2000)
+                t.tm_year = t.tm_year - 1900 + 2000;            // Apply year index mod and offset
+                t.tm_mon  = t.tm_mon - 1;                       // Month index mod
+                t.tm_hour = msg.data[3];                        // Get hour of time in byte[3] (24 hr format)
+                t.tm_min  = msg.data[4];                        // Minutes
+                t.tm_sec  = msg.data[5];                        // Seconds
+                set_time(mktime(&t));                           // Set RTC
+                time_t diff = time(NULL) - op->SysTime;         // Old time to new time change
+                op->startTime += diff;                          // Shift the startTime to new timebase
+                CAN_SUCCESS
+            }
+            CAN_FAIL
+
+            // RAM and flash profile manipulations
+        case (PROFILE_RX_ID):
+            if (msg.len == 2*sizeof(char)) {                    // 2 command bytes
+                if (msg.data[0] == 0) {                         // Load profile from a flash location to RAM
+                    int index = msg.data[1];                    // Second byte contains profile index
+                    if (msg.data[1] == 0xff) index = -1;        // If freeze (special case)
+                    if (Profile::loadProfile(index)) {          // Attempt to load (copy flash to RAM)
+                        op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
+                        op->profileModded = false;                      // Mark it as a fresh, unmodified profile
+                        CAN_SUCCESS
+                    }
+                }
+                if (msg.data[0] == 1) {                         // Write profile to flash from RAM
+                    int index = msg.data[1];                    // Get which slot to write to from message
+                    if (msg.data[1] == 0xff) index = -1;        // If freeze (special case)
+                    bool s = Profile::saveProfile(index);       // Write profile to flash slot
+                    if (s) {
+                        op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
+                        op->profileModded = false;                      // Mark it as a fresh, unmodified profile
+                        CAN_SUCCESS
+                    }
+                }
+            }
+            CAN_FAIL
+
+        case FAN_CONTROL_ID:
+            if (msg.len != 2*sizeof(float)) return false;
+            REFRESH_TIMEOUT(FANS)
+            op->dcdc.request.fan1 = *((float*)((void*)(&msg.data[0])));
+            op->dcdc.request.fan2 = *((float*)((void*)(&msg.data[4])));
+            return true;
+
+        case PUMP_CONTROL_ID:
+            if (msg.len != 2*sizeof(float)) return false;
+            REFRESH_TIMEOUT(PUMPS)
+            op->dcdc.request.pump1 = *((float*)((void*)(&msg.data[0])));
+            op->dcdc.request.pump2 = *((float*)((void*)(&msg.data[4])));
+            return true;
+
+        case AMS_MODE_ID:
+            if (msg.len != sizeof(char)) return false;
+            REFRESH_TIMEOUT(AIRS_CLOSED)
+            if (msg.data[0] & 1<<2) {     // AIRs closed?
+                op->signals |= AIRS_CLOSED;
+            } else {
+                op->signals &= ~AIRS_CLOSED;
+            }
+            return true;
+        case CHARGER_ERR_ID:
+            REFRESH_TIMEOUT(CHARGER_DET)
+            op->signals |= CHARGER_DET;
+            return true;
+        default:
+            break;
+    }
+    bool parsed=false;
+
+    CAN_CHANGE(chargeCurrent,           PROFILE_CHARGECURRENT_RX_ID     )
+    CAN_CHANGE(dischargeCurrent,        PROFILE_DISCHARGECURRENT_RX_ID  )
+    CAN_CHANGE(nominalCapacity,         PROFILE_NOMINALCAPACITY_RX_ID   )
+    CAN_CHANGE(glvBat_taps,             PROFILE_GLVBATTAPS_RX_ID        )
+    CAN_CHANGE(dcdcThreshold,           PROFILE_DCDCONTHRESHOLD_RX_ID   )
+    CAN_CHANGE(dcdcOverCurrent,         PROFILE_DCDCOVERCURRENT_RX_ID   )
+    CAN_CHANGE(dcdcStartDelay,          PROFILE_DCDCSTARTDELAY_RX_ID    )
+    CAN_CHANGE(dcdcStopDelay,           PROFILE_DCDCSTOPDELAY_RX_ID     )
+    CAN_CHANGE(dcdc_taps,               PROFILE_DCDC_TAPS_RX_ID         )
+    CAN_CHANGE(imdStartDelay,           PROFILE_IMDSTARTDELAY_RX_ID     )
+    CAN_CHANGE(internalOverTemp,        PROFILE_INTERNALOVERTEMP_RX_ID  )
+    CAN_CHANGE(CANnoAck,                PROFILE_CANNOACK_RX_ID          )
+    CAN_CHANGE(extendedSerial,          PROFILE_EXTENDSERIAL_RX_ID      )
+    CAN_CHANGE(CANtxSize,               PROFILE_CANTXSIZE_RX_ID         )
+    CAN_CHANGE(CANrxSize,               PROFILE_CANRXSIZE_RX_ID         )
+    CAN_CHANGE(SerialBaud,              PROFILE_SERIALBAUD_RX_ID        )
+    CAN_CHANGE(SerialTxSize,            PROFILE_SERIALTXSIZE_RX_ID      )
+    CAN_CHANGE(XbeeBaud,                PROFILE_XBEEBAUD_RX_ID          )
+    CAN_CHANGE(XbeeTxSize,              PROFILE_XBEETXSIZE_RX_ID        )
+    CAN_CHANGE(XbeeRxSize,              PROFILE_XBEERXSIZE_RX_ID        )
+
+    if (!parsed) return false;
+
+    if (msg.id == PROFILE_NOMINALCAPACITY_RX_ID )   return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
+    if (msg.id == PROFILE_GLVBATTAPS_RX_ID      )   return glvBat.size(param->glvBat_taps)?1:-1;
+    if (msg.id == PROFILE_DCDC_TAPS_RX_ID       )   return dcdc.size(param->dcdc_taps)?1:-1;
+    if (msg.id == PROFILE_CANTXSIZE_RX_ID       )   return can.txSize(param->CANtxSize)?1:-1;
+    if (msg.id == PROFILE_CANRXSIZE_RX_ID       )   return can.rxSize(param->CANrxSize)?1:-1;
+    if (msg.id == PROFILE_SERIALBAUD_RX_ID      )   pc.baud(param->SerialBaud);
+    if (msg.id == PROFILE_SERIALTXSIZE_RX_ID    )   return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
+    if (msg.id == PROFILE_XBEEBAUD_RX_ID        )   xbeeRelay.baud(param->XbeeBaud);
+    if (msg.id == PROFILE_XBEETXSIZE_RX_ID      )   return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
+    if (msg.id == PROFILE_XBEERXSIZE_RX_ID      )   return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;
+
+    return true;
+}
+// Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
+bool receiveMsgXbee()
+{
+    CANMessage msg;
+    if (xbeeRelay.receive(msg)) {                       // Incoming CAN message string received
+        if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT;    // Send it out on the CAN bus
+        serviceCAN(&msg);                               // Send it into the local serviceCAN routine
+        return true;
+    } else return false;
+}
+
 
 void inCommands::thread_getInputs(void const* args)
 {
     while(1) {
-        inCommands::serviceCAN();
-        inCommands::receiveMsgXbee();
+        serviceCAN(0);
+        receiveMsgXbee();
 
-        int ret = inCommands::serviceSerial();
+        int ret = serviceSerial();
         if (ret == -1) tempData.parseGoodChar = 'x';
         if (ret == 1)  tempData.parseGoodChar = 251;
         osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<2);      // Signal watchdog thread
--- a/inCommands/inCommands.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/inCommands/inCommands.h	Sat Feb 07 08:54:51 2015 +0000
@@ -3,12 +3,7 @@
 
 #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();
     void thread_getInputs(void const* args);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inCommands/inMacros.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,58 @@
+#ifndef INMACROS_H
+#define INMACROS_H
+
+/*************** Serial String Input ****************/
+
+// Compare string to a word in the serial input, shorthand
+#define CMP(w, string)      if (!strcasecmp(word[w-1], string))
+
+// Macro linking a serial string to a change function, 2 word command
+#define CHANGE_VAR(STR, NAME)                           \
+CMP(1, STR) {                                           \
+    float f = strtof(word[1], &next);                   \
+    if (*next == 0) {                                   \
+        if (param->change_##NAME(f)) {                  \
+            op->profileModded=true;                     \
+            parsed=true;                                \
+        }                                               \
+    }                                                   \
+}
+
+/*************** CAN Message Profile Out ***************/
+#include "outMacros.h"
+
+// Send out an item from the profile
+#define CAN_PROFILE(DATA, ID)       \
+SEND_CAN_SINGLE(param->DATA, ID);
+
+// Replies for commands that do not return data
+#define CAN_SUCCESS     \
+msg.data[0]=1;          \
+SEND_CAN(1, msg.id)     \
+return 1;
+
+#define CAN_FAIL        \
+msg.data[0]=0;          \
+SEND_CAN(1, msg.id)     \
+return 0;
+
+/*********************** CAN Bus Input ******************************/
+
+template <class Type>
+Type CAN_EXTRACT(Type type, CANMessage& msg) {
+   return *((Type*)((void*)(&msg.data[0])));
+}
+
+#define CAN_CHANGE(NAME, ID)                                        \
+if (msg.id == ID) {                                                 \
+    if (msg.len == sizeof(param->NAME)) {                           \
+        if (param->change_##NAME(CAN_EXTRACT(param->NAME, msg))) {  \
+            op->profileModded=true;                                 \
+            parsed = true;                                          \
+        }                                                           \
+    }                                                               \
+    CAN_PROFILE(NAME, ID)                                           \
+}
+
+
+#endif
--- a/main.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/main.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -3,64 +3,104 @@
 #include "outDiagnostics.h"
 #include "inCommands.h"
 
-//#define DEBUG_MODE
-
-void thread_watchDog(void const* args) {
+void thread_watchDog(void const* args)
+{
     tempData.wdtThreadId = Thread::gettid();
-#ifdef DEBUG_MODE
-    wdt.kick();
-#else
     wdt.kick(WDT_TIME);
-#endif
     while(1) {
-        Thread::signal_wait(0x1F);
+        Thread::wait(100);
+        //Thread::signal_wait(0x1F);
         wdt.kick();
-    }   
+    }
 }
+// Startup code to initialize interfaces and fetch the profile
+void init()
+{
+    char str[100];
+    sprintf(str, "\033[2J\033[2J\r\n");     // Clear screen
 
-int main() {
-    pc.baud(BAUD);
-    pc.format(8, SerialBase::None, 2);      // 2 Stop bits, reduce bad serial packets
-    pc.printf("\033[2J\033[2J\r\n");        // Clear screen
-    
-    can.mode(FIFO);
+    // GENERATE RESET TYPE MESSAGE
+    bool normalReset = true;
+    if (wdt.checkFlag()) {                  // Did a watchdog reset occur?
+        wdt.clearFlag();                    // Clear flag
+        op->faultCode |= WATCHDOG;          // Record fault
+        sprintf(str, "System Mgmt Watchdog Reset\r\n");
+        normalReset=false;
+    }
+    if (LPC_SC->RSID & (1<<3)) {            // Did a brownout reset occur?
+        LPC_SC->RSID = (1<<3);              // Clear flag
+        op->faultCode |= BROWNOUT;          // Record fault
+        sprintf(str, "System Mgmt Brownout Reset\r\n");
+        normalReset=false;
+    }
+    if (normalReset) sprintf(str, "System Mgmt Reset\r\n"); // Normal reset message
+
+    // SET INTERUPT PRIORITIES
     NVIC_SetPriority(TIMER0_IRQn, 0);
     NVIC_SetPriority(PWM1_IRQn, 1);
     NVIC_SetPriority(CAN_IRQn, 2);
     NVIC_SetPriority(UART0_IRQn, 3);
     NVIC_SetPriority(TIMER3_IRQn, 4);
 
-    bool normalReset = true;
-    // Did a watchdog reset occur since last power cycle?
-    if (wdt.checkFlag()) {
-        wdt.clearFlag();                    // Clear flag
-        data.watchdogReset = true;
-        pc.printf("Sys Mgmt Watchdog Reset\r\n");
-        normalReset=false;
+    // SET SYSTEM TIME (RTC)
+    int t = time(NULL);                     // Get RTC time
+    if (t == 0 || t == -1) set_time(0);     // Seed the timer if it was never setup
+    tempData.timeSynced = false;            // Time unsynced on reset, use AMS as master clock
+    op->startTime = time(NULL);             // Capture the startup time
+    op->SysTime = time(NULL);               // Caputre the system time
+    
+    // LOAD LAST-USED CONFIGURATION PROFILE
+    if (!Profile::loadStartUp()) {
+        strncat(str, "Profile load Failed, using default\r\n", 99);   
     }
-    // Did a brownout reset occur since last power cycle?
-    if (LPC_SC->RSID & (1<<3)) {
-        LPC_SC->RSID = (1<<3);              // Clear flag
-        pc.printf("Sys Mgmt Brownout Reset\r\n");
-        normalReset=false;
-    }
-    // Print normal reset string
-    if (normalReset) pc.printf("Sys Mgmt Reset\r\n");
-        
+    op->profileIndex = Profile::usingProfile();
+    wdt.kick();                                     // Kick watchdog timer before the wait
+    wait(0.5);                                      // Wait so above messages aren't immediately over-written
+    
+    // ALLOCATE/RESIZE BUFFERS
+    glvBat.size(param->glvBat_taps);
+    dcdc.size(param->dcdc_taps);
+    can.txSize(param->CANtxSize);
+    can.rxSize(param->CANrxSize);
+    pc.baud(param->SerialBaud);
+    pc.txBufferSetSize(param->SerialTxSize);
+    xbeeRelay.baud(param->XbeeBaud);
+    xbeeRelay.txSize(param->XbeeTxSize);
+    xbeeRelay.rxSize(param->XbeeRxSize);
+    
+    // INITIAL SETUP, LINK PROFILE VARIABLES
+    glvBat.changeCapacity(param->nominalCapacity);
+    glvBat.setup(&(param->chargeCurrent), &(param->dischargeCurrent));
+    dcdc.setup(&(param->dcdcThreshold), &(param->dcdcOverCurrent), &(param->dcdcStartDelay), &(param->dcdcStopDelay));
+    AMSlatch.setup(&(param->amsStartDelay));
+    IMDlatch.setup(&(param->imdStartDelay));
+    AMSlatch.delayStart();
+    IMDlatch.delayStart();
+    
+    // SETUP SERIAL PORT
+    pc.format(8, SerialBase::None, 2);      // 2 Stop bits, reduce bad serial packets
+    str[99] = 0;
+    pc.printf(str);
+}
+
+int main()
+{
+    init();
+    
     // Start the watchdog check-in thread
     Thread watchdogCheck(thread_watchDog, 0, osPriorityRealtime, 128);
-        
+
     // Start the 100Hz data timer (priotity high)
     RtosTimer sample(runTime::thread_sample, osTimerPeriodic);
     sample.start(FAST_LOOP*1000);
-    
+
     // Start the serial, CAN threads
     Thread serial_out(outDiagnostics::thread_serialOut, 0, osPriorityBelowNormal);
     Thread can_out(outDiagnostics::thread_canOut, 0, osPriorityBelowNormal, 512);
-    
+
     // Start the input polling thread
     Thread getInputs(inCommands::thread_getInputs, 0, osPriorityLow);
-        
+
     // Main task
     while(1) {
         runTime::gather(0);
--- a/outDiagnostics/outDiagnostics.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/outDiagnostics/outDiagnostics.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -1,13 +1,18 @@
 #include "outDiagnostics.h"
+#include "outMacros.h"
 
-osThreadId serialID = 0;                         // RTOS thread ID of thread_serialOut
-const int max_charsPerLine = 80;                        // Max chars per line of printed information
+Timer serialLoop;
+Timer CANloop;
+int serialTime_ms = 0;
+int CANtime_ms = 0;
+const int max_charsPerLine = 80;                 // Max chars per line of printed information
 
 // Use the txEmpty interrupt from MODSERIAL to pace the thread so it always runs as fast as possible
-void empty(MODSERIAL_IRQ_INFO *q) {
+osThreadId serialID=0;
+void empty(MODSERIAL_IRQ_INFO *q)
+{
     osSignalSet(serialID, 1);
 }
-
 // Output a string to the MODSERIAL tx buffer, wait when buffer full
 void AddtoBuffer(char *str, bool newline=true)
 {
@@ -39,182 +44,347 @@
     AddtoBuffer(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)
 {
-    serialID = Thread::gettid();                     // Record thread ID so empty() can signal this thread
-    char temp[max_charsPerLine+5];                   // String buffer to sprintf into, max 1 line
+    serialID = 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
 
-    pc.attach(&empty, MODSERIAL::TxEmpty);           // Attach the tx empty interrupt which paces this thread
+    // Use these bools to track changes in display mode between iterations
+    bool lastModeExtended = param->extendedSerial;
+    bool inExtendedMode = param->extendedSerial;
 
-    tempData.parseGoodChar = ' ';
-    tempData.inputStr[0] = 0;
-    
-    Timer serialLoop;                                // Timer to track the serial loop time, since this thread paces itself
-    int serialTime_ms = 0;
+    // For determining where the data displayed is coming from
+    bool freeze = false;                            // Not a freeze frame
+    bool notLiveProfile = false;                    // Live data
+
+    const char barSpace[4] = { ' ', 179, ' ', 0 };  // Commonly used string with char 179
+    OperatingInfo* dashOp    = op;
+    Profile*       dashParam = param;
+
     serialLoop.reset();
-    serialLoop.start();                              // Start the counter for tracking serial loop time
-
-    //const char barSpace[4] = { ' ', 179, ' ', 0 };   // Commonly used string with char 179
+    serialLoop.start();                             // Start the counter for tracking serial loop time
 
     while(1) {
-
-        serialTime_ms = serialLoop.read_ms();        // Update loop timer, reset for next loop
+        serialTime_ms = serialLoop.read_ms();       // Update loop timer, reset for next loop
         serialLoop.reset();
 
+        // Update display mode, change detection used at end of while(1) loop
+        lastModeExtended = inExtendedMode;
+        inExtendedMode = param->extendedSerial;
+
+        // Determine whether to display freeze frame, change pointers accordingly
+        if (tempData.freeze != NULL) {
+            freeze = true;                          // Indicate showing freeze
+            dashOp = &(tempData.freeze->op.op);             // Point to freeze
+            dashParam = &(tempData.freeze->param.param);    // Point to freeze
+
+            // Determine whether to display a non-live profile, change pointers accordingly
+        } else if (tempData.viewProfile != NULL && tempData.viewProfileNum != -2) {
+            dashParam = tempData.viewProfile;       // Point to other profile
+            notLiveProfile = true;                  // Indicate notLive
+
+            // Show live data only
+        } else {
+            freeze = false;             // Not freeze
+            notLiveProfile = false;     // Live profile
+            dashOp = op;                // Point to live RAM data
+            dashParam = param;          // Point to live RAM data
+        }
+
         sprintf(temp, "\033[0;0H\033[0;0H");
-        AddtoBuffer(temp, false);                     // Move to 0,0, do not append newline
-        
+        AddtoBuffer(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 - tempLength - 1; i++) {      // Fill in the rest of the line with blanks
             tempLength += sprintf(temp+tempLength, " ");                    // Append spaces
         }
         AddtoBuffer(temp);        // Add this to the chunk
-        
-        TITLE(" GLV Battery ")
-        BLANK_LINE
-        sprintf(temp, "Current: %4.3fA          Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity);
+
+        const char profile[NUM_STORED_PROFILES+2][8] = {"Freeze", "Default", "1", "2", "3"};
+        if (inExtendedMode) {
+
+            // Parameters Section
+            snprintf(temp, max_charsPerLine, " Configuration Parameters %s%s%s%s", freeze?"(Viewing Freeze of Last Fault) ":"", (notLiveProfile && !freeze)?" (Viewing Profile ":"", (notLiveProfile && !freeze)?profile[tempData.viewProfileNum+1]:"", (notLiveProfile && !freeze)?") ":"");
+            TITLE(temp)
+            snprintf(temp, max_charsPerLine, "GLVchar:    %5.2fA%sGLVdisch: %5.2fA%sGLVnomCap: %5.2fAh%sGLVtaps:     %3d", dashParam->chargeCurrent,barSpace, dashParam->dischargeCurrent,barSpace, dashParam->nominalCapacity,barSpace, dashParam->glvBat_taps);
+            ADD_SPRINTF_LINE
+            snprintf(temp, max_charsPerLine, "dcdcThres:  %5.2fA%sdcdcOver: %5.2fA%sdcdcStart: %5.2fs %sdcdcStop: %5.2fs", dashParam->dcdcThreshold,barSpace, dashParam->dcdcOverCurrent,barSpace, dashParam->dcdcStartDelay,barSpace, dashParam->dcdcStopDelay);
+            ADD_SPRINTF_LINE
+            snprintf(temp, max_charsPerLine, "dcdcTaps:     %3d %simdStart: %5.1fs%samsStart:  %5.1fs %sIntOverT: %5.1fC",  dashParam->dcdc_taps,barSpace, dashParam->imdStartDelay,barSpace, dashParam->amsStartDelay,barSpace, dashParam->internalOverTemp);
+            ADD_SPRINTF_LINE
+            snprintf(temp, max_charsPerLine, "CANtxSize:   %4d %sCANrxSize: %4d %sSerialBaud: %6d%sSerialTx:  %5d",  dashParam->CANtxSize,barSpace, dashParam->CANrxSize,barSpace, dashParam->SerialBaud,barSpace, dashParam->SerialTxSize);
+            ADD_SPRINTF_LINE
+            snprintf(temp, max_charsPerLine, "XbeeBaud:  %6d %sXbeeTxSize: %4d%sXbeeRxSize: %4d  %s CANack:   %5s",  dashParam->XbeeBaud, barSpace, dashParam->XbeeTxSize,barSpace, dashParam->XbeeRxSize,barSpace, dashParam->CANnoAck?"NOACK":"ACK");
+            ADD_SPRINTF_LINE
+
+            BLANK_LINE
+        }
+        snprintf(temp, max_charsPerLine, " Operating Info %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+
+        // Operating mode
+        const char modeStr[3][8] = {"FAULT", "OKAY", "INVALID"};
+        tempLength = 0;
+        tempLength += sprintf(temp+tempLength, "Sloop: %3dms CANloop: %3dms Time: %s", serialTime_ms, CANtime_ms, ctime(&dashOp->SysTime));
+        tempLength--;
+        char modeN = dashOp->mode;
+        if (modeN == FAULT) modeN=0;
+        else if (modeN == OKAY) modeN=1;
+        else modeN=2;
+        tempLength += sprintf(temp+tempLength, " Uptime: %5ds", dashOp->SysTime - dashOp->startTime);
         ADD_SPRINTF_LINE
-        sprintf(temp, "Amphours: %4.3fAh    SOC: %5.3f    Overcurrent: %s", data.glvAmphours, data.glvSOC, BOOL(data.glvOverCurrent));
+        tempLength = snprintf(temp, max_charsPerLine, "Profile: %7s%10s Mode: %7s%6s     Faults: ", profile[dashOp->profileIndex+1], dashOp->profileModded?", MODIFIED":"", modeStr[modeN], dashOp->faultCode?"+ERROR":"");
+
+        // Fault codes
+        const char topErrStr[12][20] = {"WATCHDOG", "BROWNOUT", "CAN_FAULT", "INT_OVER_TEMP", "IMD_LATCH", "AMS_LATCH", "IMD_FAULT", "DCDC_FAULT", "GLVBAT_FAULT", "FREEZE_FRAME"};
+        int numFaults = 0;
+        for (int i = 0; i < sizeof(dashOp->faultCode)*8; i++) {
+            if (dashOp->faultCode & (1<<i)) numFaults++;
+        }
+        if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults");                          // Start new appending chain string, faults not present
+        else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->faultCode);  // Start new appending chain string, faults present
         ADD_SPRINTF_LINE
+        tempLength = 0;
+        temp[0] = 0;
 
-        BLANK_LINE
-        TITLE(" DC-DC Converter ")
+        // Print max number of strings that will fit per line, then dump line and continue till finished all error flags
+        int num = 0;
+        for (int i = 0; i < 29; i++) {
+            if (dashOp->faultCode & (1<<i)) {        // Fault found
+                // Room for fault string?
+                if (max_charsPerLine-tempLength > strlen(topErrStr[i])+2) {
+                    // Yes, append
+                    tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
+                    num++;
+                } else {
+                    // No, Dump then start new line
+                    ADD_SPRINTF_LINE
+                    tempLength = 0;
+                    tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
+                    num++;
+                }
+            }
+            if (num >= numFaults || numFaults == 0) {
+                // Done printing all faults
+                tempLength = 0;
+                num = 0;
+                if (numFaults != 0) {
+                    ADD_SPRINTF_LINE
+                }
+                break;
+            }
+        }
         BLANK_LINE
-        char DCDC = data.dcdcStatus;
-        char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","ON","OFF"};
-        int dcdcMode = 0;
-        if (DCDC & PowerUp)        dcdcMode = 1;
-        else if (DCDC & PowerDown) dcdcMode = 2;
-        else if (DCDC & SetOn)     dcdcMode = 3;
-        else if (!(DCDC & SetOn))  dcdcMode = 4;
-        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.2fA     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));
+        snprintf(temp, max_charsPerLine, "ShtdwnCrct: %6s %s AIRs: %6s %s Charger: %4s %s IntTemp: %5.1f", (dashOp->signals & SHUTDOWN_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & AIRS_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & CHARGER_DET)?"DET":"NDET", barSpace, dashOp->internalTemp);
         ADD_SPRINTF_LINE
 
         BLANK_LINE
-        TITLE(" PWM Channels ")
+        snprintf(temp, max_charsPerLine, " GLV Battery %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        sprintf(temp, "Current: %4.3fA %s Cap: %4.3fAh %s Ah: %4.3fAh %s SOC: %5.3f", dashOp->glvBat.current, barSpace, dashOp->glvBat.capacity, barSpace, dashOp->glvBat.Ah, barSpace, dashOp->glvBat.SOC);
+        ADD_SPRINTF_LINE
+        // GLV Fault codes
+        const char glvBatErrStr[12][20] = {"OVER_CHARGE_I", "OVER_DISCHARGE_I", "CAP_INIT", "SOC_INIT" };
+        numFaults = 0;
+        for (int i = 0; i < sizeof(dashOp->glvBat.error)*8; i++) {
+            if (dashOp->glvBat.error & (1<<i)) numFaults++;
+        }
+        if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults");                          // Start new appending chain string, faults not present
+        else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->glvBat.error);  // Start new appending chain string, faults present
+        ADD_SPRINTF_LINE
+        tempLength = 0;
+        temp[0] = 0;
+
+        // Print max number of strings that will fit per line, then dump line and continue till finished all error flags
+        num = 0;
+        for (int i = 0; i < 29; i++) {
+            if (dashOp->glvBat.error & (1<<i)) {        // Fault found
+                // Room for fault string?
+                if (max_charsPerLine-tempLength > strlen(glvBatErrStr[i])+2) {
+                    // Yes, append
+                    tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
+                    num++;
+                } else {
+                    // No, Dump then start new line
+                    ADD_SPRINTF_LINE
+                    tempLength = 0;
+                    tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
+                    num++;
+                }
+            }
+            if (num >= numFaults || numFaults == 0) {
+                // Done printing all faults
+                tempLength = 0;
+                num = 0;
+                if (numFaults != 0) {
+                    ADD_SPRINTF_LINE
+                }
+                break;
+            }
+        }
+        
         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);
+        snprintf(temp, max_charsPerLine, " DC-DC Converter %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        char DCDC = dashOp->dcdc.status;
+        const char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","SET-ON","SET-OFF"};
+        int dcdcMode = 0;
+        if (DCDC & POWER_UP)        dcdcMode = 1;
+        else if (DCDC & POWER_DOWN) dcdcMode = 2;
+        else if (DCDC & SET_ON)     dcdcMode = 3;
+        else if (!(DCDC & SET_ON))  dcdcMode = 4;
+        sprintf(temp, "Active:    %3s  %s Mode: %10s %s Status:     0x%02x", (DCDC & CONV_ON)?"YES":"NO", barSpace, dcdcModesStr[dcdcMode], barSpace, DCDC);
         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);
+        sprintf(temp, "Current: %5.2fA %s Overcurrent: %3s %s SensorFault: %3s", dashOp->dcdc.current,barSpace, (DCDC & OVER_CURRENT)?"ERR":"OK",barSpace, (DCDC & SENSOR_FAULT)?"ERR":"OK");
         ADD_SPRINTF_LINE
 
         BLANK_LINE
-        TITLE(" IMD ")
-        BLANK_LINE
-        const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
-        sprintf(temp, "Status: %10s Resistance: %7.0fkohm CritError: %3s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError));
+        snprintf(temp, max_charsPerLine, " DC-DC PWM Channels %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        sprintf(temp, "Actual:      FAN1: %5.3f   FAN2: %5.3f %s PUMP1: %5.3f   PUMP2: %5.3f", dashOp->dcdc.actual.fan1, dashOp->dcdc.actual.fan2, barSpace, dashOp->dcdc.actual.pump1, dashOp->dcdc.actual.pump2);
+        ADD_SPRINTF_LINE
+        sprintf(temp, "Requested:   FAN1: %5.3f   FAN2: %5.3f %s PUMP1: %5.3f   PUMP2: %5.3f", dashOp->dcdc.request.fan1, dashOp->dcdc.request.fan2, barSpace, dashOp->dcdc.request.pump1, dashOp->dcdc.request.pump2);
         ADD_SPRINTF_LINE
 
         BLANK_LINE
-        TITLE(" Latch Circuit Monitors ")
-        BLANK_LINE
-        char AMSerr = data.AMSlatchError;
-        char IMDerr = data.IMDlatchError;
-        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));
+        snprintf(temp, max_charsPerLine, " IMD %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
+        sprintf(temp, "Status: %10s   Resistance: %7.0fKohm   Error: %3s", IMDstr[dashOp->imd.status], dashOp->imd.resistance/1.0e3, dashOp->imd.error?"ERR":"OK");
         ADD_SPRINTF_LINE
 
         BLANK_LINE
-        TITLE(" Shutdown Switches ")
+        snprintf(temp, max_charsPerLine, " Latch Monitors %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        char AMSerr = dashOp->latch.ams;
+        char IMDerr = dashOp->latch.imd;
+        sprintf(temp, "AMS -    OK: %4s Latch: %3s   SoftFault: %3s   HardFault: %3s", (AMSerr & OK_FAULT)?"LOW":"HIGH", (AMSerr & LATCHED_HARD)?"ERR":"OK", (AMSerr & LATCHED_SOFT)?"ERR":"OK", (AMSerr & HARD_FAULT)?"ERR":"OK");
+        ADD_SPRINTF_LINE
+        sprintf(temp, "IMD -    OK: %4s Latch: %3s   SoftFault: %3s   HardFault: %3s", (IMDerr & OK_FAULT)?"LOW":"HIGH", (IMDerr & LATCHED_HARD)?"ERR":"OK", (IMDerr & LATCHED_SOFT)?"ERR":"OK", (IMDerr & HARD_FAULT)?"ERR":"OK");
+        ADD_SPRINTF_LINE
+
         BLANK_LINE
-        char switches = data.switchState;
+        snprintf(temp, max_charsPerLine, " Shutdown Switches %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        char switches = dashOp->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"};
         if (switches == 0) sprintf(temp, "All switches are CLOSED.");
         else sprintf(temp, "%s is OPEN.", switchNames[switches-1]);
         ADD_SPRINTF_LINE
 
         BLANK_LINE
-        TITLE(" Telemetry ")
-        BLANK_LINE
-        sprintf(temp, "Channel 1 -    MessagesIn: %5d   MessagesOut: %5d", xbeeRelay.counterX1in, xbeeRelay.counterX1out);
+        snprintf(temp, max_charsPerLine, " Telemetry %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+        TITLE(temp)
+        sprintf(temp, "Channel 1 -    MsgIn: %5d   MsgOut: %5d %5.2f", dashOp->xbee1.msgIn, dashOp->xbee1.msgOut, dashOp->xbee1.rateOut);
         ADD_SPRINTF_LINE
-        sprintf(temp, "Channel 2 -    MessagesIn: %5d   MessagesOut: %5d", xbeeRelay.counterX2in, xbeeRelay.counterX2out);
+        sprintf(temp, "Channel 2 -    MsgIn: %5d   MsgOut: %5d %5.2f", dashOp->xbee2.msgIn, dashOp->xbee2.msgOut, dashOp->xbee2.rateOut);
         ADD_SPRINTF_LINE
 
-        BLANK_LINE
-        TITLE(" Miscellaneous ")
-        BLANK_LINE
-        sprintf(temp, "Temp: %5.1fC %3s canFault: %3s Watchdog: %3s ErrorCode: 0x%02x SerialTime: %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
+        // Erase screen every few counts to remove glitches
+        static int count = 0;
+        if (count % 50 == 0 || lastModeExtended != inExtendedMode) {
+            sprintf(temp, "\033[2J");
+            AddtoBuffer(temp, false);     // Clear the screen
         }
         count++;
     }
 }
 
-// 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;
+    bool lastCANnoAck = param->CANnoAck;        // Get initial noAck mode setting
+    bool thisCANnoAck = param->CANnoAck;
+
+    if (param->CANnoAck) {                      // Turn on noack mode
+        can.mode(NoAck);
+    } else {                                    // Setup normal mode
+        can.mode(Normal);
+    }
+    can.mode(FIFO);
+
+    CANloop.reset();
+    CANloop.start();
+
     while(1) {
 
-        // Sys Mgmt Error Frame
-        CHAR(data.errorFrame, SYS_ERROR_ID)
+        CANtime_ms = CANloop.read_ms();
+        CANloop.reset();
+
+        // Update last and this variables to check for changes in noAck mode
+        lastCANnoAck = thisCANnoAck;
+        thisCANnoAck = param->CANnoAck;
+        if (thisCANnoAck && !lastCANnoAck) {    // NoAck mode turn on
+            can.mode(NoAck);
+            can.mode(FIFO);
+            can.txFlush();                      // Must flush buffer when switching modes, or else buffer gets stuck
+        }
+        if (!thisCANnoAck && lastCANnoAck) {    // NoAck mode turn off
+            can.mode(Normal);
+            can.mode(FIFO);
+            can.txFlush();                      // Must flush buffer when switching modes, or else buffer gets stuck
+        }
+
+        // OPERATING DIAGNOSTICS
+        // Error Frame
+        CAN_SINGLE(faultCode,   SYS_ERROR_ID)
+
+        // Mode
+        CAN_SINGLE(mode,        SYS_MODE_ID)
+
+        // Flags
+        CAN_SINGLE(signals,     SYS_FLAGS_ID)
+
+        // Profile
+        char byte = (op->profileIndex != -1) ? op->profileIndex : 1<<6;   // Mark the second to last bit of the byte if using Freeze profile (data[0]=64 for freeze, data[0]=0 for default)
+        byte |= (op->profileModded) ? 1<<7 : 0;                           // Mark the last bit of the byte if the profile was modified (OR'd with profile id from above)
+        SEND_CAN_SINGLE(byte, SYS_PROFILE_ID);
+
+        // Time
+        CAN_PAIR(SysTime,       startTime,      SYS_TIME_ID)
 
         // Xbee1 Counter
-        UINT_PAIR(xbeeRelay.counterX1in, xbeeRelay.counterX1out, SYS_XBEE1_ID)
+        CAN_PAIR(xbee1.msgIn,   xbee1.msgOut,   SYS_XBEE1_MSG_ID)
 
         // Xbee2 Counter
-        UINT_PAIR(xbeeRelay.counterX2in, xbeeRelay.counterX2out, SYS_XBEE2_ID)
+        CAN_PAIR(xbee2.msgIn,   xbee2.msgOut,   SYS_XBEE2_MSG_ID)
 
         // Internal temperature
-        FLOAT(data.internalTemp, SYS_TEMP_ID)
+        CAN_SINGLE(internalTemp, SYS_TEMP_ID)
 
         // GLV Battery
-        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)
-        
+        CAN_SINGLE(glvBat.current,  SYS_GLV_CURRENT_ID)
+        CAN_SINGLE(glvBat.capacity, SYS_GLV_CAPACITY_ID)
+        CAN_SINGLE(glvBat.Ah,       SYS_GLV_AH_ID)
+        CAN_SINGLE(glvBat.SOC,      SYS_GLV_SOC_ID)
+        CAN_SINGLE(glvBat.error,    SYS_GLV_ERROR_ID)
+
         // DC-DC Converter
-        FLOAT(data.dcdcCurrent, SYS_DCDC_CURRENT_ID)
-        CHAR(data.dcdcStatus, SYS_DCDC_STATUS_ID)
+        CAN_SINGLE(dcdc.current,    SYS_DCDC_CURRENT_ID)
+        CAN_SINGLE(dcdc.status,     SYS_DCDC_STATUS_ID)
 
         // PWM Channels
-        FLOAT_PAIR(data.dcdcFan1Duty, data.dcdcFan2Duty, SYS_PWM_FAN_ID)
-        FLOAT_PAIR(data.dcdcPump1Duty, data.dcdcPump2Duty, SYS_PWM_PUMP_ID)
+        CAN_PAIR(dcdc.actual.fan1,  dcdc.actual.fan2,  SYS_PWM_FAN_ID)
+        CAN_PAIR(dcdc.actual.pump1, dcdc.actual.pump2, SYS_PWM_PUMP_ID)
 
         // IMD
-        FLOAT(data.imdStatus, SYS_IMD_STATUS_ID)
-        CHAR(data.imdResistance, SYS_IMD_RESIST_ID)
+        CAN_SINGLE(imd.status,      SYS_IMD_STATUS_ID)
+        CAN_SINGLE(imd.resistance,  SYS_IMD_RESIST_ID)
 
         // Latches
-        CHAR(data.IMDlatchError, SYS_IMD_LATCH_ID)
-        CHAR(data.AMSlatchError, SYS_AMS_LATCH_ID)
+        CAN_SINGLE(latch.imd,       SYS_IMD_LATCH_ID)
+        CAN_SINGLE(latch.ams,       SYS_AMS_LATCH_ID)
 
         // Shutdown Switches
-        CHAR(data.switchState, SYS_SWITCHES_ID)
-        
+        CAN_SINGLE(switchState,     SYS_SWITCHES_ID)
+
         osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<4);      // Signal watchdog thread
         Thread::wait(CAN_LOOP*1000);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/outDiagnostics/outMacros.h	Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,42 @@
+#ifndef OUT_MACROS_H
+#define OUT_MACROS_H
+
+/******************************* FOR WORKING WITH THE STRING BUFFER ***************************/
+
+
+// Macros for working with the strings
+#define ADD_SPRINTF_LINE    temp[max_charsPerLine-1]=0; 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
+
+
+/********************************** FOR WORKING WITH CAN MESSAGES ******************************/
+
+#define SEND_CAN(LEN, ID)   \
+msg.len = LEN;              \
+msg.id = ID;                \
+msg.type = CANData;         \
+msg.format = CANStandard;   \
+if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT;
+
+template <class Type>
+void SEND_CAN_SINGLE(Type Data, int ID)
+{
+    CANMessage msg;
+    *((Type*)((void*)&msg.data[0])) = Data;
+    SEND_CAN(sizeof(Type), ID)
+}
+template <class Type>
+void SEND_CAN_PAIR(Type Data1, Type Data2, int ID)
+{
+    CANMessage msg;
+    *((Type*)((void*)&msg.data[0])) = Data1;
+    *((Type*)((void*)&msg.data[sizeof(Type)])) = Data2;
+    SEND_CAN(2*sizeof(Type), ID)
+}
+
+#define CAN_SINGLE(DATA, ID)         SEND_CAN_SINGLE(op->DATA, ID);
+#define CAN_PAIR(DATA1, DATA2, ID)   SEND_CAN_PAIR(op->DATA1, op->DATA2, ID);
+
+#endif
--- a/runTime/runTime.cpp	Thu Jan 22 07:59:48 2015 +0000
+++ b/runTime/runTime.cpp	Sat Feb 07 08:54:51 2015 +0000
@@ -1,49 +1,103 @@
 #include "runTime.h"
 
-const float INTERNAL_OVER_TEMP_THRES = 60;  // Overtemp at 60C
-
 void runTime::gather(void const* args)
 {
-    // GLV battery coulomb counter
-    data.glvCurrent = glvBat.current();
-    data.glvSOC = glvBat.SOC();
-    data.glvAmphours = glvBat.ampHours();
-    data.glvCapacity = glvBat.capacity();
-    data.glvOverCurrent = glvBat.overCurrentDetected();
+    static int counter = 0;
+
+    // POPULATE DATA
+    op->SysTime             = time(0);      // Tick time
+
+    // GLV Battery
+    op->glvBat.current      = glvBat.current();
+    op->glvBat.SOC          = glvBat.SOC();
+    op->glvBat.Ah           = glvBat.ampHours();
+    op->glvBat.capacity     = glvBat.capacity();
+    op->glvBat.error        = glvBat.readError();
 
     // DC-DC converter & PWM channels
-    data.dcdcStatus = dcdc.getStatus();
-    data.dcdcError = dcdc.hasCritError();
-    data.dcdcCurrent = dcdc.getCurrent();
-    data.dcdcFan1Duty = dcdc.readPwm(FAN1);
-    data.dcdcFan2Duty = dcdc.readPwm(FAN2);
-    data.dcdcPump1Duty = dcdc.readPwm(PUMP1);
-    data.dcdcPump2Duty = dcdc.readPwm(PUMP2);
+    op->dcdc.status         = dcdc.getStatus();
+    op->dcdc.current        = dcdc.getCurrent();
+    op->dcdc.actual.pump1   = dcdc.readPwm(PUMP1);
+    op->dcdc.actual.pump2   = dcdc.readPwm(PUMP2);
+    op->dcdc.actual.fan1    = dcdc.readPwm(FAN1);
+    op->dcdc.actual.fan2    = dcdc.readPwm(FAN2);
 
     // IMD PWM
-    data.imdStatus = imd.status();
-    data.imdResistance = imd.resistance();
-    data.imdError = ((data.imdStatus != NORMAL) && (data.imdStatus != SPEEDSTART)) || (isnan(data.imdResistance));
-
+    op->imd.status          = imd.status();
+    op->imd.resistance      = imd.resistance();
+    static bool started=false;
+    if (counter > 30) started=true;     // Give the IMD 3 seconds to turn on
+    if (started) {
+        op->imd.error       = ((op->imd.status != NORMAL) && (op->imd.status != SPEEDSTART)) || (isnan(op->imd.resistance));
+    } else op->imd.error    = 0;
+    
     // Reset latches
-    data.AMSlatchError = AMSlatch.update();
-    data.IMDlatchError = IMDlatch.update();
+    op->latch.ams           = AMSlatch.update();
+    op->latch.imd           = IMDlatch.update();
 
     // Switches
-    data.switchState = switches.poll();
+    op->switchState         = switches.poll();
+    if (op->switchState == 0)   op->signals |= SHUTDOWN_CLOSED;
+    else                        op->signals &= ~SHUTDOWN_CLOSED;
 
     // Temperaure
-    data.internalTemp = internalTmp.read();
-    if (data.internalTemp > INTERNAL_OVER_TEMP_THRES) data.internalOverTemp = true;
-    else data.internalOverTemp = false;
+    op->internalTemp        = internalTmp.read();
 
-    // Error frame
-    data.errorFrame = data.canFault<<0 | data.watchdogReset<<1 | data.internalOverTemp<<2 | (data.IMDlatchError!=0)<<3 | (data.AMSlatchError!=0)<<4 | data.imdError<<5 | data.dcdcError<<6 | data.glvOverCurrent<<7;
+    // Telemetry trackers
+    op->xbee1.msgIn         = xbeeRelay.counterX1in;
+    op->xbee1.msgOut        = xbeeRelay.counterX1out;
+    op->xbee1.msgIn         = xbeeRelay.counterX2in;
+    op->xbee2.msgOut        = xbeeRelay.counterX2out;
+    if (counter++ % 10 == 0) {                               // Do every 1 second
+        op->xbee1.rateOut   = xbeeRelay.bytesX1out / 1000.0;   // Measure data rate in KB/s
+        op->xbee2.rateOut   = xbeeRelay.bytesX2out / 1000.0;   
+        xbeeRelay.bytesX1out = 0;                            // Clear every second
+        xbeeRelay.bytesX2out = 0;
+    }
+    
+    // CATCH ERRORS
+    if (op->glvBat.error)                                   op->faultCode |= GLVBAT_FAULT;
+    if (op->dcdc.status & (OVER_CURRENT | SENSOR_FAULT))    op->faultCode |= DCDC_FAULT;
+    if (op->internalTemp > param->internalOverTemp)         op->faultCode |= INT_OVER_TEMP;
+    if (op->latch.ams & HARD_FAULT)                         op->faultCode |= AMS_LATCH;
+    if (op->latch.imd & HARD_FAULT)                         op->faultCode |= IMD_LATCH;
+    if (op->imd.error)                                      op->faultCode |= IMD_FAULT;
+    
+    if (FreezeFrame::getError())                            op->faultCode |= FREEZE_FRAME;
+    else                                                    op->faultCode &= ~FREEZE_FRAME;
+    
+    // UPDATE MODE
+    if (op->faultCode) op->mode = FAULT;
+    else op->mode = OKAY;
+    
+    // ARREST ERROR
+    if (op->mode == FAULT) {
+        if (!FreezeFrame::getError()) {         // Write freeze frame is slot available
+            if (FreezeFrame::writeFrame()) {
+                op->faultCode |= FREEZE_FRAME;
+            } 
+        }   
+    }
+
+    // Update DC-DC converter
+    if ((op->signals & SHUTDOWN_CLOSED) && (op->signals & AIRS_CLOSED) && !(op->signals & CHARGER_DET) && (op->mode == OKAY)) {
+        dcdc.set(1);
+    } else dcdc.set(0);
+
+    // Update PWM channels
+    dcdc.setPwm(PUMP1, op->dcdc.request.pump1);
+    dcdc.setPwm(PUMP2, op->dcdc.request.pump2);
+    dcdc.setPwm(FAN1,  op->dcdc.request.fan1);
+    dcdc.setPwm(FAN2,  op->dcdc.request.fan2);
 }
-
+void runTime::clearFaults() {
+    op->faultCode = 0;
+    op->mode = OKAY;
+    FreezeFrame::clearError();
+}
 void runTime::thread_sample(void const* args)
 {
-    osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<1);      // Signal watchdog thread
     glvBat.sample();    // Integrate next sample in GLV Battery coulomb counter
     dcdc.sample();      // Handle dc-dc filter and errors
+    osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<1);      // Signal watchdog thread
 }
\ No newline at end of file
--- a/runTime/runTime.h	Thu Jan 22 07:59:48 2015 +0000
+++ b/runTime/runTime.h	Sat Feb 07 08:54:51 2015 +0000
@@ -7,6 +7,7 @@
 namespace runTime {
     void gather(void const* args);
     void thread_sample(void const* args);
+    void clearFaults();
 }
 
 #endif
\ No newline at end of file