Control Code with I/O and ADC working

Dependencies:   MODSERIAL mbed

main.cpp

Committer:
jrodenburg
Date:
2018-07-18
Revision:
21:f87464a7e7c6
Parent:
20:cdeed4dad690

File content as of revision 21:f87464a7e7c6:

// MBED SCRIPT FOR CONTROLLING THE TEMPERATURE CONTROLLED TEST FIXTURE (TCTF)
// DATE: SEPTEMBER 2017
//CHANGE THE PYTHON CODE TO REFLECT ERROR STATE ENTERED

#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 ERROR_COUNT          6
#define MIN_TEMP             10
#define MAX_TEMP             65
#define TEMP_MARGIN          10
#define HYST_LOW             0.3
#define HYST_HIGH            1
#define VALVE_ON             1
#define HEATER_ON            2
#define GREEN_STATUS_ON      3
#define RED_STATUS_ON        4
#define ERROR_STATUS_ON      5
#define ALL_OFF              6
#define sizeLUT              34
#define BACK_THERM           0
#define FRONT_THERM          1
#define HEAT_FET_AMP         2
#define VALVE_FET_AMP        3
#define NUM_EMAIL_SEND       2
#define STATUS_COOLING       1
#define STATUS_HEATING       2
#define STATUS_INACTIVE      3
#define STATUS_STANDBY       4
#define MIN_AD_COUNT         10428 //65C
#define MAX_AD_COUNT         49169 //10C
#define MAX_TIME_TO_SP       60    //allow 60 minutes for channel to reach set point

#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

// Defines for errors
#define ERR_OVER_TEMP           0
#define ERR_UNDER_TEMP          1
#define ERR_TEMP_DIFF           2
#define ERR_ACK_LTC             3
#define ERR_ACK_MCP             4
#define ERR_SP_TIME             5
#define MAX_ERROR_CNT           8

#define PRINT_ERROR_CHECK_VALS  0
#define PRINT_ERRORS            0




const char FW_Version[8] =  "V0.01D";
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 = 0x80;

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

//***********************************************
// 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;
   bool error;
   int state;
   int LTCi2cAckRxed;
   int MCPi2cAckRxed;
   bool SPReached;
   int SPTime;
};

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


//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 currADCFront;
   float currADCBack;
   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};

int errorCount[CHAN_COUNT][ERROR_COUNT] =
    {
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0}
    };

int errorEmailSent[CHAN_COUNT][ERROR_COUNT] =
    {
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0},
        {0,0,0,0,0,0}
    };
    
void resetErrorCount(){
   for(int i = 0; i < CHAN_COUNT; i++){
       for(int i = 0; i < ERROR_COUNT; i++){
           errorCount[CHAN_COUNT][ERROR_COUNT] = 0;
           errorEmailSent[CHAN_COUNT][ERROR_COUNT] = 0;
        }
    } 
}

/* 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 {
                    if((pRxPkt->setTemp < MAX_TEMP) && (pRxPkt->setTemp > MIN_TEMP)){
                        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 = false;
                        chnlStatus[pRxPkt->chanID].LTCi2cAckRxed = 0;
                        chnlStatus[pRxPkt->chanID].MCPi2cAckRxed = 0;
                        chnlStatus[pRxPkt->chanID].SPReached = false;
                        chnlStatus[pRxPkt->chanID].SPTime = 0;
                        memset(errorCount[pRxPkt->chanID], 0, sizeof(int)*ERROR_COUNT);
                        memset(errorEmailSent[pRxPkt->chanID], 0, sizeof(int)*ERROR_COUNT);
                        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].LTCi2cAckRxed = 0;
        ltc2487.setAddress(addrLUT[chnl].adc);
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chnl].LTCi2cAckRxed = !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(){
    float countVal;
    frontTempInProgTicker.detach();
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        ltc2487.setAddress(addrLUT[chnl].adc);
        countVal = ltc2487.read();
        channelTempData[chnl].currADCFront = countVal;
        channelTempData[chnl].currTempFront = get_temp(countVal);
    }
    //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].LTCi2cAckRxed = 0;
        ltc2487.setAddress(addrLUT[chnl].adc);
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chnl].LTCi2cAckRxed = !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(){
    float countVal;
    backTempInProgTicker.detach();
    for(int chnl = 0; chnl < CHAN_COUNT; chnl++){
        ltc2487.setAddress(addrLUT[chnl].adc);
        countVal = ltc2487.read();
        channelTempData[chnl].currADCBack = countVal;
        channelTempData[chnl].currTempBack = get_temp(countVal);
    }
    //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: MCP_Control
   **************************************************************
   Description: Controls valves/status LEDs
   Receives: chn: the channel of the fixture
             status: the status of channel (good (1) or bad (0))
   Returns: N/A
*/

void Chn_Control(int chn, int status){
    //writeOutput(VALVE, HEATER, GREEN STATUS LED, RED STATUS LED)
    pc.printf("[%i] CHANNEL CONTROL \r\n", chn);
    if(status == VALVE_ON){
        //valve ON, heater OFF,  green status LED ON, red status led OFF
        if(debug_control & LED_PRINT) pc.printf("VALVE ON %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(1,0,1,0);
    }
    if(status == HEATER_ON){
        //valve ON, heater ON,  green status LED ON, red status led OFF
        if(debug_control & LED_PRINT) pc.printf("HEATER ON %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(0,1,1,0);
    }
    if(status == GREEN_STATUS_ON){
        //valve OFF, heater OFF,  green status LED ON, red status led OFF
        if(debug_control & LED_PRINT) pc.printf("GREEN LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
         chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(0,0,1,0);
    }
    if(status == RED_STATUS_ON){
        //valve OFF, heater OFF,  green status LED OFF, red status led ON
        if(debug_control & LED_PRINT) pc.printf("RED LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(0,0,0,1);
    }
    if(status == ERROR_STATUS_ON){
        //valve ON, heater OFF,  green status LED OFF, red status led ON
        if(debug_control & LED_PRINT) pc.printf("RED AND BLUE LED %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(1,0,0,1);
    }
    if(status == ALL_OFF){
        //valve OFF, heater OFF,  green status LED OFF, red status led OFF
        if(debug_control & LED_PRINT) pc.printf("ALL OFF %d \r\n", chn);
        io_control.setAddress(addrLUT[chn].io);
        io_control.init();
        chnlStatus[chn].MCPi2cAckRxed = 0;
        //Check if write was ack'ed (0 =  ACK, (non)0 = NACK)
        chnlStatus[chn].MCPi2cAckRxed = !io_control.writeOutput(0,0,0,0);
    }
}


//***************************************************************
// 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;

    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");

}

/* 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: sendErrorEmail
   **************************************************************
   Description: sends error email
   Receives: chn: channel to check error on
   Returns: N/A
*/

void sendErrorEmail(int chn){
    int error;

    for (error=0; error < ERROR_COUNT; error++){
        if((errorCount[chn][error] == MAX_ERROR_CNT) && (!errorEmailSent[chn][error])){
            sendError(chn, error);
            errorEmailSent[chn][error] = 1;
        }
    }
}

/* Function: errorCheck
   **************************************************************
   Description: Updates error count for the channel
   Receives: chn: channel to update
             errorFlag: if the error was encountered
             errorNum: number of error encountered
   Returns: N/A
*/

void errorCheck(int chn, bool errorFlag, int errorNum){
    if(errorFlag){
        if(errorCount[chn][errorNum] < MAX_ERROR_CNT){
            //increase error count
            errorCount[chn][errorNum]++;
            pc.printf("[%i] ERROR ENCOUNTERED: %i %i \r\n", chn, errorNum, errorCount[chn][errorNum]);
        }
        else{
            //this is an error, set error flag
            chnlStatus[chn].error = true;
            //pc.printf("[%i] ERROR FLAGGED: %i %i\r\n", chn, errorNum, errorCount[chn][errorNum]);
        }
    }
    else{
        if(errorCount[chn][errorNum] > 0){
            //decrease error count
            errorCount[chn][errorNum]--;
        }
        //pc.printf("[%i] NO ERROR FLAGGED: %i \r\n\n", chn, errorNum);
    }
}

/* 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: SP_Time_Check
   **************************************************************
   Description: Reset the SP time for each channel every minute
               (minutes since SP has been set and SP hasn't been reach)
   Recieves: N/A
   Returns: N/A
*/

void SP_Time_Check(){
    int chan;

    for (chan=0; chan < CHAN_COUNT; chan++){
        if(chnlStatus[chan].status == 1){
            if(!chnlStatus[chan].SPReached){
                if(chnlStatus[chan].SPTime != MAX_TIME_TO_SP){
                    chnlStatus[chan].SPTime++;
                }
            }
        }
    }
}

/* 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: systemLogic
   **************************************************************
   Description: Sets setting for fixture needed to maintain temperature at SP
   Receives: N/A
   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
            pc.printf("FIXTURE IN STANDBY \r\n");
            chnlStatus[chan].state = STATUS_STANDBY;
        }
    }

}

/* Function: systemControl
   **************************************************************
   Description: Changes fixture setting in accordance to logic set in systemLOGIC function
   Receives: N/A
   Returns: N/A
*/

void systemControl()
{
    int chan;
    
    for (chan=0; chan < CHAN_COUNT; chan++){
        pc.printf("[%i] CHANNEL ERRORS: %d \r\n", chan, chnlStatus[chan].error);
        if(!chnlStatus[chan].error){
            //pc.printf("[%i] CHANNEL NO ERRORS \r\n", chan);
            if(errorCount[chan][ERR_ACK_LTC] == 0){
                if(chnlStatus[chan].state == STATUS_INACTIVE){
                    //FIXTURE INACTIVE
                    Chn_Control(chan, GREEN_STATUS_ON);
                    //SP reached
                    chnlStatus[chan].SPReached = true;
                }
                else if(chnlStatus[chan].state == STATUS_COOLING){
                    //TURN COOLING ON
                    Chn_Control(chan, VALVE_ON);
                }
                else if(chnlStatus[chan].state == STATUS_HEATING){
                    //TURN HEATER ON (have to toggle bc of charge pump in circuit)
                    Chn_Control(chan, GREEN_STATUS_ON); //turn heater off
                    Chn_Control(chan, HEATER_ON);
                }
                else if(chnlStatus[chan].state == STATUS_STANDBY){
                    //FIXTURE IN STANDBY (i.e. off)
                    if(standbyLED[chan]){
                        Chn_Control(chan, GREEN_STATUS_ON);
                        standbyLED[chan] = !standbyLED[chan];
                    }
                    else{
                        Chn_Control(chan, ALL_OFF);
                        standbyLED[chan] = !standbyLED[chan];
                    }
                }
            }
        }
        else{
            sendErrorEmail(chan);
        }
    }
}

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

void systemDiagnostic(){
    int chan;
    float backCount;
    float frontCount;
    float backTemp;
    float frontTemp;
    bool errorFlag;
    float tempDiff;
    int LTC_Ack;
    int MCP_Ack;
    int timeToSP;
    bool reachedSP;

    for (chan=0; chan < CHAN_COUNT; chan++){
        backCount = channelTempData[chan].currADCBack;
        frontCount = channelTempData[chan].currADCFront;
        frontTemp = channelTempData[chan].currTempFront;
        backTemp = channelTempData[chan].currTempBack;
        LTC_Ack = chnlStatus[chan].LTCi2cAckRxed;
        MCP_Ack = chnlStatus[chan].MCPi2cAckRxed;
        timeToSP = chnlStatus[chan].SPTime;
        reachedSP = chnlStatus[chan].SPReached;
        
        if(PRINT_ERROR_CHECK_VALS){
            pc.printf("BAKC COUNT: %f \r\n", backCount);
            pc.printf("FRONT COUNT: %f \r\n", frontCount);
            pc.printf("TEMP DIFF: %f \r\n", abs(frontTemp-backTemp));
            pc.printf("LTC ACK: %f \r\n", LTC_Ack);
            pc.printf("MCP ACK: %f \r\n", MCP_Ack);
        }

        //Check if the ADC temperature is too high (low ADC count)
        errorFlag = false;
        if((backCount < MIN_AD_COUNT) || (frontCount < MIN_AD_COUNT)) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_OVER_TEMP);

        //Check if the ADC temperature is too low (high ADC count), valve stuck on
        errorFlag = false;
        if((backCount > MAX_AD_COUNT) || (frontCount > MAX_AD_COUNT)) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_UNDER_TEMP);

        //Check for a substantial temp. diff. |b| the front & back of the fixture
        errorFlag = false;
        tempDiff = abs(frontTemp-backTemp);
        if(tempDiff > TEMP_MARGIN) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_TEMP_DIFF);

        //Check for ACK from the LTC2487 chip (temperature reading)
        errorFlag = false;
        if(!LTC_Ack) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_ACK_LTC);

        //Check for ACK from the MCP23008 chip (valve/heater controller)
        /*errorFlag = false;
        if(!MCP_Ack) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_ACK_MCP);*/

        //Check if the fixture has not reached SP in X amount of time
        errorFlag = false;
        if(!reachedSP && (timeToSP == MAX_TIME_TO_SP)) errorFlag = true;
        errorCheck(chan, errorFlag, ERR_SP_TIME);
        
    }
}

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

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


    //*********************************
    // Initial thermistors readings
    // Kick of SP time updates
    // Kick off temperature reading
    // state machine
    //*********************************
    readThermistorTicker.attach(&read_front_temp_data, .0001);
    //setPointTimeTicker.attach(&SP_Time_Check, 60);

    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
        //*********************************
        //pc.printf("LOOP \r\n");
        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;
        }
    }
}