Firmware for sample prep heater device

Dependencies:   mbed

Revision:
0:06f27dbfa599
--- /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
+}