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
main.cpp
- Committer:
- jzeeff
- Date:
- 2013-08-09
- Revision:
- 1:b5abc8ddd567
- Parent:
- 0:24cdf76455c4
- Child:
- 2:22d9c714b511
File content as of revision 1:b5abc8ddd567:
// Program to control espresso maker boiler temperatures // Similar to PID, but uses a flexible open loop table during brew // Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, SSR // 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 // 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 // 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 const int table[40] = { // preheat up to 10 seconds 0,0,0,0,0,0,18,18,18,18, // 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 }; // 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 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_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 #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); unsigned ambient_temp; // room or water tank temp double heat = INITIAL_POWER; // initial fractional heat needed while idle int main() { time_t prev_time = 0; TSISensor tsi; // used as a start button ambient_temp = read_ad(); // save temp on startup set_time(0); // start clock at zero debug("starting A/D value/temp = %u\r\n",ambient_temp); // loop forever, controlling boiler temperature for (;;) { unsigned temp; // read temp from A/D // note: in A/D counts, not degrees temp = read_ad(); // 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 } else if (temp < TARGET_TEMP - CLOSE) { ssr = 1; // turn on heater set_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; 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); } // 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 ssr = 0; // turn off heater set_color(OFF); while (time(NULL) < wakeup_time) // wait till tomorrow 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 } // 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 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 prev_time = time(NULL); } // for (;;) } // main() //================================================================= // This subroutine is called when the button is pressed, 10 seconds // before the pump is started. It does open loop PWM power/heat control. //================================================================= 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) 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); for (;;) { unsigned prev_brew_time = 0; 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 if (brew_time != prev_brew_time) debug("target temp %u = %u, temp = %u\r\n",brew_time,table[brew_time],read_ad()); prev_brew_time = brew_time; } // for ssr = 0; debug("brew done\r\n"); } // brew() // ============================================= // set multi color LED state // ============================================= DigitalOut r (LED_RED); DigitalOut g (LED_GREEN); DigitalOut b (LED_BLUE); void set_color(int color) { // turn off 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() //======================================= // read A/D value from RTD // median for accuracy //======================================= unsigned read_ad(void) { uint32_t sum=0; int i; 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 return sum / 3; } // read_ad()