brew.ino 62.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
Rel.3  
João Lino committed
7
#define DEBUG
8

9
// ######################### LIBRARIES #########################
João Lino's avatar
Rel.3  
João Lino committed
10

11 12 13 14
// ++++++++++++++++++++++++ LiquidCrystal_I2C ++++++++++++++++++++++++
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
15

16 17 18
// ++++++++++++++++++++++++ PT100 +++++++++++++++++++++++++++++++++
#include <PT100.h>

João Lino's avatar
Rel.3  
João Lino committed
19 20 21 22 23
// ++++++++++++++++++++++++ OTHER +++++++++++++++++++++++++++++++++
#include "debug.h"

#include "config.h"

24
#include "CustomDataStructures.h"
service-config's avatar
Mixer  
service-config committed
25

João Lino's avatar
Rel.3  
João Lino committed
26 27
#include "Melody.h"
#include "Display.h"
28

29
// ######################### VARIABLES #########################
30
// ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++
31
eRotaryEncoderMode      rotaryEncoderMode;
João Lino's avatar
Rel.3  
João Lino committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

eCookingStages          cookingStage;
eBeerProfile            beerProfile;

eMenuType               eMenuType;

eMainMenuOptions        eMainMenuPosition;
eMainMenuOptions        eMainMenuSelection;
eBeerProfileMenuOptions eBeerProfileMenuPosition;
eBeerProfileMenuOptions eBeerProfileMenuSelection;
eStageMenuOptions       eStageMenuPosition;
eStageMenuOptions       eStageMenuSelection;
eMaltMenuOptions        eMaltMenuPosition;
eMaltMenuOptions        eMaltMenuSelection; 
eSettingsMenuOptions    eSettingsMenuPosition;
eSettingsMenuOptions    eSettingsMenuSelection;

49 50 51
eMaltMenuOptions        maltMenuOption;
eSettingsMenuOptions    settingsMenuOption;

52
// ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
53 54 55
boolean                 cooking;
boolean                 bStageFirstRun;

João Lino's avatar
Rel.3  
João Lino committed
56 57 58 59
unsigned long           clockStartTime;
unsigned long           clockLastUpdate;
long                    clockCounter;
unsigned long           clockIgnore;
João Lino's avatar
João Lino committed
60 61 62
boolean                 clockStart;
boolean                 clockEnd;

João Lino's avatar
Rel.3  
João Lino committed
63
unsigned long           cookTime;
João Lino's avatar
João Lino committed
64 65 66
int                     cookTemperature;
//cook_mode_list        cookMode;
//int                   cookMixerSpeed;
João Lino's avatar
Rel.3  
João Lino committed
67
int                     finalYield;
João Lino's avatar
João Lino committed
68
        
João Lino's avatar
Rel.3  
João Lino committed
69 70 71 72 73 74 75 76 77 78 79 80
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;
João Lino's avatar
João Lino committed
81 82 83 84 85 86 87 88 89 90 91 92
        
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
93
int                     cleaningTemperature;
João Lino's avatar
João Lino committed
94 95 96

boolean                 refresh;
boolean                 repaint;
97

João Lino's avatar
Rel.3  
João Lino committed
98 99
boolean                 bStatusElement;

100
// ++++++++++++++++++++++++ Interrupts ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
101
static unsigned long    lastInterruptTime;
102

103
// ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
104 105 106 107 108
volatile int            rotaryEncoderVirtualPosition = 0;
volatile int            rotaryEncoderMaxPosition = 1;
volatile int            rotaryEncoderMinPosition = 0;
volatile int            rotaryEncoderSingleStep = 1;
volatile int            rotaryEncoderMultiStep = 1;
109

João Lino's avatar
João Lino committed
110 111
volatile boolean        onISR = false;

112
// ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
113 114 115
int                     iWindowSize;             // Time frame to operate in
unsigned long           windowStartTime;
double                  dWattPerPulse;
116

117 118 119
// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
int                     iPumpSpeed;             // Time frame to operate in

120
// ######################### INITIALIZE #########################
121
// ++++++++++++++++++++++++ Library - LiquidCrystal_I2C ++++++++++++++++++++++++
João Lino's avatar
João Lino committed
122
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);
123 124

// +++++++++++++++++++++++ PT100 +++++++++++++++++++++++
João Lino's avatar
Rel.3  
João Lino committed
125 126 127
PT100                   basePT100("base", PT100_BASE_OUTPUT_PIN, PT100_BASE_OUTPUT_R_PIN, PT100_BASE_INPUT_PIN, PT100_BASE_INPUT_R_PIN, PT100_BASE_TIME_BETWEEN_READINGS, PT100_BASE_DEFAULT_ADC_VMAX, PT100_BASE_DEFAULT_VS, PT100_BASE_DEFAULT_R1_RESISTENCE, PT100_BASE_DEFAULT_R2_RESISTENCE, 0.0, 0.0);//0.0, 0.0);
PT100                   upPT100("up", PT100_UP_OUTPUT_PIN, PT100_UP_OUTPUT_R_PIN, PT100_UP_INPUT_PIN, PT100_UP_INPUT_R_PIN, PT100_UP_TIME_BETWEEN_READINGS, PT100_UP_DEFAULT_ADC_VMAX, PT100_UP_DEFAULT_VS, PT100_UP_DEFAULT_R1_RESISTENCE, PT100_UP_DEFAULT_R2_RESISTENCE, -0.2, 8.0);//8.0);//0.17,  -7.2); //0.195, -7.6);//0.0, 0.0);//0.38, -3.0);  //0.112329092, -3.57); //0.0, 0.0);
PT100                   downPT100("down", PT100_DOWN_OUTPUT_PIN, PT100_DOWN_OUTPUT_R_PIN, PT100_DOWN_INPUT_PIN, PT100_DOWN_INPUT_R_PIN, PT100_DOWN_TIME_BETWEEN_READINGS, PT100_DOWN_DEFAULT_ADC_VMAX, PT100_DOWN_DEFAULT_VS, PT100_DOWN_DEFAULT_R1_RESISTENCE, PT100_DOWN_DEFAULT_R2_RESISTENCE, 0.0, -2.2);//0.228, -9); //0.26,  -10);//0.0, 0.0);//0.53, -6.6);  //0.22, -5.5); //0.0, 0.0);
128 129

// ######################### INTERRUPTS #########################
João Lino's avatar
João Lino committed
130
void isr ()  {    // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK
131
  unsigned long interruptTime = millis();
João Lino's avatar
João Lino committed
132 133
  unsigned long diff = interruptTime - lastInterruptTime;
  lastInterruptTime = interruptTime;
134
  
135
  // 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
136
  if (diff > ROTARY_ENCODER_DEBOUNCE_TIME) {
137
    switch(rotaryEncoderMode) {
138
  
139 140 141 142
      // Input of rotary encoder controling menus
      case eRotaryEncoderMode_Menu: {
        if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
143 144
        }
        else {
145
            rotaryEncoderVirtualPosition = rotaryEncoderVirtualPosition - rotaryEncoderSingleStep;
146
        }
147 148
        if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
149
        }
150 151
        if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
152 153 154 155
        }
        
        break;
      }
service-config's avatar
Mixer  
service-config committed
156
      
157 158
      // Input of rotary encoder controling time variables
      case eRotaryEncoderMode_Time: {
João Lino's avatar
João Lino committed
159
        if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
service-config's avatar
Mixer  
service-config committed
160
          if(rotaryEncoderVirtualPosition >= 60) {
161
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderMultiStep);
162 163
          }
          else {
164
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
165 166 167
          }
        }
        else {
168
          if(rotaryEncoderVirtualPosition == rotaryEncoderMinPosition) {
service-config's avatar
Mixer  
service-config committed
169
            rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + 60);
170 171
          }
          else {
172 173
            if(rotaryEncoderVirtualPosition >= (60 + rotaryEncoderMultiStep)) {
              rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition - rotaryEncoderMultiStep);
174 175
            }
            else {
176
              rotaryEncoderVirtualPosition = rotaryEncoderVirtualPosition - rotaryEncoderSingleStep;
177 178 179
            }
          }
        }
180 181
        if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
182
        }
183 184
        if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
185 186 187 188
        }
        
        break;
      }
service-config's avatar
Mixer  
service-config committed
189
      
190 191 192
      // Input of rotary encoder controling generic integer variables within a range between rotaryEncoderMinPosition and rotaryEncoderMaxPosition
      case eRotaryEncoderMode_Generic: {
        if (!digitalRead(ROTARY_ENCODER_DT_PIN)) {
193
          rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition + rotaryEncoderSingleStep);
194 195
        }
        else {
196
          rotaryEncoderVirtualPosition = (rotaryEncoderVirtualPosition - rotaryEncoderSingleStep);
197
        }
service-config's avatar
Mixer  
service-config committed
198 199
        if (rotaryEncoderVirtualPosition > rotaryEncoderMaxPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMaxPosition;
200
        }
201 202
        if (rotaryEncoderVirtualPosition < rotaryEncoderMinPosition) {
            rotaryEncoderVirtualPosition = rotaryEncoderMinPosition;
service-config's avatar
Mixer  
service-config committed
203 204 205 206
        }
       
        break;
      }
207 208 209 210 211 212
      default: {
        
      }
    }
  }
  
João Lino's avatar
João Lino committed
213 214
  repaint = true;
  refresh = true;
215 216
}

217 218 219 220 221 222 223 224 225
void xSetupRotaryEncoder( eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep ) {
  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
// ######################### START #########################
227
void xSafeHardwarePowerOff() {
228 229 230 231 232
  // Turn off gracefully
  iPumpSpeed = PUMP_SPEED_STOP;
  xRegulatePumpSpeed();

  // Force shutdown
233
  analogWrite(PUMP_PIN, PUMP_SPEED_STOP);  // analogWrite values from 0 to 255
João Lino's avatar
João Lino committed
234
  digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, LOW);  // Turn heading element OFF for safety
João Lino's avatar
Rel.3  
João Lino committed
235 236 237 238 239
  bStatusElement = false;

  basePT100.setSampleDeviation( 0.0 );
  upPT100.setSampleDeviation( 0.0 );
  downPT100.setSampleDeviation( 0.0 );
240

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

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

248 249 250
  // Play Melody;
  sing(MELODY_SUPER_MARIO_START, PIEZO_PIN);

251
  //termometerCalibration();
João Lino's avatar
João Lino committed
252
  delay(SETTING_WELCOME_TIMEOUT);      // pause for effect
João Lino's avatar
Rel.3  
João Lino committed
253
  //#endif
254 255
}

256
void setup() {
João Lino's avatar
João Lino committed
257 258 259 260 261 262 263 264 265
  // ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
  pinMode                         (ROTARY_ENCODER_CLK_PIN,INPUT);
  pinMode                         (ROTARY_ENCODER_DT_PIN, INPUT);
  pinMode                         (ROTARY_ENCODER_SW_PIN, INPUT);
  attachInterrupt                 (ROTARY_ENCODER_INTERRUPT_NUMBER, isr, FALLING);

  // ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++
  pinMode                         (HEATING_ELEMENT_OUTPUT_PIN, OUTPUT);
  digitalWrite                    (HEATING_ELEMENT_OUTPUT_PIN, LOW);
João Lino's avatar
Rel.3  
João Lino committed
266
  bStatusElement              =   false;
João Lino's avatar
João Lino committed
267 268 269 270 271 272 273
  windowStartTime             =   millis();
  dWattPerPulse               =   HEATING_ELEMENT_MAX_WATTAGE / HEATING_ELEMENT_AC_FREQUENCY_HZ;

  // ++++++++++++++++++++++++ Mixer ++++++++++++++++++++++++
  //  pinMode    (MIXER_PIN, OUTPUT);
  //  analogWrite    (MIXER_PIN, 0);

274 275 276 277 278
  // ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
  pinMode(PUMP_PIN, OUTPUT);   // sets the pin as output
  iPumpSpeed                  =   PUMP_SPEED_STOP;             // Time frame to operate in
  analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255

279 280 281
  // ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
  pinMode(PIEZO_PIN, OUTPUT);

João Lino's avatar
João Lino committed
282 283
  // ++++++++++++++++++++++++ Temperature Sensor PT100 ++++++++++++++++++++++++
  //basePT100.setup();
João Lino's avatar
Rel.3  
João Lino committed
284
  /*
João Lino's avatar
João Lino committed
285 286 287
  analogReference  (INTERNAL1V1);          // EXTERNAL && INTERNAL2V56 && INTERNAL1V1
  pinMode    (PT100_OUTPUT_PIN, OUTPUT);  // setup temperature sensor input pin
  digitalWrite    (PT100_OUTPUT_PIN, LOW);    // initialize sensor off
João Lino's avatar
Rel.3  
João Lino committed
288
  */
João Lino's avatar
João Lino committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
  // ++++++++++++++++++++++++ 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 ++++++++++++++++++++++++
  lcd.begin                       (LCD_HORIZONTAL_RESOLUTION,LCD_VERTICAL_RESOLUTION);    //  <<----- My LCD was 16x2
  lcd.setBacklightPin             (LCD_BACKLIGHT_PIN,POSITIVE);        // Setup backlight pin
  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 );

  // ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++
João Lino's avatar
Rel.3  
João Lino committed
304 305 306 307 308 309 310 311 312 313 314 315 316
  eMenuType                   =   eMenuType_Main;
  
  eMainMenuPosition           =   eMainMenu_GO;
  eMainMenuSelection          =   eMainMenu_NULL;
  eBeerProfileMenuPosition    =   eBeerProfileMenu_Basic;
  eBeerProfileMenuSelection   =   eBeerProfileMenu_NULL;
  eStageMenuPosition          =   eStageMenu_Startpoint;
  eStageMenuSelection         =   eStageMenu_NULL;
  eMaltMenuPosition           =   eMaltMenu_CastleMalting_Chteau_Pilsen_2RS;
  eMaltMenuSelection          =   eMaltMenu_NULL;
  eSettingsMenuPosition       =   eSettingsMenu_PT100_Element;
  eSettingsMenuSelection      =   eSettingsMenu_NULL;

João Lino's avatar
João Lino committed
317
  cookingStage                =   eCookingStage_Startpoint;
João Lino's avatar
Rel.3  
João Lino committed
318
  beerProfile                 =   eBeerProfile_Basic;
João Lino's avatar
João Lino committed
319
  // ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++
João Lino's avatar
Rel.3  
João Lino committed
320

João Lino's avatar
João Lino committed
321
  cooking                     =   false;
322
  bStageFirstRun              =   true;
323

João Lino's avatar
João Lino committed
324
  clockStartTime              =   0;
João Lino's avatar
Rel.3  
João Lino committed
325
  clockLastUpdate             =   0;
João Lino's avatar
João Lino committed
326 327 328 329 330 331 332 333 334
  clockCounter                =   0;
  clockIgnore                 =   0;
  clockStart                  =   false;
  clockEnd                    =   false;
                        
  cookTime                    =   3600;
  cookTemperature             =   25;
  //cookMode                  =   quick_start;
  //cookMixerSpeed            =   120;
João Lino's avatar
Rel.3  
João Lino committed
335
  finalYield                  =   25;
João Lino's avatar
João Lino committed
336

João Lino's avatar
Rel.3  
João Lino committed
337
  startpointTime              =   120;
João Lino's avatar
João Lino committed
338 339 340 341 342 343 344 345 346 347
  betaGlucanaseTime           =   0;
  debranchingTime             =   0;
  proteolyticTime             =   0;
  betaAmylaseTime             =   3600;
  alphaAmylaseTime            =   1800;
  mashoutTime                 =   300;
  recirculationTime           =   1200;
  spargeTime                  =   1200;
  boilTime                    =   5400;
  coolingTime                 =   120;
João Lino's avatar
Rel.3  
João Lino committed
348
  cleaningTime                =   SETTING_CLEANING_TIME;
João Lino's avatar
João Lino committed
349

João Lino's avatar
Rel.3  
João Lino committed
350
  startpointTemperature       =   30;
João Lino's avatar
João Lino committed
351 352 353 354 355 356 357 358 359 360
  betaGlucanaseTemperature    =   40;
  debranchingTemperature      =   40;
  proteolyticTemperature      =   50;
  betaAmylaseTemperature      =   60;
  alphaAmylaseTemperature     =   70;
  mashoutTemperature          =   80;
  recirculationTemperature    =   80;
  spargeTemperature           =   80;
  boilTemperature             =   100;
  coolingTemperature          =   25;
João Lino's avatar
Rel.3  
João Lino committed
361
  cleaningTemperature         =   SETTING_CLEANING_TEMPERATURE;
João Lino's avatar
João Lino committed
362 363 364 365 366 367 368 369 370

  refresh                     =   true;
  repaint                     =   true;

  // ++++++++++++++++++++++++ Interrupts ++++++++++++++++++++++++
  lastInterruptTime           =   0;

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

João Lino's avatar
Rel.3  
João Lino committed
372
  // ######################### Code - Run Once #########################
João Lino's avatar
João Lino committed
373
  xSafeHardwarePowerOff           ();
João Lino's avatar
Rel.3  
João Lino committed
374
  xWelcomeUser                    ();
João Lino's avatar
João Lino committed
375
  
João Lino's avatar
Rel.3  
João Lino committed
376
  xSetupRotaryEncoder             ( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
377
}
378

379
// ######################### MAIN LOOP #########################
380 381

void loop() {
João Lino's avatar
Rel.3  
João Lino committed
382 383
  //cleanSerialMonitor();

384 385
  unsigned long inactivityTime = millis() - lastInterruptTime;

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

400 401
// ######################### FUNCTIONS ########################

João Lino's avatar
Rel.3  
João Lino committed
402 403 404 405 406 407 408 409
void runMenu() {
  #ifdef DEBUG_OFF
  boolean debug_go = repaint;
  if(debug_go) {
    debugPrintFunction("runMenu");
    debugPrintVar("repaint", repaint);
    debugPrintVar("eMenuType", eMenuType);
    debugPrintVar("rotaryEncoderVirtualPosition", rotaryEncoderVirtualPosition);
João Lino's avatar
João Lino committed
410
  }
João Lino's avatar
Rel.3  
João Lino committed
411
  #endif
412

João Lino's avatar
Rel.3  
João Lino committed
413 414 415
  switch(eMenuType) {
    case eMenuType_Main: {
      eMainMenuPosition = static_cast<eMainMenuOptions>(rotaryEncoderVirtualPosition);
416

João Lino's avatar
Rel.3  
João Lino committed
417
      repaint = displayMainMenu( &lcd, eMainMenuPosition, repaint );
418

João Lino's avatar
Rel.3  
João Lino committed
419 420 421
      if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) {
        eMainMenuSelection = eMainMenuPosition;
      }
422

João Lino's avatar
Rel.3  
João Lino committed
423
      runMainMenuSelection();
João Lino's avatar
João Lino committed
424

João Lino's avatar
Rel.3  
João Lino committed
425 426 427 428 429 430
      break;
    }
    case eMenuType_BeerProfile: {
      eBeerProfileMenuPosition = static_cast<eBeerProfileMenuOptions>(rotaryEncoderVirtualPosition);
      
      repaint = displayBeerProfileMenu( &lcd, eBeerProfileMenuPosition, repaint );
João Lino's avatar
João Lino committed
431

João Lino's avatar
Rel.3  
João Lino committed
432 433 434
      if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) {
        eBeerProfileMenuSelection = eBeerProfileMenuPosition;
      }
João Lino's avatar
João Lino committed
435

João Lino's avatar
Rel.3  
João Lino committed
436 437 438
      runBeerProfileSelection();
      
      break;
João Lino's avatar
João Lino committed
439
    }
João Lino's avatar
Rel.3  
João Lino committed
440 441 442 443
    case eMenuType_Stage: {
      eStageMenuPosition = static_cast<eStageMenuOptions>(rotaryEncoderVirtualPosition);
      
      repaint = displayStageMenu( &lcd, eStageMenuPosition, repaint );
João Lino's avatar
João Lino committed
444

João Lino's avatar
Rel.3  
João Lino committed
445 446 447
      if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) {
        eStageMenuSelection = eStageMenuPosition;
      }
448

João Lino's avatar
Rel.3  
João Lino committed
449 450 451
      runStageSelection();
      
      break;
452
    }
João Lino's avatar
Rel.3  
João Lino committed
453 454 455 456
    case eMenuType_Malt: {
      eMaltMenuPosition = static_cast<eMaltMenuOptions>(rotaryEncoderVirtualPosition);
      
      repaint = displayMaltMenu( &lcd, eMaltMenuPosition, repaint );
457

João Lino's avatar
Rel.3  
João Lino committed
458 459 460
      if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) {
        eMaltMenuSelection = eMaltMenuPosition;
      }
461

João Lino's avatar
Rel.3  
João Lino committed
462 463 464
      runMaltSelection();
      
      break;
465
    }
João Lino's avatar
Rel.3  
João Lino committed
466 467 468 469
    case eMenuType_Settings: {
      eSettingsMenuPosition = static_cast<eSettingsMenuOptions>(rotaryEncoderVirtualPosition);
      
      repaint = displaySettingsMenu( &lcd, eSettingsMenuPosition, repaint );
470

João Lino's avatar
Rel.3  
João Lino committed
471 472 473
      if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) {
        eSettingsMenuSelection = eSettingsMenuPosition;
      }
474

João Lino's avatar
Rel.3  
João Lino committed
475 476 477
      runSettingsSelection();
      
      break;
478 479
    }
  }
João Lino's avatar
Rel.3  
João Lino committed
480 481 482 483

  #ifdef DEBUG_OFF
  if(debug_go) {
    debugPrintVar("repaint", repaint);
484
  }
João Lino's avatar
Rel.3  
João Lino committed
485
  #endif
486 487
}

João Lino's avatar
Rel.3  
João Lino committed
488 489 490 491 492 493
void runSettingsSelection() {
  switch(eSettingsMenuSelection) {
    case eSettingsMenu_PT100_Element: {
      // Stuff

      backToStatus();
494
      
João Lino's avatar
Rel.3  
João Lino committed
495 496
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
497 498
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
499
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
500 501 502
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
503 504 505 506
    case eSettingsMenu_PT100_Up: {
      // Stuff

      backToStatus();
507
      
João Lino's avatar
Rel.3  
João Lino committed
508 509
      eMenuType = eMenuType_Main;
      repaint = true;
510 511
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
512
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
513 514 515
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
516 517 518 519
    case eSettingsMenu_PT100_Down: {
      // Stuff

      backToStatus();
520
      
João Lino's avatar
Rel.3  
João Lino committed
521 522
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
523 524
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
525
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
526 527 528
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    case eSettingsMenu_Back: {
      eMenuType = eMenuType_Main;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );

      break;
    }
    default: {
    }
  }

  eSettingsMenuSelection = eSettingsMenu_NULL;
}

void runMaltSelection() {
  switch(eMaltMenuSelection) {
    case eMaltMenu_CastleMalting_Chteau_Pilsen_2RS: {
      // Stuff

      backToStatus();
551
      
João Lino's avatar
Rel.3  
João Lino committed
552 553
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
554 555
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
556
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
557 558 559
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
560 561 562 563
    case eMaltMenu_CastleMalting_Wheat_Blanc: {
      // Stuff

      backToStatus();
564
      
João Lino's avatar
Rel.3  
João Lino committed
565 566
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
567 568
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
569
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
570 571 572
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
573 574 575
    case eMaltMenu_Back: {
      eMenuType = eMenuType_Main;
      repaint = true;
576
      
João Lino's avatar
Rel.3  
João Lino committed
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );

      break;
    }
    default: {
    }
  }

  eMaltMenuSelection = eMaltMenu_NULL;
}

void runStageSelection() {
  switch(eStageMenuSelection) {
    case eStageMenu_Startpoint: {
      startpointTime = getTimer( startpointTime );
  
      startpointTemperature = xSetGenericValue( startpointTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
600 601
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
602
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
service-config's avatar
Mixer  
service-config committed
603 604
      
      break;
605
    }
João Lino's avatar
Rel.3  
João Lino committed
606 607 608 609 610 611 612 613 614 615 616 617
    case eStageMenu_BetaGlucanase: {
      betaGlucanaseTime = getTimer( betaGlucanaseTime );
  
      betaGlucanaseTemperature = xSetGenericValue( betaGlucanaseTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
618
      
João Lino's avatar
Rel.3  
João Lino committed
619 620 621 622 623 624 625 626 627 628 629
      break;
    }
    case eStageMenu_Debranching: {
      debranchingTime = getTimer( debranchingTime );
  
      debranchingTemperature = xSetGenericValue( debranchingTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
630 631
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
632
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
633 634 635
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
636 637
    case eStageMenu_Proteolytic: {
      proteolyticTime = getTimer( proteolyticTime );
638
      
João Lino's avatar
Rel.3  
João Lino committed
639 640 641 642 643 644
      proteolyticTemperature = xSetGenericValue( proteolyticTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
645 646
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
647
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
648 649
      
      break;
650
    }
João Lino's avatar
Rel.3  
João Lino committed
651 652 653 654 655 656 657 658 659
    case eStageMenu_BetaAmylase: {
      betaAmylaseTime = getTimer( betaAmylaseTime );
  
      betaAmylaseTemperature = xSetGenericValue( betaAmylaseTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
660 661
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
662
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
663
      
664 665
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
666 667 668 669 670 671 672 673 674
    case eStageMenu_AlphaAmylase: {
      alphaAmylaseTime = getTimer( alphaAmylaseTime );
  
      alphaAmylaseTemperature = xSetGenericValue( alphaAmylaseTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
675
      
João Lino's avatar
Rel.3  
João Lino committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eStageMenu_Mashout: {
      mashoutTime = getTimer( mashoutTime );
  
      mashoutTemperature = xSetGenericValue( mashoutTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
690 691
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
692
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
693
      
694 695
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
696 697 698 699 700 701 702 703 704 705 706 707
    case eStageMenu_Recirculation: {
      recirculationTime = getTimer( recirculationTime );
  
      recirculationTemperature = xSetGenericValue( recirculationTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
708
      
João Lino's avatar
Rel.3  
João Lino committed
709 710 711 712 713 714 715 716 717 718 719
      break;
    }
    case eStageMenu_Sparge: {
      spargeTime = getTimer( spargeTime );
  
      spargeTemperature = xSetGenericValue( spargeTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
720 721
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
722
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
723 724 725
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
726 727
    case eStageMenu_Boil: {
      boilTime = getTimer( boilTime );
728
      
João Lino's avatar
Rel.3  
João Lino committed
729 730 731 732 733 734
      boilTemperature = xSetGenericValue( boilTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
735 736
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
737
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
738
      
739 740
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
741 742
    case eStageMenu_Cooling: {
      coolingTime = getTimer( coolingTime );
743
      
João Lino's avatar
Rel.3  
João Lino committed
744 745 746 747 748 749
      coolingTemperature = xSetGenericValue( coolingTemperature, 0, 120, "temperature", "*C" );

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
750 751
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
752
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
753 754 755
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
756 757 758
    case eStageMenu_Back: {
      eMenuType = eMenuType_Main;
      repaint = true;
759
      
João Lino's avatar
Rel.3  
João Lino committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );

      break;
    }
    default: {
    }
  }

  eStageMenuSelection = eStageMenu_NULL;
}

void runBeerProfileSelection() {
  switch(eBeerProfileMenuSelection) {
    case eBeerProfileMenu_Basic: {
      beerProfile                 =   eBeerProfile_Basic;

      startpointTime              =   120;
      betaGlucanaseTime           =   0;
      debranchingTime             =   0;
      proteolyticTime             =   0;
      betaAmylaseTime             =   3600;
      alphaAmylaseTime            =   1800;
      mashoutTime                 =   300;
      recirculationTime           =   1200;
      spargeTime                  =   1200;
      boilTime                    =   5400;
      coolingTime                 =   120;

      startpointTemperature       =   30;
      betaGlucanaseTemperature    =   40;
      debranchingTemperature      =   40;
      proteolyticTemperature      =   50;
      betaAmylaseTemperature      =   60;
      alphaAmylaseTemperature     =   70;
      mashoutTemperature          =   80;
      recirculationTemperature    =   80;
      spargeTemperature           =   80;
      boilTemperature             =   100;
      coolingTemperature          =   25;

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
          
      break;
    }
    case eBeerProfileMenu_Trigo: {
      beerProfile                 =   eBeerProfile_Trigo;

      startpointTime              =   120;
      betaGlucanaseTime           =   0;
      debranchingTime             =   0;
      proteolyticTime             =   0;
      betaAmylaseTime             =   3600;
      alphaAmylaseTime            =   1800;
      mashoutTime                 =   300
      recirculationTime           =   1200
      spargeTime                  =   1200;
      boilTime                    =   5400;
      coolingTime                 =   120;

      startpointTemperature       =   45;
      betaGlucanaseTemperature    =   40;
      debranchingTemperature      =   40;
      proteolyticTemperature      =   50;
      betaAmylaseTemperature      =   62;
      alphaAmylaseTemperature     =   70;
      mashoutTemperature          =   78;
      recirculationTemperature    =   80;
      spargeTemperature           =   80;
      boilTemperature             =   100;
      coolingTemperature          =   25;

      backToStatus();

      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
842 843
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
844
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
845 846 847
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
848 849 850 851
    case eBeerProfileMenu_IPA: {
      beerProfile                 =   eBeerProfile_IPA;

      backToStatus();
852
      
João Lino's avatar
Rel.3  
João Lino committed
853 854
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
855 856
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
857 858
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );

859 860
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
861 862 863 864
    case eBeerProfileMenu_Belga: {
      beerProfile                 =   eBeerProfile_Belga;

      backToStatus();
865
      
João Lino's avatar
Rel.3  
João Lino committed
866 867
      eMenuType = eMenuType_Main;
      repaint = true;
João Lino's avatar
João Lino committed
868 869
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
870
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
871 872 873
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
874 875
    case eBeerProfileMenu_Red: {
      beerProfile                 =   eBeerProfile_Red;
876

João Lino's avatar
Rel.3  
João Lino committed
877 878 879 880
      backToStatus();
      
      eMenuType = eMenuType_Main;
      repaint = true;
881 882
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
883
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
884 885 886
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
887 888 889 890
    case eBeerProfileMenu_APA: {
      beerProfile                 =   eBeerProfile_APA;

      backToStatus();
891
      
João Lino's avatar
Rel.3  
João Lino committed
892 893
      eMenuType = eMenuType_Main;
      repaint = true;
894 895
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
896
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
897 898 899
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
    case eBeerProfileMenu_Custom: {
      beerProfile                 =   eBeerProfile_Custom;

      startpointTime              =   120;
      betaGlucanaseTime           =   120;
      debranchingTime             =   120;
      proteolyticTime             =   120;
      betaAmylaseTime             =   120;
      alphaAmylaseTime            =   120;
      mashoutTime                 =   120;
      recirculationTime           =   120;
      spargeTime                  =   120;
      boilTime                    =   120;
      coolingTime                 =   120;

      startpointTemperature       =   50;
      betaGlucanaseTemperature    =   55;
      debranchingTemperature      =   60;
      proteolyticTemperature      =   65;
      betaAmylaseTemperature      =   70;
      alphaAmylaseTemperature     =   75;
      mashoutTemperature          =   80;
      recirculationTemperature    =   85;
      spargeTemperature           =   90;
      boilTemperature             =   95;
      coolingTemperature          =   100;

      backToStatus();
      
      eMenuType = eMenuType_Main;
      repaint = true;
      
932
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
João Lino's avatar
Rel.3  
João Lino committed
933 934 935 936 937 938 939 940 941 942
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eBeerProfileMenu_Back: {
      eMenuType = eMenuType_Main;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
943

944 945 946
      break;
    }
    default: {
João Lino's avatar
Rel.3  
João Lino committed
947
    }
948
  }
949

João Lino's avatar
Rel.3  
João Lino committed
950
  eBeerProfileMenuSelection = eBeerProfileMenu_NULL;
951 952
}

João Lino's avatar
Rel.3  
João Lino committed
953 954 955 956
void runMainMenuSelection() {
  switch(eMainMenuSelection) {
    case eMainMenu_GO: {
      finalYield = xSetGenericValue( finalYield, 0, 50, "Final Yield", "l" );
957

João Lino's avatar
Rel.3  
João Lino committed
958
      startBrewing();
959

João Lino's avatar
Rel.3  
João Lino committed
960
      xSetupGlobalVariablesForStage( eCookingStage_Startpoint );
961

João Lino's avatar
Rel.3  
João Lino committed
962 963 964 965 966 967
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      xPurgePump();
968

João Lino's avatar
Rel.3  
João Lino committed
969 970 971 972
      break;
    }
    case eMainMenu_STOP: {
      stopBrewing();
973

João Lino's avatar
Rel.3  
João Lino committed
974 975 976 977 978 979 980 981 982
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_SKIP: {
      cookTime = 0;
983

João Lino's avatar
Rel.3  
João Lino committed
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_BeerProfile: {
      eMenuType = eMenuType_BeerProfile;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eBeerProfileMenuPosition, MENU_SIZE_PROFILES_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_Stage: {
      eMenuType = eMenuType_Stage;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eStageMenuPosition, MENU_SIZE_STAGE_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_Malt: {
      eMenuType = eMenuType_Malt;
      repaint = true;
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMaltMenuPosition, MENU_SIZE_MALT_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_Hops: {
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_Clean: {
      // Stop anything that might be still going on
      xSafeHardwarePowerOff();
1029

João Lino's avatar
Rel.3  
João Lino committed
1030 1031
      // Start at the Clean stage
      startBrewing();
1032

João Lino's avatar
Rel.3  
João Lino committed
1033
      xSetupGlobalVariablesForStage( eCookingStage_Clean );
1034

João Lino's avatar
Rel.3  
João Lino committed
1035 1036 1037 1038 1039 1040
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      xPurgePump();
1041

João Lino's avatar
Rel.3  
João Lino committed
1042 1043 1044 1045 1046
      break;
    }
    case eMainMenu_Purge: {
      // Stop anything that might be still going on
      xSafeHardwarePowerOff();
1047

João Lino's avatar
Rel.3  
João Lino committed
1048 1049
      // Start at the Purge stage
      startBrewing();
1050

João Lino's avatar
Rel.3  
João Lino committed
1051
      xSetupGlobalVariablesForStage( eCookingStage_Purge );
1052

João Lino's avatar
Rel.3  
João Lino committed
1053 1054 1055 1056 1057 1058
      backToStatus();
      
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
      
      xPurgePump();
1059

João Lino's avatar
Rel.3  
João Lino committed
1060 1061 1062 1063 1064
      break;
    }
    case eMainMenu_Settings: {
      eMenuType = eMenuType_Settings;
      repaint = true;
1065

João Lino's avatar
Rel.3  
João Lino committed
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eSettingsMenuPosition, MENU_SIZE_SETTINGS_MENU - 1, 1, 1, 0 );
      
      break;
    }
    case eMainMenu_Back: {
      backToStatus();
        
      // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
      xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 );
1076

João Lino's avatar
Rel.3  
João Lino committed
1077 1078 1079 1080 1081
      break;
    }
    default: {
    }
  }
1082

João Lino's avatar
Rel.3  
João Lino committed
1083
  eMainMenuSelection = eMainMenu_NULL;
1084 1085 1086
}


João Lino's avatar
Rel.3  
João Lino committed
1087
void xCountTheTime( int temperatureRange, boolean bAverageUpDown ) {
1088
  unsigned long now = millis();
João Lino's avatar
Rel.3  
João Lino committed
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
  unsigned long elapsedTime = now - clockLastUpdate;
  double temperatureCount = 0;
  
  if( bAverageUpDown ) {
    float tup = upPT100.getCurrentTemperature();
    float tdown = downPT100.getCurrentTemperature();
    if(tup > tdown) {
      temperatureCount = tup;
    }
    else {
      temperatureCount = tdown;
    }
  } else {
    temperatureCount = basePT100.getCurrentTemperature();
  }
João Lino's avatar
João Lino committed
1104

João Lino's avatar
João Lino committed
1105
  // Check if the machine is in the right temperature range, for the current mode,
João Lino's avatar
Rel.3  
João Lino committed
1106 1107 1108 1109 1110 1111 1112
  //if(!( temperatureCount > (cookTemperature - temperatureRange) && temperatureCount < (cookTemperature + temperatureRange))) {
  float margin = temperatureRange;
  if( cookTemperature >= 100.0 ) {
    margin = 2.0;
  }
  if( temperatureCount < (cookTemperature - margin) ) {
    clockIgnore += elapsedTime;
João Lino's avatar
João Lino committed
1113 1114 1115
  }
  
  // Calculate the remaining time on the clock
João Lino's avatar
João Lino committed
1116
  clockCounter = cookTime * 1000 - (now - clockStartTime - clockIgnore);
João Lino's avatar
João Lino committed
1117

João Lino's avatar
Rel.3  
João Lino committed
1118 1119 1120 1121 1122 1123 1124
  if ( clockCounter < 0 ) {
    clockCounter = 0;
  }

  clockLastUpdate = now;

  #ifdef DEBUG_OFF
1125 1126
  debugPrintFunction("xCountTheTime");
  debugPrintVar("millis()", now);
João Lino's avatar
João Lino committed
1127
  debugPrintVar("cookTime", cookTime);
1128 1129 1130
  debugPrintVar("clockStartTime", clockStartTime);
  debugPrintVar("clockIgnore", clockIgnore);
  debugPrintVar("clockCounter", clockCounter); 
João Lino's avatar
Rel.3  
João Lino committed
1131
  #endif
1132 1133 1134
}

bool isTimeLeft() {
1135 1136
  if( clockCounter > 0 ) {
    return true;
João Lino's avatar
João Lino committed
1137
  }
1138
  return false;
1139 1140
}

1141
//HEATING_ELEMENT_MAX_WATTAGE / HEATING_ELEMENT_AC_FREQUENCY_HZ
1142 1143
double ulWattToWindowTime( double ulAppliedWatts ) {
  double ulPulsesRequired = ulAppliedWatts / dWattPerPulse;
1144
  return (double)iWindowSize / 1000.0 * ulPulsesRequired * 1000.0 / HEATING_ELEMENT_AC_FREQUENCY_HZ;
1145 1146
}

João Lino's avatar
Rel.3  
João Lino committed
1147 1148
bool xRegulateTemperature( boolean bAverageUpDown ) {
  double difference = 0;
João Lino's avatar
João Lino committed
1149 1150 1151
  bool overTemperature = false;
  double wattage = 0.0;
  
João Lino's avatar
Rel.3  
João Lino committed
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
  if( bAverageUpDown ) {
    float tbase = basePT100.getCurrentTemperature();
    float tup = upPT100.getCurrentTemperature();
    float tdown = downPT100.getCurrentTemperature();

    if(tup > tdown) {
      difference = cookTemperature - tup;
    }
    else {
      difference = cookTemperature - tdown;
    }

    if (tbase > (cookTemperature + 2.0)) {
      difference = 0.0;
    }

    if (tbase < (cookTemperature)) {
      difference = cookTemperature - tbase;
    }

  } else {
    difference = cookTemperature - basePT100.getCurrentTemperature();
  }

João Lino's avatar
João Lino committed
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
  // Deviation between the cook temperature set and the cook temperature measured
  if( difference < 0.0 ) {
    difference = difference * (-1.0);
    overTemperature = true;
  }
  
  // Calculate applied wattage, based on the distance from the target temperature
  if( overTemperature ) {
    // turn it off
    wattage = 0.0;
  } else {
1187
    if(difference <= 0.1) {
João Lino's avatar
João Lino committed
1188 1189 1190
      // turn it off
      wattage = 0.0;
    } else {
João Lino's avatar
Rel.3  
João Lino committed
1191
      if(difference <= 0.5) {
João Lino's avatar
João Lino committed
1192
        // pulse lightly at 500 watt
João Lino's avatar
Rel.3  
João Lino committed
1193 1194 1195 1196 1197 1198
        if(cookTemperature > 99.0) {
          wattage = 1500.0;
        }
        else {
          wattage = 500.0;
        }
João Lino's avatar
João Lino committed
1199
      } else {
João Lino's avatar
Rel.3  
João Lino committed
1200
        if(difference <= 1.0) {
João Lino's avatar
João Lino committed
1201 1202 1203
          // pulse moderately at 1000 watt
          wattage = 1000.0;
        } else {
João Lino's avatar
Rel.3  
João Lino committed
1204
          if(difference <= 3.0) {
João Lino's avatar
João Lino committed
1205
            // pulse hardly at 2000 watt
1206
            wattage = 2000.0;
João Lino's avatar
João Lino committed
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
          } else {
            //pulse constantly at HEATING_ELEMENT_MAX_WATTAGE watt
            wattage = HEATING_ELEMENT_MAX_WATTAGE;
          }
        }
      }
    }
  }
  
  // Update the recorded time for the begining of the window, if the previous window has passed
1217 1218
  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;
1219
  }
João Lino's avatar
João Lino committed
1220 1221
  
  // Apply wattage to the element at the right time
1222
  if( ulWattToWindowTime( wattage ) > (millis() - windowStartTime) ) {
1223
    digitalWrite(HEATING_ELEMENT_OUTPUT_PIN,HIGH);
João Lino's avatar
Rel.3  
João Lino committed
1224
    bStatusElement = true;
João Lino's avatar
João Lino committed
1225 1226
  } else {
    digitalWrite(HEATING_ELEMENT_OUTPUT_PIN,LOW);
João Lino's avatar
Rel.3  
João Lino committed
1227
    bStatusElement = false;
João Lino's avatar
João Lino committed
1228
  }
1229

João Lino's avatar
Rel.3  
João Lino committed
1230
  #ifdef DEBUG_OFF
1231
  //debugPrintFunction("xRegulateTemperature");
1232
  debugPrintVar("difference", difference);
1233
  //debugPrintVar("overTemperature", overTemperature);
1234
  debugPrintVar("wattage", wattage);
1235 1236 1237 1238
  //debugPrintVar("ulWattToWindowTime( wattage )", ulWattToWindowTime( wattage ) );
  //debugPrintVar("millis()", millis());
  //debugPrintVar("windowStartTime", windowStartTime);
  //debugPrintVar("test", ulWattToWindowTime( wattage ) > (millis() - windowStartTime) ); 
João Lino's avatar
Rel.3  
João Lino committed
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
  #endif
}

void xPurgePump() {
  for(int i = 0; i < 2; i++) {
    analogWrite(PUMP_PIN, PUMP_SPEED_MAX);  // analogWrite values from 0 to 255
    delay(1000);
    analogWrite(PUMP_PIN, PUMP_SPEED_STOP);  // analogWrite values from 0 to 255
    delay(1500);
  }
1249 1250
}

1251
bool xRegulatePumpSpeed() {
João Lino's avatar
Rel.3  
João Lino committed
1252 1253 1254 1255
  //  analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255

  if(basePT100.getCurrentTemperature() > PUMP_TEMPERATURE_MAX_OPERATION) {
    analogWrite(PUMP_PIN, PUMP_SPEED_STOP);  // analogWrite values from 0 to 255
1256

João Lino's avatar
Rel.3  
João Lino committed
1257 1258 1259
    basePT100.setSampleDeviation( 0.0 );
    upPT100.setSampleDeviation( 0.0 );
    downPT100.setSampleDeviation( 0.0 );
1260 1261
  }
  else {
João Lino's avatar
Rel.3  
João Lino committed
1262 1263 1264 1265 1266
    analogWrite(PUMP_PIN, iPumpSpeed);  // analogWrite values from 0 to 255

    basePT100.setSampleDeviation( -2.0 );
    upPT100.setSampleDeviation( -2.0 );
    downPT100.setSampleDeviation( -2.0 );
1267
  }
1268 1269
}

1270
void xWarnClockEnded() {
1271
  sing(MELODY_SUPER_MARIO_START, PIEZO_PIN);
1272 1273
}

João Lino's avatar
Rel.3  
João Lino committed
1274 1275 1276 1277
void xWarnCookEnded() {
  sing(MELODY_UNDERWORLD_SHORT, PIEZO_PIN);
}

João Lino's avatar
João Lino committed
1278
void xStageFirstRun( int stageTime, int stageTemperature, int stagePumpSpeed, eCookingStages stage ) {
João Lino's avatar
Rel.3  
João Lino committed
1279 1280 1281 1282 1283
  #ifdef DEBUG_OFF
  debugPrintFunction("xStageFirstRun");
  debugPrintVar("cookingStage", stage);
  #endif

João Lino's avatar
João Lino committed
1284 1285 1286 1287
  // Set Stage
  bStageFirstRun = true;
  cookingStage = stage;

João Lino's avatar
João Lino committed
1288 1289 1290 1291 1292 1293 1294 1295
  // Set the clock
  cookTime = stageTime;
  
  // Set the target temperature
  cookTemperature = stageTemperature;
  
  // Reset the clock
  clockStartTime = millis();
João Lino's avatar
Rel.3  
João Lino committed
1296
  clockLastUpdate = clockStartTime;
João Lino's avatar
João Lino committed
1297
  clockIgnore = 0;
1298 1299 1300

  // Set the pump speed
  iPumpSpeed = stagePumpSpeed;
1301 1302
}

João Lino's avatar
Rel.3  
João Lino committed
1303 1304 1305 1306 1307
void xSetupGlobalVariablesForStage(eCookingStages nextStage) {
  #ifdef DEBUG_OFF
  debugPrintFunction("xSetupGlobalVariablesForStage");
  debugPrintVar("cookingStage", nextStage);
  #endif
João Lino's avatar
João Lino committed
1308 1309 1310 1311

  // Operate the machine according to the current mode
  switch(nextStage) {
    case eCookingStage_Startpoint: {
João Lino's avatar
Rel.3  
João Lino committed
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
      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.");

          xWaitForAction("Malt", say);

          repaint = true;

          break;
        }
        default: {

        }
      }

      // Make sure there is water
      xWaitForAction("Water", "Make sure there is water in the machine before start cooking.");

      repaint = true;

João Lino's avatar
João Lino committed
1339
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1340
      xStageFirstRun( startpointTime, startpointTemperature, PUMP_SPEED_MAX, eCookingStage_Startpoint );
João Lino's avatar
João Lino committed
1341 1342 1343 1344
      
      break;
    }
    case eCookingStage_BetaGlucanase: {
João Lino's avatar
Rel.3  
João Lino committed
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
      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.");

          xWaitForAction("Malt", say);

          repaint = true;

          break;
        }
        default: {

        }
      }

João Lino's avatar
João Lino committed
1367
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1368
      xStageFirstRun( betaGlucanaseTime, betaGlucanaseTemperature, PUMP_SPEED_MAX, eCookingStage_BetaGlucanase );
João Lino's avatar
João Lino committed
1369 1370 1371 1372 1373
      
      break;
    }
    case eCookingStage_Debranching: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1374
      xStageFirstRun( debranchingTime, debranchingTemperature, PUMP_SPEED_MAX, eCookingStage_Debranching );
João Lino's avatar
João Lino committed
1375 1376 1377 1378 1379
      
      break;
    }
    case eCookingStage_Proteolytic: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1380
      xStageFirstRun( proteolyticTime, proteolyticTemperature, PUMP_SPEED_MAX, eCookingStage_Proteolytic );
João Lino's avatar
João Lino committed
1381 1382 1383 1384 1385
      
      break;
    }
    case eCookingStage_BetaAmylase: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1386
      xStageFirstRun( betaAmylaseTime, betaAmylaseTemperature, PUMP_SPEED_MAX, eCookingStage_BetaAmylase );
João Lino's avatar
João Lino committed
1387 1388 1389 1390 1391
      
      break;
    }
    case eCookingStage_AlphaAmylase: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1392
      xStageFirstRun( alphaAmylaseTime, alphaAmylaseTemperature, PUMP_SPEED_MAX, eCookingStage_AlphaAmylase );
João Lino's avatar
João Lino committed
1393 1394 1395 1396 1397
      
      break;
    }
    case eCookingStage_Mashout: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1398
      xStageFirstRun( mashoutTime, mashoutTemperature, PUMP_SPEED_MAX, eCookingStage_Mashout );
João Lino's avatar
João Lino committed
1399 1400 1401
      
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
1402 1403 1404 1405 1406
    case eCookingStage_Recirculation: {// Make sure there is water
      xWaitForAction("Sparge Water", "Start heating your sparge water.");

      repaint = true;

João Lino's avatar
João Lino committed
1407
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1408
      xStageFirstRun( recirculationTime, recirculationTemperature, PUMP_SPEED_MAX, eCookingStage_Recirculation );
João Lino's avatar
João Lino committed
1409 1410 1411 1412
      
      break;
    }
    case eCookingStage_Sparge: {
João Lino's avatar
Rel.3  
João Lino committed
1413 1414 1415 1416 1417
      // Make sure there is water
      xWaitForAction("Sparge Water", "Start pouring the sparge water.");

      repaint = true;

João Lino's avatar
João Lino committed
1418
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1419
      xStageFirstRun( spargeTime, spargeTemperature, PUMP_SPEED_MAX, eCookingStage_Sparge );
João Lino's avatar
João Lino committed
1420 1421 1422 1423
      
      break;
    }
    case eCookingStage_Boil: {
João Lino's avatar
Rel.3  
João Lino committed
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
      switch(beerProfile) {
        case eBeerProfile_Trigo: {
          String say = "Get ";

          float hopAmount = 0.8 * ((float) finalYield);
          say += String(hopAmount);

          say += String("g of Magnum 9.4\% and Styrian Golding 5\% ready.");

          xWaitForAction("Hops", say);

          break;
        }
        default: {
          xWaitForAction("Hops", "Add the hops in the right order, at the right time.");

        }
      }

      repaint = true;

João Lino's avatar
João Lino committed
1445
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1446
      xStageFirstRun( boilTime, boilTemperature, PUMP_SPEED_MAX, eCookingStage_Boil );
João Lino's avatar
João Lino committed
1447 1448 1449 1450
      
      break;
    }
    case eCookingStage_Cooling: {
João Lino's avatar
Rel.3  
João Lino committed
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
      // Make sure there is water
      xWaitForAction("Coil", "Add the coil and connect it to the main water supply.");

      repaint = true;

      // A basic operation for a basic stage
      xStageFirstRun( coolingTime, coolingTemperature, PUMP_SPEED_MAX, eCookingStage_Cooling );

      break;
    }
    case eCookingStage_Clean: {
      // Make sure there is water
      xWaitForAction("Water", "Add 13 liters.");

      // Make sure there is water
      xWaitForAction("Star San HB", "Add 0.89oz/26ml.");

      repaint = true;

João Lino's avatar
João Lino committed
1470
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1471
      xStageFirstRun( cleaningTime, cleaningTemperature, PUMP_SPEED_MAX, eCookingStage_Clean );
João Lino's avatar
João Lino committed
1472 1473 1474 1475 1476

      break;
    }
    case eCookingStage_Purge: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1477
      xStageFirstRun( 0, 0, PUMP_SPEED_MAX, eCookingStage_Purge );
João Lino's avatar
João Lino committed
1478 1479 1480 1481 1482 1483 1484

      xRegulatePumpSpeed();

      break;
    }
    case eCookingStage_Done: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1485
      xStageFirstRun( 0, 0, PUMP_SPEED_STOP, eCookingStage_Done );
João Lino's avatar
João Lino committed
1486 1487 1488 1489

      break;
    }
  }
1490 1491 1492
}

void xTransitionIntoStage(eCookingStages nextStage) {
João Lino's avatar
João Lino committed
1493 1494 1495 1496 1497 1498 1499
  // Turn off all hardware that can damage itself if the machine is not cooking
  xSafeHardwarePowerOff();    
  
  // Warn the user a stage has ended
  xWarnClockEnded();
  
  // Reset global stage variables
João Lino's avatar
Rel.3  
João Lino committed
1500
  xSetupGlobalVariablesForStage( nextStage );
1501 1502
}

João Lino's avatar
Rel.3  
João Lino committed
1503
void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemperatureRange, eCookingStages nextStage, boolean bAverageUpDown ) {
João Lino's avatar
João Lino committed
1504 1505 1506
  if(bStageFirstRun) {
    // Don't run this again
    bStageFirstRun = false;
João Lino's avatar
João Lino committed
1507 1508

    //xStageFirstRun( iStageTime, iStageTemperature, 255 );
João Lino's avatar
João Lino committed
1509 1510 1511 1512
    
    // When the stage should be skipped
    if( iStageTime == 0) {
      // Continue to the next stage
João Lino's avatar
Rel.3  
João Lino committed
1513
      //xSetupGlobalVariablesForStage( nextStage );
João Lino's avatar
João Lino committed
1514 1515 1516 1517 1518
      
      // There is nothing to do, in this stage
      return;
    } else {
      // Set the clock, target temperature and Reset the clock
1519
      //xStageFirstRun( iStageTime, iStageTemperature, PUMP_SPEED_SLOW );
João Lino's avatar
João Lino committed
1520
      //xStageFirstRun( iStageTime, iStageTemperature, 255 );
João Lino's avatar
João Lino committed
1521 1522 1523
    }
  } else {
    // Account for time spent at the target temperature | Input 1: range in ºC within which the target temperature is considered to be reached
João Lino's avatar
Rel.3  
João Lino committed
1524 1525 1526 1527 1528
    #ifdef DEBUG_OFF
    xCountTheTime( iStageTemperatureRange, false );
    #else
    xCountTheTime( iStageTemperatureRange, bAverageUpDown );
    #endif
João Lino's avatar
João Lino committed
1529 1530 1531
    
    if( isTimeLeft() ) {
      // Do temperature control
João Lino's avatar
Rel.3  
João Lino committed
1532
      xRegulateTemperature( bAverageUpDown );
1533 1534 1535

      // Do flow control
      xRegulatePumpSpeed();
João Lino's avatar
João Lino committed
1536 1537
      
    } else {
João Lino's avatar
Rel.3  
João Lino committed
1538 1539 1540 1541
      #ifdef DEBUG_OFF
      debugPrintFunction("xBasicStageOperation");
      debugPrintVar("clockCounter", clockCounter);
      #endif
João Lino's avatar
João Lino committed
1542 1543 1544 1545 1546 1547 1548 1549 1550
      // Continue to the next stage
      xTransitionIntoStage( nextStage );
      
      // There is nothing to do, in this stage
      return;
    }
  }
  // There is nothing to do, in this iteration
  return;
1551 1552
}

João Lino's avatar
Rel.3  
João Lino committed
1553
void xManageMachineSystems() {
1554

João Lino's avatar
Rel.3  
João Lino committed
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574
  #ifdef DEBUG
  Serial.print(millis());
  Serial.print(",");
  if(cooking) {
    Serial.print("1");
  }
  else {
    Serial.print("0");
  }
  Serial.print(",");
  Serial.print(cookTemperature);
  Serial.print(",");
  if(bStatusElement) {
    Serial.print("1");
  }
  else {
    Serial.print("0");
  }
  Serial.print(",");
  #endif
1575 1576

  // Measure temperature, for effect
João Lino's avatar
Rel.3  
João Lino committed
1577 1578 1579
  basePT100.measure1(false, false);
  upPT100.measure1(false, false);
  downPT100.measure1(true, false);
1580 1581 1582

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

1585 1586
    return;
  }
João Lino's avatar
João Lino committed
1587 1588 1589 1590 1591
  
  // Operate the machine according to the current mode
  switch(cookingStage) {
    case eCookingStage_Startpoint: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1592
      xBasicStageOperation( startpointTime, startpointTemperature, 1, eCookingStage_BetaGlucanase, false);
João Lino's avatar
João Lino committed
1593 1594 1595 1596 1597
      
      break;
    }
    case eCookingStage_BetaGlucanase: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1598
      xBasicStageOperation( betaGlucanaseTime, betaGlucanaseTemperature, 0, eCookingStage_Debranching, true );
João Lino's avatar
João Lino committed
1599 1600 1601 1602 1603
      
      break;
    }
    case eCookingStage_Debranching: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1604
      xBasicStageOperation( debranchingTime, debranchingTemperature, 0, eCookingStage_Proteolytic, true );
João Lino's avatar
João Lino committed
1605 1606 1607 1608 1609
      
      break;
    }
    case eCookingStage_Proteolytic: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1610
      xBasicStageOperation( proteolyticTime, proteolyticTemperature, 0, eCookingStage_BetaAmylase, true );
João Lino's avatar
João Lino committed
1611 1612 1613 1614 1615
      
      break;
    }
    case eCookingStage_BetaAmylase: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1616
      xBasicStageOperation( betaAmylaseTime, betaAmylaseTemperature, 0, eCookingStage_AlphaAmylase, true );
João Lino's avatar
João Lino committed
1617 1618 1619 1620 1621
      
      break;
    }
    case eCookingStage_AlphaAmylase: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1622
      xBasicStageOperation( alphaAmylaseTime, alphaAmylaseTemperature, 0, eCookingStage_Mashout, true );
João Lino's avatar
João Lino committed
1623 1624 1625 1626 1627
      
      break;
    }
    case eCookingStage_Mashout: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1628
      xBasicStageOperation( mashoutTime, mashoutTemperature, 0, eCookingStage_Recirculation, true );
João Lino's avatar
João Lino committed
1629 1630 1631 1632 1633
      
      break;
    }
    case eCookingStage_Recirculation: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1634
      xBasicStageOperation( recirculationTime, recirculationTemperature, 0, eCookingStage_Sparge, true );
João Lino's avatar
João Lino committed
1635 1636 1637 1638 1639
      
      break;
    }
    case eCookingStage_Sparge: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1640
      xBasicStageOperation( spargeTime, spargeTemperature, 1, eCookingStage_Boil, false );
João Lino's avatar
João Lino committed
1641 1642 1643 1644 1645
      
      break;
    }
    case eCookingStage_Boil: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1646
      xBasicStageOperation( boilTime, boilTemperature, 2, eCookingStage_Cooling, false );
João Lino's avatar
João Lino committed
1647 1648 1649 1650 1651
      
      break;
    }
    case eCookingStage_Cooling: {
      // A basic operation for a basic stage
João Lino's avatar
Rel.3  
João Lino committed
1652 1653 1654 1655 1656 1657 1658
      xBasicStageOperation( coolingTime, coolingTemperature, 1, eCookingStage_Done, false );

      break;
    }
    case eCookingStage_Clean: {
      // A basic operation for a basic stage
      xBasicStageOperation( cleaningTime, cleaningTemperature, 1, eCookingStage_Done, false );
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668

      break;
    }
    case eCookingStage_Purge: {
      // A basic operation for a basic stage
      //xBasicStageOperation( coolingTime, coolingTemperature, 1, eCookingStage_Done );
      iPumpSpeed = PUMP_SPEED_MAX;

      xRegulatePumpSpeed();

João Lino's avatar
João Lino committed
1669 1670
      break;
    }
João Lino's avatar
Rel.3  
João Lino committed
1671 1672 1673 1674 1675 1676
    case eCookingStage_Done: {
      // Update cooking state
      stopBrewing();

      // Ask for screen refresh
      repaint = true;
João Lino's avatar
João Lino committed
1677 1678 1679
      
      // Warn the user that the cooking is done
      xWarnCookEnded();
João Lino's avatar
Rel.3  
João Lino committed
1680 1681

      break;
João Lino's avatar
João Lino committed
1682 1683
    }
  }
1684 1685 1686 1687 1688
}

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

void startBrewing() {
1689 1690
  //sing(MELODY_SUPER_MARIO, PIEZO_PIN);

1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
  cooking = true;
}

void stopBrewing() {
  cooking = false;
}

void backToStatus() {
  lastInterruptTime = millis() - SETTING_MAX_INACTIVITY_TIME - 1;
}
// #################################################### Helpers ##################################################################

// #################################################### Set Variables ##################################################################
1704
int getTimer(int init) {
João Lino's avatar
João Lino committed
1705 1706
  // set operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
  xSetupRotaryEncoder( eRotaryEncoderMode_Time, init, 7200, 0, 1, 30 );
1707 1708

  // initialize variables
service-config's avatar
Mixer  
service-config committed
1709
  int rotaryEncoderPreviousPosition = 0;
1710 1711 1712 1713 1714 1715 1716
  int minutes = 0;
  int seconds = 0;
  
  // Setup Screen
  lcd.clear();
  lcd.home();        
  lcd.print("Set Time");
1717
  lcd.setCursor (0,LCD_VERTICAL_RESOLUTION-1);
1718 1719 1720
  lcd.print("      0:00");
  
  while(true) {
1721
    // Check if pushbutton is pressed
João Lino's avatar
João Lino committed
1722
    if ((digitalRead(ROTARY_ENCODER_SW_PIN))) {  
1723
      // Wait until switch is released
João Lino's avatar
João Lino committed
1724
      while (digitalRead(ROTARY_ENCODER_SW_PIN)) {}  
1725
      
João Lino's avatar
João Lino committed
1726 1727
      // debounce
      delay(10);
1728

João Lino's avatar
João Lino committed
1729
      // Job is done, break the circle
1730
      break;
1731
    } else {
João Lino's avatar
João Lino committed
1732
      // Don't forget to keep an eye on the cooking
João Lino's avatar
Rel.3  
João Lino committed
1733
      xManageMachineSystems();
João Lino's avatar
João Lino committed
1734
    }
1735 1736
    
    // display current timer
service-config's avatar
Mixer  
service-config committed
1737 1738 1739 1740
    if (rotaryEncoderVirtualPosition != rotaryEncoderPreviousPosition) {
      rotaryEncoderPreviousPosition = rotaryEncoderVirtualPosition;
      minutes = rotaryEncoderVirtualPosition/60;
      seconds = rotaryEncoderVirtualPosition-minutes*60;
1741
      
1742
      lcd.setCursor (0,LCD_VERTICAL_RESOLUTION-1);
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
      lcd.print("      ");
      lcd.print(minutes);
      lcd.print(":");
      if(seconds<10) {
        lcd.print("0");
      }
      lcd.print(seconds);
      lcd.println("                ");
    }
  }
  
service-config's avatar
Mixer  
service-config committed
1754
  return rotaryEncoderVirtualPosition;
1755 1756 1757 1758 1759
}

int getTemperature(int init) {
  
  // set operation state
1760
  rotaryEncoderMode = eRotaryEncoderMode_Generic;
service-config's avatar
Mixer  
service-config committed
1761
  rotaryEncoderVirtualPosition = init;  
1762 1763

  // initialize variables
service-config's avatar
Mixer  
service-config committed
1764
  int rotaryEncoderPreviousPosition = 0;
1765 1766 1767 1768 1769
  
  // Setup Screen
  lcd.clear();
  lcd.home();        
  lcd.print("Set Temperature");
1770
  lcd.setCursor (0,LCD_VERTICAL_RESOLUTION-1);
1771 1772
  lcd.print("       0 *C");
  
1773
  rotaryEncoderMaxPosition = TEMPERATURE_SETTING_MAX_VALUE;
service-config's avatar
Mixer  
service-config committed
1774
  
1775
  while(true) {
1776
    // Check if pushbutton is pressed
João Lino's avatar
João Lino committed
1777
    if ((digitalRead(ROTARY_ENCODER_SW_PIN))) {  
1778
      // Wait until switch is released
João Lino's avatar
João Lino committed
1779
      while (digitalRead(ROTARY_ENCODER_SW_PIN)) {}  
1780
      
João Lino's avatar
João Lino committed
1781 1782
      // debounce
      delay(10);
1783

João Lino's avatar
João Lino committed
1784
      // Job is done, break the circle
1785
      break;
1786
    } else {
João Lino's avatar
João Lino committed
1787
      // Don't forget to keep an eye on the cooking
João Lino's avatar
Rel.3  
João Lino committed
1788
      xManageMachineSystems();
João Lino's avatar
João Lino committed
1789
    }
1790 1791
    
    // display current timer
service-config's avatar
Mixer  
service-config committed
1792 1793
    if (rotaryEncoderVirtualPosition != rotaryEncoderPreviousPosition) {
      rotaryEncoderPreviousPosition = rotaryEncoderVirtualPosition;
1794
      
1795
      lcd.setCursor (0,LCD_VERTICAL_RESOLUTION-1);
1796
      lcd.print("     ");
service-config's avatar
Mixer  
service-config committed
1797
      if(rotaryEncoderVirtualPosition<10) {
1798 1799 1800
        lcd.print("  ");
      }
      else {
service-config's avatar
Mixer  
service-config committed
1801
        if(rotaryEncoderVirtualPosition<100) {
1802 1803 1804
          lcd.print(" ");
        }
      }
service-config's avatar
Mixer  
service-config committed
1805
      lcd.print(rotaryEncoderVirtualPosition);
1806 1807 1808 1809 1810
      lcd.print(" *C");
      lcd.println("                ");
    }
  }
  
service-config's avatar
Mixer  
service-config committed
1811
  return rotaryEncoderVirtualPosition;
1812 1813
}

João Lino's avatar
João Lino committed
1814 1815 1816
int xSetGenericValue(int init, int min, int max, char *valueName, char *unit) {  
  // set operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep
  xSetupRotaryEncoder( eRotaryEncoderMode_Generic, init, max, min, 1, 5 );
1817 1818

  // initialize variables
service-config's avatar
Mixer  
service-config committed
1819
  int rotaryEncoderPreviousPosition = 0;
1820 1821 1822
  
  // Setup Screen
  lcd.clear();
1823 1824 1825 1826 1827
  lcd.home();
  lcd.print( "Set " );
  lcd.print( valueName );
  lcd.setCursor ( 0 , LCD_VERTICAL_RESOLUTION - 1 );
  lcd.print( "       0 " );
João Lino's avatar
João Lino committed
1828
  lcd.print( unit );
1829
  
1830
  while(true) {
1831 1832 1833
    // Check if pushbutton is pressed
    if ( digitalRead(ROTARY_ENCODER_SW_PIN) ) {
      // Wait until switch is released
João Lino's avatar
João Lino committed
1834
      while ( digitalRead(ROTARY_ENCODER_SW_PIN) ) {}  
1835
      
João Lino's avatar
João Lino committed
1836 1837
      // debounce
      delay( 10 );
service-config's avatar
Mixer  
service-config committed
1838

João Lino's avatar
João Lino committed
1839
      // Job is done, break the circle
service-config's avatar
Mixer  
service-config committed
1840
      break;
1841
    } else {
João Lino's avatar
João Lino committed
1842
      // Don't forget to keep an eye on the cooking
João Lino's avatar
Rel.3  
João Lino committed
1843
      xManageMachineSystems();
João Lino's avatar
João Lino committed
1844
    }
service-config's avatar
Mixer  
service-config committed
1845
    
1846 1847
    // Check if there was an update by the rotary encoder
    if( rotaryEncoderVirtualPosition != rotaryEncoderPreviousPosition ) {
service-config's avatar
Mixer  
service-config committed
1848 1849
      rotaryEncoderPreviousPosition = rotaryEncoderVirtualPosition;
      
1850 1851 1852 1853
      lcd.setCursor( 0, LCD_VERTICAL_RESOLUTION - 1 );
      lcd.print( "     " );
      if( rotaryEncoderVirtualPosition < 10 ) {
        lcd.print( "  " );
1854 1855
      }
      else {
1856 1857
        if( rotaryEncoderVirtualPosition < 100 ) {
          lcd.print( " " );
1858 1859
        }
      }
1860
      lcd.print( rotaryEncoderVirtualPosition );
1861 1862
      lcd.print( " " );
      lcd.print( unit );
1863
      lcd.println( "                " );
1864 1865
    }
  }
1866 1867
  
  return rotaryEncoderVirtualPosition;
1868 1869
}

1870
// ###################### Set Variables ##################################################
1871

João Lino's avatar
Rel.3  
João Lino committed
1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890
void xWaitForAction(String title, String message) {
  while(true) {
    // Check if pushbutton is pressed
    if ( digitalRead(ROTARY_ENCODER_SW_PIN) ) {
      // Wait until switch is released
      while ( digitalRead(ROTARY_ENCODER_SW_PIN) ) {}  
      
      // debounce
      delay( 10 );

      // Job is done, break the circle
      break;
    } else {
      sing(BUZZ_1, PIEZO_PIN);

      // Print the message
      if(! lcdPrint(&lcd, title, message)) {
        break;
      }
1891 1892 1893 1894
    }
  }
}

João Lino's avatar
Rel.3  
João Lino committed
1895 1896 1897 1898 1899 1900 1901 1902
boolean gotButtonPress(int iPin) {
  boolean ret = false;

  if ((digitalRead(iPin))) {    // check if pushbutton is pressed
    ret = true;
    while (digitalRead(iPin)) {}    // wait til switch is released
    delay(10);                            // debounce
  } 
1903

João Lino's avatar
Rel.3  
João Lino committed
1904 1905 1906 1907 1908 1909 1910 1911
  return ret;
}

void cleanSerialMonitor() {
  for (int i = 0; i < 40; i++) {
    Serial.println();
  }
}