Control Code with I/O and ADC working

Dependencies:   MODSERIAL mbed

main.cpp

Committer:
jrodenburg
Date:
2018-07-16
Revision:
20:cdeed4dad690
Parent:
19:0946665b37c7
Child:
21:f87464a7e7c6

File content as of revision 20:cdeed4dad690:

// MBED SCRIPT FOR CONTROLLING THE TEMPERATURE CONTROLLED TEST FIXTURE (TCTF)
// DATE: SEPTEMBER 2017

#include "mbed.h"
#include "MODSERIAL.h"
#include "MCP23008.h"
#include "LTC2487.h"
#include <string>


//DEFINITIVE VARIABLES
#define DEBUG_LOOP_TIME      0x00000001
#define RXDATA_PRINT         0x00000002
#define UID_PRINT            0x00000004
#define ERROR_PRINT          0x00000008
#define ADC_VAL_PRINT        0x00000010
#define HEATER_CHILLER_PRINT 0x00000020
#define HEATER_ON_TIME_PRINT 0x00000040
#define LED_PRINT            0x00000080
#define TEMP_PRINT           0x00000100
#define I2C_PRINT            0x00000200

#define IO_I2C_LINE          1
#define ADC_I2C_LINE         2


#define CHAN_COUNT           8
#define MIN_TEMP             10
#define MAX_TEMP             65
#define MEGA_MAX_TEMP        75
#define TEMP_MARGIN          10
#define HYST_LOW             0.3
#define HYST_HIGH            1
#define SAMPLES              5
#define I2C_Freq             2000
#define VALVE                1
#define HEATER               2
#define GREEN_STATUS_ON      3
#define RED_STATUS_ON        0
#define ERROR_STATUS_ON      1
#define STATUS_OFF           4
#define sizeLUT              34
#define BACK_THERM           0
#define FRONT_THERM          1
#define HEAT_FET_AMP         2
#define VALVE_FET_AMP        3
#define FET_ON_CURRENT       1.12
#define ROOM_TEMP            22
#define MAX_HEATER_ON_TIME   25
#define MAX_CHILL_TIME       10 //10sec
#define MIN_ERROR_CNT_1      4    //ALL SAFETY RELATEDD CHECKS
#define MIN_ERROR_CNT_2      600  //PEFORMANCE TIME (HAVE TO GIVE FIXTURE TIME TO SETTLE)
#define MIN_ERROR_CNT_3      60
#define NUM_EMAIL_SEND       2
#define STATUS_COOLING       1
#define STATUS_HEATING       2
#define STATUS_INACTIVE      3
#define STATUS_STANDBY       4

#define READ_TEMP_TASK_TIME  .300f //300ms to read one A/D value
#define I2C_READ_WAIT_TIME   0.08f

// Defines for use with Serial communication
#pragma pack (1)
#define RX_SOF                  0x7B
#define RX_EOF                  0x7D
#define TX_SOF                  0x7B
#define TX_EOF                  0x7D
#define DELIMETER               0x2E
#define SET_TEMPERATURE         0xB0
#define SELECT_CHANNEL          0xB1
#define READ_UID                0xB2
#define READ_FW_VERSION         0xB3
#define DEBUG_CONTROL           0xB4
#define RESPONSE_DATA           0xD0
#define TEMP_DATA               0xD1
#define UID_DATA                0xD2
#define FW_VERSION_DATA         0xD3
#define ERROR_DATA              0xDF


const char FW_Version[8] =  "V0.01C";
unsigned int chanSel_SendTemp = 0;
unsigned int chanSel_SetTemp = 0;
// Default to chan 0
int currChan = 0;
bool newTempSet = false;
bool readFrontThermistor = true;
bool newTempData = false;
bool newTempDataAvailable = false;
unsigned int UID_print_count = 0;
unsigned int debug_control = 0x2;

//***********************************************
// Timers
//***********************************************
Ticker frontTempDoneTicker;
Ticker backTempDoneTicker;
Ticker readThermistorTicker;
Ticker frontTempInProgTicker;
Ticker backTempInProgTicker;

//***********************************************
// LEDs
//***********************************************
DigitalOut rLed(LED1);
DigitalOut gLed(LED2);

//***********************************************
// Functions Declarations
//***********************************************
void sendUID ();
void sendFWVersion();
void read_front_temp();
void read_back_temp();
void update_front_temp_data();
void update_back_temp_data();
void temperatureDataReady();

//***********************************************
// Serial Rx Packet Format
//***********************************************
typedef struct {
    unsigned char SOF_flag;
    unsigned char cmd_type;
    unsigned char len;
    unsigned char Delim;
} HOST_CMD_HEADER;

typedef struct {
    HOST_CMD_HEADER cmd_header;
    unsigned char chanID;
    unsigned char tempDelim;
    float setTemp;
    unsigned char chanStatDelim;
    unsigned char chanStat;
} SET_TEMPERATURE_CMD;

typedef struct {
    HOST_CMD_HEADER cmd_header;
    unsigned char chanIDSel;
    unsigned char chanDelim;
    unsigned char chanStat;
} SELECT_CHANNEL_CMD;

typedef struct {
    unsigned char SOF_flag;
    unsigned char cmd_type;
    unsigned char len;
    unsigned char Delim;
    uint32_t channel;
    float data;
    unsigned char EOF_flag;
} RESPONSE_CMD;

typedef struct {
    unsigned char SOF_flag;
    unsigned char cmd_type;
    unsigned char len;
    unsigned char Delim;
    float chTemp[CHAN_COUNT];
    unsigned char EOF_flag;
} RESPONSE_TEMP_CMD;

typedef struct {
    HOST_CMD_HEADER cmd_header;
    unsigned char chanIDSel;
    unsigned char chanDelim;
    unsigned char chanStat;
} READ_UID_CMD;

typedef struct {
    HOST_CMD_HEADER cmd_header;
    unsigned char chanIDSel;
    unsigned char chanDelim;
    uint32_t commandData;
} GUI_STANDARD_CMD;

typedef struct {
    unsigned char SOF_flag;
    unsigned char cmd_type;
    unsigned char len;
    unsigned char Delim;
    unsigned char FW_Version[8];
    unsigned char EOF_flag;
} FW_VERSION_RESPONSE;

typedef struct {
    unsigned char SOF_flag;
    unsigned char cmd_type;
    unsigned char len;
    unsigned char Delim;
    uint32_t UIDMH;
    uint32_t UIDML;
    uint32_t UIDL;
    unsigned char EOF_flag;
} UID_RESPONSE;

//TCTF CHANNEL DATA
struct CHNL_DATA{
   bool status;
   float setTemp;
   int error;
   int state;
   int errorEmailSent;
   int i2cRxed;
};

CHNL_DATA chnlStatus[] = {
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1},
    {0, NULL, 0, 0, 0, 1}
};


//I2C AADRESS LOOK UP TABLE, CREATED IN EXCEL SHEET (COUNT, TEMP)
struct I2C_ADDR_LUT{
   int adc;
   int io;
};

I2C_ADDR_LUT addrLUT[] = {
    {0x23, 0x10},
    {0x53, 0x60},
    {0x43, 0x70},
    {0x73, 0x40},
    {0x63, 0x50},
    {0x22, 0x11},
    {0x52, 0x61},
    {0x42, 0x71}
};

//THERMISTOR LOOK UP TABLE, CREATED IN EXCEL SHEET (COUNT, TEMP)
struct THERM_LUT{
   int adc;
   int temp;
};

THERM_LUT thermLUT[] = {
    {113779,-40},
    {109152,-35},
    {103830,-30},
    {97855,-25},
    {91319,-20},
    {84352,-15},
    {77124,-10},
    {69820,-5},
    {62621,0},
    {55693,5},
    {49169,10},
    {43144,15},
    {37669,20},
    {32768,25},
    {28429,30},
    {24622,35},
    {21309,40},
    {18439,45},
    {15962,50},
    {13831,55},
    {12002,60},
    {10428,65},
    {9080,70},
    {7919,75},
    {6923,80},
    {6063,85},
    {5323,90},
    {4685,95},
    {4130,100},
    {3653,105},
    {3234,110},
    {2876,115},
    {2563,120},
    {2284,125}
};



//TCTF CHANNEL TEMPERATURE
typedef struct{
   float currTempFront;
   float currTempBack;
}CHNL_TEMP;

CHNL_TEMP channelTempData[CHAN_COUNT];

//SERIAL COMMUNICATION SETUP
MODSERIAL pc(USBTX, USBRX);

//DEFINE PINS
DigitalOut myled(LED2);

//I2C FOR MCP23008 (I/O Control)
MCP23008 io_control(PTC9, PTC8, 0x10, 100000); //sda, scl

//I2C FOR LTC2487 (ADC Control)
LTC2487 ltc2487(PTC11, PTC10, 0x23, 100000); //sda, scl

//GLOBAL VARIABLES
volatile bool dataReceived = false; //used to check if data has been received
volatile bool dataTxReady = false;
char rxBuf[50];
int chnlSel;
bool standbyLED[CHAN_COUNT] = {false,false,false,false,false,false,false,false};

/* Function: turnOffChannel
   **************************************************************
   Description: Turns off a channel
   Recieves: chnl: channel to turn off
   Returns: N/A
*/

void turnOffChannel(int chnl){
    io_control.setAddress(addrLUT[chnl].io);
    io_control.init();
    io_control.writeOutput(0,0,0,0);
}


/* Function: rxInterrupt
   **************************************************************
   Description: serial rx interupt handler
   Receives: N/A
   Returns: N/A
*/

//***********************************************
// Rx Interrupt from serial Host interface
//***********************************************
void rxInterrupt(MODSERIAL_IRQ_INFO *info)
{
    gLed = 0;
    dataReceived = true;
    //wait(.5);
    gLed = 1;
}


//***************************************************************
// Tx Interrupt from serial Host interface
//***************************************************************
void txInterrupt(MODSERIAL_IRQ_INFO *info)
{
    gLed = 1;
    dataTxReady = true;
    gLed = 1;
}


/* Function: parseRXData
   **************************************************************
   Description: The parse received data into
   Receives: chn: The channel we want to put into the channel
   Returns: N/A
*/
void processRxUARTData()
{
    HOST_CMD_HEADER *pRxPktHdr;
    string data = "";

    pRxPktHdr = (HOST_CMD_HEADER *)rxBuf;

    if (debug_control & RXDATA_PRINT) {
        pc.printf("DBG: SOF=%02x, Len=%02x, CMD=%02x\r\n", pRxPktHdr->SOF_flag, pRxPktHdr->len, pRxPktHdr->cmd_type);
        pc.printf("DBG: ");
        for (int i=0; i <5; i++)
            pc.printf("%02x ", rxBuf[i]);
        pc.printf("\n");
    }

    // Exit if the packet does not contain correct header
    // Maybe send NAK?
    if ((pRxPktHdr->SOF_flag != RX_SOF) || (pRxPktHdr->Delim != DELIMETER))
        return;

    switch (pRxPktHdr->cmd_type)
    {
        case SET_TEMPERATURE:
            // Process set temp for specified channel
            {
                SET_TEMPERATURE_CMD *pRxPkt = (SET_TEMPERATURE_CMD *)(rxBuf);

                if (debug_control & RXDATA_PRINT)
                    pc.printf("DBG: SETTEMP: ch = %02x, tempSet = %f, chanStat = %02x\r\n",
                                pRxPkt->chanID, pRxPkt->setTemp, pRxPkt->chanStat);

                if ((pRxPkt->tempDelim != DELIMETER) || (pRxPkt->chanStatDelim != DELIMETER)) {
                    // Send NAK back
                    pc.printf("DBG: Error\n");
                }
                else {
                    chanSel_SetTemp = pRxPkt->chanID;
                    chnlStatus[pRxPkt->chanID].status = pRxPkt->chanStat;
                    chnlStatus[pRxPkt->chanID].state = STATUS_INACTIVE;
                    chnlStatus[pRxPkt->chanID].setTemp = pRxPkt->setTemp;
                    chnlStatus[pRxPkt->chanID].error = 0;
                    chnlStatus[pRxPkt->chanID].errorEmailSent = 0;
                    chnlStatus[pRxPkt->chanID].i2cRxed = 0;
                    newTempSet = true;
                }
            }
            break;

        case SELECT_CHANNEL:
            // Select channel to send temp data to
            {
                SELECT_CHANNEL_CMD *pRxPkt = (SELECT_CHANNEL_CMD *)(rxBuf);

                chanSel_SendTemp = pRxPkt->chanIDSel;

                // Send back FW version for each select channel
                sendFWVersion();
                if (debug_control & RXDATA_PRINT)
                    pc.printf("DBG: CHAN_SEL: chan=%02x, chanStat = %02x\r\n", pRxPkt->chanIDSel, pRxPkt->chanStat);

            }
            break;

        case READ_UID:
            {
                READ_UID_CMD *pRxPkt = (READ_UID_CMD *)(rxBuf);
                if (debug_control & RXDATA_PRINT)
                    pc.printf("DBG: Read UID: chan%02x\n", pRxPkt->chanIDSel);

                sendUID();
            }
            break;

        case READ_FW_VERSION:
            {
                sendFWVersion();
                if (debug_control & RXDATA_PRINT)
                    pc.printf("DBG: Read SW Version\r\n");

            }
            break;

        case DEBUG_CONTROL:
            {
                GUI_STANDARD_CMD *pRxPkt = (GUI_STANDARD_CMD *)rxBuf;
                debug_control = pRxPkt->commandData;
                if (debug_control & RXDATA_PRINT)
                    pc.printf ("DBG: Rx'd DEBUG CMD: %04x\r\n", pRxPkt->commandData);
            }
            break;


        default:
            // Error
            break;
    }
}

/* Function: get_temp
   **************************************************************
   Description: Convert A/D count to temperature value
   Receives: ADC_val: the count from the A/D reading
   Returns: the temp. of the A/D reading
*/

float get_temp(float ADC_val){
    myled = 1;
    //ltc2487.setAddress(addrLUT[chn].adc);

    //float ADC_val = ltc2487.readOutput(port); //(65536*1.334)/2.5

    int i = 0;

    while((i < sizeLUT) && (thermLUT[i].adc > ADC_val)){
        i++;
    }  //find the temp. above therm temp

    //Point slope formula extrapolation:
    // m = (y1-y0)/(x1-x0)+ y0 , y = temp. value, x = adc value
    // y1 = thermLUT[i-1].temp   y0 = thermLUT[i].temp
    // x1 = thermLUT[i-1].adc    x0 =thermLUT[i].adc
    float a = float(thermLUT[i-1].temp - thermLUT[i].temp); //slope of temp between points where therm temp is between (Tmax - Tmin)
    float b = float(thermLUT[i-1].adc - thermLUT[i].adc);   //slope of adc between points where therm adc is between (Amax - Amin)

    float m = a/b;
    float y = (m*(ADC_val-thermLUT[i].adc))+thermLUT[i].temp;

    if (debug_control & ADC_VAL_PRINT)
        pc.printf("DBG: ADC VAL: %f TEMP: %f \r\n", ADC_val, y);

    return y;
}

/* Function: get_front_temp_DATA
   **************************************************************
   Description: Read A/D data from LTC2487 and set array with front temp.
                Save values in CHNL_TEMP data struct
   Receives: N/A
   Returns: int If the write recieved an ACK
*/
void read_front_temp_data()
{
    readThermistorTicker.detach();
    //Write to all 8 channels selecting the front port to read
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        chnlStatus[chnl].i2cRxed = 0;
        ltc2487.setAddress(addrLUT[chnl].adc);
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chnl].i2cRxed = !ltc2487.writePort(FRONT_THERM);
    }
    //wait until next clock cycle on LTC
    //wait(I2C_READ_WAIT_TIME);
    frontTempInProgTicker.attach(&update_front_temp_data, I2C_READ_WAIT_TIME);
}

void update_front_temp_data()
{
    frontTempInProgTicker.detach();
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        ltc2487.setAddress(addrLUT[chnl].adc);
        channelTempData[chnl].currTempFront = get_temp(ltc2487.read());
    }
    //wait until next clock cycle on LTC
    //wait(0.08);
    frontTempDoneTicker.attach(&read_back_temp, I2C_READ_WAIT_TIME);
}

/* Function: get_back_temp_DATA
   **************************************************************
   Description: Read A/D data from LTC2487 and set array with back temp.
                Save values in CHNL_TEMP data struct
   Receives: N/A
   Returns: N/A
*/

void read_back_temp()
{
    frontTempDoneTicker.detach();
    //Write to all 8 channels selecting the front port to read
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        chnlStatus[chnl].i2cRxed = 0;
        ltc2487.setAddress(addrLUT[chnl].adc);
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chnl].i2cRxed = !ltc2487.writePort(BACK_THERM);
    }

    //wait until next clock cycle on LTC
    //wait(I2C_READ_WAIT_TIME);
    backTempInProgTicker.attach(&update_back_temp_data, I2C_READ_WAIT_TIME);
}

void update_back_temp_data()
{
    backTempInProgTicker.detach();
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        ltc2487.setAddress(addrLUT[chnl].adc);
        channelTempData[chnl].currTempBack = get_temp(ltc2487.read());
    }
    //wait until next clock cycle on LTC
    //wait(0.08);
    backTempDoneTicker.attach(&temperatureDataReady, I2C_READ_WAIT_TIME);

}

/* Function: temperatureDataReady
   **************************************************************
   Description: Flag that temperature data is ready to be
                processed
                Restart task timer
   Receives: N/A
   Returns: N/A
*/
void temperatureDataReady()
{
    backTempDoneTicker.detach();
    newTempDataAvailable = true;
    readThermistorTicker.attach(&read_front_temp_data, READ_TEMP_TASK_TIME);
}

/* Function: get_heater_current
   **************************************************************
   Description: Retrieve current into heater control MOSFET
   Receives: chn: the channel of the fixture to read current from
   Returns: the current into the heater control MOSFET
*/
void get_heater_current(int chn, int port){
    ltc2487.setAddress(addrLUT[chn].adc);
    ltc2487.writePort(HEAT_FET_AMP);
    wait(0.08);
    ltc2487.read();
    wait(0.08);
}

/* Function: get_valve_current
   **************************************************************
   Description: Retrieve current into valve control MOSFET
   Receives: chn: the channel of the fixture to read current from
   Returns: the current into the valve control MOSFET
*/

void get_valve_current(int chn, int port){
    ltc2487.setAddress(addrLUT[chn].adc);
    ltc2487.writePort(VALVE_FET_AMP);
    wait(0.08);
    ltc2487.read();
    wait(0.08);
}

/* Function: turn_valve_on
   **************************************************************
   Description: Turn valve on and green status LED on
   Receives: chn: the channel of the fixture
   Returns: N/A
*/

void turn_valve_on(int chn){
    io_control.setAddress(addrLUT[chn].io);
    io_control.init();
    io_control.writeOutput(1,0,1,0);
}

/* Function: turn_valve_off
   **************************************************************
   Description: Turn valve off and green status LED on
   Receives: chn: the channel of the fixture
   Returns: N/A
*/

void turn_valve_off(int chn){
    io_control.setAddress(addrLUT[chn].io);
    io_control.init();
    io_control.writeOutput(0,0,1,0);
}

/* Function: turn_heater_on
   **************************************************************
   Description: Turn heater on and green status LED on
   Receives: chn: the channel of the fixture
   Returns: N/A
*/

void turn_heater_on(int chn){
    io_control.setAddress(addrLUT[chn].io);
    io_control.init();
    io_control.writeOutput(0,1,1,0);
}

/* Function: turn_heater_off
   **************************************************************
   Description: Turn heater off and green status LED on
   Receives: chn: the channel of the fixture
   Returns: N/A
*/

void turn_heater_off(int chn){
    io_control.setAddress(addrLUT[chn].io);
    io_control.init();
    io_control.writeOutput(0,0,1,0);
}

/* Function: status_led
   **************************************************************
   Description: Turn status LED on (turns on green or red)
   Receives: chn: the channel of the fixture
             status: the status of channel (good (1) or bad (0))
   Returns: N/A
*/

void status_led(int chn, int status){
    if(status == GREEN_STATUS_ON){
        //green LED
        if(debug_control & LED_PRINT) pc.printf("GREEN LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        io_control.writeOutput(0,0,1,0);
    }
    if(status == RED_STATUS_ON){
        //red LED
        if(debug_control & LED_PRINT) pc.printf("RED LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        io_control.writeOutput(0,0,0,1);
    }
    if(status == ERROR_STATUS_ON){
        //turn valve on too
        if(debug_control & LED_PRINT) pc.printf("RED AND BLUE LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        io_control.writeOutput(1,0,0,1);
    }
    if(status == STATUS_OFF){
        //turn valve on too
            if(debug_control & LED_PRINT) pc.printf("ALL OFF %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        io_control.writeOutput(0,0,0,0);
    }
}

/* Function: test_mcp23008
   **************************************************************
   Description: Test each output of the MCP23009
   Receives: N/A
   Returns: N/A
*/

void test_mcp23008(int chn){
    turn_valve_on(chn);
    wait(0.5);
    turn_valve_off(chn);
    wait(0.5);
    turn_heater_on(chn);
    wait(0.5);
    turn_heater_off(chn);
    wait(0.5);
    status_led(chn, 0);
    wait(0.5);
    status_led(chn, 1);
}

/* Function: test_ltc2487
   **************************************************************
   Description: Test the reading from LTC2487
   Receives: N/A
   Returns: N/A
*/

void test_ltc2487(){
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        float frontTemp = channelTempData[chnl].currTempFront;
        float backTemp = channelTempData[chnl].currTempBack;
        pc.printf("TEMPERATURE READING [%i]:: BACK: %f FRONT: %f \r\n", chnl, backTemp, frontTemp);
    }
}

//***************************************************************
// Build packet with temperature readings to send to GUI
//***************************************************************
void processTxUARTData()
{
    RESPONSE_TEMP_CMD response;
    unsigned char *ptr = (unsigned char *)&response;
    int i;

    response.SOF_flag = TX_SOF;
    response.cmd_type = TEMP_DATA;
    response.len = 5+(sizeof(response.chTemp));
    response.Delim = DELIMETER;
    response.chTemp[0] = channelTempData[0].currTempBack;
    response.chTemp[1] = channelTempData[1].currTempBack;
    response.chTemp[2] = channelTempData[2].currTempBack;
    response.chTemp[3] = channelTempData[3].currTempBack;
    response.chTemp[4] = channelTempData[4].currTempBack;
    response.chTemp[5] = channelTempData[5].currTempBack;
    response.chTemp[6] = channelTempData[6].currTempBack;
    response.chTemp[7] = channelTempData[7].currTempBack;

    // Send Errors Count/Info

    /*float *dst = (float *)&response.chTemp[0];
    float *src = (float *)&channelTempData[0];
    memcpy(dst, src, sizeof(channelTempData));*/
    response.EOF_flag = TX_EOF;

    // Send response to GUI
    for (i=0; i < response.len; i++, ptr++)
        pc.printf("%02x", *ptr);
    pc.printf("\n");
}

//***************************************************************
// Build packet with Board UID (Unique Identification)
//***************************************************************
void sendUID ()
{
    UID_RESPONSE response;
    unsigned char *ptr = (unsigned char *)&response;
    int i;

    response.SOF_flag = TX_SOF;
    response.cmd_type = UID_DATA;
    response.len = 17;
    response.Delim = DELIMETER;
    response.UIDMH = (uint32_t)SIM->UIDMH;
    response.UIDML = (uint32_t)SIM->UIDML;
    response.UIDL = (uint32_t)SIM->UIDL;
    response.EOF_flag = TX_EOF;

    // Send response to GUI
    for (i=0; i < response.len; i++, ptr++)
        pc.printf("%02x", *ptr);
    pc.printf("\n");
}

//***************************************************************
// Build packet with SW Version
//***************************************************************
void sendFWVersion()
{
    FW_VERSION_RESPONSE response;
    unsigned char *ptr = (unsigned char *)&response;
    int i;
    rLed = 1;

    for (i=0; i < 20; i++)
        rLed = 0;

    rLed = 1;

    response.SOF_flag = TX_SOF;
    response.cmd_type = FW_VERSION_DATA;
    response.len = 13;
    response.Delim = DELIMETER;
    memcpy(response.FW_Version, FW_Version, 8);
    response.FW_Version[7] = '\0';
    response.EOF_flag = TX_EOF;

    // Send response to GUI
    for (i=0; i < response.len; i++, ptr++)
        pc.printf("%02x", *ptr);
    pc.printf("\n");
}

//***************************************************************
// Build packet with errors to send to GUI
//***************************************************************
void sendError (int chan, float error)
{
    RESPONSE_CMD response;
    unsigned char *ptr = (unsigned char *)&response;
    int i;

    if(chnlStatus[chan].errorEmailSent >= NUM_EMAIL_SEND)
        return;

    response.SOF_flag = TX_SOF;
    response.cmd_type = ERROR_DATA;
    response.len = 13;
    response.Delim = DELIMETER;
    response.channel = chan;
    response.data = (float)error;

    response.EOF_flag = TX_EOF;

    // Send response to GUI
    for (i=0; i < response.len; i++, ptr++)
        pc.printf("%02x", *ptr);
    pc.printf("\n");

    chnlStatus[chan].errorEmailSent++;
}

/* Function: toggleI2C
   **************************************************************
   Description: Toggle the I2C line in an attempt to unfreeze it
   Receives: N/A
   Returns: N/A
*/

void toggleI2C(){
    DigitalOut ioSDAIn(PTC9);
    DigitalOut adcSDAIn(PTC11);
    DigitalOut ioSCLIn(PTC8);
    DigitalOut adcSCLIn(PTC10);

    ioSDAIn = 1;
    adcSDAIn = 1;
    ioSCLIn = 1;
    adcSCLIn = 1;
    wait(0.5);
    ioSDAIn = 0;
    adcSDAIn = 0;
    ioSCLIn = 0;
    adcSCLIn = 0;
    wait(0.5);
    ioSDAIn = 1;
    adcSDAIn = 1;
    ioSCLIn = 1;
    adcSCLIn = 1;
    wait(0.5);
    ioSDAIn = 0;
    adcSDAIn = 0;
    ioSCLIn = 0;
    adcSCLIn = 0;
    wait(0.5);

    MCP23008 io_control(PTC9, PTC8, 0x10, 100000); //sda, scl
    LTC2487 ltc2487(PTC11, PTC10, 0x23, 100000); //sda, scl

}

/* Function: softwareReset
   **************************************************************
   Description: soft Reset
   Recieves: N/A
   Returns: N/A
*/

void softwareReset(void){
    SCB->AIRCR = (0x5FA<<SCB_AIRCR_VECTKEY_Pos)|SCB_AIRCR_SYSRESETREQ_Msk;
    for(;;){}
}

/* Function: read I2C Line
   **************************************************************
   Description: Read the I2C line to check if pulled high
   Receives: N/A
   Returns: N/A
*/

int readI2CLine(int line)
{
    if(line == IO_I2C_LINE){
        DigitalIn ioSDAIn(PTC9);
        int ioSDA = ioSDAIn.read();
        if(I2C_PRINT) pc.printf("DBG: TEMPERATURE SDA LINE: %d \r\n", ioSDA);
        MCP23008 io_control(PTC9, PTC8, 0x10, 100000); //sda, scl
        return ioSDA;
    }
    if(line == ADC_I2C_LINE){
        DigitalIn adcSDAIn(PTC11);
        int adcSDA = adcSDAIn.read();
        if(I2C_PRINT) pc.printf("DBG: IO SDA LINE: %d \r\n", adcSDA);
        LTC2487 ltc2487(PTC11, PTC10, 0x23, 100000); //sda, scl
        return adcSDA;
    }
    return 0;
}

/* Function: error_check
   **************************************************************
   Description: Checks for any system errors
   Recieves: frontTemp: temp. of front thermistor
             backTemp: temp. of back thermistor
             currTimeMin: currentTime in minutes
             ioSDA: voltage on the SDA line
             adcSDA: voltage on the SDA line
   Returns: N/A
*/

int error_check(int chnl, float currentTempFront, float currentTempBack, int currTimeMin, int ioSDA, int adcSDA){
    //check if the thermistors are disconnected
    if((currentTempFront == 0) || (currentTempBack == 0)){
        if(chnlStatus[chnl].error >= MIN_ERROR_CNT_1){
            sendError(chnl, 2);
            status_led(chnl, RED_STATUS_ON);
        }
        else{
            chnlStatus[chnl].error++;
        }
        if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 2 \r\n", chnl);
        return 1;
    }
    //check if the channels are disconnected
    if(currentTempBack == currentTempFront){
        if(chnlStatus[chnl].error >= MIN_ERROR_CNT_1){
        sendError(chnl, 8);
            status_led(chnl, RED_STATUS_ON);
    }
        else{
            chnlStatus[chnl].error++;
        }
        if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 2A \r\n", chnl);
        return 1;
    }

    //check if there is a temperature difference |b| front and back
    if((abs(currentTempBack - currentTempFront) >= TEMP_MARGIN)){
        if(chnlStatus[chnl].error >= MIN_ERROR_CNT_2){
            sendError(chnl, 3);
            status_led(chnl, RED_STATUS_ON);
        }
        else{
            chnlStatus[chnl].error++;
        }
        if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 3 \r\n", chnl);
        return 1;
    }
    //check if channels are over the MAX temperature
    if((currentTempFront >= MAX_TEMP) || (currentTempBack >= MAX_TEMP)){
        //if fixture over 65C for more than ~60s
        if(chnlStatus[chnl].error >= MIN_ERROR_CNT_3){
            sendError(chnl, 1);
            status_led(chnl, RED_STATUS_ON);
            if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 1 SOFTWARE RESET %d \r\n", chnl, chnlStatus[chnl].error);
            softwareReset();
        }
        //if fixture over 65C for more than ~1s
        else if(chnlStatus[chnl].error >= MIN_ERROR_CNT_1){
             sendError(chnl, 1);
             status_led(chnl, RED_STATUS_ON);
             if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 1.9 SOFTWARE RESET %d \r\n", chnl, chnlStatus[chnl].error);
             chnlStatus[chnl].error++;
        }
        else{
            chnlStatus[chnl].error++;
        }
        if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 1 \r\n", chnl);
        return 1;
    }
    //check if the valves are stuck open
    if(((currentTempFront <= MIN_TEMP) && (currentTempBack <= MIN_TEMP))){
        if(chnlStatus[chnl].error >= MIN_ERROR_CNT_1){
            sendError(chnl, 4);
            status_led(chnl, RED_STATUS_ON );
        }
        else{
            chnlStatus[chnl].error++;
        }
        if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 4 \r\n", chnl);
        return 1;
    }

    //check that the SDA lines aren't stuck low
    if((ioSDA != 1) || (adcSDA != 1)){
            if(chnlStatus[chnl].error >= MIN_ERROR_CNT_1){
                sendError(chnl, 6);
                status_led(chnl, RED_STATUS_ON );
                softwareReset();
            }
            else{
                toggleI2C();
                chnlStatus[chnl].error++;
            }
            if(debug_control & ERROR_PRINT) pc.printf("DBG: [%d] ERROR 6 \r\n", chnl);
            return 1;
    }
    //no errors
    return 0;
}


/* Function: systemLogic
   **************************************************************
   Description: Checks for any system errors
   Receives: Channel to modify
   Returns: N/A
*/

void systemLogic(){
    int chan;
    float currTemp;

    for (chan=0; chan < CHAN_COUNT; chan++)
    {
        if(chnlStatus[chan].status == 1){
            //Current Temperature
            currTemp = channelTempData[chan].currTempBack;

            if(currTemp > ((chnlStatus[chan].setTemp)+HYST_HIGH)){
                //TURN COOLING ON
                chnlStatus[chan].state = STATUS_COOLING;
            }
            else if(currTemp < ((chnlStatus[chan].setTemp)-HYST_LOW)){
                //TURN HEATER ON
                chnlStatus[chan].state = STATUS_HEATING;
            }
            else{
                //FIXTURE INACTIVE
                chnlStatus[chan].state = STATUS_INACTIVE;
            }
        }
        else{
            //FIXTURE STANDBY
            chnlStatus[chan].state = STATUS_STANDBY;
        }
    }

}

/* Function: systemControl
   **************************************************************
   Description: Checks for any system errors
   Receives: chn: Channel to modify
   Returns: N/A
*/

void systemControl()
{
    int chan;

    for (chan=0; chan < CHAN_COUNT; chan++){
        if(chnlStatus[chan].status == 1){
            if(chnlStatus[chan].state == STATUS_COOLING){
                //TURN COOLING ON
                turn_valve_on(chan);
            }
            else if(chnlStatus[chan].state == STATUS_HEATING){
                //TURN HEATER ON
                turn_heater_off(chan);
                turn_heater_on(chan);
            }
            else{
                //FIXTURE INACTIVE
                status_led(chan, GREEN_STATUS_ON );
            }
        }
        else
        {
            //FIXTURE INACTIVE
            if(standbyLED[chan]){
                status_led(chan, GREEN_STATUS_ON);
                standbyLED[chan] = !standbyLED[chan];
            }
            else{
                status_led(chan, STATUS_OFF);
                standbyLED[chan] = !standbyLED[chan];
            }
        }
    }
}

void systemDiagnostic()
{
}

/*************************************************************/
/*                       NEW MAIN FUNCTION                   */
/*************************************************************/

int main(){
    /* INITIALIZATION CODE */
    // Setup serial port
    // Look for RX_EOF
    uint32_t UIDMH, UIDML, UIDL;
    int UID_print_count = 0;


    //*********************************
    // Initial thermistors readings
    // Kick off temperature reding
    // state machine
    //*********************************
    readThermistorTicker.attach(&read_front_temp_data, .0001);

    pc.baud(9600);
    pc.autoDetectChar(RX_EOF);
    pc.attach(&rxInterrupt, MODSERIAL::RxAutoDetect);

    UIDMH = (uint32_t)SIM->UIDMH;
    UIDML = (uint32_t)SIM->UIDML;
    UIDL = (uint32_t)SIM->UIDL;

    //print UID three times on start-up to ID Mbed board
    if(UID_print_count++ < 3)
        pc.printf("DBG: [UID: %04X%08X%08X]\n", UIDMH, UIDML, UIDL);


    while(1) {
        //*********************************
        // Process Rx UART data from GUI
        //*********************************
        if (dataReceived)
        {
            dataReceived = false;
            if (debug_control & RXDATA_PRINT)
                pc.printf("DBG: %d bytes Rx'd\n", pc.rxBufferGetCount());

            pc.move(rxBuf, 50);
            processRxUARTData();
            pc.rxBufferFlush();
        }

        //*********************************
        // Logic Loop
        //*********************************
        // System Logic/Control
        if (newTempDataAvailable)
            systemLogic();

        //*********************************
        // System Diagnostic & Error Check
        //*********************************
        systemDiagnostic();

        //*********************************
        // Process Tx UART data
        // Send Temperature Data/Error to GUI
        //*********************************
        if (newTempDataAvailable)
        {
            processTxUARTData();

            //*********************************
            //Actuate Loop
            //*********************************
            systemControl();
            newTempDataAvailable = false;
        }
    }

}