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

Committer:
jzeeff
Date:
Wed Aug 07 16:10:11 2013 +0000
Revision:
0:24cdf76455c4
Child:
1:b5abc8ddd567
Initial version of espresso machine boiler temp controller

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jzeeff 0:24cdf76455c4 1
jzeeff 0:24cdf76455c4 2 // Program to control espresso maker boiler temperatures
jzeeff 0:24cdf76455c4 3 // Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, SSR
jzeeff 0:24cdf76455c4 4 // Jon Zeeff, 2013
jzeeff 0:24cdf76455c4 5 // Public Domain
jzeeff 0:24cdf76455c4 6
jzeeff 0:24cdf76455c4 7 #include "mbed.h"
jzeeff 0:24cdf76455c4 8 #include "TSISensor.h"
jzeeff 0:24cdf76455c4 9
jzeeff 0:24cdf76455c4 10 DigitalOut ssr(PTA1);
jzeeff 0:24cdf76455c4 11 AnalogIn adc(PTE20);
jzeeff 0:24cdf76455c4 12
jzeeff 0:24cdf76455c4 13 #define OFF 0
jzeeff 0:24cdf76455c4 14 #define RED 1
jzeeff 0:24cdf76455c4 15 #define GREEN 2
jzeeff 0:24cdf76455c4 16 #define BLUE 3
jzeeff 0:24cdf76455c4 17 #define WHITE 4
jzeeff 0:24cdf76455c4 18 #define YELLOW 5
jzeeff 0:24cdf76455c4 19
jzeeff 0:24cdf76455c4 20 // RTD ohms
jzeeff 0:24cdf76455c4 21 // 1360 ohms = 94C
jzeeff 0:24cdf76455c4 22 // 1000 ohms = too cold (0C)
jzeeff 0:24cdf76455c4 23 // 1520 ohms = too hot (136C)
jzeeff 0:24cdf76455c4 24
jzeeff 0:24cdf76455c4 25 // note: assume a 2K divider resistor, a PT1000 RTD and a 16 bit A/D result
jzeeff 0:24cdf76455c4 26 // use this formula (RTD_OHMS/(RTD_OHMS+2000)) * 65536
jzeeff 0:24cdf76455c4 27
jzeeff 0:24cdf76455c4 28 // desired A/D value for boiler temp
jzeeff 0:24cdf76455c4 29 #define TARGET_TEMP 25600 // CHANGE THIS
jzeeff 0:24cdf76455c4 30
jzeeff 0:24cdf76455c4 31 // table of % power level vs time in seconds into brew cycle (including preheat)
jzeeff 0:24cdf76455c4 32 const int table[40] = {
jzeeff 0:24cdf76455c4 33 // preheat up to 10 seconds
jzeeff 0:24cdf76455c4 34 0,0,100,100,100,100,0,0,0,0, // CHANGE THIS
jzeeff 0:24cdf76455c4 35 // brewing (pump is on) up to 30 seconds
jzeeff 0:24cdf76455c4 36 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, // CHANGE THIS
jzeeff 0:24cdf76455c4 37 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65
jzeeff 0:24cdf76455c4 38 };
jzeeff 0:24cdf76455c4 39
jzeeff 0:24cdf76455c4 40 // these probably don't need to be changed
jzeeff 0:24cdf76455c4 41 #define CLOSE 50 // how close in A/D value before switching to proportional control
jzeeff 0:24cdf76455c4 42 #define INITIAL_POWER .03 // initial guess for steady state power needed (try .03)
jzeeff 0:24cdf76455c4 43 #define MIN_TEMP 21000 // below this is an error
jzeeff 0:24cdf76455c4 44 #define MAX_TEMP 29000 // above this is an error
jzeeff 0:24cdf76455c4 45 #define SLEEP_TIME 3600 // turn off heat after this many seconds
jzeeff 0:24cdf76455c4 46 #define BREW_TIME 30 // max brew time
jzeeff 0:24cdf76455c4 47 #define BREW_PREHEAT 10 // max preheat time
jzeeff 0:24cdf76455c4 48
jzeeff 0:24cdf76455c4 49 #define debug if (1) printf // use if (1) or if (0)
jzeeff 0:24cdf76455c4 50
jzeeff 0:24cdf76455c4 51 void brew(void);
jzeeff 0:24cdf76455c4 52 void set_color(int color);
jzeeff 0:24cdf76455c4 53 unsigned read_ad(void);
jzeeff 0:24cdf76455c4 54
jzeeff 0:24cdf76455c4 55 int main()
jzeeff 0:24cdf76455c4 56 {
jzeeff 0:24cdf76455c4 57 unsigned ambient_temp = read_ad(); // save temp on startup (future enhancements)
jzeeff 0:24cdf76455c4 58 time_t prev_time = 0;
jzeeff 0:24cdf76455c4 59 TSISensor tsi; // used as a start button
jzeeff 0:24cdf76455c4 60
jzeeff 0:24cdf76455c4 61 set_time(0); // start clock at zero
jzeeff 0:24cdf76455c4 62
jzeeff 0:24cdf76455c4 63 debug("starting A/D value/temp = %u\r\n",ambient_temp);
jzeeff 0:24cdf76455c4 64
jzeeff 0:24cdf76455c4 65 // loop forever, controlling boiler temperature
jzeeff 0:24cdf76455c4 66
jzeeff 0:24cdf76455c4 67 for (;;) {
jzeeff 0:24cdf76455c4 68 unsigned temp;
jzeeff 0:24cdf76455c4 69
jzeeff 0:24cdf76455c4 70 // read temp from A/D
jzeeff 0:24cdf76455c4 71 // note: in A/D counts, not degrees
jzeeff 0:24cdf76455c4 72 temp = read_ad();
jzeeff 0:24cdf76455c4 73
jzeeff 0:24cdf76455c4 74 // bang/bang when far away, PWM to learned value when close
jzeeff 0:24cdf76455c4 75 if (temp > TARGET_TEMP + CLOSE) {
jzeeff 0:24cdf76455c4 76 ssr = 0; // turn off heater
jzeeff 0:24cdf76455c4 77 set_color(GREEN); // set LED to green
jzeeff 0:24cdf76455c4 78 } else if (temp < TARGET_TEMP - CLOSE) {
jzeeff 0:24cdf76455c4 79 ssr = 1; // turn on heater
jzeeff 0:24cdf76455c4 80 set_color(RED); // set LED to red
jzeeff 0:24cdf76455c4 81 } else { // close to target temp
jzeeff 0:24cdf76455c4 82 // learning mode - adjust heat, the fraction of time power should be on
jzeeff 0:24cdf76455c4 83 static double heat = INITIAL_POWER; // initial fractional heat needed while idle
jzeeff 0:24cdf76455c4 84
jzeeff 0:24cdf76455c4 85 if (temp > TARGET_TEMP) // adjust best guess for % heat needed
jzeeff 0:24cdf76455c4 86 heat *= .99;
jzeeff 0:24cdf76455c4 87 else
jzeeff 0:24cdf76455c4 88 heat *= 1.01;
jzeeff 0:24cdf76455c4 89
jzeeff 0:24cdf76455c4 90 debug("learned heat = %F, temp = %u\r\n",heat, temp);
jzeeff 0:24cdf76455c4 91 ssr = 1; // turn on heater for PWM
jzeeff 0:24cdf76455c4 92 set_color(RED);
jzeeff 0:24cdf76455c4 93 wait(heat);
jzeeff 0:24cdf76455c4 94 ssr = 0; // turn off heater
jzeeff 0:24cdf76455c4 95 set_color(GREEN);
jzeeff 0:24cdf76455c4 96 wait(1-heat);
jzeeff 0:24cdf76455c4 97 } // if
jzeeff 0:24cdf76455c4 98
jzeeff 0:24cdf76455c4 99 // the user must press a button 10 seconds prior to brewing to start preheat
jzeeff 0:24cdf76455c4 100 if (tsi.readPercentage() > .5)
jzeeff 0:24cdf76455c4 101 brew();
jzeeff 0:24cdf76455c4 102
jzeeff 0:24cdf76455c4 103 // check for idle, sleep till tomorrow if it occurs
jzeeff 0:24cdf76455c4 104 if (time(NULL) > SLEEP_TIME){ // save power
jzeeff 0:24cdf76455c4 105 static time_t wakeup_time = (24 * 60 * 60) - (20 * 60); // 24 hours minus 20 min
jzeeff 0:24cdf76455c4 106
jzeeff 0:24cdf76455c4 107 ssr = 0; // turn off heater
jzeeff 0:24cdf76455c4 108 set_color(OFF);
jzeeff 0:24cdf76455c4 109 while (time(NULL) < wakeup_time) // wait till tomorrow
jzeeff 0:24cdf76455c4 110 wait(1);
jzeeff 0:24cdf76455c4 111 set_time(0); // clock runs zero to 24 hours
jzeeff 0:24cdf76455c4 112 wakeup_time = (24 * 60 * 60); // no 15 min offset needed now
jzeeff 0:24cdf76455c4 113 }
jzeeff 0:24cdf76455c4 114
jzeeff 0:24cdf76455c4 115 // check for errors (incorrect boiler temp can be dangerous)
jzeeff 0:24cdf76455c4 116 if (temp > MAX_TEMP || temp < MIN_TEMP) {
jzeeff 0:24cdf76455c4 117 ssr = 0; // turn off heater
jzeeff 0:24cdf76455c4 118 set_color(YELLOW); // set LED to indicate error
jzeeff 0:24cdf76455c4 119 for (;;); // reset needed to exit this
jzeeff 0:24cdf76455c4 120 }
jzeeff 0:24cdf76455c4 121
jzeeff 0:24cdf76455c4 122 if (time(NULL) > prev_time) debug("A/D value = %u\r\n",temp);
jzeeff 0:24cdf76455c4 123 prev_time = time(NULL);
jzeeff 0:24cdf76455c4 124
jzeeff 0:24cdf76455c4 125 } // while
jzeeff 0:24cdf76455c4 126
jzeeff 0:24cdf76455c4 127 } // main()
jzeeff 0:24cdf76455c4 128
jzeeff 0:24cdf76455c4 129
jzeeff 0:24cdf76455c4 130 //=================================================================
jzeeff 0:24cdf76455c4 131 // This subroutine is called when the button is pressed, 10 seconds
jzeeff 0:24cdf76455c4 132 // before the pump is started. It does open loop power/temp control.
jzeeff 0:24cdf76455c4 133 //=================================================================
jzeeff 0:24cdf76455c4 134
jzeeff 0:24cdf76455c4 135 void brew(void)
jzeeff 0:24cdf76455c4 136 {
jzeeff 0:24cdf76455c4 137 time_t start_time = time(NULL);
jzeeff 0:24cdf76455c4 138
jzeeff 0:24cdf76455c4 139 #define brew_time (time(NULL) - start_time)
jzeeff 0:24cdf76455c4 140
jzeeff 0:24cdf76455c4 141 debug("preheat/brew start\r\n");
jzeeff 0:24cdf76455c4 142 set_color(WHITE);
jzeeff 0:24cdf76455c4 143
jzeeff 0:24cdf76455c4 144 while (brew_time < BREW_TIME + BREW_PREHEAT) { // loop for 40 seconds
jzeeff 0:24cdf76455c4 145
jzeeff 0:24cdf76455c4 146 if (brew_time >= BREW_PREHEAT)
jzeeff 0:24cdf76455c4 147 set_color(BLUE); // set LED color to blue for start brew/pump now
jzeeff 0:24cdf76455c4 148
jzeeff 0:24cdf76455c4 149 // PWM power level over .5 second
jzeeff 0:24cdf76455c4 150 ssr = 1; // turn on heater
jzeeff 0:24cdf76455c4 151 wait(table[brew_time] / 200.0);
jzeeff 0:24cdf76455c4 152 ssr = 0; // turn off heater
jzeeff 0:24cdf76455c4 153 wait((100 - table[brew_time]) / 200.0);
jzeeff 0:24cdf76455c4 154
jzeeff 0:24cdf76455c4 155 debug("power level %u = %u %%, temp = %u\r\n",brew_time,table[brew_time],read_ad());
jzeeff 0:24cdf76455c4 156
jzeeff 0:24cdf76455c4 157 } // while
jzeeff 0:24cdf76455c4 158
jzeeff 0:24cdf76455c4 159 set_color(OFF);
jzeeff 0:24cdf76455c4 160 debug("brew done\r\n");
jzeeff 0:24cdf76455c4 161
jzeeff 0:24cdf76455c4 162 } // brew()
jzeeff 0:24cdf76455c4 163
jzeeff 0:24cdf76455c4 164
jzeeff 0:24cdf76455c4 165 // =============================================
jzeeff 0:24cdf76455c4 166 // set multi color LED state
jzeeff 0:24cdf76455c4 167 // =============================================
jzeeff 0:24cdf76455c4 168
jzeeff 0:24cdf76455c4 169 DigitalOut r (LED_RED);
jzeeff 0:24cdf76455c4 170 DigitalOut g (LED_GREEN);
jzeeff 0:24cdf76455c4 171 DigitalOut b (LED_BLUE);
jzeeff 0:24cdf76455c4 172
jzeeff 0:24cdf76455c4 173 void set_color(int color)
jzeeff 0:24cdf76455c4 174 {
jzeeff 0:24cdf76455c4 175
jzeeff 0:24cdf76455c4 176 // turn off
jzeeff 0:24cdf76455c4 177 r = g = b = 1;
jzeeff 0:24cdf76455c4 178
jzeeff 0:24cdf76455c4 179 switch (color) {
jzeeff 0:24cdf76455c4 180 case OFF:
jzeeff 0:24cdf76455c4 181 break;
jzeeff 0:24cdf76455c4 182 case GREEN:
jzeeff 0:24cdf76455c4 183 g = 0;
jzeeff 0:24cdf76455c4 184 break;
jzeeff 0:24cdf76455c4 185 case BLUE:
jzeeff 0:24cdf76455c4 186 b = 0;
jzeeff 0:24cdf76455c4 187 break;
jzeeff 0:24cdf76455c4 188 case RED:
jzeeff 0:24cdf76455c4 189 r = 0;
jzeeff 0:24cdf76455c4 190 break;
jzeeff 0:24cdf76455c4 191 case YELLOW:
jzeeff 0:24cdf76455c4 192 r = g = 0;
jzeeff 0:24cdf76455c4 193 break;
jzeeff 0:24cdf76455c4 194 case WHITE:
jzeeff 0:24cdf76455c4 195 r = g = b = 0;
jzeeff 0:24cdf76455c4 196 break;
jzeeff 0:24cdf76455c4 197 } // switch
jzeeff 0:24cdf76455c4 198
jzeeff 0:24cdf76455c4 199 } // set_color()
jzeeff 0:24cdf76455c4 200
jzeeff 0:24cdf76455c4 201 //=======================================
jzeeff 0:24cdf76455c4 202 // read A/D value from RTD
jzeeff 0:24cdf76455c4 203 // median for accuracy
jzeeff 0:24cdf76455c4 204 //=======================================
jzeeff 0:24cdf76455c4 205
jzeeff 0:24cdf76455c4 206 unsigned read_ad(void)
jzeeff 0:24cdf76455c4 207 {
jzeeff 0:24cdf76455c4 208 unsigned a, b, c;
jzeeff 0:24cdf76455c4 209
jzeeff 0:24cdf76455c4 210 a = adc.read_u16();
jzeeff 0:24cdf76455c4 211 b = adc.read_u16();
jzeeff 0:24cdf76455c4 212 c = adc.read_u16();
jzeeff 0:24cdf76455c4 213
jzeeff 0:24cdf76455c4 214 if ((a >= b && a <= c) || (a >= c && a <= b)) return a;
jzeeff 0:24cdf76455c4 215 if ((b >= a && b <= c) || (b >= c && b <= a)) return b;
jzeeff 0:24cdf76455c4 216 return c;
jzeeff 0:24cdf76455c4 217
jzeeff 0:24cdf76455c4 218 } // read_ad()
jzeeff 0:24cdf76455c4 219