brew.ino 75.3 KB
Newer Older
João Lino's avatar
Rel.3  
João Lino committed
1 2 3 4 5
/*
  brew.ino - Main execution file.
  Created by João Lino, August 28, 2014.
  Released into the public domain.
*/
6

João Lino's avatar
João Lino committed
7 8
//#define DEBUG
#define INFO
João Lino's avatar
João Lino committed
9 10
//#define HEATING_ELEMENT_ALWAYS_OFF
//#define PUMP_ALWAYS_OFF
11

12
// ######################### LIBRARIES #########################
João Lino's avatar
João Lino committed
13
#include "brew.h"
14

15
// ######################### VARIABLES #########################
16
// ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++
17
eRotaryEncoderMode      rotaryEncoderMode;
João Lino's avatar
Rel.3  
João Lino committed
18 19 20 21 22 23

eCookingStages          cookingStage;
eBeerProfile            beerProfile;

eMenuType               eMenuType;

João Lino's avatar
João Lino committed
24 25 26 27 28 29
MenuData                mdMainMenu =            { ._title = MENU_MAIN_TITLE, ._dialog = MENU_MAIN_DIALOG, ._position = MENU_MAIN_INIT_POSITION, ._selection = MENU_MAIN_INIT_SELECTION, ._repaint = MENU_MAIN_INIT_REPAINT, ._selectionFunction = MENU_MAIN_FUNCTION };
MenuData                mdBeerProfileMenu =     { ._title = MENU_PROFILE_TITLE, ._dialog = MENU_PROFILE_DIALOG, ._position = MENU_PROFILE_INIT_POSITION, ._selection = MENU_PROFILE_INIT_SELECTION, ._repaint = MENU_PROFILE_INIT_REPAINT, ._selectionFunction = MENU_PROFILE_FUNCTION };
MenuData                mdStageMenu =           { ._title = MENU_STAGE_TITLE, ._dialog = MENU_STAGE_DIALOG, ._position = MENU_STAGE_INIT_POSITION, ._selection = MENU_STAGE_INIT_SELECTION, ._repaint = MENU_STAGE_INIT_REPAINT, ._selectionFunction = MENU_STAGE_FUNCTION };
MenuData                mdMaltMenu =            { ._title = MENU_MALT_TITLE, ._dialog = MENU_MALT_DIALOG, ._position = MENU_MALT_INIT_POSITION, ._selection = MENU_MALT_INIT_SELECTION, ._repaint = MENU_MALT_INIT_REPAINT, ._selectionFunction = MENU_MALT_FUNCTION };
MenuData                mdSettingsMenu =        { ._title = MENU_SETTINGS_TITLE, ._dialog = MENU_SETTINGS_DIALOG, ._position = MENU_SETTINGS_INIT_POSITION, ._selection = MENU_SETTINGS_INIT_SELECTION, ._repaint = MENU_SETTINGS_INIT_REPAINT, ._selectionFunction = MENU_SETTINGS_FUNCTION };
MenuData                mdStartFromStageMenu =  { ._title = MENU_START_TITLE, ._dialog = MENU_START_DIALOG, ._position = MENU_START_INIT_POSITION, ._selection = MENU_START_INIT_SELECTION, ._repaint = MENU_START_INIT_REPAINT, ._selectionFunction = MENU_START_FUNCTION };
João Lino's avatar
Rel.3  
João Lino committed
30

31
// ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
32 33
boolean                 cooking;

João Lino's avatar
Rel.3  
João Lino committed
34 35 36 37
unsigned long           clockStartTime;
unsigned long           clockLastUpdate;
long                    clockCounter;
unsigned long           clockIgnore;
João Lino's avatar
João Lino committed
38 39 40
boolean                 clockStart;
boolean                 clockEnd;

João Lino's avatar
Rel.3  
João Lino committed
41
unsigned long           cookTime;
João Lino's avatar
João Lino committed
42
int                     cookTemperature;
João Lino's avatar
Rel.3  
João Lino committed
43
int                     finalYield;
44

João Lino's avatar
Rel.3  
João Lino committed
45 46 47 48 49 50 51 52 53 54 55 56
unsigned long           startpointTime;
unsigned long           betaGlucanaseTime;
unsigned long           debranchingTime;
unsigned long           proteolyticTime;
unsigned long           betaAmylaseTime;
unsigned long           alphaAmylaseTime;
unsigned long           mashoutTime;
unsigned long           recirculationTime;
unsigned long           spargeTime;
unsigned long           boilTime;
unsigned long           coolingTime;
unsigned long           cleaningTime;
57

João Lino's avatar
João Lino committed
58 59 60 61 62 63 64 65 66 67 68
int                     startpointTemperature;
int                     betaGlucanaseTemperature;
int                     debranchingTemperature;
int                     proteolyticTemperature;
int                     betaAmylaseTemperature;
int                     alphaAmylaseTemperature;
int                     mashoutTemperature;
int                     recirculationTemperature;
int                     spargeTemperature;
int                     boilTemperature;
int                     coolingTemperature;
João Lino's avatar
Rel.3  
João Lino committed
69
int                     cleaningTemperature;
João Lino's avatar
João Lino committed
70 71 72

boolean                 refresh;
boolean                 repaint;
João Lino's avatar
João Lino committed
73
boolean                 cancel;
74

75
//boolean                 bStatusElement;
João Lino's avatar
Rel.3  
João Lino committed
76

João Lino's avatar
João Lino committed
77 78 79
unsigned long           loggingTimeInterval;


80
// ++++++++++++++++++++++++ Interrupts ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
81
static unsigned long    lastInterruptTime;
82

83
// ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
84 85 86 87 88
volatile int            rotaryEncoderVirtualPosition = 0;
volatile int            rotaryEncoderMaxPosition = 1;
volatile int            rotaryEncoderMinPosition = 0;
volatile int            rotaryEncoderSingleStep = 1;
volatile int            rotaryEncoderMultiStep = 1;
89

João Lino's avatar
João Lino committed
90 91
volatile boolean        onISR = false;

João Lino's avatar
João Lino committed
92 93
unsigned long           rotarySwDetectTime;

94
// ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++
95
HeatingElement          heatingElement(HEATING_ELEMENT_OUTPUT_PIN, LOW, HIGH);
96
/*
João Lino's avatar
João Lino committed
97 98 99
int                     iWindowSize;             // Time frame to operate in
unsigned long           windowStartTime;
double                  dWattPerPulse;
João Lino's avatar
João Lino committed
100
double                  dWattage;
101
*/
102

103
// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
104 105
Pump                    pump(PUMP_PIN, PUMP_SPEED_STOP_MOSFET, PUMP_SPEED_MAX_MOSFET, PUMP_TEMPERATURE_MAX_OPERATION, PUMP_PRIMING_TIME_IN_SECONDS);
//int                     iPumpSpeed;             // Time frame to operate in
106

107
// ######################### INITIALIZE #########################
108
// ++++++++++++++++++++++++ Library - LiquidCrystal_I2C ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
109
LiquidCrystal_I2C       lcd(LCD_I2C_ADDR, LCD_EN_PIN, LCD_RW_PIN, LCD_RS_PIN, LCD_D4_PIN, LCD_D5_PIN, LCD_D6_PIN, LCD_D7_PIN);
110

João Lino's avatar
João Lino committed
111
// +++++++++++++++++++++++ Temperature +++++++++++++++++++++++
112 113 114 115
Temperature                   basePT100("base",
                                        PT100_BASE_OUTPUT_PIN,
                                        PT100_BASE_INPUT_PIN,
                                        PT100_BASE_TIME_BETWEEN_READINGS,
João Lino's avatar
João Lino committed
116 117
                                        //2.0298, 2.0259, 665.24, 662.17);      // orig
                                        2.0208, 2.0281, 663.52, 665.49);
118 119 120 121
Temperature                   upPT100("up",
                                      PT100_UP_OUTPUT_PIN,
                                      PT100_UP_INPUT_PIN,
                                      PT100_UP_TIME_BETWEEN_READINGS,
João Lino's avatar
João Lino committed
122 123
                                      //2.0274, 2.0245, 659.43, 656.72);      // orig
                                      2.0279, 2.0264, 656.57, 659);
124 125 126 127
Temperature                   downPT100("down",
                                        PT100_DOWN_OUTPUT_PIN,
                                        PT100_DOWN_INPUT_PIN,
                                        PT100_DOWN_TIME_BETWEEN_READINGS,
João Lino's avatar
João Lino committed
128 129
                                        //2.0309, 2.0288, 659.15, 655.35);      // orig
                                        2.0157, 2.0428, 662.11, 662.85);
130 131

// ######################### INTERRUPTS #########################
João Lino's avatar
João Lino committed
132
void isr ()  {    // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK
133
  unsigned long interruptTime = millis();
João Lino's avatar
João Lino committed
134
  unsigned long diff = interruptTime - lastInterruptTime;
135

136
  // If interrupts come faster than [ROTARY_ENCODER_DEBOUNCE_TIME]ms, assume it's a bounce and ignore
João Lino's avatar
João Lino committed
137
  if (diff > ROTARY_ENCODER_DEBOUNCE_TIME) {
João Lino's avatar
João Lino committed
138
    lastInterruptTime = interruptTime;
139 140

    switch (rotaryEncoderMode) {
141 142
      // Input of rotary encoder controling menus
      case eRotaryEncoderMode_Menu: {
143
          if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
144
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
145 146
          }
          else {
147
            rotaryEncoderVirtualPosition = rotaryEncoderVirtualPosition - rotaryEncoderSingleStep;
148 149
          }
          if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
João Lino's avatar
João Lino committed
150
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
151 152
          }
          if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
João Lino's avatar
João Lino committed
153
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
154 155 156
          }

          break;
157
        }
158

159 160
      // Input of rotary encoder controling time variables
      case eRotaryEncoderMode_Time: {
161 162 163 164 165 166 167
          if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
            if (rotaryEncoderVirtualPosition >= 60) {
              rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderMultiStep);
            }
            else {
              rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
            }
168 169
          }
          else {
170 171
            if (rotaryEncoderVirtualPosition == rotaryEncoderMinPosition) {
              rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + 60);
172 173
            }
            else {
174 175 176 177 178 179
              if (rotaryEncoderVirtualPosition >= (60 + rotaryEncoderMultiStep)) {
                rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition - rotaryEncoderMultiStep);
              }
              else {
                rotaryEncoderVirtualPosition = rotaryEncoderVirtualPosition - rotaryEncoderSingleStep;
              }
180 181
            }
          }
182
          if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
183
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
184 185
          }
          if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
186
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
187 188 189
          }

          break;
190
        }
191

192 193
      // Input of rotary encoder controling generic integer variables within a range between rotaryEncoderMinPosition and rotaryEncoderMaxPosition
      case eRotaryEncoderMode_Generic: {
194 195 196 197 198 199 200
          if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
          }
          else {
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition - rotaryEncoderSingleStep);
          }
          if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
service-config's avatar
Mixer  
service-config committed
201
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
202 203
          }
          if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
204
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
205 206 207
          }

          break;
service-config's avatar
Mixer  
service-config committed
208
        }
209
      default: {
210 211

        }
212
    }
213

João Lino's avatar
João Lino committed
214 215
    repaint = true;
    refresh = true;
216 217 218
  }
}

219
void xSetupRotaryEncoder( eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep ) {
220 221 222 223 224 225
  if ( newMode >= 0 ) rotaryEncoderMode = newMode;
  if ( newPosition >= 0 ) rotaryEncoderVirtualPosition = newPosition;
  if ( newMaxPosition >= 0 ) rotaryEncoderMaxPosition = newMaxPosition;
  if ( newMinPosition >= 0 ) rotaryEncoderMinPosition = newMinPosition;
  if ( newSingleStep >= 0 ) rotaryEncoderSingleStep = newSingleStep;
  if ( newMultiStep >= 0 ) rotaryEncoderMultiStep = newMultiStep;
226 227
}

228
// ######################### START #########################
229
void xSafeHardwarePowerOff() {
230
  // Turn off gracefully
João Lino's avatar
João Lino committed
231 232
  pump.shutDown();
/*
João Lino's avatar
João Lino committed
233
  iPumpSpeed = PUMP_SPEED_STOP_MOSFET;
234
  xRegulatePumpSpeed();
João Lino's avatar
João Lino committed
235
*/
236 237

  // Force shutdown
João Lino's avatar
João Lino committed
238
//  analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET);  // analogWrite values from 0 to 255
239 240 241
  heatingElement.shutDown();                      // Turn heading element OFF for safety
  //digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, LOW);  // Turn heading element OFF for safety
  //bStatusElement = false;
João Lino's avatar
Rel.3  
João Lino committed
242

João Lino's avatar
João Lino committed
243 244 245
  basePT100.setPumpStatus( false );
  upPT100.setPumpStatus( false );
  downPT100.setPumpStatus( false );
246

João Lino's avatar
Rel.3  
João Lino committed
247
  //analogWrite(MIXER_PIN, 0);        // Turn mixer OFF for safety
248 249
}

João Lino's avatar
Rel.3  
João Lino committed
250 251 252
void xWelcomeUser() {
  //#ifndef DEBUG
  lcdPrint(&lcd, "  Let's start", "    Brewing!");    // Write welcome
253

254 255 256
  // Play Melody;
  sing(MELODY_SUPER_MARIO_START, PIEZO_PIN);

257
  //termometerCalibration();
João Lino's avatar
João Lino committed
258
  delay(SETTING_WELCOME_TIMEOUT);      // pause for effect
João Lino's avatar
Rel.3  
João Lino committed
259
  //#endif
260 261
}

262 263
const char *_dialogs[] = {"", "b"};

264
void setup() {
João Lino's avatar
João Lino committed
265
  // ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
266
  pinMode                         (ROTARY_ENCODER_CLK_PIN, INPUT);
João Lino's avatar
João Lino committed
267 268 269 270 271
  pinMode                         (ROTARY_ENCODER_DT_PIN, INPUT);
  pinMode                         (ROTARY_ENCODER_SW_PIN, INPUT);
  attachInterrupt                 (ROTARY_ENCODER_INTERRUPT_NUMBER, isr, FALLING);

  // ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++
272
  /*
João Lino's avatar
João Lino committed
273 274
  pinMode                         (HEATING_ELEMENT_OUTPUT_PIN, OUTPUT);
  digitalWrite                    (HEATING_ELEMENT_OUTPUT_PIN, LOW);
João Lino's avatar
Rel.3  
João Lino committed
275
  bStatusElement              =   false;
João Lino's avatar
João Lino committed
276 277
  windowStartTime             =   millis();
  dWattPerPulse               =   HEATING_ELEMENT_MAX_WATTAGE / HEATING_ELEMENT_AC_FREQUENCY_HZ;
João Lino's avatar
João Lino committed
278
  dWattage                    =   0.0;
279
  */
João Lino's avatar
João Lino committed
280 281 282
#ifdef HEATING_ELEMENT_ALWAYS_OFF
  heatingElement.setTesting(true);
#endif
João Lino's avatar
João Lino committed
283 284 285

  // ++++++++++++++++++++++++ Mixer ++++++++++++++++++++++++

286
  // ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
287 288
  //float (Temperature::*xCheckTemperature)();
  //xCheckTemperature = &(basePT100.getCurrentTemperature);
João Lino's avatar
João Lino committed
289 290 291 292

#ifdef PUMP_ALWAYS_OFF
  pump.setTesting(true);
#endif
João Lino's avatar
João Lino committed
293 294
  pump.setCheckTemperatureFunction(&basePT100);
  /*
295
  pinMode(PUMP_PIN, OUTPUT);   // sets the pin as output
João Lino's avatar
João Lino committed
296
  iPumpSpeed                  =   PUMP_SPEED_STOP_MOSFET;             // Time frame to operate in
297
  analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255
João Lino's avatar
João Lino committed
298
  */
299

João Lino's avatar
João Lino committed
300
  // ++++++++++++++++++++++++ Piezo ++++++++++++++++++++++++
301 302
  pinMode(PIEZO_PIN, OUTPUT);

João Lino's avatar
João Lino committed
303
  // ++++++++++++++++++++++++ Temperature Sensor PT100 ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
304

João Lino's avatar
João Lino committed
305 306 307 308 309
  // ++++++++++++++++++++++++ Serial Monitor ++++++++++++++++++++++++
  Serial.begin                    (SETTING_SERIAL_MONITOR_BAUD_RATE);    // setup terminal baud rate
  Serial.println                  (SETTING_SERIAL_MONITOR_WELCOME_MESSAGE);  // print a start message to the terminal

  // ++++++++++++++++++++++++ Library - LiquidCrystal_I2C ++++++++++++++++++++++++
310 311
  lcd.begin                       (LCD_HORIZONTAL_RESOLUTION, LCD_VERTICAL_RESOLUTION);   //  <<----- My LCD was 16x2
  lcd.setBacklightPin             (LCD_BACKLIGHT_PIN, POSITIVE);       // Setup backlight pin
João Lino's avatar
João Lino committed
312 313 314 315 316 317
  lcd.setBacklight                (HIGH);              // Switch on the backlight

  // ######################### INITIALIZE #########################
  // ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
  // set operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
  xSetupRotaryEncoder             ( eRotaryEncoderMode_Disabled, 0, 0, 0, 0, 0 );
João Lino's avatar
João Lino committed
318
  rotarySwDetectTime = 0;
João Lino's avatar
João Lino committed
319 320

  // ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
321
  eMenuType                   =   MENU_INIT;
João Lino's avatar
João Lino committed
322

João Lino's avatar
João Lino committed
323 324 325 326
  cookingStage                =   SETTING_COOKING_STAGE_INIT;
  beerProfile                 =   SETTING_BEER_PROFILE_INIT;

  cancel                      =   false;
João Lino's avatar
João Lino committed
327
  // ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++
João Lino's avatar
Rel.3  
João Lino committed
328

João Lino's avatar
João Lino committed
329
  cooking                     =   false;
330

João Lino's avatar
João Lino committed
331
  clockStartTime              =   0;
João Lino's avatar
Rel.3  
João Lino committed
332
  clockLastUpdate             =   0;
João Lino's avatar
João Lino committed
333 334 335 336
  clockCounter                =   0;
  clockIgnore                 =   0;
  clockStart                  =   false;
  clockEnd                    =   false;
337

João Lino's avatar
João Lino committed
338 339
  cookTime                    =   3600;
  cookTemperature             =   25;
João Lino's avatar
João Lino committed
340
  finalYield                  =   SETTING_MACHINE_YIELD_DEFAULT;
João Lino's avatar
João Lino committed
341

342 343 344 345 346 347 348 349 350 351 352
  startpointTime              =   PROFILE_TRIGO_STARTPOINT_TIME;
  betaGlucanaseTime           =   PROFILE_TRIGO_BETAGLUCANASE_TIME;
  debranchingTime             =   PROFILE_TRIGO_DEBRANCHING_TIME;
  proteolyticTime             =   PROFILE_TRIGO_PROTEOLYTIC_TIME;
  betaAmylaseTime             =   PROFILE_TRIGO_BETAAMYLASE_TIME;
  alphaAmylaseTime            =   PROFILE_TRIGO_ALPHAAMYLASE_TIME;
  mashoutTime                 =   PROFILE_TRIGO_MASHOUT_TIME;
  recirculationTime           =   PROFILE_TRIGO_RECIRCULATION_TIME;
  spargeTime                  =   PROFILE_TRIGO_SPARGE_TIME;
  boilTime                    =   PROFILE_TRIGO_BOIL_TIME;
  coolingTime                 =   PROFILE_TRIGO_COOLING_TIME;
João Lino's avatar
Rel.3  
João Lino committed
353
  cleaningTime                =   SETTING_CLEANING_TIME;
João Lino's avatar
João Lino committed
354

355 356 357 358 359 360 361 362 363 364 365
  startpointTemperature       =   PROFILE_TRIGO_STARTPOINT_TEMPERATURE;
  betaGlucanaseTemperature    =   PROFILE_TRIGO_BETAGLUCANASE_TEMPERATURE;
  debranchingTemperature      =   PROFILE_TRIGO_DEBRANCHING_TEMPERATURE;
  proteolyticTemperature      =   PROFILE_TRIGO_PROTEOLYTIC_TEMPERATURE;
  betaAmylaseTemperature      =   PROFILE_TRIGO_BETAAMYLASE_TEMPERATURE;
  alphaAmylaseTemperature     =   PROFILE_TRIGO_ALPHAAMYLASE_TEMPERATURE;
  mashoutTemperature          =   PROFILE_TRIGO_MASHOUT_TEMPERATURE;
  recirculationTemperature    =   PROFILE_TRIGO_RECIRCULATION_TEMPERATURE;
  spargeTemperature           =   PROFILE_TRIGO_SPARGE_TEMPERATURE;
  boilTemperature             =   PROFILE_TRIGO_BOIL_TEMPERATURE;
  coolingTemperature          =   PROFILE_TRIGO_COOLING_TEMPERATURE;
João Lino's avatar
Rel.3  
João Lino committed
366
  cleaningTemperature         =   SETTING_CLEANING_TEMPERATURE;
João Lino's avatar
João Lino committed
367 368 369 370

  refresh                     =   true;
  repaint                     =   true;

João Lino's avatar
João Lino committed
371 372
  loggingTimeInterval         =   0;

João Lino's avatar
João Lino committed
373 374 375 376
  // ++++++++++++++++++++++++ Interrupts ++++++++++++++++++++++++
  lastInterruptTime           =   0;

  // ++++++++++++++++++++++++ PID  ++++++++++++++++++++++++
377
  //iWindowSize                 =   HEATING_ELEMENT_DEFAULT_WINDOW_SIZE;    // Time frame to operate in
378

João Lino's avatar
Rel.3  
João Lino committed
379
  // ######################### Code - Run Once #########################
João Lino's avatar
João Lino committed
380
  xSafeHardwarePowerOff           ();
João Lino's avatar
Rel.3  
João Lino committed
381
  xWelcomeUser                    ();
382

João Lino's avatar
Rel.3  
João Lino committed
383
  xSetupRotaryEncoder             ( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
384 385

  printBeerProfile();
386
}
387

388
// ######################### MAIN LOOP #########################
389 390

void loop() {
João Lino's avatar
João Lino committed
391
  unsigned long inactivityTime = getInactivityTime();
392

393 394
  if ( inactivityTime > SETTING_MAX_INACTIVITY_TIME ) {   // Inactivity check
    if (refresh) {
395 396 397
      repaint = true;
      refresh = false;
    }
João Lino's avatar
Rel.3  
João Lino committed
398
    repaint = displayStatus( &lcd, cooking, cookTemperature, basePT100.getCurrentTemperature(), upPT100.getCurrentTemperature(), downPT100.getCurrentTemperature(), clockCounter, repaint );
399 400
  }
  else {
João Lino's avatar
Rel.3  
João Lino committed
401
    runMenu();
402
  }
403

João Lino's avatar
Rel.3  
João Lino committed
404
  xManageMachineSystems();
405 406
}

407 408
// ######################### FUNCTIONS ########################

João Lino's avatar
João Lino committed
409
void xCountTheTime( float temperatureMarginRange, boolean bMaximumOfUpDown ) {
João Lino's avatar
João Lino committed
410
  unsigned long now = millis();
João Lino's avatar
João Lino committed
411

João Lino's avatar
João Lino committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425
  // Get current maximum sensed temperaure
  double temperatureCount = 0;
  if ( bMaximumOfUpDown ) {
    float tup = upPT100.getCurrentTemperature();
    float tdown = downPT100.getCurrentTemperature();
    if (tup > tdown) {
      temperatureCount = tdown;
    }
    else {
      temperatureCount = tup;
    }
  } else {
    temperatureCount = basePT100.getCurrentTemperature();
  }
João Lino's avatar
João Lino committed
426

427
  // Ignore time ticks if temperature is not within the acceptable margin for this stage
428
  unsigned long lastWindowTime = now - clockLastUpdate;
João Lino's avatar
João Lino committed
429
  if ( temperatureCount < (cookTemperature - temperatureMarginRange) ) {
430
    clockIgnore += lastWindowTime;
João Lino's avatar
João Lino committed
431
  }
João Lino's avatar
João Lino committed
432

João Lino's avatar
João Lino committed
433
  // Calculate the remaining time on the clock
434 435 436
  //clockCounter = cookTime * 1000 - (lastWindowTime - clockIgnore);
  unsigned long elepsedTime = now - clockStartTime;
  clockCounter = cookTime * 1000 - (elepsedTime - clockIgnore);
437

João Lino's avatar
João Lino committed
438 439 440 441
  // Don't let clock get bellow 0
  if ( clockCounter < 0 ) {
    clockCounter = 0;
  }
João Lino's avatar
João Lino committed
442

João Lino's avatar
João Lino committed
443
  clockLastUpdate = now;
João Lino's avatar
João Lino committed
444

João Lino's avatar
João Lino committed
445 446 447 448 449 450 451 452 453
#ifdef DEBUG_OFF
  debugPrintFunction("xCountTheTime");
  debugPrintVar("millis()", now);
  debugPrintVar("cookTime", cookTime);
  debugPrintVar("clockStartTime", clockStartTime);
  debugPrintVar("clockIgnore", clockIgnore);
  debugPrintVar("clockCounter", clockCounter);
#endif
}
454

João Lino's avatar
João Lino committed
455 456 457 458 459 460
bool isTimeLeft() {
  if ( clockCounter > 0 ) {
    return true;
  }
  return false;
}
João Lino's avatar
João Lino committed
461

462
/*
João Lino's avatar
João Lino committed
463 464 465 466 467
//HEATING_ELEMENT_MAX_WATTAGE / HEATING_ELEMENT_AC_FREQUENCY_HZ
double ulWattToWindowTime( double ulAppliedWatts ) {
  double ulPulsesRequired = ulAppliedWatts / dWattPerPulse;
  return (double)iWindowSize / 1000.0 * ulPulsesRequired * 1000.0 / HEATING_ELEMENT_AC_FREQUENCY_HZ;
}
468
*/
469

João Lino's avatar
João Lino committed
470
bool xRegulateTemperature( boolean bMaximumOfUpDown ) {
471 472
  double difference = 0.0;
  double temperatureTarget = cookTemperature;
João Lino's avatar
João Lino committed
473
  bool overTemperature = false;
474

475
  // Get the most recent temperature measurement
João Lino's avatar
João Lino committed
476 477 478
  float tup = upPT100.getCurrentTemperature();
  float tdown = downPT100.getCurrentTemperature();
  float tbase = basePT100.getCurrentTemperature();
479

480 481

  
João Lino's avatar
João Lino committed
482
  if ( bMaximumOfUpDown ) {
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515

    // Calculate max-up-dowd difference
    double upDownDifference = 0.0;
    if (tup > tdown) {
      upDownDifference = cookTemperature - tup;
    }
    else {
      upDownDifference = cookTemperature - tdown;
    }

    // If the deviation from the target temperature is greater than the over-temperature range, the target temperature is set to the cooking temperature + the max over-temperature range
    if ( upDownDifference > COOKING_MAX_OVER_TEMPERATURE_RANGE_ALLOWED ) {
      temperatureTarget = cookTemperature + COOKING_MAX_OVER_TEMPERATURE_RANGE_ALLOWED;
    }
    // If the deviation from the target temperature is smaller than the over-temperature range, but not null, the target temperature is set to the cooking temperature + the difference
    else if ( upDownDifference > 0.0 ) {
      temperatureTarget = cookTemperature + upDownDifference;
    }
    // If the deviation from the target temperature indicates over-temperature, the target temperature is set to the cooking temperature
    else {
      temperatureTarget = cookTemperature;
    }
    
  }
  // For base thermometer control, the cook temperature is always
  else {
    temperatureTarget = cookTemperature;
  }
  
  // Calculate the deviation between the most recent temperature measurement and the target temperature
  difference = temperatureTarget - tbase;
  
  /*if ( bMaximumOfUpDown ) {
João Lino's avatar
João Lino committed
516 517 518 519 520 521
    if (tup > tdown) {
      difference = cookTemperature - tup;
    }
    else {
      difference = cookTemperature - tdown;
    }
522

523 524
    // Override bMaximumOfUpDown to false when the base temperature goes beyond the target temparature and either the pump is off or the grain is still very cold
    if (tbase > cookTemperature && (tbase >= (PUMP_TEMPERATURE_MAX_OPERATION - 2.0) || difference >= COOKING_MAX_OVER_TEMPERATURE_ALLOWED)) {
João Lino's avatar
João Lino committed
525 526
      difference = cookTemperature - tbase;
    }
527

528
    // Override bMaximumOfUpDown to false when the base temperature is bellow the target temparature, 
João Lino's avatar
João Lino committed
529 530 531 532 533
    if ( (tbase < cookTemperature) && (difference < (cookTemperature - tbase)) ) {
      difference = cookTemperature - tbase;
    }
  } else {
    difference = cookTemperature - tbase;
534
  }*/
535

536
  // Check for over temperature
João Lino's avatar
João Lino committed
537 538 539 540
  if ( difference < 0.0 ) {
    difference = difference * (-1.0);
    overTemperature = true;
  }
541

João Lino's avatar
João Lino committed
542 543
  // Calculate applied wattage, based on the distance from the target temperature
  if ( overTemperature ) {
544 545
    //dWattage = 0.0;                      // turn it off
    heatingElement.setWattage(heatingElement.getNullWattage());             // turn it off
João Lino's avatar
João Lino committed
546 547 548
  }
  else {
    if ( difference <= 0.5 ) {
549
      if ( temperatureTarget > 99.0 ) {
550 551
        //dWattage = 2000.0;               // pulse hardly at 2000 watt
        heatingElement.setWattage(heatingElement.getTwoThirdWattage());             // pulse hardly at 2000 watt
João Lino's avatar
João Lino committed
552 553
      }
      else {
554
        if ( temperatureTarget > 70.0 ) {
555 556
          //dWattage = 1000.0;             // pulse moderately at 1000 watt
          heatingElement.setWattage(heatingElement.getOneThirdWattage());             // pulse moderately at 1000 watt
João Lino's avatar
João Lino committed
557 558
        }
        else {
559 560
          //dWattage = 500.0;              // pulse lightly at 500 watt
          heatingElement.setWattage(heatingElement.getOneSixthWattage());             // pulse lightly at 500 watt
561
        }
João Lino's avatar
Rel.3  
João Lino committed
562
      }
João Lino's avatar
João Lino committed
563 564 565
    }
    else {
      if ( difference <= 1.0 ) {
566
        if ( temperatureTarget > 99.0 ) {
567 568
          //dWattage = 2000.0;             // pulse hardly at 2000 watt
          heatingElement.setWattage(heatingElement.getTwoThirdWattage());             // pulse hardly at 2000 watt
João Lino's avatar
João Lino committed
569 570
        }
        else {
571 572
          //dWattage = 1000.0;             // pulse moderately at 1000 watt
          heatingElement.setWattage(heatingElement.getOneThirdWattage());             // pulse moderately at 1000 watt
João Lino's avatar
João Lino committed
573
        }
João Lino's avatar
João Lino committed
574 575 576
      }
      else {
        if ( difference <= 3.0 ) {
577 578
          //dWattage = 2000.0;             // pulse hardly at 2000 watt
          heatingElement.setWattage(heatingElement.getTwoThirdWattage());             // pulse hardly at 2000 watt
João Lino's avatar
João Lino committed
579 580
        }
        else {
581 582
          //dWattage = HEATING_ELEMENT_MAX_WATTAGE;  // pulse constantly at HEATING_ELEMENT_MAX_WATTAGE watt
          heatingElement.setWattage(heatingElement.getMaxWattage());  // pulse constantly at HEATING_ELEMENT_MAX_WATTAGE watt
583
        }
João Lino's avatar
João Lino committed
584 585 586
      }
    }
  }
587

588 589 590 591
  // Update the recorded time for the begining of the window, if the previous window has passed. Then apply wattage to the element at the right time
  heatingElement.process();
  
  /*
João Lino's avatar
João Lino committed
592 593 594 595
  // Update the recorded time for the begining of the window, if the previous window has passed
  while ((millis() - windowStartTime) > iWindowSize) { // Check if it's time to vary the pulse width modulation and if so do it by shifting the "Relay in ON" Window
    windowStartTime += iWindowSize;
  }
596
  
João Lino's avatar
João Lino committed
597
  // Apply wattage to the element at the right time
João Lino's avatar
João Lino committed
598
  if ( ulWattToWindowTime( dWattage ) > (millis() - windowStartTime) ) {
João Lino's avatar
João Lino committed
599 600 601 602 603
    digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, HIGH);
    bStatusElement = true;
  } else {
    digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, LOW);
    bStatusElement = false;
604
  }
605
  */
João Lino's avatar
Rel.3  
João Lino committed
606

607
#ifdef DEBUG_OFF
João Lino's avatar
João Lino committed
608 609 610
  //debugPrintFunction("xRegulateTemperature");
  debugPrintVar("difference", difference);
  //debugPrintVar("overTemperature", overTemperature);
611
  debugPrintVar("dWattage", heatingElement.getWattage());
João Lino's avatar
João Lino committed
612
  //debugPrintVar("ulWattToWindowTime( dWattage )", ulWattToWindowTime( dWattage ) );
João Lino's avatar
João Lino committed
613 614
  //debugPrintVar("millis()", millis());
  //debugPrintVar("windowStartTime", windowStartTime);
João Lino's avatar
João Lino committed
615
  //debugPrintVar("test", ulWattToWindowTime( dWattage ) > (millis() - windowStartTime) );
616
#endif
617 618
}

João Lino's avatar
João Lino committed
619
void xPurgePump() {
620
  lcdPrint(&lcd, "    Purging", "     Pump!");    // Write welcome
João Lino's avatar
João Lino committed
621 622
  pump.forcePumpSelfPrime();
  /*
João Lino's avatar
João Lino committed
623 624 625 626 627 628
  for (int i = 0; i < 2; i++) {
    analogWrite(PUMP_PIN, PUMP_SPEED_MAX_MOSFET);  // analogWrite values from 0 to 255
    delay(1000);
    analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET);  // analogWrite values from 0 to 255
    delay(1500);
  }
João Lino's avatar
João Lino committed
629
  */
João Lino's avatar
João Lino committed
630
}
João Lino's avatar
Rel.3  
João Lino committed
631

João Lino's avatar
João Lino committed
632
bool xRegulatePumpSpeed() {
João Lino's avatar
João Lino committed
633 634 635 636 637 638 639 640
  pump.process();
  
  bool isPumpOn = pump.isPumpOn();
  basePT100.setPumpStatus( isPumpOn );
  upPT100.setPumpStatus( isPumpOn );
  downPT100.setPumpStatus( isPumpOn );

  /*
João Lino's avatar
João Lino committed
641 642 643 644 645 646 647 648 649 650
  if(pump.process()) {
    basePT100.setPumpStatus( true );
    upPT100.setPumpStatus( true );
    downPT100.setPumpStatus( true );
  }
  else{
    basePT100.setPumpStatus( false );
    upPT100.setPumpStatus( false );
    downPT100.setPumpStatus( false );
  }
João Lino's avatar
João Lino committed
651
  
João Lino's avatar
João Lino committed
652
  //  analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255
João Lino's avatar
João Lino committed
653 654
  if (basePT100.getCurrentTemperature() > PUMP_TEMPERATURE_MAX_OPERATION) {
    analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET);  // analogWrite values from 0 to 255
João Lino's avatar
Rel.3  
João Lino committed
655

João Lino's avatar
João Lino committed
656 657 658 659 660 661
    basePT100.setPumpStatus( false );
    upPT100.setPumpStatus( false );
    downPT100.setPumpStatus( false );
  }
  else {
    analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255
662

João Lino's avatar
João Lino committed
663 664 665
    basePT100.setPumpStatus( true );
    upPT100.setPumpStatus( true );
    downPT100.setPumpStatus( true );
João Lino's avatar
Rel.3  
João Lino committed
666
  }
João Lino's avatar
João Lino committed
667
  */
João Lino's avatar
João Lino committed
668
}
João Lino's avatar
Rel.3  
João Lino committed
669

João Lino's avatar
João Lino committed
670 671
void xWarnClockEnded() {
  sing(MELODY_SUPER_MARIO_START, PIEZO_PIN);
João Lino's avatar
Rel.3  
João Lino committed
672 673
}

João Lino's avatar
João Lino committed
674 675 676
void xWarnCookEnded() {
  sing(MELODY_UNDERWORLD_SHORT, PIEZO_PIN);
}
João Lino's avatar
Rel.3  
João Lino committed
677

João Lino's avatar
João Lino committed
678 679 680 681 682
void xPrepareForStage( int stageTime, int stageTemperature, int stagePumpSpeed, eCookingStages stage ) {
#ifdef DEBUG_OFF
  debugPrintFunction("xPrepareForStage");
  debugPrintVar("cookingStage", stage);
#endif
683

João Lino's avatar
João Lino committed
684 685 686 687 688
  // Reset the clock
  unsigned long now = millis();
  clockStartTime  = now;
  clockLastUpdate = now;
  clockIgnore     = 0;
João Lino's avatar
Rel.3  
João Lino committed
689

João Lino's avatar
João Lino committed
690 691
  pump.setTargetPumpSpeed(stagePumpSpeed);  // Set the pump speed
  //iPumpSpeed      = stagePumpSpeed;         // Set the pump speed
João Lino's avatar
João Lino committed
692 693 694
  cookingStage    = stage;                  // Set Stage
  cookTime        = stageTime;              // Set the clock
  cookTemperature = stageTemperature;       // Set the target temperature
João Lino's avatar
Rel.3  
João Lino committed
695 696
}

João Lino's avatar
João Lino committed
697 698 699 700 701
void xSetupStage(eCookingStages nextStage) {
#ifdef DEBUG_OFF
  debugPrintFunction("xSetupStage");
  debugPrintVar("cookingStage", nextStage);
#endif
João Lino's avatar
Rel.3  
João Lino committed
702

João Lino's avatar
João Lino committed
703 704 705 706 707 708 709 710 711 712 713 714
  // Operate the machine according to the current mode
  switch (nextStage) {
    case eCookingStage_Startpoint: {
        switch (beerProfile) {
          case eBeerProfile_Trigo: {
              float wheatAmount = 0.05 * ((float) finalYield);
              float pilsnerAmount = 0.2 * ((float) finalYield);
              String say = "Cruch ";
              say += String(wheatAmount);
              say += String("Kg of Wheat and ");
              say += String(pilsnerAmount);
              say += String("Kg of Pilsner Malt into a pot.");
715

João Lino's avatar
João Lino committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
              xWaitForAction("Malt", say);
              repaint = true;
              break;
            }
          case eBeerProfile_IPA: {
              float caramelAmount = 0.013157895 * ((float) finalYield);
              float wheatAmount = 0.060526316 * ((float) finalYield);
              float pilsnerAmount = 0.115789474 * ((float) finalYield);
              float munichAmount = 0.028947368 * ((float) finalYield);
              String say = "Cruch ";
              say += String(caramelAmount);
              say += String("Kg of Caramel 120, ");
              say += String(wheatAmount);
              say += String("Kg of Wheat, ");
              say += String(pilsnerAmount);
              say += String("Kg of Pilsner, ");
              say += String(munichAmount);
              say += String("Kg of Munich into a pot.");
734

João Lino's avatar
João Lino committed
735 736 737 738 739
              xWaitForAction("Malt", say);
              repaint = true;
              break;
            }
          default: {
João Lino's avatar
Rel.3  
João Lino committed
740

João Lino's avatar
João Lino committed
741 742
            }
        }
743

João Lino's avatar
João Lino committed
744 745
        // Make sure there is water
        xWaitForAction("Water", "Make sure there is water in the machine before start cooking.");
746

João Lino's avatar
João Lino committed
747
        repaint = true;
João Lino's avatar
João Lino committed
748
        xPrepareForStage( startpointTime, startpointTemperature, pump.getMaxSpeed(), eCookingStage_Startpoint );
749 750
        break;
      }
João Lino's avatar
João Lino committed
751 752 753 754 755 756 757 758 759 760
    case eCookingStage_BetaGlucanase: {
        switch (beerProfile) {
          case eBeerProfile_Trigo: {
              float wheatAmount = 0.05 * ((float) finalYield);
              float pilsnerAmount = 0.2 * ((float) finalYield);
              String say = "Put ";
              say += String(wheatAmount);
              say += String("Kg of Wheat and ");
              say += String(pilsnerAmount);
              say += String("Kg of Pilsner Malt in.");
761

João Lino's avatar
João Lino committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
              xWaitForAction("Malt", say);
              repaint = true;
              break;
            }
          case eBeerProfile_IPA: {
              float caramelAmount = 0.013157895 * ((float) finalYield);
              float wheatAmount = 0.060526316 * ((float) finalYield);
              float pilsnerAmount = 0.115789474 * ((float) finalYield);
              float munichAmount = 0.028947368 * ((float) finalYield);
              String say = "Cruch ";
              say += String(caramelAmount);
              say += String("Kg of Caramel 120, ");
              say += String(wheatAmount);
              say += String("Kg of Wheat, ");
              say += String(pilsnerAmount);
              say += String("Kg of Pilsner, ");
              say += String(munichAmount);
              say += String("Kg of Munich into a pot.");
780

João Lino's avatar
João Lino committed
781 782 783 784 785 786
              xWaitForAction("Malt", say);
              repaint = true;
              break;
            }
          default: {}
        }
João Lino's avatar
João Lino committed
787
        xPrepareForStage( betaGlucanaseTime, betaGlucanaseTemperature, pump.getMaxSpeed(), eCookingStage_BetaGlucanase );
788 789
        break;
      }
João Lino's avatar
João Lino committed
790
    case eCookingStage_Debranching: {
João Lino's avatar
João Lino committed
791
        xPrepareForStage( debranchingTime, debranchingTemperature, pump.getMaxSpeed(), eCookingStage_Debranching );
792 793
        break;
      }
João Lino's avatar
João Lino committed
794
    case eCookingStage_Proteolytic: {
João Lino's avatar
João Lino committed
795
        xPrepareForStage( proteolyticTime, proteolyticTemperature, pump.getMaxSpeed(), eCookingStage_Proteolytic );
796 797
        break;
      }
João Lino's avatar
João Lino committed
798
    case eCookingStage_BetaAmylase: {
João Lino's avatar
João Lino committed
799
        xPrepareForStage( betaAmylaseTime, betaAmylaseTemperature, pump.getMaxSpeed(), eCookingStage_BetaAmylase );
800 801
        break;
      }
João Lino's avatar
João Lino committed
802
    case eCookingStage_AlphaAmylase: {
João Lino's avatar
João Lino committed
803
        xPrepareForStage( alphaAmylaseTime, alphaAmylaseTemperature, pump.getMaxSpeed(), eCookingStage_AlphaAmylase );
804 805
        break;
      }
João Lino's avatar
João Lino committed
806
    case eCookingStage_Mashout: {
João Lino's avatar
João Lino committed
807
        xPrepareForStage( mashoutTime, mashoutTemperature, pump.getMaxSpeed(), eCookingStage_Mashout );
808 809
        break;
      }
João Lino's avatar
João Lino committed
810 811 812
    case eCookingStage_Recirculation: {
        xWaitForAction("Sparge Water", "Start heating your sparge water.");
        repaint = true;
João Lino's avatar
João Lino committed
813
        xPrepareForStage( recirculationTime, recirculationTemperature, pump.getMaxSpeed(), eCookingStage_Recirculation );
814 815
        break;
      }
João Lino's avatar
João Lino committed
816 817 818
    case eCookingStage_Sparge: {
        xWaitForAction("Sparge Water", "Start pouring the sparge water.");
        repaint = true;
João Lino's avatar
João Lino committed
819
        xPrepareForStage( spargeTime, spargeTemperature, pump.getMaxSpeed(), eCookingStage_Sparge );
820 821
        break;
      }
João Lino's avatar
João Lino committed
822 823 824 825
    case eCookingStage_Boil: {
        switch (beerProfile) {
          case eBeerProfile_Trigo: {
              String say = "Get ";
826

João Lino's avatar
João Lino committed
827 828
              float hopAmount = 0.8 * ((float) finalYield);
              say += String(hopAmount);
829

João Lino's avatar
João Lino committed
830
              say += String("g of Magnum 9.4\% and Styrian Golding 5\% ready.");
João Lino's avatar
Rel.3  
João Lino committed
831

João Lino's avatar
João Lino committed
832
              xWaitForAction("Hops", say);
833

João Lino's avatar
João Lino committed
834 835 836 837
              break;
            }
          case eBeerProfile_IPA: {
              String say = "Get ";
João Lino's avatar
Rel.3  
João Lino committed
838

João Lino's avatar
João Lino committed
839 840
              float hopAmount = 0.8 * ((float) finalYield);
              say += String(hopAmount);
João Lino's avatar
Rel.3  
João Lino committed
841

João Lino's avatar
João Lino committed
842
              say += String("g of Chinook, Cascade and Styrian Golding ready.");
843

João Lino's avatar
João Lino committed
844
              xWaitForAction("Hops", say);
845

João Lino's avatar
João Lino committed
846 847 848 849
              break;
            }
          default: {
              xWaitForAction("Hops", "Add the hops in the right order, at the right time.");
850

João Lino's avatar
João Lino committed
851 852
            }
        }
853

João Lino's avatar
João Lino committed
854
        repaint = true;
855

João Lino's avatar
João Lino committed
856
        // A basic operation for a basic stage
João Lino's avatar
João Lino committed
857
        xPrepareForStage( boilTime, boilTemperature, pump.getMaxSpeed(), eCookingStage_Boil );
João Lino's avatar
Rel.3  
João Lino committed
858

859 860
        break;
      }
João Lino's avatar
João Lino committed
861 862 863
    case eCookingStage_Cooling: {
        // Make sure there is water
        xWaitForAction("Coil", "Add the coil and connect it to the main water supply.");
864

João Lino's avatar
João Lino committed
865 866 867
        repaint = true;

        // A basic operation for a basic stage
João Lino's avatar
João Lino committed
868
        xPrepareForStage( coolingTime, coolingTemperature, pump.getMaxSpeed(), eCookingStage_Cooling );
869 870 871

        break;
      }
João Lino's avatar
João Lino committed
872 873 874
    case eCookingStage_Clean: {
        // Make sure there is water
        xWaitForAction("Water", "Add 13 liters.");
875

João Lino's avatar
João Lino committed
876 877
        // Make sure there is water
        xWaitForAction("Star San HB", "Add 0.89oz/26ml.");
878

João Lino's avatar
João Lino committed
879
        repaint = true;
880

João Lino's avatar
João Lino committed
881
        // A basic operation for a basic stage
João Lino's avatar
João Lino committed
882
        xPrepareForStage( cleaningTime, cleaningTemperature, pump.getMaxSpeed(), eCookingStage_Clean );
883 884 885

        break;
      }
João Lino's avatar
João Lino committed
886 887
    case eCookingStage_Purge: {
        // A basic operation for a basic stage
João Lino's avatar
João Lino committed
888
        xPrepareForStage( 0, 0, pump.getMaxSpeed(), eCookingStage_Purge );
889

João Lino's avatar
João Lino committed
890
        xRegulatePumpSpeed();
891 892 893

        break;
      }
João Lino's avatar
João Lino committed
894 895
    case eCookingStage_Done: {
        // A basic operation for a basic stage
João Lino's avatar
João Lino committed
896
        xPrepareForStage( 0, 0, pump.getNullSpeed(), eCookingStage_Done );
897 898 899

        break;
      }
900
  }
901 902
}

João Lino's avatar
João Lino committed
903 904 905
void xTransitionIntoStage(eCookingStages nextStage) {
  // Turn off all hardware that can damage itself if the machine is not cooking
  xSafeHardwarePowerOff();
906

João Lino's avatar
João Lino committed
907 908 909 910 911 912 913
  // Warn the user a stage has ended
  xWarnClockEnded();

  // Reset global stage variables
  xSetupStage( nextStage );
}

João Lino's avatar
João Lino committed
914
void xBasicStageOperation( int iStageTime, int iStageTemperature, float iStageTemperatureRange, eCookingStages nextStage, boolean bMaximumOfUpDown ) {
João Lino's avatar
João Lino committed
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938

  // Account for time spent at the target temperature | Input 1: range in ºC within which the target temperature is considered to be reached
#ifdef DEBUG_OFF
  xCountTheTime( iStageTemperatureRange, false );
#else
  xCountTheTime( iStageTemperatureRange, bMaximumOfUpDown );
#endif

  if ( isTimeLeft() ) {
    // Do temperature control
    xRegulateTemperature( bMaximumOfUpDown );

    // Do flow control
    xRegulatePumpSpeed();

  } else {
#ifdef DEBUG_OFF
    debugPrintFunction("xBasicStageOperation");
    debugPrintVar("clockCounter", clockCounter);
#endif

    // Continue to the next stage, there is nothing to do, in this stage
    xTransitionIntoStage( nextStage );
    return;
939
  }
João Lino's avatar
João Lino committed
940 941 942 943 944 945 946 947 948

  return;
}

void xManageMachineSystems() {
  // Measure temperature, for effect
  basePT100.measure(false);
  upPT100.measure(false);
  downPT100.measure(true);
João Lino's avatar
João Lino committed
949

950 951 952 953 954 955 956 957 958 959 960 961 962 963
#ifdef INFO
  unsigned long now = millis();
  if( now - loggingTimeInterval > SETTING_MACHINE_LOGGING_INTERVAL ) {
    loggingTimeInterval = now;
    Serial.print("|");
    Serial.print(now);
    Serial.print("|");
    if (cooking) {
      Serial.print("1");
    }
    else {
      Serial.print("0");
    }
    Serial.print("|");
João Lino's avatar
João Lino committed
964 965 966 967
    Serial.print( mdStageMenu._dialog[cookingStage+1] );
    Serial.print("|");
    Serial.print(clockCounter);
    Serial.print("|");
968 969 970 971 972 973 974 975
    Serial.print(cookTemperature);
    Serial.print("|");
    Serial.print(basePT100.getCurrentTemperature());
    Serial.print("|");
    Serial.print(upPT100.getCurrentTemperature());
    Serial.print("|");
    Serial.print(downPT100.getCurrentTemperature());
    Serial.print("|");
976
    Serial.print(heatingElement.getWattage());
João Lino's avatar
João Lino committed
977
    Serial.print("|");
978 979
//    if (bStatusElement) {
    if (heatingElement.isStatusElement()) {
980 981 982 983 984 985 986 987 988
      Serial.print("1");
    }
    else {
      Serial.print("0");
    }
    Serial.println("|");
  } 
#endif

João Lino's avatar
João Lino committed
989 990 991
  // If cooking is done, return (this is a nice place to double check safety and ensure the cooking parts aren't on.
  if (!cooking) {
    xSafeHardwarePowerOff();
João Lino's avatar
João Lino committed
992

João Lino's avatar
João Lino committed
993 994
    return;
  }
João Lino's avatar
João Lino committed
995

João Lino's avatar
João Lino committed
996 997 998
  // Operate the machine according to the current mode
  switch (cookingStage) {
    case eCookingStage_Startpoint: {
João Lino's avatar
João Lino committed
999
        xBasicStageOperation( startpointTime, startpointTemperature, 0.0, eCookingStage_BetaGlucanase, false);
1000 1001
        break;
      }
João Lino's avatar
João Lino committed
1002
    case eCookingStage_BetaGlucanase: {
João Lino's avatar
João Lino committed
1003
        xBasicStageOperation( betaGlucanaseTime, betaGlucanaseTemperature, 3.0, eCookingStage_Debranching, true );
1004 1005
        break;
      }
João Lino's avatar
João Lino committed
1006
    case eCookingStage_Debranching: {
João Lino's avatar
João Lino committed
1007
        xBasicStageOperation( debranchingTime, debranchingTemperature, 3.0, eCookingStage_Proteolytic, true );
1008 1009
        break;
      }
João Lino's avatar
João Lino committed
1010
    case eCookingStage_Proteolytic: {
João Lino's avatar
João Lino committed
1011
        xBasicStageOperation( proteolyticTime, proteolyticTemperature, 3.0, eCookingStage_BetaAmylase, true );
1012 1013
        break;
      }
João Lino's avatar
João Lino committed
1014
    case eCookingStage_BetaAmylase: {
João Lino's avatar
João Lino committed
1015
        xBasicStageOperation( betaAmylaseTime, betaAmylaseTemperature, 7.0, eCookingStage_AlphaAmylase, true );
1016 1017
        break;
      }
João Lino's avatar
João Lino committed
1018
    case eCookingStage_AlphaAmylase: {
João Lino's avatar
João Lino committed
1019
        xBasicStageOperation( alphaAmylaseTime, alphaAmylaseTemperature, 2.5, eCookingStage_Mashout, true );
1020 1021
        break;
      }
João Lino's avatar
João Lino committed
1022
    case eCookingStage_Mashout: {
João Lino's avatar
João Lino committed
1023
        xBasicStageOperation( mashoutTime, mashoutTemperature, 3.0, eCookingStage_Recirculation, true );
1024 1025
        break;
      }
João Lino's avatar
João Lino committed
1026
    case eCookingStage_Recirculation: {
João Lino's avatar
João Lino committed
1027
        xBasicStageOperation( recirculationTime, recirculationTemperature, 2.0, eCookingStage_Sparge, true );
1028 1029
        break;
      }
João Lino's avatar
João Lino committed
1030
    case eCookingStage_Sparge: {
João Lino's avatar
João Lino committed
1031
        xBasicStageOperation( spargeTime, spargeTemperature, 3.0, eCookingStage_Boil, false );
1032 1033
        break;
      }
João Lino's avatar
João Lino committed
1034
    case eCookingStage_Boil: {
João Lino's avatar
João Lino committed
1035
        xBasicStageOperation( boilTime, boilTemperature, 2.0, eCookingStage_Cooling, false );
1036 1037
        break;
      }
João Lino's avatar
João Lino committed
1038
    case eCookingStage_Cooling: {
João Lino's avatar
João Lino committed
1039
        xBasicStageOperation( coolingTime, coolingTemperature, 0.0, eCookingStage_Done, false );
1040 1041
        break;
      }
João Lino's avatar
João Lino committed
1042
    case eCookingStage_Clean: {
João Lino's avatar
João Lino committed
1043
        xBasicStageOperation( cleaningTime, cleaningTemperature, 0.0, eCookingStage_Done, false );
1044 1045
        break;
      }
João Lino's avatar
João Lino committed
1046
    case eCookingStage_Purge: {
João Lino's avatar
João Lino committed
1047 1048
        pump.process(pump.getMaxSpeed());
        /*
João Lino's avatar
João Lino committed
1049 1050
        iPumpSpeed = PUMP_SPEED_MAX_MOSFET;
        xRegulatePumpSpeed();
João Lino's avatar
João Lino committed
1051
        */
João Lino's avatar
João Lino committed
1052 1053
        break;
      }
João Lino's avatar
João Lino committed
1054
    case eCookingStage_Done: {
João Lino's avatar
João Lino committed
1055 1056 1057 1058 1059 1060
        stopBrewing();                  // Update cooking state
        repaint = true;                 // Ask for screen refresh
        xWarnCookEnded();               // Warn the user that the cooking is done
        break;
      }
  }
João Lino's avatar
João Lino committed
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
}

// ##################################################### Menus ###################################################################

// *************************** MENU BASE *********************************
void runMenu() {
#ifdef DEBUG_OFF
  boolean debug_go = repaint;
  if (debug_go) {
    debugPrintFunction("runMenu");
    debugPrintVar("repaint", repaint);
    debugPrintVar("eMenuType", eMenuType);
    debugPrintVar("rotaryEncoderVirtualPosition", rotaryEncoderVirtualPosition);
  }
#endif

  switch (eMenuType) {
    case eMenuType_Main: {
João Lino's avatar
João Lino committed
1079 1080 1081
        runMenuProcessor( &mdMainMenu );
        break;
      }
João Lino's avatar
João Lino committed
1082
    case eMenuType_BeerProfile: {
João Lino's avatar
João Lino committed
1083 1084 1085
        runMenuProcessor( &mdBeerProfileMenu );
        break;
      }
João Lino's avatar
João Lino committed
1086
    case eMenuType_Stage: {
João Lino's avatar
João Lino committed
1087 1088 1089
        runMenuProcessor( &mdStageMenu );
        break;
      }
João Lino's avatar
João Lino committed
1090
    case eMenuType_Malt: {
João Lino's avatar
João Lino committed
1091 1092 1093
        runMenuProcessor( &mdMaltMenu );
        break;
      }
João Lino's avatar
João Lino committed
1094
    case eMenuType_Settings: {
João Lino's avatar
João Lino committed
1095 1096 1097
        runMenuProcessor( &mdSettingsMenu );
        break;
      }
João Lino's avatar
João Lino committed
1098
    case eMenuType_StartFromStage: {
João Lino's avatar
João Lino committed
1099 1100 1101
        runMenuProcessor( &mdStartFromStageMenu );
        break;
      }
João Lino's avatar
João Lino committed
1102 1103 1104 1105 1106
  }

#ifdef DEBUG_OFF
  if (debug_go) {
    debugPrintVar("repaint", repaint);
João Lino's avatar
João Lino committed
1107
  }
João Lino's avatar
João Lino committed
1108
#endif
João Lino's avatar
João Lino committed
1109 1110
}

João Lino's avatar
João Lino committed
1111
// ************************ MENU SELECTIONS ******************************
João Lino's avatar
Rel.3  
João Lino committed
1112
void runMainMenuSelection() {
1113
  switch (mdMainMenu._selection) {
João Lino's avatar
João Lino committed
1114
    //switch (eMainMenuSelection) {
João Lino's avatar
João Lino committed
1115
    case eMainMenu_GO: {
1116 1117 1118
        xStartStage( NULL, NULL, eCookingStage_Startpoint, true, true, false, false );
        break;
      }
João Lino's avatar
João Lino committed
1119
    case eMainMenu_GO_FROM_STAGE: {
1120 1121
        eMenuType = eMenuType_StartFromStage;
        repaint = true;
João Lino's avatar
João Lino committed
1122
        xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdBeerProfileMenu._position, MENU_SIZE_PROFILES_MENU - 1, 1, 1, 0 );
1123 1124
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1125
    case eMainMenu_STOP: {
1126 1127 1128 1129
        stopBrewing();
        backToStatus();
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1130
    case eMainMenu_SKIP: {
1131 1132 1133 1134
        cookTime = 0;
        backToStatus();
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1135
    case eMainMenu_BeerProfile: {
1136 1137
        eMenuType = eMenuType_BeerProfile;
        repaint = true;
João Lino's avatar
João Lino committed
1138
        xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdBeerProfileMenu._position, MENU_SIZE_PROFILES_MENU - 1, 1, 1, 0 );
1139 1140 1141 1142 1143
        break;
      }
    case eMainMenu_Stage: {
        eMenuType = eMenuType_Stage;
        repaint = true;
João Lino's avatar
João Lino committed
1144
        xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdStageMenu._position, MENU_SIZE_STAGE_MENU - 1, 1, 1, 0 );
1145 1146
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1147
    case eMainMenu_Malt: {
1148 1149
        eMenuType = eMenuType_Malt;
        repaint = true;
João Lino's avatar
João Lino committed
1150
        xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdMaltMenu._position, MENU_SIZE_MALT_MENU - 1, 1, 1, 0 );
1151 1152
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1153
    case eMainMenu_Hops: {
1154 1155 1156
        backToStatus();
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1157
    case eMainMenu_Clean: {
1158 1159 1160
        xStartStageHeadless( eCookingStage_Clean, true );
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1161
    case eMainMenu_Purge: {
1162 1163 1164
        xStartStageHeadless( eCookingStage_Purge, true );
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1165
    case eMainMenu_Settings: {
1166 1167
        eMenuType = eMenuType_Settings;
        repaint = true;
João Lino's avatar
João Lino committed
1168
        xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdSettingsMenu._position, MENU_SIZE_SETTINGS_MENU - 1, 1, 1, 0 );
1169 1170
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1171
    case eMainMenu_Back: {
1172 1173 1174
        backToStatus();
        break;
      }
João Lino's avatar
Rel.3  
João Lino committed
1175
    default: {
1176
      }
João Lino's avatar
Rel.3  
João Lino committed
1177
  }
1178
  mdMainMenu._selection = eMainMenu_NULL;
1179 1180
}

João Lino's avatar
João Lino committed
1181

João Lino's avatar
João Lino committed
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
void runStartFromStageSelection() {
  switch (mdStartFromStageMenu._selection) {
    case eStageMenu_Startpoint: {
        xStartStageInteractive( &startpointTime, &startpointTemperature, eCookingStage_Startpoint );
        break;
      }
    case eStageMenu_BetaGlucanase: {
        xStartStageInteractive( &betaGlucanaseTime, &betaGlucanaseTemperature, eCookingStage_BetaGlucanase );
        break;
      }
    case eStageMenu_Debranching: {
        xStartStageInteractive( &debranchingTime, &debranchingTemperature, eCookingStage_Debranching );
        break;
      }
    case eStageMenu_Proteolytic: {
        xStartStageInteractive( &proteolyticTime, &proteolyticTemperature, eCookingStage_Proteolytic );
        break;
      }
    case eStageMenu_BetaAmylase: {
        xStartStageInteractive( &betaAmylaseTime, &betaAmylaseTemperature, eCookingStage_BetaAmylase );
        break;
      }
    case eStageMenu_AlphaAmylase: {
        xStartStageInteractive( &alphaAmylaseTime, &alphaAmylaseTemperature, eCookingStage_AlphaAmylase );
        break;
      }
    case eStageMenu_Mashout: {
        xStartStageInteractive( &mashoutTime, &mashoutTemperature, eCookingStage_Mashout );
        break;
      }
    case eStageMenu_Recirculation: {
        xStartStageInteractive( &recirculationTime, &recirculationTemperature, eCookingStage_Recirculation );
        break;
      }
    case eStageMenu_Sparge: {
        xStartStageInteractive( &spargeTime, &spargeTemperature, eCookingStage_Sparge );
        break;
      }
    case eStageMenu_Boil: {
        xStartStageInteractive( &boilTime, &boilTemperature, eCookingStage_Boil );
        break;
      }
    case eStageMenu_Cooling: {
        xStartStageInteractive( &coolingTime, &coolingTemperature, eCookingStage_Cooling );
        break;
      }
    case eStageMenu_Back: {
        resetMenu( true );
        break;
      }
    default: {
      }
João Lino's avatar
João Lino committed
1234
  }
João Lino's avatar
João Lino committed
1235 1236
  mdStartFromStageMenu._selection = eStageMenu_NULL;
}
1237

João Lino's avatar
João Lino committed
1238 1239 1240
void runBeerProfileSelection() {
  switch (mdBeerProfileMenu._selection) {
    case eBeerProfileMenu_Basic: {
João Lino's avatar
João Lino committed
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
        beerProfile                 =   eBeerProfile_Basic;
        startpointTime              =   PROFILE_BASIC_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_BASIC_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_BASIC_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_BASIC_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_BASIC_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_BASIC_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_BASIC_MASHOUT_TIME;
        recirculationTime           =   PROFILE_BASIC_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_BASIC_SPARGE_TIME;
        boilTime                    =   PROFILE_BASIC_BOIL_TIME;
        coolingTime                 =   PROFILE_BASIC_COOLING_TIME;
        startpointTemperature       =   PROFILE_BASIC_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_BASIC_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_BASIC_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_BASIC_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_BASIC_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_BASIC_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_BASIC_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_BASIC_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_BASIC_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_BASIC_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_BASIC_COOLING_TEMPERATURE;

1265
        printBeerProfile();
João Lino's avatar
João Lino committed
1266 1267 1268
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1269
    case eBeerProfileMenu_Trigo: {
João Lino's avatar
João Lino committed
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
        beerProfile                 =   eBeerProfile_Trigo;
        startpointTime              =   PROFILE_TRIGO_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_TRIGO_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_TRIGO_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_TRIGO_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_TRIGO_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_TRIGO_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_TRIGO_MASHOUT_TIME;
        recirculationTime           =   PROFILE_TRIGO_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_TRIGO_SPARGE_TIME;
        boilTime                    =   PROFILE_TRIGO_BOIL_TIME;
        coolingTime                 =   PROFILE_TRIGO_COOLING_TIME;
        startpointTemperature       =   PROFILE_TRIGO_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_TRIGO_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_TRIGO_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_TRIGO_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_TRIGO_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_TRIGO_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_TRIGO_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_TRIGO_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_TRIGO_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_TRIGO_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_TRIGO_COOLING_TEMPERATURE;
João Lino's avatar
João Lino committed
1293

1294
        printBeerProfile();
João Lino's avatar
João Lino committed
1295 1296 1297
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1298
    case eBeerProfileMenu_IPA: {
João Lino's avatar
João Lino committed
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
        beerProfile                 =   eBeerProfile_IPA;
        startpointTime              =   PROFILE_IPA_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_IPA_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_IPA_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_IPA_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_IPA_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_IPA_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_IPA_MASHOUT_TIME;
        recirculationTime           =   PROFILE_IPA_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_IPA_SPARGE_TIME;
        boilTime                    =   PROFILE_IPA_BOIL_TIME;
        coolingTime                 =   PROFILE_IPA_COOLING_TIME;
        startpointTemperature       =   PROFILE_IPA_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_IPA_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_IPA_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_IPA_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_IPA_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_IPA_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_IPA_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_IPA_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_IPA_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_IPA_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_IPA_COOLING_TEMPERATURE;
João Lino's avatar
João Lino committed
1322

1323
        printBeerProfile();
João Lino's avatar
João Lino committed
1324 1325 1326
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1327
    case eBeerProfileMenu_Belga: {
João Lino's avatar
João Lino committed
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
        beerProfile                 =   eBeerProfile_Belga;
        startpointTime              =   PROFILE_BELGA_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_BELGA_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_BELGA_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_BELGA_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_BELGA_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_BELGA_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_BELGA_MASHOUT_TIME;
        recirculationTime           =   PROFILE_BELGA_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_BELGA_SPARGE_TIME;
        boilTime                    =   PROFILE_BELGA_BOIL_TIME;
        coolingTime                 =   PROFILE_BELGA_COOLING_TIME;
        startpointTemperature       =   PROFILE_BELGA_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_BELGA_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_BELGA_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_BELGA_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_BELGA_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_BELGA_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_BELGA_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_BELGA_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_BELGA_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_BELGA_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_BELGA_COOLING_TEMPERATURE;
João Lino's avatar
João Lino committed
1351

1352
        printBeerProfile();
João Lino's avatar
João Lino committed
1353 1354 1355
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1356
    case eBeerProfileMenu_Red: {
João Lino's avatar
João Lino committed
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
        beerProfile                 =   eBeerProfile_Red;
        startpointTime              =   PROFILE_RED_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_RED_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_RED_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_RED_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_RED_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_RED_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_RED_MASHOUT_TIME;
        recirculationTime           =   PROFILE_RED_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_RED_SPARGE_TIME;
        boilTime                    =   PROFILE_RED_BOIL_TIME;
        coolingTime                 =   PROFILE_RED_COOLING_TIME;
        startpointTemperature       =   PROFILE_RED_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_RED_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_RED_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_RED_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_RED_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_RED_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_RED_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_RED_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_RED_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_RED_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_RED_COOLING_TEMPERATURE;
1380 1381
        
        printBeerProfile();
João Lino's avatar
João Lino committed
1382 1383 1384
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1385
    case eBeerProfileMenu_APA: {
João Lino's avatar
João Lino committed
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
        beerProfile                 =   eBeerProfile_APA;
        startpointTime              =   PROFILE_APA_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_APA_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_APA_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_APA_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_APA_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_APA_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_APA_MASHOUT_TIME;
        recirculationTime           =   PROFILE_APA_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_APA_SPARGE_TIME;
        boilTime                    =   PROFILE_APA_BOIL_TIME;
        coolingTime                 =   PROFILE_APA_COOLING_TIME;
        startpointTemperature       =   PROFILE_APA_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_APA_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_APA_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_APA_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_APA_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_APA_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_APA_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_APA_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_APA_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_APA_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_APA_COOLING_TEMPERATURE;
João Lino's avatar
João Lino committed
1409

1410
        printBeerProfile();
João Lino's avatar
João Lino committed
1411 1412 1413
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1414
    case eBeerProfileMenu_Custom: {
João Lino's avatar
João Lino committed
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
        beerProfile                 =   eBeerProfile_Custom;
        startpointTime              =   PROFILE_CUSTOM_STARTPOINT_TIME;
        betaGlucanaseTime           =   PROFILE_CUSTOM_BETAGLUCANASE_TIME;
        debranchingTime             =   PROFILE_CUSTOM_DEBRANCHING_TIME;
        proteolyticTime             =   PROFILE_CUSTOM_PROTEOLYTIC_TIME;
        betaAmylaseTime             =   PROFILE_CUSTOM_BETAAMYLASE_TIME;
        alphaAmylaseTime            =   PROFILE_CUSTOM_ALPHAAMYLASE_TIME;
        mashoutTime                 =   PROFILE_CUSTOM_MASHOUT_TIME;
        recirculationTime           =   PROFILE_CUSTOM_RECIRCULATION_TIME;
        spargeTime                  =   PROFILE_CUSTOM_SPARGE_TIME;
        boilTime                    =   PROFILE_CUSTOM_BOIL_TIME;
        coolingTime                 =   PROFILE_CUSTOM_COOLING_TIME;
        startpointTemperature       =   PROFILE_CUSTOM_STARTPOINT_TEMPERATURE;
        betaGlucanaseTemperature    =   PROFILE_CUSTOM_BETAGLUCANASE_TEMPERATURE;
        debranchingTemperature      =   PROFILE_CUSTOM_DEBRANCHING_TEMPERATURE;
        proteolyticTemperature      =   PROFILE_CUSTOM_PROTEOLYTIC_TEMPERATURE;
        betaAmylaseTemperature      =   PROFILE_CUSTOM_BETAAMYLASE_TEMPERATURE;
        alphaAmylaseTemperature     =   PROFILE_CUSTOM_ALPHAAMYLASE_TEMPERATURE;
        mashoutTemperature          =   PROFILE_CUSTOM_MASHOUT_TEMPERATURE;
        recirculationTemperature    =   PROFILE_CUSTOM_RECIRCULATION_TEMPERATURE;
        spargeTemperature           =   PROFILE_CUSTOM_SPARGE_TEMPERATURE;
        boilTemperature             =   PROFILE_CUSTOM_BOIL_TEMPERATURE;
        coolingTemperature          =   PROFILE_CUSTOM_COOLING_TEMPERATURE;
João Lino's avatar
João Lino committed
1438

1439
        printBeerProfile();
João Lino's avatar
João Lino committed
1440 1441 1442
        backToStatus();
        break;
      }
João Lino's avatar
João Lino committed
1443
    case eBeerProfileMenu_Back: {
João Lino's avatar
João Lino committed
1444 1445 1446
        resetMenu( true );
        break;
      }
João Lino's avatar
João Lino committed
1447
    default: {}
João Lino's avatar
João Lino committed
1448
  }
João Lino's avatar
João Lino committed
1449
  mdBeerProfileMenu._selection = eBeerProfileMenu_NULL;
1450 1451
}

João Lino's avatar
João Lino committed
1452 1453 1454
void runStageSelection() {
  switch (mdStageMenu._selection) {
    case eStageMenu_Startpoint: {
João Lino's avatar
João Lino committed
1455 1456 1457
        runStageSelection_Generic( &startpointTime, &startpointTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1458
    case eStageMenu_BetaGlucanase: {
João Lino's avatar
João Lino committed
1459 1460 1461
        runStageSelection_Generic( &betaGlucanaseTime, &betaGlucanaseTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1462
    case eStageMenu_Debranching: {
João Lino's avatar
João Lino committed
1463 1464 1465
        runStageSelection_Generic( &debranchingTime, &debranchingTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1466
    case eStageMenu_Proteolytic: {
João Lino's avatar
João Lino committed
1467 1468 1469
        runStageSelection_Generic( &proteolyticTime, &proteolyticTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1470
    case eStageMenu_BetaAmylase: {
João Lino's avatar
João Lino committed
1471 1472 1473
        runStageSelection_Generic( &betaAmylaseTime, &betaAmylaseTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1474
    case eStageMenu_AlphaAmylase: {
João Lino's avatar
João Lino committed
1475 1476 1477
        runStageSelection_Generic( &alphaAmylaseTime, &alphaAmylaseTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1478
    case eStageMenu_Mashout: {
João Lino's avatar
João Lino committed
1479 1480 1481
        runStageSelection_Generic( &mashoutTime, &mashoutTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1482
    case eStageMenu_Recirculation: {
João Lino's avatar
João Lino committed
1483 1484 1485
        runStageSelection_Generic( &recirculationTime, &recirculationTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1486
    case eStageMenu_Sparge: {
João Lino's avatar
João Lino committed
1487 1488 1489
        runStageSelection_Generic( &spargeTime, &spargeTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1490
    case eStageMenu_Boil: {
João Lino's avatar
João Lino committed
1491 1492 1493
        runStageSelection_Generic( &boilTime, &boilTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1494
    case eStageMenu_Cooling: {
João Lino's avatar
João Lino committed
1495 1496 1497
        runStageSelection_Generic( &coolingTime, &coolingTemperature );
        break;
      }
João Lino's avatar
João Lino committed
1498
    case eStageMenu_Back: {
João Lino's avatar
João Lino committed
1499 1500 1501
        resetMenu( true );
        break;
      }
João Lino's avatar
João Lino committed
1502
    default: {}
João Lino's avatar
João Lino committed
1503
  }
João Lino's avatar
João Lino committed
1504 1505
  mdStageMenu._selection = eStageMenu_NULL;
}
1506

João Lino's avatar
João Lino committed
1507 1508 1509
void runSettingsSelection() {
  switch (mdSettingsMenu._selection) {
    case eSettingsMenu_Pump: {
João Lino's avatar
João Lino committed
1510 1511
        //bool bNewPumpStatus = xSetGenericValue( iPumpSpeed ? 0 : 1, PUMP_SPEED_DEFAULT, 0, 1, "pump", "bool" );
        bool bNewPumpStatus = xSetGenericValue( pump.isPumpOn() ? 1 : 0, PUMP_SPEED_DEFAULT, 0, 1, "pump", "bool" );
João Lino's avatar
João Lino committed
1512
        if ( cancel ) {
João Lino's avatar
João Lino committed
1513 1514
          cancel = false;
        }
João Lino's avatar
João Lino committed
1515 1516
        else {
          if ( bNewPumpStatus ) {
João Lino's avatar
João Lino committed
1517 1518
            pump.setTargetPumpSpeed(pump.getMaxSpeed());
            //iPumpSpeed = PUMP_SPEED_MAX_MOSFET;
João Lino's avatar
João Lino committed
1519
          } else {
João Lino's avatar
João Lino committed
1520 1521
            pump.setTargetPumpSpeed(pump.getNullSpeed());
            //iPumpSpeed = PUMP_SPEED_STOP_MOSFET;
João Lino's avatar
João Lino committed
1522
          }
João Lino's avatar
João Lino committed
1523 1524
          //analogWrite(PUMP_PIN, iPumpSpeed);
          pump.process();
João Lino's avatar
João Lino committed
1525
        }
João Lino's avatar
João Lino committed
1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
        backToStatus();
        break;
      }
    case eSettingsMenu_PT100_Element: {
        backToStatus();
        break;
      }
    case eSettingsMenu_PT100_Up: {
        backToStatus();
        break;
      }
    case eSettingsMenu_PT100_Down: {
        backToStatus();
        break;
João Lino's avatar
João Lino committed
1540
      }
João Lino's avatar
João Lino committed
1541
    case eSettingsMenu_Back: {
João Lino's avatar
João Lino committed
1542 1543 1544
        resetMenu( true );
        break;
      }
João Lino's avatar
João Lino committed
1545
    default: {}
João Lino's avatar
João Lino committed
1546
  }
João Lino's avatar
João Lino committed
1547
  mdSettingsMenu._selection = eSettingsMenu_NULL;
João Lino's avatar
Rel.3  
João Lino committed
1548 1549
}

João Lino's avatar
João Lino committed
1550 1551 1552 1553 1554
void runMaltSelection() {
  switch (mdMaltMenu._selection) {
    case eMaltMenu_CastleMalting_Chteau_Pilsen_2RS: {
        backToStatus();
        break;
João Lino's avatar
João Lino committed
1555
      }
João Lino's avatar
João Lino committed
1556 1557 1558
    case eMaltMenu_CastleMalting_Wheat_Blanc: {
        backToStatus();
        break;
João Lino's avatar
João Lino committed
1559
      }
João Lino's avatar
João Lino committed
1560
    case eMaltMenu_Back: {
João Lino's avatar
João Lino committed
1561 1562 1563
        resetMenu( true );
        break;
      }
João Lino's avatar
João Lino committed
1564
    default: {}
João Lino's avatar
Rel.3  
João Lino committed
1565
  }
João Lino's avatar
João Lino committed
1566
  mdMaltMenu._selection = eMaltMenu_NULL;
1567 1568
}

João Lino's avatar
João Lino committed
1569 1570 1571
// ************************ MENU HELPERS ******************************
void runMenuProcessor( MenuData *data ) {
  data->_position = rotaryEncoderVirtualPosition;         // Read position
1572

João Lino's avatar
João Lino committed
1573 1574
  data->_repaint = repaint;                               // Request repaint
  repaint = displayGenericMenu( &lcd, data );             // Display menu
João Lino's avatar
Rel.3  
João Lino committed
1575

João Lino's avatar
João Lino committed
1576
  if ( checkForEncoderSwitchPush( true ) ) {              // Read selection
João Lino's avatar
João Lino committed
1577
    if ( cancel ) {
João Lino's avatar
João Lino committed
1578 1579 1580
      resetMenu( true );
      return;
    }
João Lino's avatar
João Lino committed
1581
    data->_selection = data->_position;
1582
  }
João Lino's avatar
João Lino committed
1583

João Lino's avatar
João Lino committed
1584
  (data->_selectionFunction)();                           // Run selection function
1585 1586
}

João Lino's avatar
João Lino committed
1587
void runStageSelection_Generic( unsigned long * selectedStageTime, int *selectedStageTemperature) {
João Lino's avatar
João Lino committed
1588 1589
  unsigned long selectedStageTimeStorage = *selectedStageTime;
  int selectedStageTemperatureStorage = *selectedStageTemperature;
João Lino's avatar
João Lino committed
1590

João Lino's avatar
João Lino committed
1591
  *selectedStageTime = getTimer( *selectedStageTime );
João Lino's avatar
João Lino committed
1592
  if ( cancel ) {
João Lino's avatar
João Lino committed
1593 1594 1595
    *selectedStageTime = selectedStageTimeStorage;
    cancel = false;
  }
João Lino's avatar
João Lino committed
1596 1597
  else {
    *selectedStageTemperature = getTemperature( *selectedStageTemperature );
João Lino's avatar
João Lino committed
1598
    if ( cancel ) {
João Lino's avatar
João Lino committed
1599 1600 1601 1602
      *selectedStageTime = selectedStageTimeStorage;
      *selectedStageTemperature = selectedStageTemperatureStorage;
      cancel = false;
    }
João Lino's avatar
João Lino committed
1603
  }
João Lino's avatar
João Lino committed
1604
  backToStatus();
1605 1606
}

João Lino's avatar
João Lino committed
1607 1608
void xStartStageHeadless( eCookingStages nextStage, bool bPurgePump ) {
  xStartStage( NULL, NULL, nextStage, bPurgePump, false, false, false );
João Lino's avatar
Rel.3  
João Lino committed
1609 1610
}

João Lino's avatar
João Lino committed
1611 1612
void xStartStageInteractive( unsigned long *stageTime, int *stageTemperature, eCookingStages nextStage ) {
  xStartStage( stageTime, stageTemperature, nextStage, true, true, true, true );
1613 1614
}

João Lino's avatar
João Lino committed
1615
void xStartStage( unsigned long *stageTime, int *stageTemperature, eCookingStages nextStage, bool bPurgePump, bool bSetFinalYield, bool bSetTime, bool bSetTemperature ) {
João Lino's avatar
João Lino committed
1616 1617 1618
  int finalYieldStorage = finalYield;
  unsigned long stageTimeStorage = *stageTime;
  int stageTemperatureStorage = *stageTemperature;
João Lino's avatar
João Lino committed
1619

João Lino's avatar
João Lino committed
1620
  if (bSetFinalYield) {
João Lino's avatar
João Lino committed
1621
    finalYield = getFinalYield( finalYield, SETTING_MACHINE_YIELD_DEFAULT );
João Lino's avatar
João Lino committed
1622
    if ( cancel ) {
João Lino's avatar
João Lino committed
1623
      finalYield = finalYieldStorage;
João Lino's avatar
João Lino committed
1624

João Lino's avatar
João Lino committed
1625
      cancel = false;
João Lino's avatar
João Lino committed
1626
      backToStatus();
João Lino's avatar
João Lino committed
1627 1628
      return;
    }
João Lino's avatar
Rel.3  
João Lino committed
1629
  }
João Lino's avatar
João Lino committed
1630
  if (bSetTime) {
João Lino's avatar
João Lino committed
1631
    (*stageTime) = getTimer( clockCounter / 1000, *stageTime );
João Lino's avatar
João Lino committed
1632
    if ( cancel ) {
João Lino's avatar
João Lino committed
1633 1634
      finalYield = finalYieldStorage;
      *stageTime = stageTimeStorage;
João Lino's avatar
João Lino committed
1635

João Lino's avatar
João Lino committed
1636
      cancel = false;
João Lino's avatar
João Lino committed
1637
      backToStatus();
João Lino's avatar
João Lino committed
1638 1639
      return;
    }
João Lino's avatar
Rel.3  
João Lino committed
1640
  }
João Lino's avatar
João Lino committed
1641
  if (bSetTemperature) {
João Lino's avatar
João Lino committed
1642
    (*stageTemperature) = getTemperature( cookTemperature, *stageTemperature );
João Lino's avatar
João Lino committed
1643
    if ( cancel ) {
João Lino's avatar
João Lino committed
1644 1645 1646
      finalYield = finalYieldStorage;
      *stageTime = stageTimeStorage;
      *stageTemperature = stageTemperatureStorage;
João Lino's avatar
João Lino committed
1647

João Lino's avatar
João Lino committed
1648
      cancel = false;
João Lino's avatar
João Lino committed
1649
      backToStatus();
João Lino's avatar
João Lino committed
1650 1651
      return;
    }
João Lino's avatar
Rel.3  
João Lino committed
1652
  }
João Lino's avatar
João Lino committed
1653

João Lino's avatar
João Lino committed
1654
  xSafeHardwarePowerOff();                      // Stop anything that might be still going on
João Lino's avatar
João Lino committed
1655 1656
  if (bPurgePump) {
    xPurgePump();
1657
  }
João Lino's avatar
João Lino committed
1658 1659 1660
  startBrewing();
  xSetupStage( nextStage );
  backToStatus();
1661
}
João Lino's avatar
João Lino committed
1662
// ##################################################### Menus ###################################################################
1663 1664 1665 1666

// #################################################### Helpers ##################################################################

void startBrewing() {
1667 1668
  //sing(MELODY_SUPER_MARIO, PIEZO_PIN);

1669 1670 1671 1672 1673
  cooking = true;
}

void stopBrewing() {
  cooking = false;
1674
  xSafeHardwarePowerOff();
1675 1676
}

João Lino's avatar
João Lino committed
1677 1678 1679
void resetMenu( boolean requestRepaintPaint ) {
  eMenuType = eMenuType_Main;

1680
  if ( requestRepaintPaint ) {
João Lino's avatar
João Lino committed
1681 1682
    repaint = true;
  }
1683

João Lino's avatar
João Lino committed
1684
  xSetupRotaryEncoder( eRotaryEncoderMode_Menu, mdMainMenu._position, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );       // reset operation state
João Lino's avatar
João Lino committed
1685 1686
}

1687 1688
void backToStatus() {
  lastInterruptTime = millis() - SETTING_MAX_INACTIVITY_TIME - 1;
João Lino's avatar
João Lino committed
1689
  resetMenu(true);
1690 1691 1692 1693
}
// #################################################### Helpers ##################################################################

// #################################################### Set Variables ##################################################################
João Lino's avatar
João Lino committed
1694

João Lino's avatar
João Lino committed
1695 1696 1697 1698 1699 1700
int getTemperature(int initialValue ) {
  return getTemperature( initialValue, initialValue );
}
int getTemperature(int initialValue, int defaultValue ) {
  return xSetGenericValue( initialValue, defaultValue, TEMPERATURE_MIN_VALUE, TEMPERATURE_MAX_VALUE, MENU_GLOBAL_STR_TEMPERATURE, MENU_GLOBAL_STR_CELSIUS );
}
João Lino's avatar
João Lino committed
1701

João Lino's avatar
João Lino committed
1702 1703 1704 1705 1706 1707
int getFinalYield( int initialValue ) {
  return getFinalYield( initialValue, SETTING_MACHINE_YIELD_DEFAULT );
}
int getFinalYield( int initialValue, int defaultValue ) {
  return xSetGenericValue( initialValue, defaultValue, SETTING_MACHINE_YIELD_CAPACITY_MIN, SETTING_MACHINE_YIELD_CAPACITY_MAX, "Final Yield", "l" );
}
João Lino's avatar
João Lino committed
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727

int xSetGenericValue(int initialValue, int defaultValue, int minimumValue, int maximumValue, const char *valueName, const char *unit) {
  xSetupRotaryEncoder( eRotaryEncoderMode_Generic, initialValue, maximumValue, minimumValue, 1, 5 );

  // initialize variables
  int rotaryEncoderPreviousPosition = 0;

  // Setup Screen
  lcd.clear();
  lcd.home();
  lcd.print( "Set " );
  lcd.print( valueName );
  lcd.print( "(" );
  lcd.print( defaultValue );
  lcd.print( ")" );
  lcd.setCursor ( 0 , LCD_VERTICAL_RESOLUTION - 1 );
  lcd.print( "       0 " );
  lcd.print( unit );

  while (true) {
João Lino's avatar
João Lino committed
1728 1729
    if ( checkForEncoderSwitchPush( true ) ) {                  // Check if pushbutton is pressed
      if ( cancel ) return rotaryEncoderVirtualPosition;
João Lino's avatar
João Lino committed
1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763
      break;
    }
    else {
      xManageMachineSystems();                            // Don't forget to keep an eye on the cooking
    }

    // Check if there was an update by the rotary encoder
    if ( rotaryEncoderVirtualPosition != rotaryEncoderPreviousPosition ) {
      rotaryEncoderPreviousPosition = rotaryEncoderVirtualPosition;

      lcd.setCursor( 0, LCD_VERTICAL_RESOLUTION - 1 );
      lcd.print( "     " );
      if ( rotaryEncoderVirtualPosition < 10 ) {
        lcd.print( "  " );
      }
      else {
        if ( rotaryEncoderVirtualPosition < 100 ) {
          lcd.print( " " );
        }
      }
      lcd.print( rotaryEncoderVirtualPosition );
      lcd.print( " " );
      lcd.print( unit );
      lcd.println( "                " );
    }
  }

  return rotaryEncoderVirtualPosition;
}

int getTimer( int initialValue ) {
  return getTimer( initialValue, initialValue );
}

João Lino's avatar
João Lino committed
1764
int getTimer( int initialValue, int defaultValue ) {
João Lino's avatar
João Lino committed
1765
  // set operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
João Lino committed
1766
  xSetupRotaryEncoder( eRotaryEncoderMode_Time, initialValue, 7200, 0, 1, 30 );
1767 1768

  // initialize variables
service-config's avatar
Mixer  
service-config committed
1769
  int rotaryEncoderPreviousPosition = 0;
1770 1771
  int minutes = 0;
  int seconds = 0;
1772

1773 1774
  // Setup Screen
  lcd.clear();
1775
  lcd.home();
João Lino's avatar
João Lino committed
1776
  lcd.print("Set Time (");
1777
  minutes = defaultValue / 60;
João Lino's avatar
João Lino committed
1778
  lcd.print(minutes);
1779
  seconds = defaultValue - minutes * 60;
João Lino's avatar
João Lino committed
1780
  lcd.print(":");
1781
  if (seconds < 10) {
João Lino's avatar
João Lino committed
1782 1783 1784 1785
    lcd.print("0");
  }
  lcd.print(seconds);
  lcd.print(")");
1786
  lcd.setCursor (0, LCD_VERTICAL_RESOLUTION - 1);
1787
  lcd.print("      0:00");
1788 1789

  while (true) {
João Lino's avatar
João Lino committed
1790 1791
    if ( checkForEncoderSwitchPush( true ) ) {
      if ( cancel ) return rotaryEncoderVirtualPosition;
1792
      break;
João Lino's avatar
João Lino committed
1793 1794 1795
    }
    else {
      xManageMachineSystems();                            // Don't forget to keep an eye on the cooking
João Lino's avatar
João Lino committed
1796
    }
1797

1798
    // display current timer
service-config's avatar
Mixer  
service-config committed
1799 1800
    if (rotaryEncoderVirtualPosition != rotaryEncoderPreviousPosition) {
      rotaryEncoderPreviousPosition = rotaryEncoderVirtualPosition;
1801 1802 1803 1804 1805 1806
      minutes = rotaryEncoderVirtualPosition / 60;
      seconds = rotaryEncoderVirtualPosition - minutes * 60;

      lcd.setCursor (0, LCD_VERTICAL_RESOLUTION - 1);

      if (minutes < 100) {
João Lino's avatar
João Lino committed
1807 1808
        lcd.print(" ");
      }
1809
      if (minutes < 10) {
João Lino's avatar
João Lino committed
1810 1811 1812
        lcd.print(" ");
      }
      lcd.print("    ");
1813 1814
      lcd.print(minutes);
      lcd.print(":");
1815
      if (seconds < 10) {
1816 1817 1818 1819 1820 1821
        lcd.print("0");
      }
      lcd.print(seconds);
      lcd.println("                ");
    }
  }
1822

service-config's avatar
Mixer  
service-config committed
1823
  return rotaryEncoderVirtualPosition;
1824 1825
}

João Lino's avatar
João Lino committed
1826 1827 1828 1829 1830 1831
boolean checkForEncoderSwitchPush( bool cancelable ) {
  boolean gotPush = digitalRead(ROTARY_ENCODER_SW_PIN);
  if (gotPush) {           // Check if pushbutton is pressed
    unsigned long cancleTimer = millis();
    while (digitalRead(ROTARY_ENCODER_SW_PIN)) {        // Wait until switch is released
      delay(ROTARY_ENCODER_SW_DEBOUNCE_TIME);           // debounce
João Lino's avatar
João Lino committed
1832 1833

      if ( ((millis() - cancleTimer) >= SETTING_CANCEL_TIMER ) && cancelable ) {
João Lino's avatar
João Lino committed
1834
        sing(BUZZ_1, PIEZO_PIN);
1835 1836
      }
    }
1837

João Lino's avatar
João Lino committed
1838
    if ( ((millis() - cancleTimer) >= SETTING_CANCEL_TIMER) && cancelable ) {
João Lino's avatar
João Lino committed
1839
      cancel = true;
1840 1841
    }
  }
1842

João Lino's avatar
João Lino committed
1843
  return gotPush;
João Lino's avatar
João Lino committed
1844 1845
}

João Lino's avatar
João Lino committed
1846 1847 1848 1849
unsigned long getInactivityTime() {
  unsigned long now = millis();
  unsigned long rotaryEncoderInactivityTime = now - lastInterruptTime;

1850
  if (rotaryEncoderInactivityTime > SETTING_MAX_INACTIVITY_TIME) {
João Lino's avatar
João Lino committed
1851
    if (checkForEncoderSwitchPush( false )) {
1852 1853 1854 1855 1856 1857
      now = millis();
      rotaryEncoderInactivityTime = now - lastInterruptTime;
      rotarySwDetectTime = now;

      repaint = true;
      refresh = true;
João Lino's avatar
João Lino committed
1858 1859
    }
  }
1860

João Lino's avatar
João Lino committed
1861 1862 1863 1864
  unsigned long switchInactivityTime = now - rotarySwDetectTime;
  return rotaryEncoderInactivityTime > switchInactivityTime ? switchInactivityTime : rotaryEncoderInactivityTime ;
}

1865
// ###################### Set Variables ##################################################
1866

João Lino's avatar
Rel.3  
João Lino committed
1867
void xWaitForAction(String title, String message) {
1868 1869
  unsigned long now = millis();
  unsigned long warningBeepTimeInterval = 0;
1870
  while (true) {
1871 1872
    now = millis();
    
João Lino's avatar
João Lino committed
1873
    if ( checkForEncoderSwitchPush( false ) ) {                  // Check if pushbutton is pressed
João Lino's avatar
Rel.3  
João Lino committed
1874
      break;
João Lino's avatar
João Lino committed
1875 1876
    }
    else {
João Lino's avatar
Rel.3  
João Lino committed
1877
      // Print the message
1878
      if (! lcdPrint(&lcd, title, message)) {
João Lino's avatar
Rel.3  
João Lino committed
1879 1880
        break;
      }
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976

      unsigned long now = millis();
      if( now - warningBeepTimeInterval > SETTING_WARNING_BEEP_INTERVAL ) {
        warningBeepTimeInterval = now;
        sing(BUZZ_2, PIEZO_PIN);
      }
    }
  }
}

void printBeerProfile( void ) {
  #ifdef INFO
  Serial.println("############# Beer Profile #############");
  Serial.print("#  beerProfile       ");
  Serial.print( mdBeerProfileMenu._dialog[beerProfile+1] );
  Serial.println("  #");
  Serial.println("############ Time per Stage ############");
  Serial.print("#  startpointTime              ");
  printTime(startpointTime           );
  Serial.println("  #");
  Serial.print("#  betaGlucanaseTime           ");
  printTime(betaGlucanaseTime        );
  Serial.println("  #");
  Serial.print("#  debranchingTime             ");
  printTime(debranchingTime          );
  Serial.println("  #");
  Serial.print("#  proteolyticTime             ");
  printTime(proteolyticTime          );
  Serial.println("  #");
  Serial.print("#  betaAmylaseTime             ");
  printTime(betaAmylaseTime          );
  Serial.println("  #");
  Serial.print("#  alphaAmylaseTime            ");
  printTime(alphaAmylaseTime         );
  Serial.println("  #");
  Serial.print("#  mashoutTime                 ");
  printTime(mashoutTime              );
  Serial.println("  #");
  Serial.print("#  recirculationTime           ");
  printTime(recirculationTime        );
  Serial.println("  #");
  Serial.print("#  spargeTime                  ");
  printTime(spargeTime               );
  Serial.println("  #");
  Serial.print("#  boilTime                    ");
  printTime(boilTime                 );
  Serial.println("  #");
  Serial.print("#  coolingTime                 ");
  printTime(coolingTime              );
  Serial.println("  #");
  Serial.println("######## Teperature per Stage ##########");
  Serial.print("#  startpointTemperature       ");
  printTemperature(startpointTemperature    );
  Serial.println("  #");
  Serial.print("#  betaGlucanaseTemperature    ");
  printTemperature(betaGlucanaseTemperature );
  Serial.println("  #");
  Serial.print("#  debranchingTemperature      ");
  printTemperature(debranchingTemperature   );
  Serial.println("  #");
  Serial.print("#  proteolyticTemperature      ");
  printTemperature(proteolyticTemperature   );
  Serial.println("  #");
  Serial.print("#  betaAmylaseTemperature      ");
  printTemperature(betaAmylaseTemperature   );
  Serial.println("  #");
  Serial.print("#  alphaAmylaseTemperature     ");
  printTemperature(alphaAmylaseTemperature  );
  Serial.println("  #");
  Serial.print("#  mashoutTemperature          ");
  printTemperature(mashoutTemperature       );
  Serial.println("  #");
  Serial.print("#  recirculationTemperature    ");
  printTemperature(recirculationTemperature );
  Serial.println("  #");
  Serial.print("#  spargeTemperature           ");
  printTemperature(spargeTemperature        );
  Serial.println("  #");
  Serial.print("#  boilTemperature             ");
  printTemperature(boilTemperature          );
  Serial.println("  #");
  Serial.print("#  coolingTemperature          ");
  printTemperature(coolingTemperature       );
  Serial.println("  #");
  Serial.println("########################################");
  if( beerProfile == eBeerProfile_Trigo) {
    Serial.print("#  finalYield                    ");
    if(finalYield < 10) {
      Serial.print(" ");
    }
    Serial.print(finalYield);
    Serial.println(" l  #");
    float wheatAmount = PROFILE_TRIGO_WHEAT_MULTIPLIER * ((float) finalYield);
    Serial.print("#  wheatAmount               ");
    if(wheatAmount < 10) {
      Serial.print(" ");
1977
    }
1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
    Serial.print(wheatAmount);
    Serial.println(" Kg  #");
    float pilsnerAmount = PROFILE_TRIGO_PILSNER_MULTIPLIER * ((float) finalYield);
    Serial.print("#  pilsnerAmount             ");
    if(pilsnerAmount < 10) {
      Serial.print(" ");
    }
    Serial.print(pilsnerAmount);
    Serial.println(" Kg  #");
    float strikeWaterAmount = (pilsnerAmount + wheatAmount) * PROFILE_TRIGO_MASH_THICKNESS;
    Serial.print("#  strikeWaterAmount          ");
    if(strikeWaterAmount < 10) {
      Serial.print(" ");
    }
    Serial.print(strikeWaterAmount);
    Serial.println(" l  #");
    float spargeWaterAmount = PROFILE_TRIGO_SPARGE_WATER_MULTIPLIER * ((float) finalYield);
    Serial.print("#  spargeWaterAmount          ");
    if(spargeWaterAmount < 10) {
      Serial.print(" ");
    }
    Serial.print(spargeWaterAmount);
    Serial.println(" l  #");
    float herkulesAmount = PROFILE_TRIGO_HERKULES_MULTIPLIER * ((float) finalYield);
    Serial.print("#  herkulesAmount             ");
    if(herkulesAmount < 10) {
      Serial.print(" ");
    }
    Serial.print(herkulesAmount);
    Serial.println(" g  #");
    float traditionAmount = PROFILE_TRIGO_TRADITION_MULTIPLIER * ((float) finalYield);
    Serial.print("#  traditionAmount            ");
    if(traditionAmount < 10) {
      Serial.print(" ");
    }
    Serial.print(traditionAmount);
    Serial.println(" g  #");
    Serial.println("########################################");
  }
#endif
}

void printTime( unsigned long timeToPrint ) {
  unsigned long minutes = timeToPrint /60;
  unsigned long seconds = timeToPrint %60;

  if (minutes < 100) {
    Serial.print(" ");
  }
  if (minutes < 10) {
    Serial.print(" ");
  }
  Serial.print(minutes);
  Serial.print(":");
  if (seconds < 10) {
    Serial.print("0");
  }
  Serial.print(seconds);
}

void printTemperature( int temparatureToPrint ) {
  if (temparatureToPrint < 100) {
    Serial.print(" ");
  }
  if (temparatureToPrint < 10) {
    Serial.print(" ");
2044
  }
2045 2046 2047 2048
  Serial.print(temparatureToPrint);
  Serial.print(" ");
  Serial.write(176);
  Serial.print("C");
2049
}
2050