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
Diff: main.cpp
- 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