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 Oct 02 13:38:23 2013 +0000
Revision:
4:3d661b485d59
Parent:
3:eb60e36b03f6
Child:
5:0393adfdd439
Added initial scale code

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 3:eb60e36b03f6 3 // Similar to multiple PID control (pre-brew, brew and steam),
jzeeff 3:eb60e36b03f6 4 // but uses a flexible open and closed loop table during brew
jzeeff 4:3d661b485d59 5 // Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, SSR
jzeeff 4:3d661b485d59 6 // Can also control the pump
jzeeff 3:eb60e36b03f6 7 // See www.coffeegeeks.com for discussion
jzeeff 0:24cdf76455c4 8 // Jon Zeeff, 2013
jzeeff 0:24cdf76455c4 9 // Public Domain
jzeeff 0:24cdf76455c4 10
jzeeff 4:3d661b485d59 11 // PT1000 RTD ohms (use Google to find a full table, remember to add offset)
jzeeff 4:3d661b485d59 12 // 1362 ohms = 94C
jzeeff 4:3d661b485d59 13 // 1374 ohms = 97C
jzeeff 0:24cdf76455c4 14 // 1000 ohms = too cold (0C)
jzeeff 0:24cdf76455c4 15 // 1520 ohms = too hot (136C)
jzeeff 0:24cdf76455c4 16
jzeeff 4:3d661b485d59 17 // note: assume a precise 2.2K divider resistor, a PT1000 RTD and a 16 bit A/D result
jzeeff 2:22d9c714b511 18 // use this formula: A/D = (RTD_OHMS/(RTD_OHMS+2200)) * 65536
jzeeff 0:24cdf76455c4 19
jzeeff 1:b5abc8ddd567 20 // desired A/D value for boiler temp while idling
jzeeff 4:3d661b485d59 21 // note: there is usually some offset between boiler wall temp sensors and actual water temp (10-15C?)
jzeeff 4:3d661b485d59 22 #define TARGET_OHMS 1400 // Desired PT1000 RTD Ohms - CHANGE THIS
jzeeff 1:b5abc8ddd567 23
jzeeff 4:3d661b485d59 24 // Table of adjustments (degrees C) to TARGET_TEMP and heat vs time (seconds) into brew cycle (including preheat period)
jzeeff 2:22d9c714b511 25 // The idea is that extra heat is needed as cool water comes into the boiler during brew.
jzeeff 2:22d9c714b511 26 // Extra heat is provided by a higher than normal boiler wall temp.
jzeeff 3:eb60e36b03f6 27 // NOTE: the fractional portion of the value is used as the PWM value to be applied if more heat is needed.
jzeeff 2:22d9c714b511 28 // This can prevent overshoot.
jzeeff 3:eb60e36b03f6 29 // Example: 5.3 means that the boiler wall should be 5 degrees C above normal at this time point. If not, apply 30% power.
jzeeff 3:eb60e36b03f6 30 // Example: 99.99 means (roughly) that the heater should be completely on for the 1 second period
jzeeff 4:3d661b485d59 31 // Note: heat on a Gaggia Classic takes about 4 seconds before it is seen by the sensor
jzeeff 4:3d661b485d59 32
jzeeff 4:3d661b485d59 33 #define BREW_TIME 44 // max brew time
jzeeff 4:3d661b485d59 34 #define BREW_PREHEAT 6 // max preheat time
jzeeff 2:22d9c714b511 35
jzeeff 4:3d661b485d59 36 const double table[BREW_TIME+BREW_PREHEAT] = {
jzeeff 4:3d661b485d59 37 // preheat up to 6 seconds
jzeeff 4:3d661b485d59 38 0,0,0,0,99.99,99.99, // CHANGE THIS
jzeeff 0:24cdf76455c4 39 // brewing (pump is on) up to 30 seconds
jzeeff 4:3d661b485d59 40 0,0,0,0,0,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.40,99.35,99.35, // CHANGE THIS
jzeeff 4:3d661b485d59 41 99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35, // CHANGE THIS
jzeeff 4:3d661b485d59 42 99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35
jzeeff 3:eb60e36b03f6 43 };
jzeeff 3:eb60e36b03f6 44
jzeeff 4:3d661b485d59 45 // pump power over time for flush or preinfusion or pressure profiling
jzeeff 4:3d661b485d59 46 const double pump_table[BREW_TIME+BREW_PREHEAT] = {
jzeeff 3:eb60e36b03f6 47 // during pre-brew period
jzeeff 4:3d661b485d59 48 0,0,0,0,.80,.80, // CHANGE THIS
jzeeff 3:eb60e36b03f6 49 // brewing up to 30 seconds
jzeeff 4:3d661b485d59 50 .85,.90,1.0,0,0,0,0,0,.80,1.0,1.0,1.0,1.0,1.0,1.0,1.0, // CHANGE THIS
jzeeff 4:3d661b485d59 51 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, // CHANGE THIS
jzeeff 4:3d661b485d59 52 .85,.85,.85,.85,.85,.85,.85,.85,.85,.85
jzeeff 4:3d661b485d59 53 };
jzeeff 4:3d661b485d59 54
jzeeff 4:3d661b485d59 55 // desired total weight of espresso over brew period in grams
jzeeff 4:3d661b485d59 56 const int scale_table[BREW_TIME+BREW_PREHEAT] = {
jzeeff 4:3d661b485d59 57 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,
jzeeff 4:3d661b485d59 58 60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60
jzeeff 3:eb60e36b03f6 59 };
jzeeff 0:24cdf76455c4 60
jzeeff 1:b5abc8ddd567 61 // these probably don't need to be changed if you are using a Gaggia Classic
jzeeff 4:3d661b485d59 62 #define AD_PER_DEGREE 43 // how many A/D counts equal a 1 degree C change
jzeeff 4:3d661b485d59 63 #define AD_PER_GRAM 76.75 // how many A/D count equal 1 gram of weight
jzeeff 3:eb60e36b03f6 64 #define CLOSE 60 // how close in A/D value before switching to learned value control
jzeeff 4:3d661b485d59 65 #define GAIN .01 // how fast to adjust (eg 1% percent per 2.7s control period)
jzeeff 3:eb60e36b03f6 66 #define INITIAL_POWER .03 // initial guess for steady state heater power needed (try .03 = 3%)
jzeeff 0:24cdf76455c4 67 #define MIN_TEMP 21000 // below this is an error
jzeeff 4:3d661b485d59 68 #define MAX_TEMP 29700 // above this is an error
jzeeff 3:eb60e36b03f6 69 #define STEAM_TEMP 28000 // boiler temp while steaming
jzeeff 4:3d661b485d59 70 #define ROOM_TEMP 21707 // A/D value at standard ambient room temp 23C
jzeeff 4:3d661b485d59 71 #define MAX_ROOM_TEMP (ROOM_TEMP + (10 * AD_PER_DEGREE)) // above this means ambient isn't valid
jzeeff 4:3d661b485d59 72 #define SLEEP_PERIOD (3*3600) // turn off heat after this many seconds
jzeeff 4:3d661b485d59 73 #define WAKEUP_TIME 12 // time in 0-23 hours, GMT to wake up. 99 to disable. Example: 12 for noon GMT
jzeeff 4:3d661b485d59 74
jzeeff 4:3d661b485d59 75 #define TARGET_TEMP ((TARGET_OHMS*65536)/(TARGET_OHMS+2200)) // how hot the boiler should be in A/D
jzeeff 0:24cdf76455c4 76 #define debug if (1) printf // use if (1) or if (0)
jzeeff 0:24cdf76455c4 77
jzeeff 3:eb60e36b03f6 78 #include "mbed.h"
jzeeff 3:eb60e36b03f6 79 #include "TSISensor.h" // touch sensor
jzeeff 3:eb60e36b03f6 80 #include "DS1307.h" // real-time clock
jzeeff 3:eb60e36b03f6 81 #include "FastPWM.h" // better PWM routine for pump control
jzeeff 4:3d661b485d59 82
jzeeff 4:3d661b485d59 83 #define BOILER 0
jzeeff 4:3d661b485d59 84 #define GROUP 1
jzeeff 4:3d661b485d59 85 #define SCALE 2
jzeeff 4:3d661b485d59 86
jzeeff 3:eb60e36b03f6 87 #define OFF 0
jzeeff 3:eb60e36b03f6 88 #define RED 1
jzeeff 3:eb60e36b03f6 89 #define GREEN 2
jzeeff 3:eb60e36b03f6 90 #define BLUE 3
jzeeff 3:eb60e36b03f6 91 #define WHITE 4
jzeeff 3:eb60e36b03f6 92 #define YELLOW 5
jzeeff 3:eb60e36b03f6 93 #define AQUA 6
jzeeff 3:eb60e36b03f6 94 #define PINK 7
jzeeff 3:eb60e36b03f6 95
jzeeff 3:eb60e36b03f6 96 #define ON 1
jzeeff 3:eb60e36b03f6 97 #define OFF 0
jzeeff 1:b5abc8ddd567 98
jzeeff 4:3d661b485d59 99 // pin assignments
jzeeff 3:eb60e36b03f6 100 DigitalOut heater(PTD7); // Solid State Relay - PTD6&7 have high drive capability
jzeeff 3:eb60e36b03f6 101 FastPWM pump(PTD4); // Solid State Relay - PTD4 can do PWM @ 10K hz
jzeeff 3:eb60e36b03f6 102 DigitalOut led_green(LED_GREEN);
jzeeff 4:3d661b485d59 103 I2C gI2c(PTE0, PTE1); // SDA, SCL - use pullups somewhere
jzeeff 3:eb60e36b03f6 104 RtcDs1307 rtclock(gI2c); // DS1307 is a real time clock chip
jzeeff 3:eb60e36b03f6 105 Serial pc(USBTX, USBRX); // Serial to pc connection
jzeeff 4:3d661b485d59 106 TSISensor tsi; // used as a brew start button
jzeeff 4:3d661b485d59 107 AnalogIn scale(PTC2); // A/D converter reads scale
jzeeff 4:3d661b485d59 108
jzeeff 3:eb60e36b03f6 109 void brew(void);
jzeeff 3:eb60e36b03f6 110 void led_color(int color);
jzeeff 4:3d661b485d59 111 unsigned read_temp(int device);
jzeeff 4:3d661b485d59 112 unsigned read_ad(AnalogIn adc);
jzeeff 3:eb60e36b03f6 113 void steam(int seconds);
jzeeff 3:eb60e36b03f6 114
jzeeff 3:eb60e36b03f6 115 unsigned ambient_temp; // room or water tank temp (startup)
jzeeff 3:eb60e36b03f6 116 double heat = INITIAL_POWER; // initial fractional heat needed while idle
jzeeff 4:3d661b485d59 117 unsigned boiler_log[BREW_TIME+BREW_PREHEAT]; // record boiler temp during brew
jzeeff 4:3d661b485d59 118 unsigned group_log[BREW_TIME+BREW_PREHEAT]; // record basket temp during brew
jzeeff 4:3d661b485d59 119 int scale_log[BREW_TIME+BREW_PREHEAT]; // record weight during brew
jzeeff 3:eb60e36b03f6 120
jzeeff 3:eb60e36b03f6 121 int main() // start of program
jzeeff 0:24cdf76455c4 122 {
jzeeff 0:24cdf76455c4 123 time_t prev_time = 0;
jzeeff 4:3d661b485d59 124
jzeeff 4:3d661b485d59 125 led_color(OFF);
jzeeff 4:3d661b485d59 126
jzeeff 4:3d661b485d59 127 wait(1); // let settle
jzeeff 4:3d661b485d59 128 ambient_temp = read_temp(BOILER); // save temp on startup
jzeeff 3:eb60e36b03f6 129
jzeeff 3:eb60e36b03f6 130 #if 0
jzeeff 4:3d661b485d59 131 DateTime compiled(__DATE__, __TIME__); // to set RT clock initially
jzeeff 4:3d661b485d59 132 rtclock.adjust(compiled);
jzeeff 4:3d661b485d59 133 #endif
jzeeff 4:3d661b485d59 134 DateTime dt = rtclock.now(); // check clock value
jzeeff 4:3d661b485d59 135 debug("RTC = %u/%u/%02u %2u:%02u:%02u\r\n"
jzeeff 4:3d661b485d59 136 ,dt.month(),dt.day(),dt.year()
jzeeff 4:3d661b485d59 137 ,dt.hour(),dt.minute(),dt.second());
jzeeff 4:3d661b485d59 138 set_time(0); // set active clock
jzeeff 3:eb60e36b03f6 139
jzeeff 4:3d661b485d59 140 debug("starting A/D value/temp = %u %u\r\n",ambient_temp,read_temp(GROUP));
jzeeff 4:3d661b485d59 141
jzeeff 4:3d661b485d59 142 pump = 0; // duty cycle.
jzeeff 4:3d661b485d59 143 pump.period_us(410); // period of PWM signal in us
jzeeff 4:3d661b485d59 144
jzeeff 3:eb60e36b03f6 145 if (pc.readable()) // clear any data on serial port
jzeeff 4:3d661b485d59 146 pc.getc();
jzeeff 4:3d661b485d59 147
jzeeff 3:eb60e36b03f6 148 if (ambient_temp < MAX_ROOM_TEMP)
jzeeff 4:3d661b485d59 149 steam(5 * 60); // do accelerated warmup by overheating for awhile
jzeeff 3:eb60e36b03f6 150
jzeeff 3:eb60e36b03f6 151 // loop forever, controlling boiler temperature
jzeeff 0:24cdf76455c4 152
jzeeff 0:24cdf76455c4 153 for (;;) {
jzeeff 0:24cdf76455c4 154 // read temp from A/D
jzeeff 0:24cdf76455c4 155 // note: in A/D counts, not degrees
jzeeff 4:3d661b485d59 156 unsigned temp = read_temp(BOILER);
jzeeff 3:eb60e36b03f6 157
jzeeff 0:24cdf76455c4 158 // bang/bang when far away, PWM to learned value when close
jzeeff 0:24cdf76455c4 159 if (temp > TARGET_TEMP + CLOSE) {
jzeeff 3:eb60e36b03f6 160 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 161 led_color(GREEN); // set LED to green
jzeeff 4:3d661b485d59 162 wait(.17);
jzeeff 0:24cdf76455c4 163 } else if (temp < TARGET_TEMP - CLOSE) {
jzeeff 3:eb60e36b03f6 164 heater = ON; // turn on heater
jzeeff 3:eb60e36b03f6 165 led_color(RED); // set LED to red
jzeeff 4:3d661b485d59 166 wait(.17);
jzeeff 0:24cdf76455c4 167 } else { // close to target temp
jzeeff 0:24cdf76455c4 168 // learning mode - adjust heat, the fraction of time power should be on
jzeeff 3:eb60e36b03f6 169
jzeeff 3:eb60e36b03f6 170 if (temp > TARGET_TEMP) // adjust best guess for % heat needed
jzeeff 3:eb60e36b03f6 171 heat *= (1-GAIN);
jzeeff 0:24cdf76455c4 172 else
jzeeff 3:eb60e36b03f6 173 heat *= (1+GAIN);
jzeeff 3:eb60e36b03f6 174
jzeeff 3:eb60e36b03f6 175 heater = ON; // turn on heater for PWM
jzeeff 3:eb60e36b03f6 176 led_color(RED);
jzeeff 3:eb60e36b03f6 177 wait(heat * 2.7); // 1.7 to reduce interaction with 50/60Hz power
jzeeff 3:eb60e36b03f6 178 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 179 led_color(GREEN);
jzeeff 3:eb60e36b03f6 180 wait((1-heat) * 2.7); // total time is 2.7 seconds
jzeeff 0:24cdf76455c4 181 } // if
jzeeff 0:24cdf76455c4 182
jzeeff 0:24cdf76455c4 183 // the user must press a button 10 seconds prior to brewing to start preheat
jzeeff 4:3d661b485d59 184 if (tsi.readPercentage() > .5) {
jzeeff 0:24cdf76455c4 185 brew();
jzeeff 4:3d661b485d59 186 set_time(0); // stay awake for awhile more
jzeeff 4:3d661b485d59 187 }
jzeeff 4:3d661b485d59 188
jzeeff 3:eb60e36b03f6 189 // if they signaled for steam
jzeeff 4:3d661b485d59 190 //if (tsi.readPercentage() > .1 && tsi.readPercentage() < .5)
jzeeff 4:3d661b485d59 191 // steam(120);
jzeeff 4:3d661b485d59 192
jzeeff 4:3d661b485d59 193 if (pc.readable()) { // Check if data is available on serial port.
jzeeff 4:3d661b485d59 194 pc.getc();
jzeeff 4:3d661b485d59 195 // debug, print out brew temp log
jzeeff 3:eb60e36b03f6 196 int i;
jzeeff 4:3d661b485d59 197 for (i = 0; i < BREW_TIME+BREW_PREHEAT; ++i)
jzeeff 4:3d661b485d59 198 printf("log %d: %u %u %d\r\n",i,boiler_log[i],group_log[i],scale_log[i]);
jzeeff 3:eb60e36b03f6 199 } // if
jzeeff 3:eb60e36b03f6 200
jzeeff 4:3d661b485d59 201 // check for idle shutdown, sleep till tomorrow am if it occurs
jzeeff 4:3d661b485d59 202 if (time(NULL) > SLEEP_PERIOD) { // save power
jzeeff 4:3d661b485d59 203 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 204 led_color(OFF);
jzeeff 3:eb60e36b03f6 205 printf("sleep\r\n");
jzeeff 4:3d661b485d59 206
jzeeff 4:3d661b485d59 207 for (;;) { // loop till wakeup in the morning
jzeeff 4:3d661b485d59 208 DateTime dt;
jzeeff 4:3d661b485d59 209
jzeeff 4:3d661b485d59 210 if (pc.readable()) // user wakeup
jzeeff 4:3d661b485d59 211 break;
jzeeff 4:3d661b485d59 212
jzeeff 4:3d661b485d59 213 dt = rtclock.now(); // read real time clock
jzeeff 4:3d661b485d59 214 if (dt.hour() == WAKEUP_TIME && dt.minute() == 0) // GMT time to wake up
jzeeff 4:3d661b485d59 215 break;
jzeeff 4:3d661b485d59 216
jzeeff 4:3d661b485d59 217 wait(30);
jzeeff 4:3d661b485d59 218 } // for
jzeeff 4:3d661b485d59 219
jzeeff 4:3d661b485d59 220 set_time(0); // reset active timer
jzeeff 4:3d661b485d59 221 debug("exit idle\r\n");
jzeeff 4:3d661b485d59 222 ambient_temp = read_temp(BOILER); // save temp on startup
jzeeff 4:3d661b485d59 223 } // if
jzeeff 3:eb60e36b03f6 224
jzeeff 0:24cdf76455c4 225 // check for errors (incorrect boiler temp can be dangerous)
jzeeff 4:3d661b485d59 226 while (temp > MAX_TEMP || temp < MIN_TEMP) {
jzeeff 4:3d661b485d59 227 heater = OFF; // turn off heater
jzeeff 4:3d661b485d59 228 led_color(YELLOW); // set LED to indicate error
jzeeff 1:b5abc8ddd567 229 debug("error A/D = %u\r\n",temp);
jzeeff 4:3d661b485d59 230 wait(60);
jzeeff 4:3d661b485d59 231 temp = read_temp(BOILER);
jzeeff 0:24cdf76455c4 232 }
jzeeff 0:24cdf76455c4 233
jzeeff 4:3d661b485d59 234 if (time(NULL) > prev_time)
jzeeff 4:3d661b485d59 235 debug("A/D value = %u %u, heat = %F, scale = %u\r\n",temp,read_temp(GROUP),heat,read_ad(scale)); // once per second
jzeeff 1:b5abc8ddd567 236 prev_time = time(NULL);
jzeeff 0:24cdf76455c4 237
jzeeff 1:b5abc8ddd567 238 } // for (;;)
jzeeff 0:24cdf76455c4 239
jzeeff 0:24cdf76455c4 240 } // main()
jzeeff 0:24cdf76455c4 241
jzeeff 0:24cdf76455c4 242
jzeeff 0:24cdf76455c4 243 //=================================================================
jzeeff 0:24cdf76455c4 244 // This subroutine is called when the button is pressed, 10 seconds
jzeeff 4:3d661b485d59 245 // before the pump is started. It does both open loop and closed
jzeeff 3:eb60e36b03f6 246 // loop PWM power/heat control.
jzeeff 0:24cdf76455c4 247 //=================================================================
jzeeff 0:24cdf76455c4 248
jzeeff 0:24cdf76455c4 249 void brew(void)
jzeeff 0:24cdf76455c4 250 {
jzeeff 1:b5abc8ddd567 251 double adjust = 1; // default is no adjustment
jzeeff 3:eb60e36b03f6 252
jzeeff 2:22d9c714b511 253 // adjust for higher or lower tank temp (assumed to be equal to ambient at startup)
jzeeff 3:eb60e36b03f6 254 // add in "heat"??
jzeeff 3:eb60e36b03f6 255 //if (ambient_temp < MAX_ROOM_TEMP) // sanity check
jzeeff 3:eb60e36b03f6 256 // adjust = (double)(ROOM_TEMP - TARGET_TEMP) / (double)(ambient_temp - TARGET_TEMP);
jzeeff 4:3d661b485d59 257
jzeeff 3:eb60e36b03f6 258 led_color(WHITE);
jzeeff 4:3d661b485d59 259
jzeeff 4:3d661b485d59 260 unsigned brew_time; // in seconds
jzeeff 3:eb60e36b03f6 261 double pwm;
jzeeff 4:3d661b485d59 262 unsigned temp;
jzeeff 4:3d661b485d59 263 unsigned scale_zero = read_ad(scale);
jzeeff 4:3d661b485d59 264 int grams;
jzeeff 3:eb60e36b03f6 265
jzeeff 4:3d661b485d59 266 debug("preheat/brew start, adjust = %F, zero = %u\r\n", adjust,scale_zero);
jzeeff 3:eb60e36b03f6 267
jzeeff 4:3d661b485d59 268 for (brew_time = 0; brew_time < BREW_PREHEAT + BREW_TIME; ++brew_time) { // loop until end of brew
jzeeff 4:3d661b485d59 269
jzeeff 4:3d661b485d59 270 if (brew_time == BREW_PREHEAT) {
jzeeff 3:eb60e36b03f6 271 led_color(BLUE); // set LED color to blue for start brew/pump now
jzeeff 4:3d661b485d59 272 }
jzeeff 3:eb60e36b03f6 273
jzeeff 4:3d661b485d59 274 pump = pump_table[brew_time]; // duty cycle or on/off of pump for this period
jzeeff 4:3d661b485d59 275
jzeeff 4:3d661b485d59 276 pwm = table[brew_time] - (int)table[brew_time]; // decimal part only
jzeeff 3:eb60e36b03f6 277
jzeeff 4:3d661b485d59 278 temp = read_temp(BOILER);
jzeeff 3:eb60e36b03f6 279
jzeeff 2:22d9c714b511 280 // if too cold, apply the PWM value, if too hot, do nothing
jzeeff 4:3d661b485d59 281 if (temp < (TARGET_TEMP + (table[brew_time] * AD_PER_DEGREE)) * adjust) {
jzeeff 4:3d661b485d59 282 if (pwm > 0.0 && pwm <= 1.0) {
jzeeff 4:3d661b485d59 283 heater = ON;
jzeeff 4:3d661b485d59 284 wait(pwm);
jzeeff 4:3d661b485d59 285 heater = OFF;
jzeeff 4:3d661b485d59 286 pwm = 1 - pwm;
jzeeff 4:3d661b485d59 287 if (pwm > 0.0 && pwm <= 1.0)
jzeeff 4:3d661b485d59 288 wait(pwm);
jzeeff 4:3d661b485d59 289 } else
jzeeff 4:3d661b485d59 290 wait(1.0);
jzeeff 4:3d661b485d59 291 } else
jzeeff 4:3d661b485d59 292 wait(1.0);
jzeeff 3:eb60e36b03f6 293
jzeeff 4:3d661b485d59 294 group_log[brew_time] = read_temp(GROUP); // record group temp
jzeeff 4:3d661b485d59 295 boiler_log[brew_time] = temp; // record boiler temp
jzeeff 4:3d661b485d59 296 grams = ((double)read_ad(scale) - scale_zero) / AD_PER_GRAM;
jzeeff 4:3d661b485d59 297 scale_log[brew_time] = grams;
jzeeff 4:3d661b485d59 298
jzeeff 4:3d661b485d59 299 if (grams < 2) // scale clock only starts when it hits two grams
jzeeff 4:3d661b485d59 300 scale_time = 0;
jzeeff 4:3d661b485d59 301 else
jzeeff 4:3d661b485d59 302 ++scale_time;
jzeeff 4:3d661b485d59 303
jzeeff 4:3d661b485d59 304 //if (grams > scale_table[scale_time])
jzeeff 4:3d661b485d59 305 //else
jzeeff 4:3d661b485d59 306
jzeeff 4:3d661b485d59 307 //debug("target temp %u = %F, temp = %u %u\r\n",brew_time,table[brew_time],read_temp(BOILER),read_temp(GROUP));
jzeeff 4:3d661b485d59 308
jzeeff 4:3d661b485d59 309 // early exit if final weight reached
jzeeff 4:3d661b485d59 310 if (grams >= scale_table[BREW_TIME+BREW_PREHEAT-1])
jzeeff 4:3d661b485d59 311 break;
jzeeff 4:3d661b485d59 312
jzeeff 4:3d661b485d59 313 // early exit based on user input
jzeeff 4:3d661b485d59 314 if (tsi.readPercentage() > .1 && tsi.readPercentage() < .5)
jzeeff 4:3d661b485d59 315 break;
jzeeff 0:24cdf76455c4 316
jzeeff 1:b5abc8ddd567 317 } // for
jzeeff 3:eb60e36b03f6 318
jzeeff 4:3d661b485d59 319 // shut down
jzeeff 4:3d661b485d59 320 led_color(OFF);
jzeeff 0:24cdf76455c4 321 debug("brew done\r\n");
jzeeff 4:3d661b485d59 322 pump = OFF;
jzeeff 4:3d661b485d59 323 heater = OFF;
jzeeff 3:eb60e36b03f6 324 } // brew()
jzeeff 0:24cdf76455c4 325
jzeeff 3:eb60e36b03f6 326 //===========================================================
jzeeff 3:eb60e36b03f6 327 // control to a higher steam temperature for n seconds
jzeeff 3:eb60e36b03f6 328 //===========================================================
jzeeff 3:eb60e36b03f6 329
jzeeff 3:eb60e36b03f6 330 void steam(int seconds)
jzeeff 3:eb60e36b03f6 331 {
jzeeff 3:eb60e36b03f6 332 unsigned start_time = time(NULL);
jzeeff 3:eb60e36b03f6 333
jzeeff 3:eb60e36b03f6 334 debug("steam start, time = %d\r\n", seconds);
jzeeff 3:eb60e36b03f6 335
jzeeff 3:eb60e36b03f6 336 while (time(NULL) - start_time < seconds) {
jzeeff 4:3d661b485d59 337 if (read_temp(BOILER) > STEAM_TEMP) {
jzeeff 4:3d661b485d59 338 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 339 led_color(AQUA); // set LED to aqua
jzeeff 3:eb60e36b03f6 340 } else {
jzeeff 4:3d661b485d59 341 heater = ON; // turn on heater
jzeeff 3:eb60e36b03f6 342 led_color(PINK); // set LED to pink
jzeeff 3:eb60e36b03f6 343 }
jzeeff 4:3d661b485d59 344
jzeeff 4:3d661b485d59 345 if (tsi.readPercentage() > .5) // abort steam
jzeeff 4:3d661b485d59 346 break;
jzeeff 4:3d661b485d59 347
jzeeff 3:eb60e36b03f6 348 } // while
jzeeff 3:eb60e36b03f6 349
jzeeff 3:eb60e36b03f6 350 heater = OFF; // turn off
jzeeff 3:eb60e36b03f6 351
jzeeff 3:eb60e36b03f6 352 } // steam()
jzeeff 0:24cdf76455c4 353
jzeeff 0:24cdf76455c4 354
jzeeff 0:24cdf76455c4 355 // =============================================
jzeeff 0:24cdf76455c4 356 // set multi color LED state
jzeeff 0:24cdf76455c4 357 // =============================================
jzeeff 0:24cdf76455c4 358
jzeeff 0:24cdf76455c4 359 DigitalOut r (LED_RED);
jzeeff 0:24cdf76455c4 360 DigitalOut g (LED_GREEN);
jzeeff 0:24cdf76455c4 361 DigitalOut b (LED_BLUE);
jzeeff 0:24cdf76455c4 362
jzeeff 3:eb60e36b03f6 363 void led_color(int color)
jzeeff 0:24cdf76455c4 364 {
jzeeff 0:24cdf76455c4 365 // turn off
jzeeff 3:eb60e36b03f6 366 r = g = b = 1;
jzeeff 0:24cdf76455c4 367
jzeeff 3:eb60e36b03f6 368 switch (color) {
jzeeff 3:eb60e36b03f6 369 case OFF:
jzeeff 3:eb60e36b03f6 370 break;
jzeeff 3:eb60e36b03f6 371 case GREEN:
jzeeff 3:eb60e36b03f6 372 g = 0;
jzeeff 3:eb60e36b03f6 373 break;
jzeeff 3:eb60e36b03f6 374 case BLUE:
jzeeff 3:eb60e36b03f6 375 b = 0;
jzeeff 3:eb60e36b03f6 376 break;
jzeeff 3:eb60e36b03f6 377 case RED:
jzeeff 3:eb60e36b03f6 378 r = 0;
jzeeff 3:eb60e36b03f6 379 break;
jzeeff 3:eb60e36b03f6 380 case YELLOW:
jzeeff 3:eb60e36b03f6 381 r = g = 0;
jzeeff 3:eb60e36b03f6 382 break;
jzeeff 3:eb60e36b03f6 383 case AQUA:
jzeeff 3:eb60e36b03f6 384 b = g = 0;
jzeeff 3:eb60e36b03f6 385 break;
jzeeff 3:eb60e36b03f6 386 case PINK:
jzeeff 3:eb60e36b03f6 387 r = b = 0;
jzeeff 3:eb60e36b03f6 388 break;
jzeeff 3:eb60e36b03f6 389 case WHITE:
jzeeff 3:eb60e36b03f6 390 r = g = b = 0;
jzeeff 3:eb60e36b03f6 391 break;
jzeeff 3:eb60e36b03f6 392 } // switch
jzeeff 3:eb60e36b03f6 393
jzeeff 3:eb60e36b03f6 394 } // led_color()
jzeeff 0:24cdf76455c4 395
jzeeff 4:3d661b485d59 396 #if 1
jzeeff 4:3d661b485d59 397 // reduce noise by making unused A/D into digital outs
jzeeff 4:3d661b485d59 398 DigitalOut x1(PTB0);
jzeeff 4:3d661b485d59 399 DigitalOut x2(PTB1);
jzeeff 4:3d661b485d59 400 DigitalOut x3(PTB2);
jzeeff 4:3d661b485d59 401 DigitalOut x4(PTB3);
jzeeff 4:3d661b485d59 402 DigitalOut x5(PTE21);
jzeeff 4:3d661b485d59 403 DigitalOut x6(PTE23);
jzeeff 4:3d661b485d59 404 DigitalOut x7(PTD5);
jzeeff 4:3d661b485d59 405 DigitalOut x8(PTD6);
jzeeff 4:3d661b485d59 406 DigitalOut x9(PTD1);
jzeeff 4:3d661b485d59 407 DigitalOut x10(PTC0);
jzeeff 4:3d661b485d59 408 #endif
jzeeff 4:3d661b485d59 409
jzeeff 0:24cdf76455c4 410 //=======================================
jzeeff 4:3d661b485d59 411 // A/D routines
jzeeff 0:24cdf76455c4 412 //=======================================
jzeeff 0:24cdf76455c4 413
jzeeff 4:3d661b485d59 414 DigitalOut ad_power(PTB9); // used to turn on/off power to resistors
jzeeff 4:3d661b485d59 415
jzeeff 4:3d661b485d59 416 AnalogIn boiler(PTE20); // A/D converter reads temperature on boiler
jzeeff 4:3d661b485d59 417 AnalogIn group(PTE22); // A/D for group basket temp
jzeeff 4:3d661b485d59 418 AnalogIn vref(PTE29); // A/D for A/D power supply (ad_power)
jzeeff 4:3d661b485d59 419
jzeeff 4:3d661b485d59 420
jzeeff 4:3d661b485d59 421 unsigned read_ad(AnalogIn adc)
jzeeff 0:24cdf76455c4 422 {
jzeeff 3:eb60e36b03f6 423 uint32_t sum=0;
jzeeff 3:eb60e36b03f6 424 int i;
jzeeff 4:3d661b485d59 425
jzeeff 4:3d661b485d59 426 adc.read_u16(); // throw away one
jzeeff 4:3d661b485d59 427
jzeeff 4:3d661b485d59 428 #define COUNT 77 // number of samples to average
jzeeff 4:3d661b485d59 429
jzeeff 4:3d661b485d59 430 for (i = 0; i < COUNT; ++i) { // average multiple for more accuracy
jzeeff 4:3d661b485d59 431 uint16_t a, b, c;
jzeeff 1:b5abc8ddd567 432
jzeeff 4:3d661b485d59 433 a = adc.read_u16(); // take median of 3 values to filter noise
jzeeff 3:eb60e36b03f6 434 b = adc.read_u16();
jzeeff 3:eb60e36b03f6 435 c = adc.read_u16();
jzeeff 0:24cdf76455c4 436
jzeeff 3:eb60e36b03f6 437 if ((a >= b && a <= c) || (a >= c && a <= b)) sum += a;
jzeeff 3:eb60e36b03f6 438 else if ((b >= a && b <= c) || (b >= c && b <= a)) sum += b;
jzeeff 4:3d661b485d59 439 else sum += c;
jzeeff 4:3d661b485d59 440
jzeeff 3:eb60e36b03f6 441 } // for
jzeeff 1:b5abc8ddd567 442
jzeeff 4:3d661b485d59 443 return sum / COUNT;
jzeeff 4:3d661b485d59 444
jzeeff 4:3d661b485d59 445 } // read_temp()
jzeeff 4:3d661b485d59 446
jzeeff 4:3d661b485d59 447
jzeeff 4:3d661b485d59 448 // read a temperature in A/D counts
jzeeff 4:3d661b485d59 449 // adjust it for vref variations
jzeeff 4:3d661b485d59 450
jzeeff 4:3d661b485d59 451 unsigned read_temp(int device)
jzeeff 4:3d661b485d59 452 {
jzeeff 4:3d661b485d59 453 unsigned value;
jzeeff 4:3d661b485d59 454 unsigned max; // A/D reading for the vref supply voltage
jzeeff 3:eb60e36b03f6 455
jzeeff 4:3d661b485d59 456 // send power to analog resistors only when needed
jzeeff 4:3d661b485d59 457 // this limits self heating
jzeeff 4:3d661b485d59 458
jzeeff 4:3d661b485d59 459 ad_power = 1; // turn on supply voltage
jzeeff 4:3d661b485d59 460 max = read_ad(vref); // read supply voltage
jzeeff 4:3d661b485d59 461
jzeeff 4:3d661b485d59 462 if (device == BOILER)
jzeeff 4:3d661b485d59 463 value = (read_ad(boiler) * 65536) / max; // scale to vref
jzeeff 4:3d661b485d59 464 else
jzeeff 4:3d661b485d59 465 value = (read_ad(group) * 65536) / max; // scale to vref
jzeeff 4:3d661b485d59 466
jzeeff 4:3d661b485d59 467 ad_power = 0;
jzeeff 4:3d661b485d59 468
jzeeff 4:3d661b485d59 469 return value;
jzeeff 4:3d661b485d59 470 } // read_temp()
jzeeff 4:3d661b485d59 471
jzeeff 3:eb60e36b03f6 472
jzeeff 3:eb60e36b03f6 473
jzeeff 3:eb60e36b03f6 474
jzeeff 3:eb60e36b03f6 475
jzeeff 4:3d661b485d59 476