Firmware for sample prep heater device

Dependencies:   mbed

Files at this revision

API Documentation at this revision

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(&nothing);                                           //detach InteruptIn functions
+    keydown.fall(&nothing);
+    keyOK.fall(&nothing);
+    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(&nothing);                                           //detach key interrupt routines
+    keyright.fall(&nothing);
+    keyup.fall(&nothing);
+    keydown.fall(&nothing);
+}
+
+// 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