Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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;
}
}
}
}