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

main.cpp

Committer:
jzeeff
Date:
2013-08-07
Revision:
0:24cdf76455c4
Child:
1:b5abc8ddd567

File content as of revision 0:24cdf76455c4:


// Program to control espresso maker boiler temperatures
// Used with a Gaggia Classic, FreeScale FRDM-KL25Z computer, PT1000 RTD, SSR
// Jon Zeeff, 2013
// Public Domain

#include "mbed.h"
#include "TSISensor.h"

DigitalOut ssr(PTA1);
AnalogIn   adc(PTE20);

#define OFF 0
#define RED 1
#define GREEN 2
#define BLUE 3
#define WHITE 4
#define YELLOW 5

// RTD ohms
// 1360 ohms = 94C
// 1000 ohms = too cold (0C)
// 1520 ohms = too hot (136C)

// note: assume a 2K divider resistor, a PT1000 RTD and a 16 bit A/D result
// use this formula (RTD_OHMS/(RTD_OHMS+2000)) * 65536

// desired A/D value for boiler temp
#define TARGET_TEMP 25600       // CHANGE THIS  

// table of % power level vs time in seconds into brew cycle (including preheat)
const int table[40] = {
    // preheat up to 10 seconds
    0,0,100,100,100,100,0,0,0,0,                   // CHANGE THIS
    // brewing (pump is on) up to 30 seconds
    65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,  // CHANGE THIS
    65,65,65,65,65,65,65,65,65,65,65,65,65,65,65
}; 

// these probably don't need to be changed
#define CLOSE 50                // how close in A/D value before switching to proportional control
#define INITIAL_POWER  .03      // initial guess for steady state power needed (try .03)
#define MIN_TEMP 21000          // below this is an error
#define MAX_TEMP 29000          // above this is an error
#define SLEEP_TIME 3600         // turn off heat after this many seconds
#define BREW_TIME 30            // max brew time
#define BREW_PREHEAT 10         // max preheat time

#define debug if (1) printf     // use if (1) or if (0)

void brew(void);
void set_color(int color);
unsigned read_ad(void);
   
int main()
{
    unsigned ambient_temp = read_ad();      // save temp on startup (future enhancements)
    time_t prev_time = 0;
    TSISensor tsi;                          // used as a start button
     
    set_time(0);                            // start clock at zero
   
    debug("starting A/D value/temp = %u\r\n",ambient_temp);
  
// loop forever, controlling boiler temperature

    for (;;) {
        unsigned temp;

        // read temp from A/D
        // note: in A/D counts, not degrees
        temp = read_ad();
       
        // bang/bang when far away, PWM to learned value when close
        if (temp > TARGET_TEMP + CLOSE) {
           ssr = 0;                // turn off heater
           set_color(GREEN);       // set LED to green
        } else if (temp < TARGET_TEMP - CLOSE) {
            ssr = 1;                         // turn on heater           
            set_color(RED);                  // set LED to red
        } else {   // close to target temp
            // learning mode - adjust heat, the fraction of time power should be on
            static double heat = INITIAL_POWER;     // initial fractional heat needed while idle
            
            if (temp > TARGET_TEMP)          // adjust best guess for % heat needed
                  heat *= .99;
            else
                  heat *= 1.01;
                  
            debug("learned heat = %F, temp = %u\r\n",heat, temp);
            ssr = 1;            // turn on heater for PWM
            set_color(RED);
            wait(heat);             
            ssr = 0;            // turn off heater
            set_color(GREEN);               
            wait(1-heat);       
        } // if

        // the user must press a button 10 seconds prior to brewing to start preheat
        if (tsi.readPercentage() > .5)
            brew();

        // check for idle, sleep till tomorrow if it occurs
        if (time(NULL) > SLEEP_TIME){   // save power
            static time_t wakeup_time = (24 * 60 * 60) - (20 * 60);  // 24 hours minus 20 min
            
            ssr = 0;                    // turn off heater
            set_color(OFF);           
            while (time(NULL) < wakeup_time)    // wait till tomorrow
                   wait(1);
            set_time(0);                        // clock runs zero to 24 hours
            wakeup_time = (24 * 60 * 60);       // no 15 min offset needed now
        }
      
        // check for errors (incorrect boiler temp can be dangerous)
        if (temp > MAX_TEMP || temp < MIN_TEMP) {
            ssr = 0;            // turn off heater
            set_color(YELLOW);  // set LED to indicate error
            for (;;);           // reset needed to exit this
        }

    if (time(NULL) > prev_time) debug("A/D value = %u\r\n",temp);
    prev_time = time(NULL);

    } // while

} // main()


//=================================================================
// This subroutine is called when the button is pressed, 10 seconds
// before the pump is started.  It does open loop power/temp control.
//=================================================================

void brew(void)
{
    time_t start_time = time(NULL);
    
    #define brew_time (time(NULL) - start_time)
 
    debug("preheat/brew start\r\n");
    set_color(WHITE);  
            
    while (brew_time < BREW_TIME + BREW_PREHEAT) {   // loop for 40 seconds

        if (brew_time >= BREW_PREHEAT)
           set_color(BLUE);    // set LED color to blue for start brew/pump now

        // PWM power level over .5 second
        ssr = 1;  // turn on heater
        wait(table[brew_time] / 200.0);
        ssr = 0;  // turn off heater
        wait((100 - table[brew_time]) / 200.0);
        
        debug("power level %u = %u %%, temp = %u\r\n",brew_time,table[brew_time],read_ad());

    } // while

    set_color(OFF);
    debug("brew done\r\n");

} // brew()


// =============================================
// set multi color LED state
// =============================================

DigitalOut r (LED_RED);
DigitalOut g (LED_GREEN);
DigitalOut b (LED_BLUE);

void set_color(int color)
{

// turn off
r = g = b = 1;

switch (color) {
    case OFF:
        break;
    case GREEN:
        g = 0;
        break;
    case BLUE:
        b = 0;
        break;
    case RED:
        r = 0;
        break;
    case YELLOW:
        r = g = 0;
        break;
    case WHITE:
        r = g = b = 0;
        break;
 }  // switch
 
 } // set_color()

//=======================================
// read A/D value from RTD
// median for accuracy
//=======================================

unsigned read_ad(void) 
{
unsigned a, b, c;

    a = adc.read_u16();
    b = adc.read_u16();
    c = adc.read_u16();
    
    if ((a >= b && a <= c) || (a >= c && a <= b)) return a;
    if ((b >= a && b <= c) || (b >= c && b <= a)) return b;
    return c;
    
}  // read_ad()