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:
Sat Jul 05 20:52:06 2014 +0000
Revision:
6:56b205b46b42
Parent:
5:0393adfdd439
Stable version

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