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

Revision:
2:22d9c714b511
Parent:
1:b5abc8ddd567
Child:
3:eb60e36b03f6
--- a/main.cpp	Fri Aug 09 20:36:28 2013 +0000
+++ b/main.cpp	Sun Aug 11 20:39:57 2013 +0000
@@ -1,6 +1,6 @@
 
 // Program to control espresso maker boiler temperatures
-// Similar to PID, but uses a flexible open loop table during brew
+// 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
 // Jon Zeeff, 2013
 // Public Domain
@@ -18,36 +18,38 @@
 #define WHITE 4
 #define YELLOW 5
 
-// PT1000 RTD ohms
+// PT1000 RTD ohms (use Google to find a full table)
 // 1360 ohms = 94C
 // 1000 ohms = too cold (0C)
 // 1520 ohms = too hot (136C)
 
 // note: assume a 2.2K divider resistor, a PT1000 RTD and a 16 bit A/D result
-// use this formula: (RTD_OHMS/(RTD_OHMS+2200)) * 65536
+// 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 25850       // 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
-// note: if you alternate very high and negative values here, you get the equivalent of open loop/PWM/temp surfing
+#define TARGET_TEMP 25900       // CHANGE THIS  
 
-const int table[40] = {
+// 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.
+// This can prevent overshoot.
+
+const double table[40] = {
     // preheat up to 10 seconds
-    0,0,0,0,0,0,18,18,18,18,                            // CHANGE THIS
+    0,0,0,0,18.99,18.99,0,0,0,0,                     // CHANGE THIS
     // brewing (pump is on) up to 30 seconds
-    18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,       // CHANGE THIS
-    18,18,18,18,18,18,18,18,18,18,18,18,18,18,18
+    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
 }; 
 
 // 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 proportional control
+#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 MIN_TEMP 21000          // below this is an error
-#define MAX_TEMP 218000          // above this is an error
-#define ROOM_TEMP 22000         // A/D at standard ambient room temp
+#define MAX_TEMP 29000          // above this is an error
+#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 BREW_TIME 30            // max brew time
@@ -75,11 +77,9 @@
 // loop forever, controlling boiler temperature
 
     for (;;) {
-        unsigned temp;
-
         // read temp from A/D
         // note: in A/D counts, not degrees
-        temp = read_ad();
+        unsigned temp = read_ad();
        
         // bang/bang when far away, PWM to learned value when close
         if (temp > TARGET_TEMP + CLOSE) {
@@ -114,7 +114,8 @@
             static time_t wakeup_time = (24 * 60 * 60) - (20 * 60);  // 24 hours minus 20 min
             
             ssr = 0;                    // turn off heater
-            set_color(OFF);           
+            set_color(OFF);     
+            printf("sleep\r\n");      
             while (time(NULL) < wakeup_time)    // wait till tomorrow
                    wait(1);
             set_time(0);                        // clock runs zero to 24 hours
@@ -146,11 +147,10 @@
 void brew(void)
 {
     unsigned start_time = time(NULL);
-    #define brew_time (time(NULL) - start_time)
     
     double adjust = 1;              // default is no adjustment
     
-    // adjust for tank temp (assumed to be equal to ambient at startup)
+    // 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); 
    
@@ -158,22 +158,29 @@
     set_color(WHITE);  
             
     for (;;) {
-        unsigned prev_brew_time = 0;
+        unsigned brew_time;
+        static unsigned prev_brew_time;
         
+        brew_time = time(NULL) - start_time;
+       
         if (brew_time >= BREW_PREHEAT + BREW_TIME)
            break;
  
         if (brew_time == BREW_PREHEAT)
            set_color(BLUE);    // set LED color to blue for start brew/pump now
-
-        // bang/bang temp to the table value
-        if (read_ad() < (TARGET_TEMP + (table[brew_time] * AD_PER_DEGREE)) * adjust)
-           ssr = 1;             // heater on
-        else
-           ssr = 0;             // heater off
+         
+        double 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 PWM
         
-        if (brew_time != prev_brew_time)
-           debug("target temp %u = %u, temp = %u\r\n",brew_time,table[brew_time],read_ad());
+        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;
 
     } // for