Controls both heat and pump pressure based on a temperature probe and a scale- ie, it does temperature and flow profiling. Should work with any vibratory pump machine.

Dependencies:   Adafruit_RTCLib FastPWM TSI mbed

Files at this revision

API Documentation at this revision

Comitter:
jzeeff
Date:
Thu Aug 29 14:55:52 2013 +0000
Parent:
2:22d9c714b511
Child:
4:3d661b485d59
Commit message:
basic pump functions

Changed in this revision

Adafruit_RTCLib.lib Show annotated file Show diff for this revision Revisions of this file
FastPWM.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Adafruit_RTCLib.lib	Thu Aug 29 14:55:52 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/nkhorman/code/Adafruit_RTCLib/#2c4e81ecda67
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FastPWM.lib	Thu Aug 29 14:55:52 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/Sissors/code/FastPWM/#a7b9f778c4b4
--- a/main.cpp	Sun Aug 11 20:39:57 2013 +0000
+++ b/main.cpp	Thu Aug 29 14:55:52 2013 +0000
@@ -1,23 +1,12 @@
 
 // Program to control espresso maker boiler temperatures
-// Similar to multiple PID control, but uses a flexible open or closed loop table during brew
-// Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, SSR
+// Similar to multiple PID control (pre-brew, brew and steam),
+// but uses a flexible open and closed loop table during brew
+// Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, heater
+// See www.coffeegeeks.com for discussion
 // Jon Zeeff, 2013
 // Public Domain
 
-#include "mbed.h"
-#include "TSISensor.h"
-
-DigitalOut ssr(PTA1);       // Solid State Relay
-AnalogIn   adc(PTE20);      // A/D converter reads temperature
-
-#define OFF 0
-#define RED 1
-#define GREEN 2
-#define BLUE 3
-#define WHITE 4
-#define YELLOW 5
-
 // PT1000 RTD ohms (use Google to find a full table)
 // 1360 ohms = 94C
 // 1000 ohms = too cold (0C)
@@ -27,111 +16,180 @@
 // use this formula: A/D = (RTD_OHMS/(RTD_OHMS+2200)) * 65536
 
 // desired A/D value for boiler temp while idling
-// note: there is an offset between boiler wall temp sensors and actual water temp
-#define TARGET_TEMP 25900       // CHANGE THIS  
+// note: there is usually some offset between boiler wall temp sensors and actual water temp
+#define TARGET_TEMP 25440       // CHANGE THIS 
 
 // Table of adjustments (degrees C) to TARGET_TEMP vs time (seconds) into brew cycle (including preheat period)
 // The idea is that extra heat is needed as cool water comes into the boiler during brew.
 // Extra heat is provided by a higher than normal boiler wall temp.
-// NOTE: the decimal portion of the value is used as the PWM value to be applied if more heat is needed.
+// NOTE: the fractional portion of the value is used as the PWM value to be applied if more heat is needed.
 // This can prevent overshoot.
+// Example: 5.3 means that the boiler wall should be 5 degrees C above normal at this time point.  If not, apply 30% power.
+// Example: 99.99 means (roughly) that the heater should be completely on for the 1 second period
+// Note: heat takes at least 4 seconds before it is seen by the sensor
 
 const double table[40] = {
     // preheat up to 10 seconds
-    0,0,0,0,18.99,18.99,0,0,0,0,                     // CHANGE THIS
+    0.0,0.0,0.0,0.0,0.0,0.0,0.0,99.9,99.9,99.20,                  // CHANGE THIS
     // brewing (pump is on) up to 30 seconds
-    18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,18.7,    // CHANGE THIS
-    15.7,15.7,15.7,15.7,15.7,15.7,15.7,15.7,15.7,15.7,15.7,0,0,0,0                 // CHANGE THIS
-}; 
+    99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20, // CHANGE THIS
+    99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,0,0,0,0                  // CHANGE THIS
+};
+
+const double pump_table[40] = {
+    // during pre-brew period
+    0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,                  // CHANGE THIS
+    // brewing up to 30 seconds
+    0.0,0.0,99.99,99.99,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,                // CHANGE THIS
+    99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,99.20,0,0,0,0                 // CHANGE THIS
+};
 
 // these probably don't need to be changed if you are using a Gaggia Classic
-#define CLOSE 30                // how close in A/D value before switching to learned value control
-#define INITIAL_POWER  .05      // initial guess for steady state power needed (try .05 = 5%)
+#define CLOSE 60                // how close in A/D value before switching to learned value control
+#define GAIN .01                // how fast to adjust  
+#define INITIAL_POWER  .03      // initial guess for steady state heater power needed (try .03 = 3%)
 #define MIN_TEMP 21000          // below this is an error
-#define MAX_TEMP 29000          // above this is an error
+#define MAX_TEMP 29500          // above this is an error
+#define STEAM_TEMP 28000        // boiler temp while steaming
 #define ROOM_TEMP 22000         // A/D value at standard ambient room temp
 #define MAX_ROOM_TEMP 22500     // above this means ambient isn't valid
-#define SLEEP_TIME 3600         // turn off heat after this many seconds
+#define SLEEP_TIME 7200         // turn off heat after this many seconds
 #define BREW_TIME 30            // max brew time
 #define BREW_PREHEAT 10         // max preheat time
 #define AD_PER_DEGREE 44        // how many A/D counts equal a 1 degree C change 
 #define debug if (1) printf     // use if (1) or if (0)
 
-void brew(void);
-void set_color(int color);
-unsigned read_ad(void);
+#include "mbed.h"
+#include "TSISensor.h"      // touch sensor
+#include "DS1307.h"         // real-time clock
+#include "FastPWM.h"        // better PWM routine for pump control
+ 
+#define OFF 0
+#define RED 1
+#define GREEN 2
+#define BLUE 3
+#define WHITE 4
+#define YELLOW 5
+#define AQUA 6
+#define PINK 7
+
+#define ON 1
+#define OFF 0
 
-unsigned ambient_temp;           // room or water tank temp
-double heat = INITIAL_POWER;     // initial fractional heat needed while idle
-     
-int main()
+DigitalOut heater(PTD7);        // Solid State Relay - PTD6&7 have high drive capability
+FastPWM    pump(PTD4);          // Solid State Relay - PTD4 can do PWM @ 10K hz
+AnalogIn   boiler(PTE20);       // A/D converter reads temperature on boiler
+AnalogIn   group(PTE22);        // A/D for group basket temp
+DigitalOut led_green(LED_GREEN);
+I2C        gI2c(PTE0, PTE1);    // SDA, SCL - use pullups
+RtcDs1307  rtclock(gI2c);       // DS1307 is a real time clock chip
+Serial     pc(USBTX, USBRX);    // Serial to pc connection
+ 
+void brew(void);
+void led_color(int color);
+unsigned read_temp(AnalogIn adc);
+void steam(int seconds);
+
+unsigned ambient_temp;          // room or water tank temp (startup)
+double heat = INITIAL_POWER;    // initial fractional heat needed while idle
+unsigned boiler_log[40];        // record boiler temp during brew
+unsigned group_log[40];         // record basket temp during brew
+
+
+int main()                      // start of program
 {
     time_t prev_time = 0;
-    TSISensor tsi;                          // used as a start button
-    ambient_temp = read_ad();               // save temp on startup 
-     
+    TSISensor tsi;                          // used as a brew start button
+    ambient_temp = read_temp(boiler);       // save temp on startup
+
     set_time(0);                            // start clock at zero
-   
+#if 0
+    DateTime dt = gRtc.now();
+ 
+    debug("%u/%u/%02u %2u:%02u:%02u\r\n"
+                ,dt.month(),dt.day(),dt.year()
+                ,dt.hour(),dt.minute(),dt.second());
+#endif                
     debug("starting A/D value/temp = %u\r\n",ambient_temp);
-  
-// loop forever, controlling boiler temperature
+    
+    pump.period_ms(500);    // period of PWM signal
+    //pump = 100;           // duty cycle.  For DC, use 6 msec pulses
+    
+    if (pc.readable())      // clear any data on serial port
+       pc.getc(); 
+            
+    if (ambient_temp < MAX_ROOM_TEMP)
+        steam(180);         // do accelerated warmup by overheating
+
+    // loop forever, controlling boiler temperature
 
     for (;;) {
         // read temp from A/D
         // note: in A/D counts, not degrees
-        unsigned temp = read_ad();
-       
+        unsigned temp = read_temp(boiler);
+
         // bang/bang when far away, PWM to learned value when close
         if (temp > TARGET_TEMP + CLOSE) {
-           ssr = 0;                // turn off heater
-           set_color(GREEN);       // set LED to green
+            heater = OFF;                   // turn off heater
+            led_color(GREEN);               // set LED to green
         } else if (temp < TARGET_TEMP - CLOSE) {
-            ssr = 1;                         // turn on heater           
-            set_color(RED);                  // set LED to red
+            heater = ON;                    // turn on heater
+            led_color(RED);                 // set LED to red
         } else {   // close to target temp
             // learning mode - adjust heat, the fraction of time power should be on
-           
-            if (temp > TARGET_TEMP)          // adjust best guess for % heat needed
-                  heat *= .98;
+
+            if (temp > TARGET_TEMP)         // adjust best guess for % heat needed
+                heat *= (1-GAIN);
             else
-                  heat *= 1.02;
-                  
-            debug("learned heat = %F, temp = %u\r\n",heat, temp);
-            ssr = 1;            // turn on heater for PWM
-            set_color(RED);
-            wait(heat);             
-            ssr = 0;            // turn off heater
-            set_color(GREEN);               
-            wait(1-heat);       
+                heat *= (1+GAIN);
+
+            heater = ON;                // turn on heater for PWM
+            led_color(RED);
+            wait(heat * 2.7);           // 1.7 to reduce interaction with 50/60Hz power
+            heater = OFF;               // turn off heater
+            led_color(GREEN);
+            wait((1-heat) * 2.7);       // total time is 2.7 seconds
         } // if
 
         // the user must press a button 10 seconds prior to brewing to start preheat
         if (tsi.readPercentage() > .5)
             brew();
-
-        // check for idle, sleep till tomorrow if it occurs
-        if (time(NULL) > SLEEP_TIME){   // save power
-            static time_t wakeup_time = (24 * 60 * 60) - (20 * 60);  // 24 hours minus 20 min
+            
+        // if they signaled for steam
+        if (tsi.readPercentage() > .2 && tsi.readPercentage() < .5)
+            steam(120);
             
-            ssr = 0;                    // turn off heater
-            set_color(OFF);     
-            printf("sleep\r\n");      
+        if (pc.readable()){    // Check if data is available on serial port.
+            pc.getc(); 
+            // debug, print out temp log
+            int i;
+            for (i = 0; i < 40; ++i)
+                printf("log %d: %u %u\r\n",i,boiler_log[i],group_log[i]);
+        } // if
+            
+        // check for idle shutdown, sleep till tomorrow if it occurs
+        if (time(NULL) > SLEEP_TIME) {  // save power
+            static time_t wakeup_time = (24 * 60 * 60) - (20 * 60);  // 24 hours minus 20 min
+
+            heater = OFF;                    // turn off heater
+            led_color(OFF);
+            printf("sleep\r\n");
             while (time(NULL) < wakeup_time)    // wait till tomorrow
-                   wait(1);
+                wait(1);
             set_time(0);                        // clock runs zero to 24 hours
             wakeup_time = (24 * 60 * 60);       // no 20 min offset needed now
-            ambient_temp = read_ad();           // save temp on startup  
+            ambient_temp = read_temp(boiler);           // save temp on startup
         }
-      
+
         // check for errors (incorrect boiler temp can be dangerous)
         if (temp > MAX_TEMP || temp < MIN_TEMP) {
-            ssr = 0;            // turn off heater
-            set_color(YELLOW);  // set LED to indicate error
+            heater = OFF;            // turn off heater
+            led_color(YELLOW);  // set LED to indicate error
             debug("error A/D = %u\r\n",temp);
             for (;;);           // reset needed to exit this
         }
 
-        if (time(NULL) > prev_time) debug("A/D value = %u\r\n",temp);  // every second
+        if (time(NULL) > prev_time) debug("A/D value = %u %u, heat = %F\r\n",temp,read_temp(group),heat);  // once per second
         prev_time = time(NULL);
 
     } // for (;;)
@@ -141,54 +199,87 @@
 
 //=================================================================
 // This subroutine is called when the button is pressed, 10 seconds
-// before the pump is started.  It does open loop PWM power/heat control.
+// before the pump is started.  It does both open loop and closed 
+// loop PWM power/heat control.
 //=================================================================
 
 void brew(void)
 {
     unsigned start_time = time(NULL);
-    
+ 
     double adjust = 1;              // default is no adjustment
-    
+
     // adjust for higher or lower tank temp (assumed to be equal to ambient at startup)
-    if (ambient_temp < MAX_ROOM_TEMP)    // sanity check
-       adjust = (double)(ROOM_TEMP - TARGET_TEMP) / (double)(ambient_temp - TARGET_TEMP); 
-   
+    // add in "heat"??
+    //if (ambient_temp < MAX_ROOM_TEMP)    // sanity check
+    //    adjust = (double)(ROOM_TEMP - TARGET_TEMP) / (double)(ambient_temp - TARGET_TEMP);
+
     debug("preheat/brew start, adjust = %F\r\n", adjust);
-    set_color(WHITE);  
-            
+    led_color(WHITE);
+    
+    unsigned prev_brew_time = 999;
+    unsigned brew_time;
+    double pwm;
+    
     for (;;) {
-        unsigned brew_time;
-        static unsigned prev_brew_time;
-        
-        brew_time = time(NULL) - start_time;
-       
+        brew_time = time(NULL) - start_time;        // seconds into cycle
+
         if (brew_time >= BREW_PREHEAT + BREW_TIME)
-           break;
- 
+            break;                                  // brew is done
+
         if (brew_time == BREW_PREHEAT)
-           set_color(BLUE);    // set LED color to blue for start brew/pump now
-         
-        double pwm = table[brew_time] - (int)table[brew_time];    // decimal part only
- 
+            led_color(BLUE);    // set LED color to blue for start brew/pump now
+
+        //pump = pump_table[brew_time];                      // duty cycle
+        
+        pwm = table[brew_time] - (int)table[brew_time];    // decimal part only
+
         // if too cold, apply the PWM value, if too hot, do nothing
-        if (read_ad() < (TARGET_TEMP + (table[brew_time] * AD_PER_DEGREE)) * adjust) {
-           ssr = 1;      
-           wait(pwm / 2);
-           ssr = 0;
-           wait((1 - pwm) / 2);
+        if (read_temp(boiler) < (TARGET_TEMP + (table[brew_time] * AD_PER_DEGREE)) * adjust) {
+            if (pwm > 0) {
+               heater = ON;
+               wait(pwm/2);
+               heater = OFF;
+               wait((1 - pwm)/2);
+            }
         }  // if PWM
-        
-        if (brew_time != prev_brew_time)     // print status every second
-           debug("target temp %u = %F, temp = %u\r\n",brew_time,table[brew_time],read_ad());
-        prev_brew_time = brew_time;
+
+        if (brew_time != prev_brew_time){                   // every second
+            group_log[brew_time] = read_temp(group);        // record group temp 
+            boiler_log[brew_time] = read_temp(boiler);      // record boiler temp
+            debug("target temp %u = %F, temp = %u %u\r\n",brew_time,table[brew_time],read_temp(boiler),read_temp(group));
+            prev_brew_time = brew_time;
+        }
 
     } // for
-  
-    ssr = 0;
+
     debug("brew done\r\n");
+ 
+} // brew()
 
-} // brew()
+//===========================================================
+// control to a higher steam temperature for n seconds
+//===========================================================
+
+void steam(int seconds)
+{
+    unsigned start_time = time(NULL);
+
+    debug("steam start, time = %d\r\n", seconds);
+
+    while (time(NULL) - start_time < seconds) {
+        if (read_temp(boiler) > STEAM_TEMP) {
+            heater = OFF;            // turn off heater
+            led_color(AQUA);    // set LED to aqua
+        } else {
+            heater = ON;            // turn on heater
+            led_color(PINK);    // set LED to pink
+        }
+    } // while
+
+    heater = OFF;    // turn off
+
+} // steam()
 
 
 // =============================================
@@ -199,58 +290,65 @@
 DigitalOut g (LED_GREEN);
 DigitalOut b (LED_BLUE);
 
-void set_color(int color)
+void led_color(int color)
 {
-
 // turn off
-r = g = b = 1;
+    r = g = b = 1;
 
-switch (color) {
-    case OFF:
-        break;
-    case GREEN:
-        g = 0;
-        break;
-    case BLUE:
-        b = 0;
-        break;
-    case RED:
-        r = 0;
-        break;
-    case YELLOW:
-        r = g = 0;
-        break;
-    case WHITE:
-        r = g = b = 0;
-        break;
- }  // switch
- 
- } // set_color()
+    switch (color) {
+        case OFF:
+            break;
+        case GREEN:
+            g = 0;
+            break;
+        case BLUE:
+            b = 0;
+            break;
+        case RED:
+            r = 0;
+            break;
+        case YELLOW:
+            r = g = 0;
+            break;
+        case AQUA:
+            b = g = 0;
+            break;
+        case PINK:
+            r = b = 0;
+            break;
+        case WHITE:
+            r = g = b = 0;
+            break;
+    }  // switch
+
+} // led_color()
 
 //=======================================
 // read A/D value from RTD
-// median for accuracy
+// median and average for accuracy
 //=======================================
 
-unsigned read_ad(void) 
+unsigned read_temp(AnalogIn adc)
 {
+    uint32_t sum=0;
+    int i;
 
-uint32_t sum=0;
-int i;
+    for (i = 0; i < 33; ++i) {    // average multiple for more accuracy
+        unsigned a, b, c;
+
+        a = adc.read_u16();       // take median of 3 values
+        b = adc.read_u16();
+        c = adc.read_u16();
 
-for (i = 0; i < 3; ++i) {    // average multiple for more accuracy
-    unsigned a, b, c;
-    
-    a = adc.read_u16();     // take median of 3 values
-    b = adc.read_u16();
-    c = adc.read_u16();
-    
-    if ((a >= b && a <= c) || (a >= c && a <= b)) sum += a;
-    else if ((b >= a && b <= c) || (b >= c && b <= a)) sum += b;
-    else sum += c;
-} // for
+        if ((a >= b && a <= c) || (a >= c && a <= b)) sum += a;
+        else if ((b >= a && b <= c) || (b >= c && b <= a)) sum += b;
+        else sum += c;
+    } // for
 
-return sum / 3;
-    
-}  // read_ad()
- 
\ No newline at end of file
+    return sum / 33;
+
+}  // read_temp()
+
+
+
+