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:
Mon Apr 14 20:35:30 2014 +0000
Revision:
5:0393adfdd439
Parent:
4:3d661b485d59
Child:
6:56b205b46b42
Apr 2014

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 5:0393adfdd439 22 #define TARGET_OHMS 1400 // Desired PT1000 RTD Ohms / boiler temp - CHANGE THIS
jzeeff 5:0393adfdd439 23
jzeeff 5:0393adfdd439 24 #define BREW_TIME 44 // max brew time
jzeeff 5:0393adfdd439 25 #define BREW_PREHEAT 6 // max preheat time (when to open brew valve)
jzeeff 1:b5abc8ddd567 26
jzeeff 4:3d661b485d59 27 // Table of adjustments (degrees C) to TARGET_TEMP and heat vs time (seconds) into brew cycle (including preheat period)
jzeeff 2:22d9c714b511 28 // The idea is that extra heat is needed as cool water comes into the boiler during brew.
jzeeff 2:22d9c714b511 29 // Extra heat is provided by a higher than normal boiler wall temp.
jzeeff 3:eb60e36b03f6 30 // NOTE: the fractional portion of the value is used as the PWM value to be applied if more heat is needed.
jzeeff 2:22d9c714b511 31 // This can prevent overshoot.
jzeeff 3:eb60e36b03f6 32 // 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 33 // Example: 99.99 means (roughly) that the heater should be completely on for the 1 second period
jzeeff 4:3d661b485d59 34 // Note: heat on a Gaggia Classic takes about 4 seconds before it is seen by the sensor
jzeeff 4:3d661b485d59 35
jzeeff 5:0393adfdd439 36 const double table[BREW_TIME+BREW_PREHEAT] = { // CHANGE THIS
jzeeff 5:0393adfdd439 37 0,0,0,0, // nothing (pumo is off)
jzeeff 5:0393adfdd439 38 99.99,99.99, // step heat up before flow
jzeeff 5:0393adfdd439 39 0,0,0,0, // filling portafilter
jzeeff 5:0393adfdd439 40 0,99.35,99.35, // preinfusion
jzeeff 5:0393adfdd439 41 99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.40,99.35,99.35,
jzeeff 5:0393adfdd439 42 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,
jzeeff 4:3d661b485d59 43 99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35,99.35
jzeeff 3:eb60e36b03f6 44 };
jzeeff 3:eb60e36b03f6 45
jzeeff 5:0393adfdd439 46 // pump power over time for preinfusion/slow ramp/pressure profiling
jzeeff 5:0393adfdd439 47 // range: 0 to 1
jzeeff 5:0393adfdd439 48 const double pump_table[BREW_TIME+BREW_PREHEAT] = { // CHANGE THIS
jzeeff 5:0393adfdd439 49 0,0,0,0, // nothing (pump is off)
jzeeff 5:0393adfdd439 50 .45,.45, // hold low pressure until valve is opened
jzeeff 5:0393adfdd439 51 .45,.55,.65,.75, // ramp pressure up slowly and fill portafilter
jzeeff 5:0393adfdd439 52 0,0,0, // preinfusion delay
jzeeff 5:0393adfdd439 53 .75,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, // brew
jzeeff 4:3d661b485d59 54 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 5:0393adfdd439 55 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
jzeeff 4:3d661b485d59 56 };
jzeeff 4:3d661b485d59 57
jzeeff 5:0393adfdd439 58 // table for flow profiling
jzeeff 5:0393adfdd439 59 // desired flow rate of espresso in grams/sec for each second of brew
jzeeff 5:0393adfdd439 60 // starts when the total grams in the cup achieves the first entry, not time zero
jzeeff 5:0393adfdd439 61 const double scale_table[BREW_TIME+BREW_PREHEAT] = {
jzeeff 5:0393adfdd439 62 1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,
jzeeff 5:0393adfdd439 63 1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,
jzeeff 5:0393adfdd439 64 1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,
jzeeff 5:0393adfdd439 65 1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,
jzeeff 5:0393adfdd439 66 1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75
jzeeff 3:eb60e36b03f6 67 };
jzeeff 0:24cdf76455c4 68
jzeeff 5:0393adfdd439 69 #define END_GRAMS 30 // end when this many grams total (in 27 secs?)
jzeeff 5:0393adfdd439 70 #define FLOW_PERIOD 23.0 // desired shot duration (not used)
jzeeff 5:0393adfdd439 71 #define START_FLOW_PROF 14 // when (seconds) to start flow profiling
jzeeff 5:0393adfdd439 72
jzeeff 1:b5abc8ddd567 73 // these probably don't need to be changed if you are using a Gaggia Classic
jzeeff 4:3d661b485d59 74 #define AD_PER_DEGREE 43 // how many A/D counts equal a 1 degree C change
jzeeff 5:0393adfdd439 75 #define AD_PER_GRAM 76.70 // how many A/D count equal 1 gram of weight
jzeeff 3:eb60e36b03f6 76 #define CLOSE 60 // how close in A/D value before switching to learned value control
jzeeff 5:0393adfdd439 77 #define GAIN .01 // how fast to adjust heat(eg 1% percent per 2.7s control period)
jzeeff 3:eb60e36b03f6 78 #define INITIAL_POWER .03 // initial guess for steady state heater power needed (try .03 = 3%)
jzeeff 0:24cdf76455c4 79 #define MIN_TEMP 21000 // below this is an error
jzeeff 4:3d661b485d59 80 #define MAX_TEMP 29700 // above this is an error
jzeeff 3:eb60e36b03f6 81 #define STEAM_TEMP 28000 // boiler temp while steaming
jzeeff 4:3d661b485d59 82 #define ROOM_TEMP 21707 // A/D value at standard ambient room temp 23C
jzeeff 4:3d661b485d59 83 #define MAX_ROOM_TEMP (ROOM_TEMP + (10 * AD_PER_DEGREE)) // above this means ambient isn't valid
jzeeff 5:0393adfdd439 84 #define SLEEP_PERIOD (4*3600) // turn off heat after this many seconds
jzeeff 4:3d661b485d59 85 #define WAKEUP_TIME 12 // time in 0-23 hours, GMT to wake up. 99 to disable. Example: 12 for noon GMT
jzeeff 4:3d661b485d59 86
jzeeff 4:3d661b485d59 87 #define TARGET_TEMP ((TARGET_OHMS*65536)/(TARGET_OHMS+2200)) // how hot the boiler should be in A/D
jzeeff 0:24cdf76455c4 88 #define debug if (1) printf // use if (1) or if (0)
jzeeff 0:24cdf76455c4 89
jzeeff 3:eb60e36b03f6 90 #include "mbed.h"
jzeeff 3:eb60e36b03f6 91 #include "TSISensor.h" // touch sensor
jzeeff 3:eb60e36b03f6 92 #include "DS1307.h" // real-time clock
jzeeff 3:eb60e36b03f6 93 #include "FastPWM.h" // better PWM routine for pump control
jzeeff 4:3d661b485d59 94
jzeeff 4:3d661b485d59 95 #define BOILER 0
jzeeff 4:3d661b485d59 96 #define GROUP 1
jzeeff 4:3d661b485d59 97 #define SCALE 2
jzeeff 4:3d661b485d59 98
jzeeff 3:eb60e36b03f6 99 #define OFF 0
jzeeff 3:eb60e36b03f6 100 #define RED 1
jzeeff 3:eb60e36b03f6 101 #define GREEN 2
jzeeff 3:eb60e36b03f6 102 #define BLUE 3
jzeeff 3:eb60e36b03f6 103 #define WHITE 4
jzeeff 3:eb60e36b03f6 104 #define YELLOW 5
jzeeff 3:eb60e36b03f6 105 #define AQUA 6
jzeeff 3:eb60e36b03f6 106 #define PINK 7
jzeeff 3:eb60e36b03f6 107
jzeeff 3:eb60e36b03f6 108 #define ON 1
jzeeff 3:eb60e36b03f6 109 #define OFF 0
jzeeff 1:b5abc8ddd567 110
jzeeff 4:3d661b485d59 111 // pin assignments
jzeeff 3:eb60e36b03f6 112 DigitalOut heater(PTD7); // Solid State Relay - PTD6&7 have high drive capability
jzeeff 3:eb60e36b03f6 113 FastPWM pump(PTD4); // Solid State Relay - PTD4 can do PWM @ 10K hz
jzeeff 3:eb60e36b03f6 114 DigitalOut led_green(LED_GREEN);
jzeeff 4:3d661b485d59 115 I2C gI2c(PTE0, PTE1); // SDA, SCL - use pullups somewhere
jzeeff 3:eb60e36b03f6 116 RtcDs1307 rtclock(gI2c); // DS1307 is a real time clock chip
jzeeff 3:eb60e36b03f6 117 Serial pc(USBTX, USBRX); // Serial to pc connection
jzeeff 5:0393adfdd439 118 Serial bluetooth(PTC4,PTC3); // Serial via wireless TX,RX
jzeeff 4:3d661b485d59 119 TSISensor tsi; // used as a brew start button
jzeeff 4:3d661b485d59 120 AnalogIn scale(PTC2); // A/D converter reads scale
jzeeff 4:3d661b485d59 121
jzeeff 3:eb60e36b03f6 122 void brew(void);
jzeeff 3:eb60e36b03f6 123 void led_color(int color);
jzeeff 4:3d661b485d59 124 unsigned read_temp(int device);
jzeeff 5:0393adfdd439 125 int read_scale(void);
jzeeff 5:0393adfdd439 126 int read_scale2(void);
jzeeff 5:0393adfdd439 127 int read_scale3(void);
jzeeff 4:3d661b485d59 128 unsigned read_ad(AnalogIn adc);
jzeeff 3:eb60e36b03f6 129 void steam(int seconds);
jzeeff 5:0393adfdd439 130 inline int median(int a, int b, int c);
jzeeff 3:eb60e36b03f6 131
jzeeff 3:eb60e36b03f6 132 unsigned ambient_temp; // room or water tank temp (startup)
jzeeff 3:eb60e36b03f6 133 double heat = INITIAL_POWER; // initial fractional heat needed while idle
jzeeff 4:3d661b485d59 134 unsigned boiler_log[BREW_TIME+BREW_PREHEAT]; // record boiler temp during brew
jzeeff 4:3d661b485d59 135 unsigned group_log[BREW_TIME+BREW_PREHEAT]; // record basket temp during brew
jzeeff 4:3d661b485d59 136 int scale_log[BREW_TIME+BREW_PREHEAT]; // record weight during brew
jzeeff 3:eb60e36b03f6 137
jzeeff 5:0393adfdd439 138 uint16_t slog[5000];
jzeeff 5:0393adfdd439 139 int scount=0;
jzeeff 5:0393adfdd439 140
jzeeff 3:eb60e36b03f6 141 int main() // start of program
jzeeff 0:24cdf76455c4 142 {
jzeeff 0:24cdf76455c4 143 time_t prev_time = 0;
jzeeff 4:3d661b485d59 144
jzeeff 4:3d661b485d59 145 led_color(OFF);
jzeeff 5:0393adfdd439 146
jzeeff 5:0393adfdd439 147 wait(1); // let settle
jzeeff 4:3d661b485d59 148 ambient_temp = read_temp(BOILER); // save temp on startup
jzeeff 3:eb60e36b03f6 149
jzeeff 3:eb60e36b03f6 150 #if 0
jzeeff 4:3d661b485d59 151 DateTime compiled(__DATE__, __TIME__); // to set RT clock initially
jzeeff 4:3d661b485d59 152 rtclock.adjust(compiled);
jzeeff 4:3d661b485d59 153 #endif
jzeeff 4:3d661b485d59 154 DateTime dt = rtclock.now(); // check clock value
jzeeff 4:3d661b485d59 155 debug("RTC = %u/%u/%02u %2u:%02u:%02u\r\n"
jzeeff 4:3d661b485d59 156 ,dt.month(),dt.day(),dt.year()
jzeeff 4:3d661b485d59 157 ,dt.hour(),dt.minute(),dt.second());
jzeeff 4:3d661b485d59 158 set_time(0); // set active clock
jzeeff 5:0393adfdd439 159
jzeeff 4:3d661b485d59 160 debug("starting A/D value/temp = %u %u\r\n",ambient_temp,read_temp(GROUP));
jzeeff 4:3d661b485d59 161
jzeeff 4:3d661b485d59 162 pump = 0; // duty cycle.
jzeeff 4:3d661b485d59 163 pump.period_us(410); // period of PWM signal in us
jzeeff 4:3d661b485d59 164
jzeeff 3:eb60e36b03f6 165 if (ambient_temp < MAX_ROOM_TEMP)
jzeeff 5:0393adfdd439 166 steam(7 * 60); // do accelerated warmup by overheating for awhile
jzeeff 3:eb60e36b03f6 167
jzeeff 3:eb60e36b03f6 168 // loop forever, controlling boiler temperature
jzeeff 0:24cdf76455c4 169
jzeeff 0:24cdf76455c4 170 for (;;) {
jzeeff 0:24cdf76455c4 171 // read temp from A/D
jzeeff 0:24cdf76455c4 172 // note: in A/D counts, not degrees
jzeeff 4:3d661b485d59 173 unsigned temp = read_temp(BOILER);
jzeeff 3:eb60e36b03f6 174
jzeeff 0:24cdf76455c4 175 // bang/bang when far away, PWM to learned value when close
jzeeff 0:24cdf76455c4 176 if (temp > TARGET_TEMP + CLOSE) {
jzeeff 3:eb60e36b03f6 177 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 178 led_color(GREEN); // set LED to green
jzeeff 4:3d661b485d59 179 wait(.17);
jzeeff 0:24cdf76455c4 180 } else if (temp < TARGET_TEMP - CLOSE) {
jzeeff 3:eb60e36b03f6 181 heater = ON; // turn on heater
jzeeff 3:eb60e36b03f6 182 led_color(RED); // set LED to red
jzeeff 4:3d661b485d59 183 wait(.17);
jzeeff 0:24cdf76455c4 184 } else { // close to target temp
jzeeff 0:24cdf76455c4 185 // learning mode - adjust heat, the fraction of time power should be on
jzeeff 3:eb60e36b03f6 186
jzeeff 3:eb60e36b03f6 187 if (temp > TARGET_TEMP) // adjust best guess for % heat needed
jzeeff 3:eb60e36b03f6 188 heat *= (1-GAIN);
jzeeff 0:24cdf76455c4 189 else
jzeeff 3:eb60e36b03f6 190 heat *= (1+GAIN);
jzeeff 3:eb60e36b03f6 191
jzeeff 3:eb60e36b03f6 192 heater = ON; // turn on heater for PWM
jzeeff 3:eb60e36b03f6 193 led_color(RED);
jzeeff 3:eb60e36b03f6 194 wait(heat * 2.7); // 1.7 to reduce interaction with 50/60Hz power
jzeeff 3:eb60e36b03f6 195 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 196 led_color(GREEN);
jzeeff 3:eb60e36b03f6 197 wait((1-heat) * 2.7); // total time is 2.7 seconds
jzeeff 0:24cdf76455c4 198 } // if
jzeeff 0:24cdf76455c4 199
jzeeff 0:24cdf76455c4 200 // the user must press a button 10 seconds prior to brewing to start preheat
jzeeff 4:3d661b485d59 201 if (tsi.readPercentage() > .5) {
jzeeff 0:24cdf76455c4 202 brew();
jzeeff 4:3d661b485d59 203 set_time(0); // stay awake for awhile more
jzeeff 4:3d661b485d59 204 }
jzeeff 4:3d661b485d59 205
jzeeff 3:eb60e36b03f6 206 // if they signaled for steam
jzeeff 4:3d661b485d59 207 //if (tsi.readPercentage() > .1 && tsi.readPercentage() < .5)
jzeeff 4:3d661b485d59 208 // steam(120);
jzeeff 4:3d661b485d59 209
jzeeff 5:0393adfdd439 210 char key = 0;
jzeeff 5:0393adfdd439 211 if (pc.readable()) // Check if data is available on serial port.
jzeeff 5:0393adfdd439 212 key = pc.getc();
jzeeff 5:0393adfdd439 213
jzeeff 5:0393adfdd439 214 if (key == 'l') { // debug, print out brew temp log
jzeeff 3:eb60e36b03f6 215 int i;
jzeeff 4:3d661b485d59 216 for (i = 0; i < BREW_TIME+BREW_PREHEAT; ++i)
jzeeff 4:3d661b485d59 217 printf("log %d: %u %u %d\r\n",i,boiler_log[i],group_log[i],scale_log[i]);
jzeeff 5:0393adfdd439 218 for (i = 0; i < scount; ++i)
jzeeff 5:0393adfdd439 219 printf("%u\r\n",slog[i]);
jzeeff 3:eb60e36b03f6 220 } // if
jzeeff 3:eb60e36b03f6 221
jzeeff 5:0393adfdd439 222 if (key == 'p') { // cycle pump for flush
jzeeff 5:0393adfdd439 223 pump = 1;
jzeeff 5:0393adfdd439 224 wait(5);
jzeeff 5:0393adfdd439 225 pump = 0;
jzeeff 5:0393adfdd439 226 }
jzeeff 5:0393adfdd439 227
jzeeff 4:3d661b485d59 228 // check for idle shutdown, sleep till tomorrow am if it occurs
jzeeff 5:0393adfdd439 229 if (time(NULL) > SLEEP_PERIOD || key == 'i') { // save power
jzeeff 4:3d661b485d59 230 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 231 led_color(OFF);
jzeeff 3:eb60e36b03f6 232 printf("sleep\r\n");
jzeeff 5:0393adfdd439 233
jzeeff 4:3d661b485d59 234 for (;;) { // loop till wakeup in the morning
jzeeff 4:3d661b485d59 235 DateTime dt;
jzeeff 5:0393adfdd439 236
jzeeff 5:0393adfdd439 237 if (pc.readable() && pc.getc() == ' ') // user wakeup
jzeeff 5:0393adfdd439 238 break;
jzeeff 5:0393adfdd439 239
jzeeff 4:3d661b485d59 240 dt = rtclock.now(); // read real time clock
jzeeff 4:3d661b485d59 241 if (dt.hour() == WAKEUP_TIME && dt.minute() == 0) // GMT time to wake up
jzeeff 5:0393adfdd439 242 break;
jzeeff 5:0393adfdd439 243
jzeeff 4:3d661b485d59 244 wait(30);
jzeeff 4:3d661b485d59 245 } // for
jzeeff 5:0393adfdd439 246
jzeeff 4:3d661b485d59 247 set_time(0); // reset active timer
jzeeff 4:3d661b485d59 248 debug("exit idle\r\n");
jzeeff 4:3d661b485d59 249 ambient_temp = read_temp(BOILER); // save temp on startup
jzeeff 4:3d661b485d59 250 } // if
jzeeff 3:eb60e36b03f6 251
jzeeff 0:24cdf76455c4 252 // check for errors (incorrect boiler temp can be dangerous)
jzeeff 4:3d661b485d59 253 while (temp > MAX_TEMP || temp < MIN_TEMP) {
jzeeff 4:3d661b485d59 254 heater = OFF; // turn off heater
jzeeff 4:3d661b485d59 255 led_color(YELLOW); // set LED to indicate error
jzeeff 1:b5abc8ddd567 256 debug("error A/D = %u\r\n",temp);
jzeeff 4:3d661b485d59 257 wait(60);
jzeeff 4:3d661b485d59 258 temp = read_temp(BOILER);
jzeeff 0:24cdf76455c4 259 }
jzeeff 0:24cdf76455c4 260
jzeeff 5:0393adfdd439 261 if (time(NULL) > prev_time)
jzeeff 5:0393adfdd439 262 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 263 prev_time = time(NULL);
jzeeff 0:24cdf76455c4 264
jzeeff 1:b5abc8ddd567 265 } // for (;;)
jzeeff 0:24cdf76455c4 266
jzeeff 0:24cdf76455c4 267 } // main()
jzeeff 0:24cdf76455c4 268
jzeeff 0:24cdf76455c4 269
jzeeff 5:0393adfdd439 270 // turn off the heater
jzeeff 5:0393adfdd439 271 void heater_off(void)
jzeeff 5:0393adfdd439 272 {
jzeeff 5:0393adfdd439 273 heater = OFF;
jzeeff 5:0393adfdd439 274 }
jzeeff 5:0393adfdd439 275
jzeeff 0:24cdf76455c4 276 //=================================================================
jzeeff 5:0393adfdd439 277 // This subroutine is called when the button is pressed, n seconds
jzeeff 4:3d661b485d59 278 // before the pump is started. It does both open loop and closed
jzeeff 3:eb60e36b03f6 279 // loop PWM power/heat control.
jzeeff 0:24cdf76455c4 280 //=================================================================
jzeeff 0:24cdf76455c4 281
jzeeff 0:24cdf76455c4 282 void brew(void)
jzeeff 0:24cdf76455c4 283 {
jzeeff 1:b5abc8ddd567 284 double adjust = 1; // default is no adjustment
jzeeff 3:eb60e36b03f6 285
jzeeff 2:22d9c714b511 286 // adjust for higher or lower tank temp (assumed to be equal to ambient at startup)
jzeeff 5:0393adfdd439 287 // add in "heat" value as a measure of ambient??
jzeeff 3:eb60e36b03f6 288 //if (ambient_temp < MAX_ROOM_TEMP) // sanity check
jzeeff 3:eb60e36b03f6 289 // adjust = (double)(ROOM_TEMP - TARGET_TEMP) / (double)(ambient_temp - TARGET_TEMP);
jzeeff 5:0393adfdd439 290
jzeeff 3:eb60e36b03f6 291 led_color(WHITE);
jzeeff 4:3d661b485d59 292
jzeeff 5:0393adfdd439 293 unsigned brew_time; // seconds since start of brew
jzeeff 5:0393adfdd439 294 unsigned flow_time = 0; // clock that runs once flow starts
jzeeff 5:0393adfdd439 295 double target_pump = 1; // current value of pump power for desired flow
jzeeff 5:0393adfdd439 296 double grams;
jzeeff 5:0393adfdd439 297 double prev_grams = 0;
jzeeff 3:eb60e36b03f6 298
jzeeff 5:0393adfdd439 299 wait(.5); // stabilize
jzeeff 5:0393adfdd439 300 int scale_zero = read_scale2(); // weight of empty cup
jzeeff 5:0393adfdd439 301 debug("preheat/brew start, adjust = %f, scale zero = %u counts\r\n", adjust,scale_zero);
jzeeff 5:0393adfdd439 302
jzeeff 5:0393adfdd439 303 Timeout heater_timer; // used to schedule off time
jzeeff 5:0393adfdd439 304
jzeeff 5:0393adfdd439 305 Timer timer; // used to keep syncronized, 1 update per second
jzeeff 5:0393adfdd439 306 timer.start();
jzeeff 5:0393adfdd439 307
jzeeff 5:0393adfdd439 308 for (brew_time = 0; brew_time < BREW_PREHEAT + BREW_TIME;) { // loop until end of brew
jzeeff 5:0393adfdd439 309
jzeeff 4:3d661b485d59 310 if (brew_time == BREW_PREHEAT) {
jzeeff 3:eb60e36b03f6 311 led_color(BLUE); // set LED color to blue for start brew/pump now
jzeeff 4:3d661b485d59 312 }
jzeeff 3:eb60e36b03f6 313
jzeeff 5:0393adfdd439 314 // *** heat control
jzeeff 5:0393adfdd439 315 unsigned temp = read_temp(BOILER); // minimal time needed for this
jzeeff 2:22d9c714b511 316 // if too cold, apply the PWM value, if too hot, do nothing
jzeeff 5:0393adfdd439 317 if (temp < TARGET_TEMP + (table[brew_time] * AD_PER_DEGREE)) {
jzeeff 5:0393adfdd439 318 double pwm = table[brew_time] - (int)table[brew_time]; // decimal part of brew heat
jzeeff 5:0393adfdd439 319 // adjust?
jzeeff 5:0393adfdd439 320 if (pwm > 0.0 && pwm <= 1.0) {
jzeeff 4:3d661b485d59 321 heater = ON;
jzeeff 5:0393adfdd439 322 heater_timer.attach(&heater_off, pwm); // schedule turn off
jzeeff 5:0393adfdd439 323 } else
jzeeff 4:3d661b485d59 324 heater = OFF;
jzeeff 5:0393adfdd439 325 } // if
jzeeff 5:0393adfdd439 326
jzeeff 5:0393adfdd439 327 // *** pump power control
jzeeff 5:0393adfdd439 328
jzeeff 5:0393adfdd439 329 #define MIN_PUMP .4 // below this is effectively zero
jzeeff 5:0393adfdd439 330 grams = ((double)read_scale2() - scale_zero) / AD_PER_GRAM; // current espresso weight
jzeeff 5:0393adfdd439 331 if (grams < 0) // clip impossible result
jzeeff 5:0393adfdd439 332 grams = 0;
jzeeff 5:0393adfdd439 333 double delta_grams = grams - prev_grams;
jzeeff 5:0393adfdd439 334 if (delta_grams < 0)
jzeeff 5:0393adfdd439 335 delta_grams = 0;
jzeeff 5:0393adfdd439 336 prev_grams = grams;
jzeeff 5:0393adfdd439 337
jzeeff 5:0393adfdd439 338 if (brew_time >= START_FLOW_PROF) { // start flow profiling at specified time
jzeeff 5:0393adfdd439 339 ++flow_time; // seconds of significant flow
jzeeff 5:0393adfdd439 340 // adjust flow rate by changing pump power
jzeeff 5:0393adfdd439 341 // Proportional control
jzeeff 5:0393adfdd439 342 #define UP_GAIN .25
jzeeff 5:0393adfdd439 343 #define DOWN_GAIN 1.2
jzeeff 5:0393adfdd439 344
jzeeff 5:0393adfdd439 345 double error = (delta_grams - scale_table[flow_time]) / scale_table[flow_time];
jzeeff 5:0393adfdd439 346
jzeeff 5:0393adfdd439 347 if (error > 0)
jzeeff 5:0393adfdd439 348 target_pump /= 1 + (error * DOWN_GAIN); // too fast
jzeeff 5:0393adfdd439 349 else
jzeeff 5:0393adfdd439 350 target_pump += -error * UP_GAIN; // too slow
jzeeff 5:0393adfdd439 351
jzeeff 5:0393adfdd439 352 if (target_pump > pump_table[brew_time]) // clip to max allowed
jzeeff 5:0393adfdd439 353 target_pump = pump_table[brew_time];
jzeeff 5:0393adfdd439 354 else if (target_pump < 0) // clip to min
jzeeff 5:0393adfdd439 355 target_pump = 0;
jzeeff 4:3d661b485d59 356 } else
jzeeff 5:0393adfdd439 357 target_pump = pump_table[brew_time]; // use pump power profiling
jzeeff 5:0393adfdd439 358
jzeeff 5:0393adfdd439 359 pump = MIN_PUMP + (1 - MIN_PUMP) * target_pump; // use the flow profiling value
jzeeff 3:eb60e36b03f6 360
jzeeff 5:0393adfdd439 361 debug("time = %u %u, grams = %F, delta = %F, target_pump = %F\r\n",brew_time,flow_time,grams,delta_grams,target_pump);
jzeeff 5:0393adfdd439 362 //debug("target temp %u = %f, temp = %u %u\r\n",brew_time,table[brew_time],read_temp(BOILER),read_temp(GROUP));
jzeeff 5:0393adfdd439 363
jzeeff 5:0393adfdd439 364 // record values for debugging and graphing
jzeeff 4:3d661b485d59 365 group_log[brew_time] = read_temp(GROUP); // record group temp
jzeeff 4:3d661b485d59 366 boiler_log[brew_time] = temp; // record boiler temp
jzeeff 5:0393adfdd439 367 scale_log[brew_time] = grams; // record espresso weight
jzeeff 5:0393adfdd439 368
jzeeff 4:3d661b485d59 369 // early exit if final weight reached
jzeeff 5:0393adfdd439 370 if (grams >= END_GRAMS)
jzeeff 5:0393adfdd439 371 break;
jzeeff 5:0393adfdd439 372
jzeeff 5:0393adfdd439 373 // early exit based on user input (read twice for noise)
jzeeff 5:0393adfdd439 374 //if (brew_time > 10 && tsi.readPercentage() > .1 && tsi.readPercentage() < .5) {
jzeeff 5:0393adfdd439 375 // wait_ms(5);
jzeeff 5:0393adfdd439 376 // if (tsi.readPercentage() > .1 && tsi.readPercentage() < .5)
jzeeff 5:0393adfdd439 377 // break;
jzeeff 5:0393adfdd439 378 //}
jzeeff 5:0393adfdd439 379
jzeeff 5:0393adfdd439 380 // wait till next second
jzeeff 5:0393adfdd439 381 ++brew_time;
jzeeff 5:0393adfdd439 382 int ms = (brew_time * 1000) - timer.read_ms();
jzeeff 5:0393adfdd439 383 if (ms > 0)
jzeeff 5:0393adfdd439 384 wait_ms(ms);
jzeeff 5:0393adfdd439 385
jzeeff 5:0393adfdd439 386 // cleanup
jzeeff 5:0393adfdd439 387 heater = OFF; // should be off already, but just in case
jzeeff 5:0393adfdd439 388 heater_timer.detach(); // disable off timer
jzeeff 0:24cdf76455c4 389
jzeeff 1:b5abc8ddd567 390 } // for
jzeeff 3:eb60e36b03f6 391
jzeeff 4:3d661b485d59 392 // shut down
jzeeff 4:3d661b485d59 393 led_color(OFF);
jzeeff 4:3d661b485d59 394 pump = OFF;
jzeeff 4:3d661b485d59 395 heater = OFF;
jzeeff 5:0393adfdd439 396 debug("brew done, time = %u, grams = %f, target_pump = %F\r\n",brew_time, grams, target_pump);
jzeeff 5:0393adfdd439 397
jzeeff 3:eb60e36b03f6 398 } // brew()
jzeeff 0:24cdf76455c4 399
jzeeff 3:eb60e36b03f6 400 //===========================================================
jzeeff 3:eb60e36b03f6 401 // control to a higher steam temperature for n seconds
jzeeff 3:eb60e36b03f6 402 //===========================================================
jzeeff 3:eb60e36b03f6 403
jzeeff 3:eb60e36b03f6 404 void steam(int seconds)
jzeeff 3:eb60e36b03f6 405 {
jzeeff 3:eb60e36b03f6 406 unsigned start_time = time(NULL);
jzeeff 3:eb60e36b03f6 407
jzeeff 3:eb60e36b03f6 408 debug("steam start, time = %d\r\n", seconds);
jzeeff 3:eb60e36b03f6 409
jzeeff 3:eb60e36b03f6 410 while (time(NULL) - start_time < seconds) {
jzeeff 4:3d661b485d59 411 if (read_temp(BOILER) > STEAM_TEMP) {
jzeeff 4:3d661b485d59 412 heater = OFF; // turn off heater
jzeeff 3:eb60e36b03f6 413 led_color(AQUA); // set LED to aqua
jzeeff 3:eb60e36b03f6 414 } else {
jzeeff 4:3d661b485d59 415 heater = ON; // turn on heater
jzeeff 3:eb60e36b03f6 416 led_color(PINK); // set LED to pink
jzeeff 3:eb60e36b03f6 417 }
jzeeff 5:0393adfdd439 418
jzeeff 4:3d661b485d59 419 if (tsi.readPercentage() > .5) // abort steam
jzeeff 5:0393adfdd439 420 break;
jzeeff 5:0393adfdd439 421
jzeeff 3:eb60e36b03f6 422 } // while
jzeeff 3:eb60e36b03f6 423
jzeeff 3:eb60e36b03f6 424 heater = OFF; // turn off
jzeeff 3:eb60e36b03f6 425
jzeeff 3:eb60e36b03f6 426 } // steam()
jzeeff 0:24cdf76455c4 427
jzeeff 0:24cdf76455c4 428
jzeeff 0:24cdf76455c4 429 // =============================================
jzeeff 0:24cdf76455c4 430 // set multi color LED state
jzeeff 0:24cdf76455c4 431 // =============================================
jzeeff 0:24cdf76455c4 432
jzeeff 0:24cdf76455c4 433 DigitalOut r (LED_RED);
jzeeff 0:24cdf76455c4 434 DigitalOut g (LED_GREEN);
jzeeff 0:24cdf76455c4 435 DigitalOut b (LED_BLUE);
jzeeff 0:24cdf76455c4 436
jzeeff 3:eb60e36b03f6 437 void led_color(int color)
jzeeff 0:24cdf76455c4 438 {
jzeeff 0:24cdf76455c4 439 // turn off
jzeeff 3:eb60e36b03f6 440 r = g = b = 1;
jzeeff 0:24cdf76455c4 441
jzeeff 3:eb60e36b03f6 442 switch (color) {
jzeeff 3:eb60e36b03f6 443 case OFF:
jzeeff 3:eb60e36b03f6 444 break;
jzeeff 3:eb60e36b03f6 445 case GREEN:
jzeeff 3:eb60e36b03f6 446 g = 0;
jzeeff 3:eb60e36b03f6 447 break;
jzeeff 3:eb60e36b03f6 448 case BLUE:
jzeeff 3:eb60e36b03f6 449 b = 0;
jzeeff 3:eb60e36b03f6 450 break;
jzeeff 3:eb60e36b03f6 451 case RED:
jzeeff 3:eb60e36b03f6 452 r = 0;
jzeeff 3:eb60e36b03f6 453 break;
jzeeff 3:eb60e36b03f6 454 case YELLOW:
jzeeff 3:eb60e36b03f6 455 r = g = 0;
jzeeff 3:eb60e36b03f6 456 break;
jzeeff 3:eb60e36b03f6 457 case AQUA:
jzeeff 3:eb60e36b03f6 458 b = g = 0;
jzeeff 3:eb60e36b03f6 459 break;
jzeeff 3:eb60e36b03f6 460 case PINK:
jzeeff 3:eb60e36b03f6 461 r = b = 0;
jzeeff 3:eb60e36b03f6 462 break;
jzeeff 3:eb60e36b03f6 463 case WHITE:
jzeeff 3:eb60e36b03f6 464 r = g = b = 0;
jzeeff 3:eb60e36b03f6 465 break;
jzeeff 3:eb60e36b03f6 466 } // switch
jzeeff 3:eb60e36b03f6 467
jzeeff 3:eb60e36b03f6 468 } // led_color()
jzeeff 0:24cdf76455c4 469
jzeeff 4:3d661b485d59 470 #if 1
jzeeff 4:3d661b485d59 471 // reduce noise by making unused A/D into digital outs
jzeeff 4:3d661b485d59 472 DigitalOut x1(PTB0);
jzeeff 4:3d661b485d59 473 DigitalOut x2(PTB1);
jzeeff 4:3d661b485d59 474 DigitalOut x3(PTB2);
jzeeff 4:3d661b485d59 475 DigitalOut x4(PTB3);
jzeeff 4:3d661b485d59 476 DigitalOut x5(PTE21);
jzeeff 4:3d661b485d59 477 DigitalOut x6(PTE23);
jzeeff 4:3d661b485d59 478 DigitalOut x7(PTD5);
jzeeff 4:3d661b485d59 479 DigitalOut x8(PTD6);
jzeeff 4:3d661b485d59 480 DigitalOut x9(PTD1);
jzeeff 5:0393adfdd439 481 //DigitalOut x11(PTC3);
jzeeff 4:3d661b485d59 482 #endif
jzeeff 4:3d661b485d59 483
jzeeff 0:24cdf76455c4 484 //=======================================
jzeeff 4:3d661b485d59 485 // A/D routines
jzeeff 0:24cdf76455c4 486 //=======================================
jzeeff 0:24cdf76455c4 487
jzeeff 5:0393adfdd439 488 DigitalOut ad_power(PTB9); // used to turn on/off power to resistors (self heating)
jzeeff 5:0393adfdd439 489 AnalogIn boiler(PTE20); // A/D converter reads temperature on boiler
jzeeff 5:0393adfdd439 490 AnalogIn group(PTE22); // A/D for group basket temp
jzeeff 5:0393adfdd439 491 AnalogIn vref(PTE29); // A/D for A/D power supply (ad_power)
jzeeff 4:3d661b485d59 492
jzeeff 5:0393adfdd439 493 inline int median(int a, int b, int c)
jzeeff 5:0393adfdd439 494 {
jzeeff 5:0393adfdd439 495 if ((a >= b && a <= c) || (a >= c && a <= b)) return a;
jzeeff 5:0393adfdd439 496 else if ((b >= a && b <= c) || (b >= c && b <= a)) return b;
jzeeff 5:0393adfdd439 497 else return c;
jzeeff 5:0393adfdd439 498 } // median()
jzeeff 4:3d661b485d59 499
jzeeff 4:3d661b485d59 500
jzeeff 5:0393adfdd439 501 // heavily averaged A/D reading
jzeeff 5:0393adfdd439 502
jzeeff 4:3d661b485d59 503 unsigned read_ad(AnalogIn adc)
jzeeff 0:24cdf76455c4 504 {
jzeeff 3:eb60e36b03f6 505 uint32_t sum=0;
jzeeff 5:0393adfdd439 506
jzeeff 5:0393adfdd439 507 #define COUNT 77 // number of samples to average
jzeeff 1:b5abc8ddd567 508
jzeeff 5:0393adfdd439 509 for (int i = 0; i < COUNT; ++i) // average multiple for more accuracy
jzeeff 5:0393adfdd439 510 sum += median(adc.read_u16(),adc.read_u16(),adc.read_u16());
jzeeff 1:b5abc8ddd567 511
jzeeff 4:3d661b485d59 512 return sum / COUNT;
jzeeff 5:0393adfdd439 513
jzeeff 5:0393adfdd439 514 } // read_ad()
jzeeff 4:3d661b485d59 515
jzeeff 4:3d661b485d59 516
jzeeff 4:3d661b485d59 517 // read a temperature in A/D counts
jzeeff 4:3d661b485d59 518 // adjust it for vref variations
jzeeff 4:3d661b485d59 519
jzeeff 4:3d661b485d59 520 unsigned read_temp(int device)
jzeeff 4:3d661b485d59 521 {
jzeeff 5:0393adfdd439 522 unsigned value;
jzeeff 5:0393adfdd439 523 unsigned max; // A/D reading for the vref supply voltage
jzeeff 3:eb60e36b03f6 524
jzeeff 5:0393adfdd439 525 // send power to analog resistors only when needed
jzeeff 5:0393adfdd439 526 // this limits self heating
jzeeff 5:0393adfdd439 527
jzeeff 4:3d661b485d59 528 ad_power = 1; // turn on supply voltage
jzeeff 4:3d661b485d59 529 max = read_ad(vref); // read supply voltage
jzeeff 5:0393adfdd439 530
jzeeff 5:0393adfdd439 531 if (device == BOILER)
jzeeff 5:0393adfdd439 532 value = (read_ad(boiler) * 65536) / max; // scale to vref
jzeeff 4:3d661b485d59 533 else
jzeeff 5:0393adfdd439 534 value = (read_ad(group) * 65536) / max; // scale to vref
jzeeff 5:0393adfdd439 535
jzeeff 4:3d661b485d59 536 ad_power = 0;
jzeeff 4:3d661b485d59 537
jzeeff 4:3d661b485d59 538 return value;
jzeeff 5:0393adfdd439 539 } // read_temp()
jzeeff 3:eb60e36b03f6 540
jzeeff 3:eb60e36b03f6 541
jzeeff 5:0393adfdd439 542 // scale
jzeeff 5:0393adfdd439 543 #define FILTER .99
jzeeff 5:0393adfdd439 544 #define SLEW 10
jzeeff 5:0393adfdd439 545
jzeeff 5:0393adfdd439 546 // average scale value over 800 msec
jzeeff 5:0393adfdd439 547 // approx 6.6 samples/msec
jzeeff 5:0393adfdd439 548
jzeeff 5:0393adfdd439 549 int read_scale2()
jzeeff 5:0393adfdd439 550 {
jzeeff 5:0393adfdd439 551 int sum=0, count=0;
jzeeff 5:0393adfdd439 552 int raw, prev_raw;
jzeeff 5:0393adfdd439 553
jzeeff 5:0393adfdd439 554 Timer t;
jzeeff 5:0393adfdd439 555 t.start();
jzeeff 5:0393adfdd439 556 prev_raw = scale.read_u16();
jzeeff 5:0393adfdd439 557
jzeeff 5:0393adfdd439 558 scount = 0;
jzeeff 5:0393adfdd439 559
jzeeff 5:0393adfdd439 560 for (count = 0; t.read_ms() < 800; ++count) {
jzeeff 5:0393adfdd439 561 raw = scale.read_u16();
jzeeff 5:0393adfdd439 562
jzeeff 5:0393adfdd439 563 slog[scount] = raw; // log it
jzeeff 5:0393adfdd439 564 if (++scount >= 5000)
jzeeff 5:0393adfdd439 565 scount = 5000;
jzeeff 5:0393adfdd439 566
jzeeff 5:0393adfdd439 567 // clip to slew rate limits
jzeeff 5:0393adfdd439 568 if (raw > prev_raw + SLEW)
jzeeff 5:0393adfdd439 569 raw = prev_raw + SLEW;
jzeeff 5:0393adfdd439 570 else if (raw < prev_raw - SLEW)
jzeeff 5:0393adfdd439 571 raw = prev_raw - SLEW;
jzeeff 5:0393adfdd439 572
jzeeff 5:0393adfdd439 573 prev_raw = raw;
jzeeff 5:0393adfdd439 574
jzeeff 5:0393adfdd439 575 sum += raw;
jzeeff 5:0393adfdd439 576 } // for
jzeeff 5:0393adfdd439 577
jzeeff 5:0393adfdd439 578 return sum / count;
jzeeff 5:0393adfdd439 579 }
jzeeff 5:0393adfdd439 580
jzeeff 5:0393adfdd439 581 // take average of scale A/D max and min over multiple inflection points
jzeeff 5:0393adfdd439 582 // this reduces oscillation noise
jzeeff 5:0393adfdd439 583
jzeeff 5:0393adfdd439 584 int read_scale()
jzeeff 5:0393adfdd439 585 {
jzeeff 5:0393adfdd439 586 int value, prev_value=0, prev_prev_value, max1, max2, min;
jzeeff 5:0393adfdd439 587 unsigned tmp;
jzeeff 5:0393adfdd439 588 scount = 0;
jzeeff 5:0393adfdd439 589
jzeeff 5:0393adfdd439 590 Timer t;
jzeeff 5:0393adfdd439 591 Timer total;
jzeeff 5:0393adfdd439 592 t.start();
jzeeff 5:0393adfdd439 593 total.start();
jzeeff 5:0393adfdd439 594
jzeeff 5:0393adfdd439 595 // note: the effectiveness of this HF noise filter is highly dependent on sample rate
jzeeff 5:0393adfdd439 596
jzeeff 5:0393adfdd439 597 #define update_value() {\
jzeeff 5:0393adfdd439 598 tmp = scale.read_u16(); \
jzeeff 5:0393adfdd439 599 slog[scount++] = tmp; \
jzeeff 5:0393adfdd439 600 value = FILTER * value + (1.0-FILTER) * tmp; \
jzeeff 5:0393adfdd439 601 prev_prev_value = prev_value; \
jzeeff 5:0393adfdd439 602 prev_value = value; }
jzeeff 5:0393adfdd439 603
jzeeff 5:0393adfdd439 604 // get a good filtered value
jzeeff 5:0393adfdd439 605 value = scale.read_u16();
jzeeff 5:0393adfdd439 606 for (int i = 0; i < 50; ++i)
jzeeff 5:0393adfdd439 607 update_value();
jzeeff 5:0393adfdd439 608
jzeeff 5:0393adfdd439 609 // wait for upward slope
jzeeff 5:0393adfdd439 610 while (value < prev_value || prev_value < prev_prev_value)
jzeeff 5:0393adfdd439 611 update_value();
jzeeff 5:0393adfdd439 612
jzeeff 5:0393adfdd439 613 // delay for 2 msec
jzeeff 5:0393adfdd439 614 for (t.reset(); t.read_ms()< 2;)
jzeeff 5:0393adfdd439 615 update_value();
jzeeff 5:0393adfdd439 616
jzeeff 5:0393adfdd439 617 // find local max
jzeeff 5:0393adfdd439 618 for (;;) {
jzeeff 5:0393adfdd439 619 value = FILTER * value + (1.0-FILTER) * scale.read_u16(); // IIR filter
jzeeff 5:0393adfdd439 620 if (prev_value > value && prev_value > prev_prev_value) {
jzeeff 5:0393adfdd439 621 max1 = prev_value;
jzeeff 5:0393adfdd439 622 break;
jzeeff 5:0393adfdd439 623 }
jzeeff 5:0393adfdd439 624 prev_prev_value = prev_value;
jzeeff 5:0393adfdd439 625 prev_value = value;
jzeeff 5:0393adfdd439 626 } // for
jzeeff 5:0393adfdd439 627
jzeeff 5:0393adfdd439 628 // delay for 2 msec
jzeeff 5:0393adfdd439 629 for (t.reset(); t.read_ms()< 2;)
jzeeff 5:0393adfdd439 630 update_value();
jzeeff 5:0393adfdd439 631
jzeeff 5:0393adfdd439 632 // find local min
jzeeff 5:0393adfdd439 633 for (;;) {
jzeeff 5:0393adfdd439 634 value = FILTER * value + (1.0-FILTER) * scale.read_u16(); // IIR filter
jzeeff 5:0393adfdd439 635 if (prev_value < value && prev_value < prev_prev_value) {
jzeeff 5:0393adfdd439 636 min = prev_value;
jzeeff 5:0393adfdd439 637 break;
jzeeff 5:0393adfdd439 638 }
jzeeff 5:0393adfdd439 639 prev_prev_value = prev_value;
jzeeff 5:0393adfdd439 640 prev_value = value;
jzeeff 5:0393adfdd439 641 } // for
jzeeff 5:0393adfdd439 642
jzeeff 5:0393adfdd439 643 // delay for 2 msec
jzeeff 5:0393adfdd439 644 for (t.reset(); t.read_ms()< 2;)
jzeeff 5:0393adfdd439 645 update_value();
jzeeff 5:0393adfdd439 646
jzeeff 5:0393adfdd439 647 // find another max
jzeeff 5:0393adfdd439 648 for (;;) {
jzeeff 5:0393adfdd439 649 value = FILTER * value + (1.0-FILTER) * scale.read_u16(); // IIR filter
jzeeff 5:0393adfdd439 650 if (prev_value > value && prev_value > prev_prev_value) {
jzeeff 5:0393adfdd439 651 max2 = prev_value;
jzeeff 5:0393adfdd439 652 break;
jzeeff 5:0393adfdd439 653 }
jzeeff 5:0393adfdd439 654 prev_prev_value = prev_value;
jzeeff 5:0393adfdd439 655 prev_value = value;
jzeeff 5:0393adfdd439 656 } // for
jzeeff 5:0393adfdd439 657
jzeeff 5:0393adfdd439 658 //debug("read scale in %d msec, %d %d %d\r\n",total.read_ms(),max1, max2, min);
jzeeff 5:0393adfdd439 659
jzeeff 5:0393adfdd439 660 return (((max1 + max2) / 2) + min) / 2;
jzeeff 5:0393adfdd439 661 } // read_scale()
jzeeff 5:0393adfdd439 662
jzeeff 5:0393adfdd439 663 int read_scale3()
jzeeff 5:0393adfdd439 664 {
jzeeff 5:0393adfdd439 665 int max=0, min=65535;
jzeeff 5:0393adfdd439 666 int value, prev_raw;
jzeeff 5:0393adfdd439 667
jzeeff 5:0393adfdd439 668 scount = 0;
jzeeff 5:0393adfdd439 669
jzeeff 5:0393adfdd439 670 // note: the effectiveness of this HF noise filter is highly dependent on sample rate
jzeeff 5:0393adfdd439 671 // about 6.6 samples/msec
jzeeff 5:0393adfdd439 672
jzeeff 5:0393adfdd439 673 Timer t;
jzeeff 5:0393adfdd439 674 t.start();
jzeeff 5:0393adfdd439 675
jzeeff 5:0393adfdd439 676 prev_raw = value = scale.read_u16();
jzeeff 5:0393adfdd439 677
jzeeff 5:0393adfdd439 678 while (t.read_ms() < 40) { // 25 msec should always include a full oscillation
jzeeff 5:0393adfdd439 679 int raw = scale.read_u16();
jzeeff 5:0393adfdd439 680 slog[scount++] = raw;
jzeeff 5:0393adfdd439 681
jzeeff 5:0393adfdd439 682 // clip to slew rate limits
jzeeff 5:0393adfdd439 683 if (raw > prev_raw + SLEW)
jzeeff 5:0393adfdd439 684 raw = prev_raw + SLEW;
jzeeff 5:0393adfdd439 685 else if (raw < prev_raw - SLEW)
jzeeff 5:0393adfdd439 686 raw = prev_raw - SLEW;
jzeeff 5:0393adfdd439 687
jzeeff 5:0393adfdd439 688 prev_raw = raw;
jzeeff 5:0393adfdd439 689
jzeeff 5:0393adfdd439 690 value = FILTER * value + (1.0-FILTER) * raw;
jzeeff 5:0393adfdd439 691
jzeeff 5:0393adfdd439 692 if (scount > 50) { // only start after enough data
jzeeff 5:0393adfdd439 693 if (value > max)
jzeeff 5:0393adfdd439 694 max = value;
jzeeff 5:0393adfdd439 695 if (value < min)
jzeeff 5:0393adfdd439 696 min = value;
jzeeff 5:0393adfdd439 697 } // if
jzeeff 5:0393adfdd439 698 } // while
jzeeff 5:0393adfdd439 699
jzeeff 5:0393adfdd439 700 //debug("%d values, %d %d\r\n",scount,max,min);
jzeeff 5:0393adfdd439 701 return (max + min) / 2;
jzeeff 5:0393adfdd439 702
jzeeff 5:0393adfdd439 703 } // read_scale3()
jzeeff 3:eb60e36b03f6 704
jzeeff 3:eb60e36b03f6 705
jzeeff 5:0393adfdd439 706 // flow meter sends a pulse every .5 ml of flow
jzeeff 4:3d661b485d59 707
jzeeff 5:0393adfdd439 708 InterruptIn flow_meter(PTA4); // digital input pin
jzeeff 5:0393adfdd439 709 Timer flow_timer;
jzeeff 5:0393adfdd439 710 int flow_period; // time between pulses in usec
jzeeff 5:0393adfdd439 711
jzeeff 5:0393adfdd439 712 void flow_pulse() // interrupt routine
jzeeff 5:0393adfdd439 713 {
jzeeff 5:0393adfdd439 714 static int prev_time = 0;
jzeeff 5:0393adfdd439 715 int pulse_time = flow_timer.read_us();
jzeeff 5:0393adfdd439 716
jzeeff 5:0393adfdd439 717 flow_period = pulse_time - prev_time;
jzeeff 5:0393adfdd439 718 prev_time = pulse_time;
jzeeff 5:0393adfdd439 719 }
jzeeff 5:0393adfdd439 720
jzeeff 5:0393adfdd439 721 void flow_setup()
jzeeff 5:0393adfdd439 722 {
jzeeff 5:0393adfdd439 723 flow_timer.start();
jzeeff 5:0393adfdd439 724 flow_meter.rise(&flow_pulse); // attach the address of the flip function to the rising edge
jzeeff 5:0393adfdd439 725 } // flow_setup()