Control Code with I/O and ADC working

Dependencies:   MODSERIAL mbed

main.cpp

Committer:
jrodenburg
Date:
2018-05-08
Revision:
13:604e6933366f
Parent:
12:1cada1fe4743
Child:
14:69cd53434783

File content as of revision 13:604e6933366f:

// 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            0
#define DEBUG1           0
#define DEBUG2           0
#define DEBUG3           1
#define CHN_COUNT        8
#define MIN_TEMP         10
#define MAX_TEMP         65
#define TEMP_MARGIN      20
#define HYST_LOW         0.3
#define HYST_HIGH        1
#define SAMPLES          5
#define I2C_Freq         2000
#define VALVE            1
#define HEATER           2
#define STATUS_GOOD      3
#define STATUS_BAD       0
#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

// 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 CMD_RESPONSE            0xD0
#define CMD_DATA                0xD1
#define ERROR_DATA              0xD2


unsigned int chanSel_SendTemp = 0;
unsigned int chanSel_SetTemp = 0;
// Default to chan 0
int currChan = 0;
bool newTempSet = false;


//***********************************************
// 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;
    float data;
    unsigned char EOF_flag;
} RESPONSE_CMD;

//TCTF CHANNEL DATA
struct CHNL_DATA{
   bool status;
   float setTemp;
   bool error;
};

CHNL_DATA chnlStatus[] = {
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 0},
    {0, NULL, 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},
    {0x72, 0x41},
    {0x62, 0x51},
    {0x21, 0x12},
    {0x51, 0x62},
    {0x41, 0x72},
    {0x71, 0x42},
    {0x61, 0x52},
    {0x20, 0x13},
    {0x50, 0x63},
    {0x40, 0x73},
    {0x70, 0x43},
    {0x60, 0x53},
    {0x26, 0x14},
    {0x56, 0x64},
    {0x46, 0x74},
    {0x76, 0x44},
    {0x66, 0x54},
    {0x2C, 0x1F},
    {0x5C, 0x6F},
    {0x4C, 0x7F},
    {0x7C, 0x4F},
    {0x6C, 0x5F},
    {0x2D, 0x1E},
    {0x5D, 0x6E},
    {0x4D, 0x7E},
    {0x7D, 0x4E},
    {0x6D, 0x5E},
    {0x2E, 0x1D},
    {0x5E, 0x6D},
    {0x4E, 0x7D},
    {0x7E, 0x4D},
    {0x6E, 0x5D},
    {0x2F, 0x1C},
    {0x5F, 0x6C},
    {0x4F, 0x7C},
    {0x7F, 0x4C},
    {0x6F, 0x5C},
    {0x20, 0x1B},
    {0x50, 0x6B},
    {0x40, 0x7B},
};

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

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


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

DigitalOut rLed(LED1);
DigitalOut gLed(LED2);

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


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


/* Function: parseRXData
   **************************************************************
   Description: The parse received data into
   Receives: chn: The channel we want to put into the channel
   Returns: N/A
*/

void parseRXData()
{
    HOST_CMD_HEADER *pRxPktHdr;
    string data = "";
    unsigned char *ptemp;
    int i;

    pRxPktHdr = (HOST_CMD_HEADER *)rxBuf;

#ifdef DEBUG
    pc.printf("DBG: fl = %02x\n", pRxPktHdr->SOF_flag);
    pc.printf("DBG: len = %02x\n", pRxPktHdr->len);
#endif

    // 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 1 //def DEBUG
                pc.printf("DBG: ch = %02x\n", pRxPkt->chanID);
                pc.printf("DBG: tempSet = %f\n", pRxPkt->setTemp);
                pc.printf("DBG: chanStat = %02x\n", pRxPkt->chanStat);
#endif
                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].setTemp = pRxPkt->setTemp;
                    chnlStatus[pRxPkt->chanID].error = 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;
#if 1 //def DEBUG
                pc.printf("DBG: chanSel = %02x\n", pRxPkt->chanIDSel);
                pc.printf("DBG: chanStat = %02x\n", pRxPkt->chanStat);
#endif
            }
            break;

        default:
            // Error
            break;
    }
}

/* Function: get_temp
   **************************************************************
   Description: Retrieve data from thermistor
   Receives: chn: the channel of the fixture to read temp. from
             port: the I/O channel to read
   Returns: the temperature of the fixture (front or back)
*/

float get_temp(int chn, int port){
    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:
    // y1 = m (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(DEBUG2) pc.printf("DBG: CHAN: %i PORT: %i ADC VAL: %f TEMP: %f \r\n", chn, port, ADC_val, y);

    return y;
}

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

float get_heater_current(int chn, int port){
    return ltc2487.readOutput(port);
}

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

float get_valve_current(int chn, int port){
    return ltc2487.readOutput(port);
}

/* 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){
    io_control.setAddress(addrLUT[chn].io);
    if(status){
        io_control.writeOutput(0,0,1,0);
    }
    else{
        //turn valve on too
        io_control.writeOutput(1,0,0,1);
    }
}

/* 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(int chn){
    get_temp(chn, FRONT_THERM);
    wait(0.1);
    get_temp(chn, BACK_THERM);
    wait(0.1);
    get_temp(chn, VALVE_FET_AMP);
    wait(0.1);
    //if(DEBUG1) pc.printf("TEMPERATURE READING: %f \r\n", get_temp(chn));
}

/* Function: error_check
   **************************************************************
   Description: Checks for any system errors
   Recieves: frontTemp: temp. of front thermistor
             backTemp: temp. of back thermistor
             valveFet: current through valve FET
             heaterFet: current through heater FET
   Returns: N/A
*/

void error_check(int chn, float frontTemp, float backTemp, float valveFet, float heaterFet){
    float setTemp = chnlStatus[chn].setTemp;

    //CHECK IF THERMISTOR READINGS ARE OFF (> 5 DEGREES)
    if(abs(frontTemp-backTemp) > 5){
        //ERROR 6: Thermistor reading off
        chnlStatus[chn].error = 1;
    }

    //CHECK IF HEATER'S STUCK ON OR CELL OVERHEATING
    if((frontTemp >= (setTemp+TEMP_MARGIN)) || (backTemp >= (setTemp+TEMP_MARGIN))){
         //ERROR 0
         chnlStatus[chn].error = 1;
         status_led(chn, STATUS_BAD);
         if(heaterFet >= FET_ON_CURRENT){
         //ERROR 1: Heater FET stuck on
         }
         if(valveFet <= FET_ON_CURRENT){
         //ERROR 2: valve FET stuck off
         }
    }

    //CHECK IF VALVE STUCK ON OR CELL OVERHEATING
    if((frontTemp <= (setTemp-TEMP_MARGIN)) || (backTemp <= (setTemp-TEMP_MARGIN))){
        //ERROR 0
        chnlStatus[chn].error = 1;
        status_led(chn, STATUS_BAD);
        if(heaterFet <= FET_ON_CURRENT){
        //ERROR 2: Heater FET stuck off
        }
        else if(valveFet >= FET_ON_CURRENT){
        //ERROR 3: Chiller FET stuck on
        }
    }
}

//***************************************************************
// Build packet with temperature readings to send to GUI
//***************************************************************
void sendTempReadings (int chan, float currentTemp)
{
    RESPONSE_CMD response;
    unsigned char *ptr = (unsigned char *)&response;
    int i;

    response.SOF_flag = TX_SOF;
    response.cmd_type = CMD_DATA;
    response.len = 9;
    response.Delim = DELIMETER;
    response.data = (float)currentTemp;
    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 = 9;
    response.Delim = DELIMETER;
    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");
}


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

int main() {

    Timer t;

    // Setup serial port
    // Look for RX_EOF
    pc.baud(9600);
    pc.autoDetectChar(RX_EOF);
    pc.attach(&rxInterrupt, MODSERIAL::RxAutoDetect);

    myled = 1;
    rLed = 1;
    gLed = 0;

    t.start();

    while(1) {

        if(DEBUG3)
            pc.printf("DBG: PROGRAM STARTED \r\n");

        /*float time_sec = t.read();
        
        if(time_sec >= 60){
            t.reset();
        }
        
        pc.printf("Time: %f \r\n", time_sec);*/

        for(int chnl = 0; chnl < CHN_COUNT; chnl++){
            float currentTempFront = get_temp(chnl, FRONT_THERM);
            //wait(0.08);
            
            //float valveCurrent = get_valve_current(chnl, VALVE_FET_AMP);
            //float heaterCurrent = get_heater_current(chnl, HEAT_FET_AMP);
            //float currentTemp = currentTempFront;
            //currentTemp = currentTempFront;
            

            //check if we received data/need to update TCTF data
            if(dataReceived){
                dataReceived = false;
                pc.printf("DBG: %d bytes Rx'd\n", pc.rxBufferGetCount());
                pc.move(rxBuf, 50);
                parseRXData();
                pc.rxBufferFlush();
            }

            float currentTempBack = get_temp(chnl, BACK_THERM);
            float currentTemp = currentTempBack;

            if (chnl == chanSel_SendTemp){
                //Send temp readings for selected chan to GUI
                sendTempReadings(currChan, currentTemp);
            }
            
            

            //CONTROL LOOP:
            if(chnlStatus[chnl].status == 1){
                if(DEBUG3) pc.printf("DBG: [%d] Temp: F: %f B: %f\r\n", chnl, currentTempFront, currentTempBack);

                //Error check on fixture
                //error_check(chnl, currentTempFront, currentTempBack, valveCurrent, heaterCurrent);
                if(((currentTempFront >= MAX_TEMP) && (currentTempBack >= MAX_TEMP)) ||
                   ((currentTempFront == 0) || (currentTempBack == 0))||
                    (abs(currentTempBack - currentTempFront) >= TEMP_MARGIN)||
                   ((currentTempFront <= MIN_TEMP) && (currentTempBack <= MIN_TEMP))||
                    (chnlStatus[chnl].error == 1)){
                    status_led(chnl, STATUS_BAD);
                    sendError(chnl, 1);
                    chnlStatus[chnl].error = 1;
                }

                if(chnlStatus[chnl].error == 0){
                    if(currentTemp > ((chnlStatus[chnl].setTemp)+HYST_HIGH)){
                        if(DEBUG3) pc.printf("DBG: [%d] Chiller ON \r\n", chnl);
                        //check if the temp. diff. is small and can't wait for an entire period (~4.18) to turn valve off
                        if(abs(currentTemp - (chnlStatus[chnl].setTemp)) < 5){
                            turn_valve_on(chnl);
                            while(currentTemp > ((chnlStatus[chnl].setTemp)+HYST_HIGH)){
                                currentTemp = get_temp(chnl, BACK_THERM);
                            }
                            turn_valve_off(chnl);
                        }
                        else{
                            //Turn chiller on
                            turn_valve_on(chnl);    
                        }
                    }
                    else if (currentTemp < ((chnlStatus[chnl].setTemp)-HYST_LOW)){
                        if(DEBUG3) pc.printf("DBG: [%d] Heater ON \r\n", chnl);
                        //Turn heater on
                        turn_heater_on(chnl);
                    }
                    else{
                        if(DEBUG3) pc.printf("DBG: [%d] All OFF \r\n", chnl);
                        //turn off chiller
                        turn_valve_off(chnl);
                        //turn off heater
                        turn_heater_off(chnl);
                        //turn on green LED status light
                        status_led(chnl, 1);
                    }
                }
            }
            else{
                //turn off chiller
                turn_valve_off(chnl);
                //turn off heater
                turn_heater_off(chnl);
                //turn on green LED status light
                status_led(chnl, 1);
            }
        }
    }
}