Marc Bax
/
PoopCooker_070
Firmware for sample prep heater device
Revision 0:06f27dbfa599, committed 2011-09-04
- Comitter:
- marcbax
- Date:
- Sun Sep 04 18:35:09 2011 +0000
- Commit message:
- Version 0.70
Changed in this revision
main.cpp | Show annotated file Show diff for this revision Revisions of this file |
mbed.bld | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 06f27dbfa599 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sun Sep 04 18:35:09 2011 +0000 @@ -0,0 +1,372 @@ +//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 +}
diff -r 000000000000 -r 06f27dbfa599 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sun Sep 04 18:35:09 2011 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912