Marc Bax
/
PoopCooker_070
Firmware for sample prep heater device
main.cpp
- Committer:
- marcbax
- Date:
- 2011-09-04
- Revision:
- 0:06f27dbfa599
File content as of revision 0:06f27dbfa599:
//Firmware for P173_PoopCooker sample preparation heater block #include "mbed.h" char* fwversion="0.70"; //this version published 5/9/2011 //Pin and object assignments DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4); DigitalOut buzzer(p30); AnalogIn ntc(p19); DigitalOut heater(p18); Serial display(p13, p14); InterruptIn keyup(p7), keyright(p5), keydown(p6); InterruptIn keyleft(p9), keyOK(p8); DigitalOut fan(p24); PwmOut rled(p21), gled(p22), bled(p23); Timer kitchentimer; Timeout kitchenalarm; Timeout heateroff; Ticker showtime; Ticker heatercontrol; // Global constants definitions float heaterperiod = 5; //cycle time for heater control float propterm = 0.1; //proportional term in PID algorithm float intterm = 0.001; //integral term in PID algorithm float difterm = 0; //differential term in PID algorithm float ntcB = 3988; //beta value for exponential NTC calculation float ntcR25 = 10000; //resistance value of NTC at 25 degC float rdivntc = 3300; //resistance value of upper ladder resistor for NTC float maxblocktemp = 115; //block temperature cutout float minblocktemp = 25; //25 degC minimal setpoint temp float defaultblocktemp = 60; //this needs to be changed to the standard extraction temperature int maxcountdown = 6540; //99 minutes max countdown int defaultcountdown = 600; //10 minutes default countdown // Global variable definitions float btemp; //current hot block temperature in degC float blocksettemp; //hot block setpoint temperature float acterror, interror, diferror; //error terms for the PID algorithm float ntcA; //calculated from ntcB and ntcR25 int countdownset; //kitchen timer setting in seconds int remainingtime; //kitchen timer current remaining time char ntcerror; //0 = no error, 1 = open circuit, 2 = short circuit float LedRGB[3] = {0, 0, 0}; // PWM dutycycle red, green, blue bool kitchentimerset; // true if kitchen countdown timer is set char lastkeypressed; // 1=up, 2=right, 3=down, 4=left, 5=OK bool beepsdone; // Function definitions // Clears and homes the display void cleardisplay() { display.printf("\e[2J\e[H"); } // Short beep void beeponce() { buzzer = 1; wait(0.1); buzzer = 0; } // beep beep beep void multibeeps(int number, float interval) { for(int i=0;i<number;i++){ beeponce(); wait(interval); } } // Device power up routine void initdevice() { //set all outputs to "off" led1=led2=led3=led4=0; buzzer=0; heater=0; fan=0; bled=0; rled=0; gled=0; rled.period_us(100); //PWM frequency set to 10kHz ntcerror=0; beepsdone=1; //key pull-up modes keyup.mode(PullUp); keyright.mode(PullUp); keydown.mode(PullUp); keyleft.mode(PullUp); keyOK.mode(PullUp); //calculate ntcA ntcA = ntcR25/exp(ntcB/298); //298K = 25degC //reset error terms acterror = interror = diferror = 0; //default block temp setting blocksettemp = defaultblocktemp; //initialise kitchentimer countdownset = defaultcountdown; kitchentimerset=0; //initialise the display and show splash screen display.baud(19200); //set display baud rate to 19200 wait(0.2); //display power-on delay display.putc(0x0d); //send CR to display - this needs to be first character sent after power-up wait(0.2); //wait for display to adjust to serial baud rate display.printf("\e[4L\e[20C\e[2m"); //set display to 4 lines of 20 characters, cursor off cleardisplay(); //clear screen and home cursor display.printf("\e[2;2HLumora Sample Prep"); //display splash screen display.printf("\e[3;4HFirmware: %s", fwversion); //display firmware version wait(3); //time that splash screen is shown cleardisplay(); beeponce(); } // Functions called by key interrupts void nothing() { //this is a no-operation function, used to detach an InterruptIn } void int_keyup() { lastkeypressed = 1; beeponce(); } void int_keyright() { lastkeypressed = 2; beeponce(); } void int_keydown() { lastkeypressed = 3; beeponce(); } void int_keyleft() { lastkeypressed = 4; beeponce(); } void int_keyOK() { lastkeypressed = 5; beeponce(); } void incrementsettemp() { if (blocksettemp < maxblocktemp) blocksettemp = blocksettemp + 1; beeponce(); } void decrementsettemp() { if (blocksettemp > minblocktemp) blocksettemp = blocksettemp - 1; beeponce(); } void incrementtimer() { if (countdownset < maxcountdown) countdownset = countdownset + 60; beeponce(); } void decrementtimer() { if (countdownset > 60) countdownset = countdownset - 60; beeponce(); } // Display update routine (also handles alarm) void updatedisplay() { display.printf("\e[2;3HBlock Temp %3.0f%cC", btemp, 223); remainingtime = countdownset - kitchentimer.read(); if (kitchentimerset) { display.printf("\e[3;3HCountdown %02u:%02u", remainingtime/60, remainingtime%60); } else { display.printf("\e[3;3HSet Timer %02u:%02u", remainingtime/60, remainingtime%60); } if (remainingtime <= 0) { //alarm condition kitchentimer.stop(); //stop timer kitchentimerset=0; //clear flag if (!beepsdone) multibeeps(3,0.25); //duration of multibeeps needs to be less then 0.9 seconds beepsdone = 1; //set beepsdone to only sound alarm once } } // Stops the countdown timer void stopkitchentimer() { beeponce(); if (kitchentimerset) { kitchentimer.stop(); } else { kitchentimer.reset(); } kitchentimerset=0; } // Starts the countdown timer void startkitchentimer() { kitchentimer.start(); beeponce(); beepsdone=0; kitchentimerset = 1; } // Returns the current hot block temperature in degrees centigrade as measured by the NTC float blocktemp() { float vntc, rntc; wait(0.03); vntc = ntc.read()*3.3; if (vntc > 3) { //check for NTC open circuit ntcerror=1; return 120; //on error return high temperature } else { if (vntc < 0.3) { //check for NTC open circuit ntcerror = 2; return 120; //on error return high temperature } else { rntc = (vntc * rdivntc)/(3.3 - vntc); //calculate Rntc from Vntc return ntcB/(log(rntc)-log(ntcA))-273; //calculate temperature using exponential approximation curve (beta curve) } } } // Returns the next duty-cycle for the heater using a PID algorithm float getheaterPWM() { float prevtemp, outpt; prevtemp = btemp; //store previous temperature to calculate differential btemp = blocktemp(); //measure current block temperature if (btemp < (blocksettemp - 10)) { //heater constantly on when temperature way too low, no error build-up return 1; } else { //only build up integral error when temperature is within range acterror = blocksettemp - btemp; //calculate current error term interror = interror + acterror; //update integral error term diferror = btemp - prevtemp; //calculate differential error term outpt = (propterm * acterror); //calculate and set proportional contribution outpt = outpt + (intterm * interror); //calculate and add integral contribution outpt = outpt + (difterm * diferror); //calculate and add differential contribution if (outpt <0) outpt=0; //minimum output is "off" if (outpt >1) outpt=1; //maximum output is "on" } if (btemp > maxblocktemp) outpt=0; //software overtemperature cutout return outpt; } //Translates block temperature to RGB value of indication LEDs void temp2RGB (float temp) { if (temp < 40) { //LEDs show pure blue for low temperature LedRGB[0] = 0; LedRGB[1] = 0; LedRGB[2] = 0.5; } else { if (temp > 60) { //LEDs show pure red for high temperatures LedRGB[0] = 1; LedRGB[1] = 0; LedRGB[2] = 0; } else { //LEDs mix red and blue LedRGB[0] = (temp-40)/20; LedRGB[1] = 0; LedRGB[2] = (60-temp)/20; } } } // Drives the temperature indication LEDs in the top plate void driveRGBled() { //uses global array, as functions cannot pass array variables rled = LedRGB[0]; gled = LedRGB[1]; bled = LedRGB[2]; } // Set or adjust the block setpoint temperature void adjustblocktemp () { keyup.fall(&incrementsettemp); //define "up" key function keydown.fall(&decrementsettemp); //define "down" key function keyOK.fall(&int_keyOK); //define "OK" key function display.printf("\e[2;6HSettings"); while (lastkeypressed != 5) { //loop until OK is pressed display.printf("\e[3;3HSet temp= %3.0f%cC", blocksettemp, 223); wait(0.2); } keyup.fall(¬hing); //detach InteruptIn functions keydown.fall(¬hing); keyOK.fall(¬hing); lastkeypressed = 0; //clear flag } // Warm-up to operating temperature void warmupblock (float settemp) { btemp = blocktemp(); float heaterdutycycle; cleardisplay(); if (settemp < maxblocktemp) { //this test avoids overtemperature due to incorrect function call while (btemp < (settemp -0.5)) { //setpoint reached if temperature within 0.5 degrees btemp = blocktemp(); display.printf("\e[2;2HWarming up: %3.0f%cC", btemp, 223); temp2RGB(btemp); driveRGBled(); heaterdutycycle = getheaterPWM(); //PWM heater with longish period heater = 1; wait (5 * heaterdutycycle); heater = 0; wait (5 * (1 - heaterdutycycle)); } } heater = 0; } // Cooling down void coolblockdown() { showtime.detach(); //avoid showing timer on display cleardisplay(); while (1) { btemp = blocktemp(); fan = (btemp > 40); //fan is on when block temperature above 40 degrees display.printf("\e[2;3HCool down: %3.0f%cC", btemp, 223); temp2RGB(btemp); driveRGBled(); if (!fan) display.printf("\e[3;3HSafe to touch"); wait(5); //one update per 5 seconds is more then enough } } // Regulating to a constant temperature void stayhot() { float heaterdutycycle; cleardisplay(); showtime.attach(&updatedisplay, 1.0); //set ticker for routine that updates display every second keyOK.fall(&int_keyOK); //define key interrupt routines keyright.fall(&startkitchentimer); keyup.fall(&incrementtimer); keydown.fall(&decrementtimer); keyleft.fall(&stopkitchentimer); while (lastkeypressed != 5) { //main program loop until OK key pressed btemp = blocktemp(); temp2RGB(btemp); driveRGBled(); heaterdutycycle = getheaterPWM(); //PWM heater with longish period heater = 1; wait (5 * heaterdutycycle); heater = 0; wait (5 * (1 - heaterdutycycle)); } stopkitchentimer(); //stop timer keyOK.fall(¬hing); //detach key interrupt routines keyright.fall(¬hing); keyup.fall(¬hing); keydown.fall(¬hing); } // Main program int main() { initdevice(); //power-on initialisation btemp = blocktemp(); adjustblocktemp(); beeponce(); warmupblock(blocksettemp); //warming up stayhot(); //this is where to program spends most of its time coolblockdown(); //fan-assisted cool down }