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:
- 0:24cdf76455c4
- Child:
- 1:b5abc8ddd567
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Aug 07 16:10:11 2013 +0000 @@ -0,0 +1,219 @@ + +// Program to control espresso maker boiler temperatures +// 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); +AnalogIn adc(PTE20); + +#define OFF 0 +#define RED 1 +#define GREEN 2 +#define BLUE 3 +#define WHITE 4 +#define YELLOW 5 + +// RTD ohms +// 1360 ohms = 94C +// 1000 ohms = too cold (0C) +// 1520 ohms = too hot (136C) + +// note: assume a 2K divider resistor, a PT1000 RTD and a 16 bit A/D result +// use this formula (RTD_OHMS/(RTD_OHMS+2000)) * 65536 + +// desired A/D value for boiler temp +#define TARGET_TEMP 25600 // CHANGE THIS + +// table of % power level vs time in seconds into brew cycle (including preheat) +const int table[40] = { + // preheat up to 10 seconds + 0,0,100,100,100,100,0,0,0,0, // CHANGE THIS + // brewing (pump is on) up to 30 seconds + 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, // CHANGE THIS + 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65 +}; + +// these probably don't need to be changed +#define CLOSE 50 // how close in A/D value before switching to proportional control +#define INITIAL_POWER .03 // initial guess for steady state power needed (try .03) +#define MIN_TEMP 21000 // below this is an error +#define MAX_TEMP 29000 // above this is an error +#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 debug if (1) printf // use if (1) or if (0) + +void brew(void); +void set_color(int color); +unsigned read_ad(void); + +int main() +{ + unsigned ambient_temp = read_ad(); // save temp on startup (future enhancements) + time_t prev_time = 0; + TSISensor tsi; // used as a start button + + 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 + static double heat = INITIAL_POWER; // initial fractional heat needed while idle + + if (temp > TARGET_TEMP) // adjust best guess for % heat needed + heat *= .99; + else + heat *= 1.01; + + 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 15 min offset needed now + } + + // 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 + for (;;); // reset needed to exit this + } + + if (time(NULL) > prev_time) debug("A/D value = %u\r\n",temp); + prev_time = time(NULL); + + } // while + +} // main() + + +//================================================================= +// This subroutine is called when the button is pressed, 10 seconds +// before the pump is started. It does open loop power/temp control. +//================================================================= + +void brew(void) +{ + time_t start_time = time(NULL); + + #define brew_time (time(NULL) - start_time) + + debug("preheat/brew start\r\n"); + set_color(WHITE); + + while (brew_time < BREW_TIME + BREW_PREHEAT) { // loop for 40 seconds + + if (brew_time >= BREW_PREHEAT) + set_color(BLUE); // set LED color to blue for start brew/pump now + + // PWM power level over .5 second + ssr = 1; // turn on heater + wait(table[brew_time] / 200.0); + ssr = 0; // turn off heater + wait((100 - table[brew_time]) / 200.0); + + debug("power level %u = %u %%, temp = %u\r\n",brew_time,table[brew_time],read_ad()); + + } // while + + set_color(OFF); + 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) +{ +unsigned a, b, c; + + a = adc.read_u16(); + b = adc.read_u16(); + c = adc.read_u16(); + + if ((a >= b && a <= c) || (a >= c && a <= b)) return a; + if ((b >= a && b <= c) || (b >= c && b <= a)) return b; + return c; + +} // read_ad() + \ No newline at end of file