Working on rewriting how we acquire data from LTC chip and sending all temp data over serial for python logging
Fork of TCTF_Control_Main by
main.cpp
- Committer:
- jrodenburg
- Date:
- 2018-05-16
- Revision:
- 14:69cd53434783
- Parent:
- 13:604e6933366f
- Child:
- 15:74a01aaeb60e
File content as of revision 14:69cd53434783:
// 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 DEBUG5 0 #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 #define MAX_HEATER_ON_TIME 25 #define MAX_CHILL_TIME 10 //10sec // 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; int heater_init_time; }; CHNL_DATA chnlStatus[] = { {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 0}, {0, NULL, 0, 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; chnlStatus[pRxPkt->chanID].heater_init_time = 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: // 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(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)); } //*************************************************************** // 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"); } /* Function: error_check ************************************************************** Description: Checks for any system errors Recieves: frontTemp: temp. of front thermistor backTemp: temp. of back thermistor currTimeMin: currentTime in minutes Returns: N/A */ void error_check(int chnl, float currentTempFront, float currentTempBack, int currTimeMin){ if((currentTempFront >= MAX_TEMP) && (currentTempBack >= MAX_TEMP)){ status_led(chnl, STATUS_BAD); sendError(chnl, 1); chnlStatus[chnl].error = 1; if(DEBUG3) pc.printf("DBG: [%d] ERROR 1 \r\n", chnl); } if(((currentTempFront == 0) || (currentTempBack == 0))){ status_led(chnl, STATUS_BAD); sendError(chnl, 2); chnlStatus[chnl].error = 1; if(DEBUG3) pc.printf("DBG: [%d] ERROR 2 \r\n", chnl); } if((abs(currentTempBack - currentTempFront) >= TEMP_MARGIN)){ status_led(chnl, STATUS_BAD); sendError(chnl, 3); chnlStatus[chnl].error = 1; if(DEBUG3) pc.printf("DBG: [%d] ERROR 3 \r\n", chnl); } if(((currentTempFront <= MIN_TEMP) && (currentTempBack <= MIN_TEMP))){ status_led(chnl, STATUS_BAD); sendError(chnl, 4); chnlStatus[chnl].error = 1; if(DEBUG3) pc.printf("DBG: [%d] ERROR 4 \r\n", chnl); } if(chnlStatus[chnl].heater_init_time != 0){ int init_time = chnlStatus[chnl].heater_init_time; int on_time_heater = currTimeMin - init_time; //account for 0 crossover if(init_time > currTimeMin){ on_time_heater = (60 - init_time)+currTimeMin; } if(on_time_heater > MAX_HEATER_ON_TIME){ status_led(chnl, STATUS_BAD); sendError(chnl, 5); chnlStatus[chnl].error = 1; chnlStatus[chnl].heater_init_time = 0; if(DEBUG3) pc.printf("DBG: [%d] ERROR 5 \r\n", chnl); } } if(chnlStatus[chnl].error == 1){ status_led(chnl, STATUS_BAD); chnlStatus[chnl].error = 1; } } /*************************************************************/ /* MAIN FUNCTION */ /*************************************************************/ int main() { //variables for controlling board Timer t; Timer t_cool; int time_min = 0; int init_heat_time = 0; // 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"); //setup timer used to track how long the heater is on float time_sec = t.read(); if(time_sec >= 60){ time_min++; //time_sec = 0; t.reset(); } if(time_min >= 60){ time_min = 0; } //pc.printf("Time: %f \r\n", time_sec); for(int chnl = 0; chnl < CHN_COUNT; chnl++){ float currentTempFront = get_temp(chnl, FRONT_THERM); //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); } //Error check on fixture error_check(chnl, currentTempFront, currentTempBack, time_min); //CONTROL LOOP: if(chnlStatus[chnl].status == 1){ if(DEBUG3) pc.printf("DBG: [%d] Temp: F: %f B: %f\r\n", chnl, currentTempFront, currentTempBack); //main loop for channels if(chnlStatus[chnl].error == 0){ if(currentTemp > ((chnlStatus[chnl].setTemp)+HYST_HIGH)){ if(DEBUG3) pc.printf("DBG: [%d] Chiller ON \r\n", chnl); //reset heater on time chnlStatus[chnl].heater_init_time = 0; //reset cooling timer float time_sec = 0; //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){ //start timer t_cool.start(); //turn chiller on turn_valve_on(chnl); //read timer time_sec = t_cool.read(); while((currentTemp > ((chnlStatus[chnl].setTemp)+HYST_HIGH)) && (time_sec < MAX_CHILL_TIME)){ currentTemp = get_temp(chnl, BACK_THERM); time_sec = t_cool.read(); if(DEBUG5) pc.printf("DBG: [%d] TIME: %f \r\n", chnl, time_sec); } time_sec = 0; t_cool.stop(); t_cool.reset(); 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); //establish starting time for heater if(chnlStatus[chnl].heater_init_time == 0){ //check if it is the first time that heater has turned on //make sure time isn't zero, otherwise keeping looping until we are not at 0 if(time_min != 0){ chnlStatus[chnl].heater_init_time = time_min; } } if(DEBUG5) pc.printf("DBG: TIME ON: %d %d %f %d %d\r\n", chnlStatus[chnl].heater_init_time, time_min, time_sec, init_heat_time, (chnlStatus[chnl].heater_init_time == NULL)); //Turn heater on turn_heater_on(chnl); } else{ if(DEBUG3) pc.printf("DBG: [%d] All OFF \r\n", chnl); //reset heater on time chnlStatus[chnl].heater_init_time = 0; //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{ if(chnlStatus[chnl].error == 0){ //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{ status_led(chnl, STATUS_BAD); } //reset heater on time chnlStatus[chnl].heater_init_time = 0; } } } }