diff --git a/CustomDataStructures.h b/CustomDataStructures.h index 8cd26ec7fdf0e34ceb409bfd4031dadd4b688e6d..72a888d66d26a7276599f1b022bf1e465b573ac8 100644 --- a/CustomDataStructures.h +++ b/CustomDataStructures.h @@ -4,6 +4,7 @@ Released into the public domain. */ #ifndef CustomDataStructures_h +#define CustomDataStructures_h enum eRotaryEncoderMode { eRotaryEncoderMode_Menu, @@ -13,42 +14,65 @@ enum eRotaryEncoderMode { }; // menu +enum eMenuType { + eMenuType_Main, + eMenuType_BeerProfile, + eMenuType_Stage, + eMenuType_Malt, + eMenuType_Settings +}; enum eMainMenuOptions { + eMainMenu_NULL, eMainMenu_GO, - eMainMenu_Presets, + eMainMenu_STOP, + eMainMenu_SKIP, + eMainMenu_BeerProfile, + eMainMenu_Stage, eMainMenu_Malt, - eMainMenu_Startpoint, - eMainMenu_BetaGlucanase, - eMainMenu_Debranching, - eMainMenu_Proteolytic, - eMainMenu_BetaAmylase, - eMainMenu_AlphaAmylase, - eMainMenu_Mashout, - eMainMenu_Recirculation, - eMainMenu_Sparge, - eMainMenu_Boil, eMainMenu_Hops, - eMainMenu_Cooling, + eMainMenu_Clean, + eMainMenu_Purge, eMainMenu_Settings, eMainMenu_Back }; -enum ePresetsMenuOptions { - ePresetsMenu_Trigo, - ePresetsMenu_IPA, - ePresetsMenu_Belga, - ePresetsMenu_Red, - ePresetsMenu_APA, - ePresetsMenu_Back +enum eStageMenuOptions { + eStageMenu_NULL, + eStageMenu_Startpoint, + eStageMenu_BetaGlucanase, + eStageMenu_Debranching, + eStageMenu_Proteolytic, + eStageMenu_BetaAmylase, + eStageMenu_AlphaAmylase, + eStageMenu_Mashout, + eStageMenu_Recirculation, + eStageMenu_Sparge, + eStageMenu_Boil, + eStageMenu_Cooling, + eStageMenu_Back +}; + +enum eBeerProfileMenuOptions { + eBeerProfileMenu_NULL, + eBeerProfileMenu_Basic, + eBeerProfileMenu_Trigo, + eBeerProfileMenu_IPA, + eBeerProfileMenu_Belga, + eBeerProfileMenu_Red, + eBeerProfileMenu_APA, + eBeerProfileMenu_Custom, + eBeerProfileMenu_Back }; enum eMaltMenuOptions { + eMaltMenu_NULL, eMaltMenu_CastleMalting_Chteau_Pilsen_2RS, eMaltMenu_CastleMalting_Wheat_Blanc, eMaltMenu_Back }; enum eSettingsMenuOptions { + eSettingsMenu_NULL, eSettingsMenu_PT100_Element, eSettingsMenu_PT100_Up, eSettingsMenu_PT100_Down, @@ -68,7 +92,19 @@ enum eCookingStages { eCookingStage_Sparge, eCookingStage_Boil, eCookingStage_Cooling, + eCookingStage_Clean, + eCookingStage_Purge, eCookingStage_Done }; +enum eBeerProfile { + eBeerProfile_Basic, + eBeerProfile_Trigo, + eBeerProfile_IPA, + eBeerProfile_Belga, + eBeerProfile_Red, + eBeerProfile_APA, + eBeerProfile_Custom +}; + #endif \ No newline at end of file diff --git a/Display.cpp b/Display.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79dee85cb11414f2172b22455c730135fbe202b2 --- /dev/null +++ b/Display.cpp @@ -0,0 +1,464 @@ +/* + Display.cpp - Display functions code file. + Created by João Lino, September 25, 2015. + Released into the public domain. +*/ + +#include "Display.h" + +boolean lcdPrint(LiquidCrystal_I2C *lcd, String title, String message) { + int messageLength = message.length(); + + lcd->clear(); + + // print title + lcd->home(); + lcd->print(title); + + // print message + if(messageLength <= LCD_HORIZONTAL_RESOLUTION) { + lcd->setCursor(0,LCD_VERTICAL_RESOLUTION-1); + lcd->print(message); + delay(1000); + } + // print scrolling message + else { + String output_message = " "; + output_message += message; + messageLength = output_message.length(); + + // Adjust the message size for proper printing + if ( messageLength & 1 == 1 ) { + output_message+=" "; + messageLength+=2; + } + + // print scrolling message + for (int cursor = 0; cursor < messageLength - LCD_HORIZONTAL_RESOLUTION; cursor+=2) { + 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 + return false; + } else { + lcd->setCursor(0,LCD_VERTICAL_RESOLUTION-1); + lcd->print(output_message.substring(cursor, cursor+16)); + delay(500); + } + } + } + + return true; +} + +void xPaintStatusTemplate(LiquidCrystal_I2C *lcd, boolean cooking) { + // Clear LCD + lcd->clear(); + + // Position the cursor at the begining of where the temperature template goes onto the screen + lcd->home(); + + // Print the target and measured temperature template + if(cooking) { + lcd->print("ON XX 000.0/000C"); + } + else { + lcd->print("OFF XX 000.0/000C"); + } + + // Position the cursor at the begining of where the mode and time template goes onto the screen + lcd->setCursor (0,LCD_VERTICAL_RESOLUTION-1); + + lcd->print("**** 00:00"); +} + +boolean displayStatus(LiquidCrystal_I2C *lcd, boolean cooking, float cookTemperature, float baseTemperature, float upTemperature, float downTemperature, unsigned long clockCounter, boolean repaint) { + boolean ret = repaint; + + // Check whether a template repaint is required + if(repaint) { + // Repaint the LCD template + xPaintStatusTemplate(lcd, cooking); + + // Reset the repaint flag after the repaint has been done + ret = false; + } + + double displayTemperature = 0.0; + unsigned long ulTimeToShow = millis() % 6000; + + lcd->setCursor (4,0); + if(ulTimeToShow < 2000) { + displayTemperature = baseTemperature; + + lcd->print("TS"); + } + else { + if(ulTimeToShow < 4000) { + displayTemperature = upTemperature; + + lcd->print("UP"); + } + else { + displayTemperature = downTemperature; + + lcd->print("DW"); + } + } + + // Print positions with no numbers, before the measured temperature value + lcd->setCursor (7,0); + if (displayTemperature < 10) { + lcd->print(" "); + } + else { + if (displayTemperature < 100) { + lcd->print(" "); + } + } + + // Print measured temperature value onto the LCD + lcd->print(displayTemperature, 1); + + // Print positions with no numbers, before the target temperature value + lcd->setCursor (13,0); + if (cookTemperature < 10) { + lcd->print(" "); + } + else { + if (cookTemperature < 100) { + lcd->print(" "); + } + } + + // Print target temperature value onto the LCD + lcd->print(cookTemperature); + + // Calculate the numbers on the timer clock + unsigned long minutes = clockCounter / 1000 / 60; + unsigned long seconds = (clockCounter / 1000) % 60; + + // Position the cursor at the begining of where the timer goes onto the screen + lcd->setCursor (10, 1); + + // Print the timer values onto the LCD + if (minutes < 10) { + lcd->print(" 0"); + } + else { + if (minutes < 100) { + lcd->print(" "); + } + } + lcd->print(minutes); + lcd->print(":"); + if(seconds<10) { + lcd->print("0"); + } + lcd->print(seconds); + + return ret; +} + +boolean displayMainMenu(LiquidCrystal_I2C *lcd, eMainMenuOptions position, boolean repaint) { + boolean ret = repaint; + + if(repaint) { + // display menu + lcd->clear(); + lcd->home (); // go home + lcd->print("Brewery Menu"); + + switch(position) { + case eMainMenu_GO: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> GO "); + break; + } + case eMainMenu_STOP: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> STOP "); + break; + } + case eMainMenu_SKIP: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> SKIP "); + break; + } + case eMainMenu_BeerProfile: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> BeerProfile "); + break; + } + case eMainMenu_Stage: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Stages "); + break; + } + case eMainMenu_Malt: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Malt "); + break; + } + case eMainMenu_Hops: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Hops "); + break; + } + case eMainMenu_Clean: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Clean "); + break; + } + case eMainMenu_Purge: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Purge "); + break; + } + case eMainMenu_Settings: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Settings "); + break; + } + case eMainMenu_Back: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Back "); + break; + } + default: { + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep + //xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + } + } + + ret = false; + } + + return ret; +} + +boolean displayBeerProfileMenu(LiquidCrystal_I2C *lcd, eBeerProfileMenuOptions position, boolean repaint) { + boolean ret = repaint; + + if(repaint) { + // display menu + lcd->clear(); + lcd->home (); // go home + lcd->print("Preset Menu"); + + switch(position) { + case eBeerProfileMenu_Basic: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Basic "); + break; + } + case eBeerProfileMenu_Trigo: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Trigo "); + break; + } + case eBeerProfileMenu_IPA: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> IPA "); + break; + } + case eBeerProfileMenu_Belga: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Belga "); + break; + } + case eBeerProfileMenu_Red: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Red "); + break; + } + case eBeerProfileMenu_APA: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> APA "); + break; + } + case eBeerProfileMenu_Custom: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Custom "); + break; + } + case eBeerProfileMenu_Back: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Back "); + break; + } + default: { + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep + //xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + } + } + + ret = false; + } + + return ret; +} +boolean displayStageMenu(LiquidCrystal_I2C *lcd, eStageMenuOptions position, boolean repaint) { + boolean ret = repaint; + + if(repaint) { + // display menu + lcd->clear(); + lcd->home (); // go home + lcd->print("Stage Menu"); + + switch(position) { + case eStageMenu_Startpoint: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Startpoint "); + break; + } + case eStageMenu_BetaGlucanase: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> BetaGlucanase"); + break; + } + case eStageMenu_Debranching: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Debranching "); + break; + } + case eStageMenu_Proteolytic: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Proteolytic "); + break; + } + case eStageMenu_BetaAmylase: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Beta Amylase "); + break; + } + case eStageMenu_AlphaAmylase: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Alpha Amylase"); + break; + } + case eStageMenu_Mashout: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Mashout "); + break; + } + case eStageMenu_Recirculation: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Recirculation"); + break; + } + case eStageMenu_Sparge: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Sparge "); + break; + } + case eStageMenu_Boil: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Boil "); + break; + } + case eStageMenu_Cooling: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Cooling "); + break; + } + case eStageMenu_Back: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Back "); + break; + } + default: { + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep + //xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + } + } + + ret = false; + } + + return ret; +} + +boolean displayMaltMenu(LiquidCrystal_I2C *lcd, eMaltMenuOptions position, boolean repaint) { + boolean ret = repaint; + + if(repaint) { + // display menu + lcd->clear(); + lcd->home (); // go home + lcd->print("Malt Menu"); + + switch(position) { + case eMaltMenu_CastleMalting_Chteau_Pilsen_2RS: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> CM Ch. Pilsen"); + break; + } + case eMaltMenu_CastleMalting_Wheat_Blanc: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> CM Wheat Blan"); + break; + } + case eMaltMenu_Back: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Back "); + break; + } + default: { + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep + //xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + } + } + + ret = false; + } + + return ret; +} + +boolean displaySettingsMenu(LiquidCrystal_I2C *lcd, eSettingsMenuOptions position, boolean repaint) { + boolean ret = repaint; + + if(repaint) { + // display menu + lcd->clear(); + lcd->home (); // go home + lcd->print("Settings Menu"); + + switch(position) { + case eSettingsMenu_PT100_Element: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> PT100 Element"); + break; + } + case eSettingsMenu_PT100_Up: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> PT100 Up "); + break; + } + case eSettingsMenu_PT100_Down: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> PT100 Down "); + break; + } + case eSettingsMenu_Back: { + lcd->setCursor (0,1); // go to start of 2nd line + lcd->print("-> Back "); + break; + } + default: { + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep + //xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + } + } + + ret = false; + } + + return ret; +} \ No newline at end of file diff --git a/Display.h b/Display.h new file mode 100644 index 0000000000000000000000000000000000000000..ff4680ab49e7aaaf7afdbe9dd255fb2d07105811 --- /dev/null +++ b/Display.h @@ -0,0 +1,31 @@ +/* + Display.h - Display functions header file. + Created by João Lino, September 25, 2015. + Released into the public domain. +*/ + +#ifndef DISPLAY_h +#define DISPLAY_h + +#include + +#include +#include +#include + +#include "config.h" +#include "CustomDataStructures.h" + +boolean lcdPrint(LiquidCrystal_I2C *lcd, String title, String message); + +void xPaintStatusTemplate(LiquidCrystal_I2C *lcd, boolean cooking); + +boolean displayStatus(LiquidCrystal_I2C *lcd, boolean cooking, float cookTemperature, float baseTemperature, float upTemperature, float downTemperature, unsigned long clockCounter, boolean repaint); + +boolean displayMainMenu(LiquidCrystal_I2C *lcd, eMainMenuOptions position, boolean repaint); +boolean displayBeerProfileMenu(LiquidCrystal_I2C *lcd, eBeerProfileMenuOptions position, boolean repaint); +boolean displayStageMenu(LiquidCrystal_I2C *lcd, eStageMenuOptions position, boolean repaint); +boolean displayMaltMenu(LiquidCrystal_I2C *lcd, eMaltMenuOptions position, boolean repaint); +boolean displaySettingsMenu(LiquidCrystal_I2C *lcd, eSettingsMenuOptions position, boolean repaint); + +#endif \ No newline at end of file diff --git a/MaybeUseful.h b/MaybeUseful.h new file mode 100644 index 0000000000000000000000000000000000000000..81b1896201fba46427a711aab91fbc79fd841174 --- /dev/null +++ b/MaybeUseful.h @@ -0,0 +1,35 @@ +/* +void termometerCalibration() { + float temperaturePumpOFF = 200.0; + float temperaturePumpON = 200.0; + +#ifdef DEBUG + debugPrintFunction("termometerCalibration"); +#endif + + for( int i = 0; i < 300; i++ ) { + basePT100.measure(); + delay(100); + } + + temperaturePumpOFF = basePT100.getCurrentTemperature(); + + + analogWrite(PUMP_PIN, PUMP_SPEED_MAX); + + for( int i = 0; i < 300; i++ ) { + basePT100.measure(); + delay(100); + } + + temperaturePumpON = basePT100.getCurrentTemperature(); + + analogWrite(PUMP_PIN, PUMP_SPEED_STOP); + +#ifdef DEBUG + debugPrintVar("temperaturePumpOFF", temperaturePumpOFF); + debugPrintVar("temperaturePumpON", temperaturePumpON); + debugPrintVar("diff", temperaturePumpON - temperaturePumpOFF); +#endif +} +*/ \ No newline at end of file diff --git a/Melody.cpp b/Melody.cpp index e12fac7decee4aaed622af4eef4323df43284218..8efe1794e1edfa4f3720f25a84af91881b3cfe02 100644 --- a/Melody.cpp +++ b/Melody.cpp @@ -71,6 +71,18 @@ int supermario_tempo[] = { 12, 12, 12, 12, }; +//Underworld short melody +int underworld_melody_short[] = { + NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4, + NOTE_AS3, NOTE_AS4 +}; + +//Underwolrd short tempo +int underworld_tempo_short[] = { + 12, 12, 12, 12, + 12, 12 +}; + //Underworld melody int underworld_melody[] = { NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4, @@ -117,6 +129,16 @@ int underworld_tempo[] = { 3, 3, 3 }; +int buzz_1_melody[] = { + NOTE_E7, NOTE_E7, NOTE_E7, NOTE_E7, + 0, 0, 0, 0, +}; +//Mario main them tempo +int buzz_1_tempo[] = { + 12, 12, 12, 12, + 12, 12, 12, 12, +}; + int song = 0; void buzz(int targetPin, long frequency, long length) { @@ -139,7 +161,7 @@ void sing(int s, int pin) { song = s; switch(song) { - case 1: { + case MELODY_SUPER_MARIO: { int size = sizeof(supermario_melody) / sizeof(int); for (int thisNote = 0; thisNote < size; thisNote++) { @@ -161,7 +183,7 @@ void sing(int s, int pin) { } break; } - case 2: { + case MELODY_UNDERWORLD: { int size = sizeof(underworld_melody) / sizeof(int); for (int thisNote = 0; thisNote < size; thisNote++) { @@ -183,7 +205,7 @@ void sing(int s, int pin) { } break; } - case 3: { + case MELODY_SUPER_MARIO_START: { int size = sizeof(supermario_start_melody) / sizeof(int); for (int thisNote = 0; thisNote < size; thisNote++) { @@ -205,6 +227,50 @@ void sing(int s, int pin) { } break; } + case MELODY_UNDERWORLD_SHORT: { + int size = sizeof(underworld_melody_short) / sizeof(int); + for (int thisNote = 0; thisNote < size; thisNote++) { + + // to calculate the note duration, take one second + // divided by the note type. + //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. + int noteDuration = 1000 / underworld_tempo_short[thisNote]; + + buzz(pin, underworld_melody_short[thisNote], noteDuration); + + // to distinguish the notes, set a minimum time between them. + // the note's duration + 30% seems to work well: + int pauseBetweenNotes = noteDuration * 1.30; + delay(pauseBetweenNotes); + + // stop the tone playing: + buzz(pin, 0, noteDuration); + + } + break; + } + case BUZZ_1: { + int size = sizeof(buzz_1_melody) / sizeof(int); + for (int thisNote = 0; thisNote < size; thisNote++) { + + // to calculate the note duration, take one second + // divided by the note type. + //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. + int noteDuration = 1000 / buzz_1_tempo[thisNote]; + + buzz(pin, buzz_1_melody[thisNote], noteDuration); + + // to distinguish the notes, set a minimum time between them. + // the note's duration + 30% seems to work well: + int pauseBetweenNotes = noteDuration * 1.30; + delay(pauseBetweenNotes); + + // stop the tone playing: + buzz(pin, 0, noteDuration); + + } + break; + } } } \ No newline at end of file diff --git a/Melody.h b/Melody.h index c0246a0db804fb9cfa235ff87a16f339f5256f2b..78612a85776eaedc38cb10006740e4b74ae503bc 100644 --- a/Melody.h +++ b/Melody.h @@ -103,9 +103,11 @@ // Define a special note, 'R', to represent a rest #define _R 0 -#define MELODY_SUPER_MARIO 1 -#define MELODY_UNDERWORLD 2 -#define MELODY_SUPER_MARIO_START 3 +#define MELODY_SUPER_MARIO 1 +#define MELODY_UNDERWORLD 2 +#define MELODY_SUPER_MARIO_START 3 +#define MELODY_UNDERWORLD_SHORT 4 +#define BUZZ_1 5 // melody[] is an array of notes, accompanied by beats[], diff --git a/brew.ino b/brew.ino index 2e3ebba19af2a121bbd14b0ea6387036f0ebed1a..7dcbc51466ad03c8761b42744e7e1fadd075b2f7 100644 --- a/brew.ino +++ b/brew.ino @@ -1,151 +1,83 @@ -#define DEBUG - -// ######################### CONSTANTS ######################### - -// ######################### SETTINGS ######################### -// ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++ -#define HEATING_ELEMENT_DEFAULT_WINDOW_SIZE 1000 -#define HEATING_ELEMENT_OUTPUT_PIN 24 -#define HEATING_ELEMENT_MAX_HEAT_PWM_INTEGER 5 -#define HEATING_ELEMENT_MAX_HEAT_PWM_FLOAT 5.0 -#define HEATING_ELEMENT_MAX_WATTAGE 3000.0 // Minimum = 2000.0 -#define HEATING_ELEMENT_AC_FREQUENCY_HZ 50.0 - -// ++++++++++++++++++++++++ Temperature ++++++++++++++++++++++++ -#define PT100_BASE_INPUT_PIN A4 -#define PT100_BASE_OUTPUT_PIN 32 -#define PT100_BASE_TIME_BETWEEN_READINGS 100 -#define PT100_UP_INPUT_PIN A5 -#define PT100_UP_OUTPUT_PIN 30 -#define PT100_UP_TIME_BETWEEN_READINGS 100 -#define PT100_DOWN_INPUT_PIN A6 -#define PT100_DOWN_OUTPUT_PIN 31 -#define PT100_DOWN_TIME_BETWEEN_READINGS 100 - -#define PT100_BASE_DEFAULT_ADC_VMAX 1.1 -#define PT100_BASE_DEFAULT_VS 5.0 -#define PT100_BASE_DEFAULT_R1_RESISTENCE 605.2 //608.99 -#define PT100_BASE_DEFAULT_LINE_RESISTENCE 0.0 -#define PT100_BASE_DEFAULT_OPERATION_RESISTENCE 0.0 -#define PT100_UP_DEFAULT_ADC_VMAX 1.1 -#define PT100_UP_DEFAULT_VS 5.0 -#define PT100_UP_DEFAULT_R1_RESISTENCE 615.6 //621 //620 //618.1 -#define PT100_UP_DEFAULT_LINE_RESISTENCE 0.0 -#define PT100_UP_DEFAULT_OPERATION_RESISTENCE 0.0 -#define PT100_DOWN_DEFAULT_ADC_VMAX 1.1 -#define PT100_DOWN_DEFAULT_VS 5.0 -#define PT100_DOWN_DEFAULT_R1_RESISTENCE 618.6 //619.3 //617.2 -#define PT100_DOWN_DEFAULT_LINE_RESISTENCE 0.0 -#define PT100_DOWN_DEFAULT_OPERATION_RESISTENCE 0.0 - -// ++++++++++++++++++++++++ Mixer ++++++++++++++++++++++++ -//#define MIXER_PIN 12 -//#define MIXER_MAX_POSITION 255 - -// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++ -#include "Melody.h" -#define PIEZO_PIN 25 - -// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++ -#define PUMP_PIN 6 -#define PUMP_SPEED_STOP 0 -#define PUMP_SPEED_SLOW 64 -#define PUMP_SPEED_AVERAGE 128 -#define PUMP_SPEED_FAST 192 -#define PUMP_SPEED_MAX 255 - -// ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++ -#define ROTARY_ENCODER_INTERRUPT_NUMBER 1 // On Mega2560 boards, interrupt 1 is on pin 3 -#define ROTARY_ENCODER_CLK_PIN 3 // Used for generating interrupts using CLK signal -#define ROTARY_ENCODER_DT_PIN 22 // Used for reading DT signal -#define ROTARY_ENCODER_SW_PIN 23 // Used for the push button switch -#define ROTARY_ENCODER_DEBOUNCE_TIME 20 // Number of miliseconds to ignore new signals a signal is received +/* + brew.ino - Main execution file. + Created by João Lino, August 28, 2014. + Released into the public domain. +*/ -// ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++ -#define SETTING_WELCOME_TIMEOUT 100 -#define SETTING_MAX_INACTIVITY_TIME 3000 -#define MENU_MAX_DEPTH 10 -#define MENU_INIT_VALUES -1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -#define MENU_SIZE_MAIN_MENU 17 -#define SETTING_SERIAL_MONITOR_BAUD_RATE 9600 -#define SETTING_SERIAL_MONITOR_WELCOME_MESSAGE "Let's start Brewing!" +#define DEBUG // ######################### LIBRARIES ######################### + // ++++++++++++++++++++++++ LiquidCrystal_I2C ++++++++++++++++++++++++ #include #include #include -#define LCD_I2C_ADDR 0x27 // <<----- Add your address here. Find it from I2C Scanner -#define LCD_HORIZONTAL_RESOLUTION 16 -#define LCD_VERTICAL_RESOLUTION 2 -#define LCD_BACKLIGHT_PIN 3 -#define LCD_EN_PIN 2 -#define LCD_RW_PIN 1 -#define LCD_RS_PIN 0 -#define LCD_D4_PIN 4 -#define LCD_D5_PIN 5 -#define LCD_D6_PIN 6 -#define LCD_D7_PIN 7 - // ++++++++++++++++++++++++ PT100 +++++++++++++++++++++++++++++++++ #include -// ++++++++++++++++++++++++ ENUM +++++++++++++++++++++++++++++++++ +// ++++++++++++++++++++++++ OTHER +++++++++++++++++++++++++++++++++ +#include "debug.h" + +#include "config.h" + #include "CustomDataStructures.h" -// ######################### TEMPLATES ######################### -// ++++++++++++++++++++++++ Debug ++++++++++++++++++++++++ -template void debugPrintVar( char *name, const T& value ); -template void debugPrintVar( char *name, const T& value ) { - Serial.print("["); - Serial.print(name); - Serial.print(":"); - Serial.print(value); - Serial.println("]"); -} -void debugPrintFunction( char *name ) { - Serial.print("++++++++++++++++++++++++ "); - Serial.print(name); - Serial.println("++++++++++++++++++++++++"); -} +#include "Melody.h" +#include "Display.h" // ######################### VARIABLES ######################### // ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++ eRotaryEncoderMode rotaryEncoderMode; -eMainMenuOptions mainMenuOption; -ePresetsMenuOptions presetsMenuOption; + +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; + eMaltMenuOptions maltMenuOption; eSettingsMenuOptions settingsMenuOption; -eCookingStages cookingStage; // ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++ boolean cooking; boolean bStageFirstRun; -unsigned long clockStartTime; -unsigned long clockCounter; -unsigned long clockIgnore; +unsigned long clockStartTime; +unsigned long clockLastUpdate; +long clockCounter; +unsigned long clockIgnore; boolean clockStart; boolean clockEnd; -unsigned long cookTime; +unsigned long cookTime; int cookTemperature; //cook_mode_list cookMode; //int cookMixerSpeed; -int cookHeatPWM; +int finalYield; -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 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; int startpointTemperature; int betaGlucanaseTemperature; @@ -158,13 +90,13 @@ int recirculationTemperature; int spargeTemperature; int boilTemperature; int coolingTemperature; - -//int menuSize; -int menu_position[MENU_MAX_DEPTH] = {MENU_INIT_VALUES}; +int cleaningTemperature; boolean refresh; boolean repaint; +boolean bStatusElement; + // ++++++++++++++++++++++++ Interrupts ++++++++++++++++++++++++ static unsigned long lastInterruptTime; @@ -190,9 +122,9 @@ int iPumpSpeed; // Time frame to operate in 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); // +++++++++++++++++++++++ PT100 +++++++++++++++++++++++ -PT100 basePT100("base", PT100_BASE_OUTPUT_PIN, PT100_BASE_INPUT_PIN, PT100_BASE_TIME_BETWEEN_READINGS, PT100_BASE_DEFAULT_ADC_VMAX, PT100_BASE_DEFAULT_VS, PT100_BASE_DEFAULT_R1_RESISTENCE, PT100_BASE_DEFAULT_LINE_RESISTENCE, PT100_BASE_DEFAULT_OPERATION_RESISTENCE, 0.0, 0.0); -PT100 upPT100("up", PT100_UP_OUTPUT_PIN, PT100_UP_INPUT_PIN, PT100_UP_TIME_BETWEEN_READINGS, PT100_UP_DEFAULT_ADC_VMAX, PT100_UP_DEFAULT_VS, PT100_UP_DEFAULT_R1_RESISTENCE, PT100_UP_DEFAULT_LINE_RESISTENCE, PT100_UP_DEFAULT_OPERATION_RESISTENCE, 0.112329092, -3.57); -PT100 downPT100("down", PT100_DOWN_OUTPUT_PIN, PT100_DOWN_INPUT_PIN, PT100_DOWN_TIME_BETWEEN_READINGS, PT100_DOWN_DEFAULT_ADC_VMAX, PT100_DOWN_DEFAULT_VS, PT100_DOWN_DEFAULT_R1_RESISTENCE, PT100_DOWN_DEFAULT_LINE_RESISTENCE, PT100_DOWN_DEFAULT_OPERATION_RESISTENCE, 0.22, -5.5); +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); // ######################### INTERRUPTS ######################### void isr () { // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK @@ -300,20 +232,25 @@ void xSafeHardwarePowerOff() { // Force shutdown analogWrite(PUMP_PIN, PUMP_SPEED_STOP); // analogWrite values from 0 to 255 digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, LOW); // Turn heading element OFF for safety + bStatusElement = false; + + basePT100.setSampleDeviation( 0.0 ); + upPT100.setSampleDeviation( 0.0 ); + downPT100.setSampleDeviation( 0.0 ); -//analogWrite(MIXER_PIN, 0); // Turn mixer OFF for safety + //analogWrite(MIXER_PIN, 0); // Turn mixer OFF for safety } -void displayWelcome() { -#ifndef DEBUG - lcdPrint(" Let's start", " Brewing!"); // Write welcome +void xWelcomeUser() { + //#ifndef DEBUG + lcdPrint(&lcd, " Let's start", " Brewing!"); // Write welcome // Play Melody; sing(MELODY_SUPER_MARIO_START, PIEZO_PIN); //termometerCalibration(); delay(SETTING_WELCOME_TIMEOUT); // pause for effect -#endif + //#endif } void setup() { @@ -326,6 +263,7 @@ void setup() { // ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++ pinMode (HEATING_ELEMENT_OUTPUT_PIN, OUTPUT); digitalWrite (HEATING_ELEMENT_OUTPUT_PIN, LOW); + bStatusElement = false; windowStartTime = millis(); dWattPerPulse = HEATING_ELEMENT_MAX_WATTAGE / HEATING_ELEMENT_AC_FREQUENCY_HZ; @@ -343,11 +281,11 @@ void setup() { // ++++++++++++++++++++++++ Temperature Sensor PT100 ++++++++++++++++++++++++ //basePT100.setup(); -/* + /* analogReference (INTERNAL1V1); // EXTERNAL && INTERNAL2V56 && INTERNAL1V1 pinMode (PT100_OUTPUT_PIN, OUTPUT); // setup temperature sensor input pin digitalWrite (PT100_OUTPUT_PIN, LOW); // initialize sensor off -*/ + */ // ++++++++++++++++++++++++ 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 @@ -363,15 +301,28 @@ void setup() { xSetupRotaryEncoder ( eRotaryEncoderMode_Disabled, 0, 0, 0, 0, 0 ); // ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++ - presetsMenuOption = ePresetsMenu_Trigo; - maltMenuOption = eMaltMenu_CastleMalting_Chteau_Pilsen_2RS; - settingsMenuOption = eSettingsMenu_PT100_Element; + 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; + cookingStage = eCookingStage_Startpoint; + beerProfile = eBeerProfile_Basic; // ++++++++++++++++++++++++ Global Variables ++++++++++++++++++++++++ + cooking = false; bStageFirstRun = true; clockStartTime = 0; + clockLastUpdate = 0; clockCounter = 0; clockIgnore = 0; clockStart = false; @@ -381,9 +332,9 @@ void setup() { cookTemperature = 25; //cookMode = quick_start; //cookMixerSpeed = 120; - cookHeatPWM = 5; + finalYield = 25; - startpointTime = 3600; //120; + startpointTime = 120; betaGlucanaseTime = 0; debranchingTime = 0; proteolyticTime = 0; @@ -394,8 +345,9 @@ void setup() { spargeTime = 1200; boilTime = 5400; coolingTime = 120; + cleaningTime = SETTING_CLEANING_TIME; - startpointTemperature = 50; //30; + startpointTemperature = 30; betaGlucanaseTemperature = 40; debranchingTemperature = 40; proteolyticTemperature = 50; @@ -406,6 +358,7 @@ void setup() { spargeTemperature = 80; boilTemperature = 100; coolingTemperature = 25; + cleaningTemperature = SETTING_CLEANING_TEMPERATURE; refresh = true; repaint = true; @@ -416,16 +369,18 @@ void setup() { // ++++++++++++++++++++++++ PID ++++++++++++++++++++++++ iWindowSize = HEATING_ELEMENT_DEFAULT_WINDOW_SIZE; // Time frame to operate in -// ######################### Code - Run Once ######################### + // ######################### Code - Run Once ######################### xSafeHardwarePowerOff (); - displayWelcome (); + xWelcomeUser (); - xSetupRotaryEncoder ( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder ( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); } // ######################### MAIN LOOP ######################### void loop() { + //cleanSerialMonitor(); + unsigned long inactivityTime = millis() - lastInterruptTime; if(inactivityTime > SETTING_MAX_INACTIVITY_TIME) { // Inactivity check @@ -433,598 +388,747 @@ void loop() { repaint = true; refresh = false; } - displayStatus(); + repaint = displayStatus( &lcd, cooking, cookTemperature, basePT100.getCurrentTemperature(), upPT100.getCurrentTemperature(), downPT100.getCurrentTemperature(), clockCounter, repaint ); } else { - displayMainMenu(); + runMenu(); } - operateMachine(); + xManageMachineSystems(); } // ######################### FUNCTIONS ######################## -void xPaintStatusTemplate() { - // Clear LCD - lcd.clear(); - - // Position the cursor at the begining of where the temperature template goes onto the screen - lcd.home(); - - // Print the target and measured temperature template - if(cooking) { - lcd.print("ON XX 000.0/000C"); - } - else { - lcd.print("OFF XX 000.0/000C"); +void runMenu() { + #ifdef DEBUG_OFF + boolean debug_go = repaint; + if(debug_go) { + debugPrintFunction("runMenu"); + debugPrintVar("repaint", repaint); + debugPrintVar("eMenuType", eMenuType); + debugPrintVar("rotaryEncoderVirtualPosition", rotaryEncoderVirtualPosition); } + #endif - // Position the cursor at the begining of where the mode and time template goes onto the screen - lcd.setCursor (0,LCD_VERTICAL_RESOLUTION-1); + switch(eMenuType) { + case eMenuType_Main: { + eMainMenuPosition = static_cast(rotaryEncoderVirtualPosition); - lcd.print("**** 00:00"); -} + repaint = displayMainMenu( &lcd, eMainMenuPosition, repaint ); -void displayStatus() { + if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) { + eMainMenuSelection = eMainMenuPosition; + } - // Check whether a template repaint is required - if(repaint) { - // Repaint the LCD template - xPaintStatusTemplate(); - - // Reset the repaint flag after the repaint has been done - repaint = false; - } - - double displayTemperature = 0.0; - unsigned long ulTimeToShow = millis() % 6000; + runMainMenuSelection(); - lcd.setCursor (4,0); - if(ulTimeToShow < 2000) { - displayTemperature = basePT100.getCurrentTemperature(); + break; + } + case eMenuType_BeerProfile: { + eBeerProfileMenuPosition = static_cast(rotaryEncoderVirtualPosition); + + repaint = displayBeerProfileMenu( &lcd, eBeerProfileMenuPosition, repaint ); - lcd.print("TS"); - } - else { - if(ulTimeToShow < 4000) { - displayTemperature = upPT100.getCurrentTemperature(); + if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) { + eBeerProfileMenuSelection = eBeerProfileMenuPosition; + } - lcd.print("UP"); + runBeerProfileSelection(); + + break; } - else { - displayTemperature = downPT100.getCurrentTemperature(); + case eMenuType_Stage: { + eStageMenuPosition = static_cast(rotaryEncoderVirtualPosition); + + repaint = displayStageMenu( &lcd, eStageMenuPosition, repaint ); - lcd.print("DW"); - } - } + if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) { + eStageMenuSelection = eStageMenuPosition; + } - // Print positions with no numbers, before the measured temperature value - lcd.setCursor (7,0); - if (displayTemperature < 10) { - lcd.print(" "); - } - else { - if (displayTemperature < 100) { - lcd.print(" "); + runStageSelection(); + + break; } - } + case eMenuType_Malt: { + eMaltMenuPosition = static_cast(rotaryEncoderVirtualPosition); + + repaint = displayMaltMenu( &lcd, eMaltMenuPosition, repaint ); - // Print measured temperature value onto the LCD - lcd.print(displayTemperature, 1); + if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) { + eMaltMenuSelection = eMaltMenuPosition; + } - // Print positions with no numbers, before the target temperature value - lcd.setCursor (13,0); - if (cookTemperature < 10) { - lcd.print(" "); - } - else { - if (cookTemperature < 100) { - lcd.print(" "); + runMaltSelection(); + + break; } - } + case eMenuType_Settings: { + eSettingsMenuPosition = static_cast(rotaryEncoderVirtualPosition); + + repaint = displaySettingsMenu( &lcd, eSettingsMenuPosition, repaint ); - // Print target temperature value onto the LCD - lcd.print(cookTemperature); - - // Calculate the numbers on the timer clock - unsigned long minutes = clockCounter / 1000 / 60; - unsigned long seconds = (clockCounter / 1000) % 60; + if ( gotButtonPress( ROTARY_ENCODER_SW_PIN ) ) { + eSettingsMenuSelection = eSettingsMenuPosition; + } - // Position the cursor at the begining of where the timer goes onto the screen - lcd.setCursor (10, 1); - - // Print the timer values onto the LCD - if (minutes < 10) { - lcd.print(" 0"); - } - else { - if (minutes < 100) { - lcd.print(" "); + runSettingsSelection(); + + break; } } - lcd.print(minutes); - lcd.print(":"); - if(seconds<10) { - lcd.print("0"); + + #ifdef DEBUG_OFF + if(debug_go) { + debugPrintVar("repaint", repaint); } - lcd.print(seconds); + #endif } -void displayMainMenu() { - switch(menu_position[0]) { - case eMainMenu_GO: { - MainMenu_GO(); +void runSettingsSelection() { + switch(eSettingsMenuSelection) { + case eSettingsMenu_PT100_Element: { + // Stuff + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_STOP: { - MainMenu_STOP(); + case eSettingsMenu_PT100_Up: { + // Stuff + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Presets: { - MainMenu_Presets(); + case eSettingsMenu_PT100_Down: { + // Stuff + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Presets, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Malt: { - MainMenu_Malt(); + 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(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Malt, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Startpoint: { - MainMenu_Startpoint(); + case eMaltMenu_CastleMalting_Wheat_Blanc: { + // Stuff + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Startpoint, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_BetaGlucanase: { - MainMenu_BetaGlucanase(); + case eMaltMenu_Back: { + eMenuType = eMenuType_Main; + repaint = true; - menu_position[0] = -1; + // 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; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_BetaGlucanase, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Debranching: { - MainMenu_Debranching(); + 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 ); - menu_position[0] = -1; + break; + } + case eStageMenu_Debranching: { + debranchingTime = getTimer( debranchingTime ); + + debranchingTemperature = xSetGenericValue( debranchingTemperature, 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, eMainMenu_Settings, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Proteolytic: { - MainMenu_Proteolytic(); + case eStageMenu_Proteolytic: { + proteolyticTime = getTimer( proteolyticTime ); - menu_position[0] = -1; + proteolyticTemperature = xSetGenericValue( proteolyticTemperature, 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, eMainMenu_Proteolytic, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_BetaAmylase: { - MainMenu_BetaAmylase(); - - menu_position[0] = -1; + case eStageMenu_BetaAmylase: { + betaAmylaseTime = getTimer( betaAmylaseTime ); + + betaAmylaseTemperature = xSetGenericValue( betaAmylaseTemperature, 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, eMainMenu_BetaAmylase, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_AlphaAmylase: { - MainMenu_AlphaAmylase(); + case eStageMenu_AlphaAmylase: { + alphaAmylaseTime = getTimer( alphaAmylaseTime ); + + alphaAmylaseTemperature = xSetGenericValue( alphaAmylaseTemperature, 0, 120, "temperature", "*C" ); + + backToStatus(); + + eMenuType = eMenuType_Main; + repaint = true; - menu_position[0] = -1; + // 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; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_AlphaAmylase, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Mashout: { - MainMenu_Mashout(); + 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 ); - menu_position[0] = -1; + break; + } + case eStageMenu_Sparge: { + spargeTime = getTimer( spargeTime ); + + spargeTemperature = xSetGenericValue( spargeTemperature, 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, eMainMenu_Mashout, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Recirculation: { - MainMenu_Recirculation(); + case eStageMenu_Boil: { + boilTime = getTimer( boilTime ); - menu_position[0] = -1; + boilTemperature = xSetGenericValue( boilTemperature, 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, eMainMenu_Recirculation, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Sparge: { - MainMenu_Sparge(); + case eStageMenu_Cooling: { + coolingTime = getTimer( coolingTime ); - menu_position[0] = -1; + coolingTemperature = xSetGenericValue( coolingTemperature, 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, eMainMenu_Sparge, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Boil: { - MainMenu_Boil(); + case eStageMenu_Back: { + eMenuType = eMenuType_Main; + repaint = true; - menu_position[0] = -1; + // 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; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Boil, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Hops: { - MainMenu_Hops(); + case eBeerProfileMenu_IPA: { + beerProfile = eBeerProfile_IPA; + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Hops, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); - + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); + break; } - case eMainMenu_Cooling: { - MainMenu_Cooling(); + case eBeerProfileMenu_Belga: { + beerProfile = eBeerProfile_Belga; + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Cooling, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Purge: { - MainMenu_Purge(); + case eBeerProfileMenu_Red: { + beerProfile = eBeerProfile_Red; - menu_position[0] = -1; + 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, eMainMenu_Purge, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Settings: { - MainMenu_Settings(); + case eBeerProfileMenu_APA: { + beerProfile = eBeerProfile_APA; + + backToStatus(); - menu_position[0] = -1; + eMenuType = eMenuType_Main; + repaint = true; // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Settings, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenuPosition, MENU_SIZE_MAIN_MENU - 1, 1, 1, 0 ); break; } - case eMainMenu_Back: { - MainMenu_Back(); - - menu_position[0] = -1; - + 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; + // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_Back, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); + 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 ); break; } default: { - if(repaint) { - - // display menu - lcd.clear(); - lcd.home (); // go home - lcd.print("Brewery Menu"); - - switch(rotaryEncoderVirtualPosition) { - case eMainMenu_GO: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> GO "); - break; - } - case eMainMenu_STOP: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> STOP "); - break; - } - case eMainMenu_Presets: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Presets "); - break; - } - case eMainMenu_Malt: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Malt "); - break; - } - case eMainMenu_Startpoint: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Startpoint "); - break; - } - case eMainMenu_BetaGlucanase: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> BetaGlucanase"); - break; - } - case eMainMenu_Debranching: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Debranching "); - break; - } - case eMainMenu_Proteolytic: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Proteolytic "); - break; - } - case eMainMenu_BetaAmylase: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Beta Amylase "); - break; - } - case eMainMenu_AlphaAmylase: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Alpha Amylase"); - break; - } - case eMainMenu_Mashout: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Mashout "); - break; - } - case eMainMenu_Recirculation: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Recirculation"); - break; - } - case eMainMenu_Sparge: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Sparge "); - break; - } - case eMainMenu_Boil: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Boil "); - break; - } - case eMainMenu_Hops: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Hops "); - break; - } - case eMainMenu_Cooling: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Cooling "); - break; - } - case eMainMenu_Purge: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Purge "); - break; - } - case eMainMenu_Settings: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Settings "); - break; - } - case eMainMenu_Back: { - lcd.setCursor (0,1); // go to start of 2nd line - lcd.print("-> Back "); - break; - } - default: { - // reset operation state | INPUT : eRotaryEncoderMode newMode, int newPosition, int newMaxPosition, int newMinPosition, int newSingleStep, int newMultiStep - xSetupRotaryEncoder( eRotaryEncoderMode_Menu, eMainMenu_GO, MENU_SIZE_MAIN_MENU - 1, 0, 1, 0 ); - } - } - - repaint = false; - } - - if ((digitalRead(ROTARY_ENCODER_SW_PIN))) { // check if pushbutton is pressed - menu_position[0] = rotaryEncoderVirtualPosition; - while (digitalRead(ROTARY_ENCODER_SW_PIN)) {} // wait til switch is released - delay(10); // debounce - break; - } - } + } } -} - -void MainMenu_GO() { - startBrewing(); - - xTransitionIntoStage_GlobalVariables( eCookingStage_Startpoint ); - - backToStatus(); -} - -void MainMenu_STOP() { - stopBrewing(); - - backToStatus(); -} - -void MainMenu_Presets() { - - backToStatus(); - -} - -void MainMenu_Malt() { - - backToStatus(); - -} - -void MainMenu_Startpoint() { - startpointTime = getTimer( startpointTime ); - - startpointTemperature = xSetGenericValue( startpointTemperature, 0, 120, "temperature", "*C" ); - - backToStatus(); -} - -void MainMenu_BetaGlucanase() { - betaGlucanaseTime = getTimer( betaGlucanaseTime ); - - betaGlucanaseTemperature = xSetGenericValue( betaGlucanaseTemperature, 0, 120, "temperature", "*C" ); - backToStatus(); + eBeerProfileMenuSelection = eBeerProfileMenu_NULL; } -void MainMenu_Debranching() { - debranchingTime = getTimer( debranchingTime ); - - debranchingTemperature = xSetGenericValue( debranchingTemperature, 0, 120, "temperature", "*C" ); - - backToStatus(); -} - -void MainMenu_Proteolytic() { - proteolyticTime = getTimer( proteolyticTime ); - - proteolyticTemperature = xSetGenericValue( proteolyticTemperature, 0, 120, "temperature", "*C" ); - - backToStatus(); -} - -void MainMenu_BetaAmylase() { - betaAmylaseTime = getTimer( betaAmylaseTime ); - - betaAmylaseTemperature = xSetGenericValue( betaAmylaseTemperature, 0, 120, "temperature", "*C" ); - - backToStatus(); -} - -void MainMenu_AlphaAmylase() { - alphaAmylaseTime = getTimer( alphaAmylaseTime ); - - alphaAmylaseTemperature = xSetGenericValue( alphaAmylaseTemperature, 0, 120, "temperature", "*C" ); - - backToStatus(); -} +void runMainMenuSelection() { + switch(eMainMenuSelection) { + case eMainMenu_GO: { + finalYield = xSetGenericValue( finalYield, 0, 50, "Final Yield", "l" ); -void MainMenu_Mashout() { - mashoutTime = getTimer( mashoutTime ); - - mashoutTemperature = xSetGenericValue( mashoutTemperature, 0, 120, "temperature", "*C" ); + startBrewing(); - backToStatus(); -} + xSetupGlobalVariablesForStage( eCookingStage_Startpoint ); -void MainMenu_Recirculation() { - recirculationTime = getTimer( recirculationTime ); - - recirculationTemperature = xSetGenericValue( recirculationTemperature, 0, 120, "temperature", "*C" ); + 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(); - backToStatus(); -} + break; + } + case eMainMenu_STOP: { + stopBrewing(); -void MainMenu_Sparge() { - spargeTime = getTimer( spargeTime ); - - spargeTemperature = xSetGenericValue( spargeTemperature, 0, 120, "temperature", "*C" ); + 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; - backToStatus(); -} + 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(); -void MainMenu_Boil() { - boilTime = getTimer( boilTime ); - - boilTemperature = xSetGenericValue( boilTemperature, 0, 120, "temperature", "*C" ); + // Start at the Clean stage + startBrewing(); - backToStatus(); -} + xSetupGlobalVariablesForStage( eCookingStage_Clean ); -void MainMenu_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 ); + + xPurgePump(); - backToStatus(); - -} + break; + } + case eMainMenu_Purge: { + // Stop anything that might be still going on + xSafeHardwarePowerOff(); -void MainMenu_Cooling() { - coolingTime = getTimer( coolingTime ); - - coolingTemperature = xSetGenericValue( coolingTemperature, 0, 120, "temperature", "*C" ); + // Start at the Purge stage + startBrewing(); - backToStatus(); -} + xSetupGlobalVariablesForStage( eCookingStage_Purge ); -void MainMenu_Purge() { - // Stop anything that might be still going on - xSafeHardwarePowerOff(); + 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(); - // Start at the Purge stage - startBrewing(); - xTransitionIntoStage_GlobalVariables( eCookingStage_Purge ); + break; + } + case eMainMenu_Settings: { + eMenuType = eMenuType_Settings; + repaint = true; - backToStatus(); -} + // 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 ); -void MainMenu_Settings() { - iPumpSpeed = xSetGenericValue( iPumpSpeed, 0, 255, "Pump Speed", "PWM" ); + break; + } + default: { + } + } - backToStatus(); + eMainMenuSelection = eMainMenu_NULL; } -void MainMenu_Back() { - backToStatus(); -} -void xCountTheTime( int temperatureRange ) { +void xCountTheTime( int temperatureRange, boolean bAverageUpDown ) { unsigned long now = millis(); + 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(); + } // Check if the machine is in the right temperature range, for the current mode, - if(!(basePT100.getCurrentTemperature() > (cookTemperature - temperatureRange) && basePT100.getCurrentTemperature() < (cookTemperature + temperatureRange))) { - clockIgnore += now - clockStartTime - clockIgnore; + //if(!( temperatureCount > (cookTemperature - temperatureRange) && temperatureCount < (cookTemperature + temperatureRange))) { + float margin = temperatureRange; + if( cookTemperature >= 100.0 ) { + margin = 2.0; + } + if( temperatureCount < (cookTemperature - margin) ) { + clockIgnore += elapsedTime; } // Calculate the remaining time on the clock clockCounter = cookTime * 1000 - (now - clockStartTime - clockIgnore); -#ifdef DEBUG_OFF + if ( clockCounter < 0 ) { + clockCounter = 0; + } + + clockLastUpdate = now; + + #ifdef DEBUG_OFF debugPrintFunction("xCountTheTime"); debugPrintVar("millis()", now); debugPrintVar("cookTime", cookTime); debugPrintVar("clockStartTime", clockStartTime); debugPrintVar("clockIgnore", clockIgnore); debugPrintVar("clockCounter", clockCounter); -#endif + #endif } bool isTimeLeft() { @@ -1040,11 +1144,35 @@ double ulWattToWindowTime( double ulAppliedWatts ) { return (double)iWindowSize / 1000.0 * ulPulsesRequired * 1000.0 / HEATING_ELEMENT_AC_FREQUENCY_HZ; } -bool xRegulateTemperature() { - double difference = cookTemperature - basePT100.getCurrentTemperature(); +bool xRegulateTemperature( boolean bAverageUpDown ) { + double difference = 0; bool overTemperature = false; double wattage = 0.0; + 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(); + } + // Deviation between the cook temperature set and the cook temperature measured if( difference < 0.0 ) { difference = difference * (-1.0); @@ -1060,15 +1188,20 @@ bool xRegulateTemperature() { // turn it off wattage = 0.0; } else { - if(difference <= 1) { + if(difference <= 0.5) { // pulse lightly at 500 watt - wattage = 500.0; + if(cookTemperature > 99.0) { + wattage = 1500.0; + } + else { + wattage = 500.0; + } } else { - if(difference <= 3) { + if(difference <= 1.0) { // pulse moderately at 1000 watt wattage = 1000.0; } else { - if(difference <= 6) { + if(difference <= 3.0) { // pulse hardly at 2000 watt wattage = 2000.0; } else { @@ -1088,11 +1221,13 @@ bool xRegulateTemperature() { // Apply wattage to the element at the right time if( ulWattToWindowTime( wattage ) > (millis() - windowStartTime) ) { digitalWrite(HEATING_ELEMENT_OUTPUT_PIN,HIGH); + bStatusElement = true; } else { digitalWrite(HEATING_ELEMENT_OUTPUT_PIN,LOW); + bStatusElement = false; } -#ifdef DEBUG_OFF + #ifdef DEBUG_OFF //debugPrintFunction("xRegulateTemperature"); debugPrintVar("difference", difference); //debugPrintVar("overTemperature", overTemperature); @@ -1101,23 +1236,34 @@ bool xRegulateTemperature() { //debugPrintVar("millis()", millis()); //debugPrintVar("windowStartTime", windowStartTime); //debugPrintVar("test", ulWattToWindowTime( wattage ) > (millis() - windowStartTime) ); -#endif + #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); + } } bool xRegulatePumpSpeed() { - analogWrite(PUMP_PIN, iPumpSpeed); // analogWrite values from 0 to 255 + // 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 - if( iPumpSpeed ) { - //basePT100.setPower( PT100_BASE_DARLINGTON_ADC_VMAX, PT100_BASE_DARLINGTON_VS ); - basePT100.setMeasuredTemperatureDeviation( -0.90 ); - upPT100.setMeasuredTemperatureDeviation( -1.00 ); - downPT100.setMeasuredTemperatureDeviation( -1.10 ); + basePT100.setSampleDeviation( 0.0 ); + upPT100.setSampleDeviation( 0.0 ); + downPT100.setSampleDeviation( 0.0 ); } else { - //basePT100.setPower( PT100_BASE_DEFAULT_ADC_VMAX, PT100_BASE_DEFAULT_VS ); - basePT100.setMeasuredTemperatureDeviation( 0.0 ); - upPT100.setMeasuredTemperatureDeviation( 0.0 ); - downPT100.setMeasuredTemperatureDeviation( 0.0 ); + analogWrite(PUMP_PIN, iPumpSpeed); // analogWrite values from 0 to 255 + + basePT100.setSampleDeviation( -2.0 ); + upPT100.setSampleDeviation( -2.0 ); + downPT100.setSampleDeviation( -2.0 ); } } @@ -1125,7 +1271,16 @@ void xWarnClockEnded() { sing(MELODY_SUPER_MARIO_START, PIEZO_PIN); } +void xWarnCookEnded() { + sing(MELODY_UNDERWORLD_SHORT, PIEZO_PIN); +} + void xStageFirstRun( int stageTime, int stageTemperature, int stagePumpSpeed, eCookingStages stage ) { + #ifdef DEBUG_OFF + debugPrintFunction("xStageFirstRun"); + debugPrintVar("cookingStage", stage); + #endif + // Set Stage bStageFirstRun = true; cookingStage = stage; @@ -1138,85 +1293,188 @@ void xStageFirstRun( int stageTime, int stageTemperature, int stagePumpSpeed, eC // Reset the clock clockStartTime = millis(); + clockLastUpdate = clockStartTime; clockIgnore = 0; // Set the pump speed iPumpSpeed = stagePumpSpeed; } -void xTransitionIntoStage_GlobalVariables(eCookingStages nextStage) { +void xSetupGlobalVariablesForStage(eCookingStages nextStage) { + #ifdef DEBUG_OFF + debugPrintFunction("xSetupGlobalVariablesForStage"); + debugPrintVar("cookingStage", nextStage); + #endif // 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."); + + 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; + // A basic operation for a basic stage - xStageFirstRun( startpointTime, startpointTemperature, 255, eCookingStage_Startpoint ); + xStageFirstRun( startpointTime, startpointTemperature, PUMP_SPEED_MAX, eCookingStage_Startpoint ); break; } 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."); + + xWaitForAction("Malt", say); + + repaint = true; + + break; + } + default: { + + } + } + // A basic operation for a basic stage - xStageFirstRun( betaGlucanaseTime, betaGlucanaseTemperature, 255, eCookingStage_BetaGlucanase ); + xStageFirstRun( betaGlucanaseTime, betaGlucanaseTemperature, PUMP_SPEED_MAX, eCookingStage_BetaGlucanase ); break; } case eCookingStage_Debranching: { // A basic operation for a basic stage - xStageFirstRun( debranchingTime, debranchingTemperature, 255, eCookingStage_Debranching ); + xStageFirstRun( debranchingTime, debranchingTemperature, PUMP_SPEED_MAX, eCookingStage_Debranching ); break; } case eCookingStage_Proteolytic: { // A basic operation for a basic stage - xStageFirstRun( proteolyticTime, proteolyticTemperature, 255, eCookingStage_Proteolytic ); + xStageFirstRun( proteolyticTime, proteolyticTemperature, PUMP_SPEED_MAX, eCookingStage_Proteolytic ); break; } case eCookingStage_BetaAmylase: { // A basic operation for a basic stage - xStageFirstRun( betaAmylaseTime, betaAmylaseTemperature, 255, eCookingStage_BetaAmylase ); + xStageFirstRun( betaAmylaseTime, betaAmylaseTemperature, PUMP_SPEED_MAX, eCookingStage_BetaAmylase ); break; } case eCookingStage_AlphaAmylase: { // A basic operation for a basic stage - xStageFirstRun( alphaAmylaseTime, alphaAmylaseTemperature, 255, eCookingStage_AlphaAmylase ); + xStageFirstRun( alphaAmylaseTime, alphaAmylaseTemperature, PUMP_SPEED_MAX, eCookingStage_AlphaAmylase ); break; } case eCookingStage_Mashout: { // A basic operation for a basic stage - xStageFirstRun( mashoutTime, mashoutTemperature, 255, eCookingStage_Mashout ); + xStageFirstRun( mashoutTime, mashoutTemperature, PUMP_SPEED_MAX, eCookingStage_Mashout ); break; } - case eCookingStage_Recirculation: { + case eCookingStage_Recirculation: {// Make sure there is water + xWaitForAction("Sparge Water", "Start heating your sparge water."); + + repaint = true; + // A basic operation for a basic stage - xStageFirstRun( recirculationTime, recirculationTemperature, 255, eCookingStage_Recirculation ); + xStageFirstRun( recirculationTime, recirculationTemperature, PUMP_SPEED_MAX, eCookingStage_Recirculation ); break; } case eCookingStage_Sparge: { + // Make sure there is water + xWaitForAction("Sparge Water", "Start pouring the sparge water."); + + repaint = true; + // A basic operation for a basic stage - xStageFirstRun( spargeTime, spargeTemperature, 255, eCookingStage_Sparge ); + xStageFirstRun( spargeTime, spargeTemperature, PUMP_SPEED_MAX, eCookingStage_Sparge ); break; } case eCookingStage_Boil: { + 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; + // A basic operation for a basic stage - xStageFirstRun( boilTime, boilTemperature, 255, eCookingStage_Boil ); + xStageFirstRun( boilTime, boilTemperature, PUMP_SPEED_MAX, eCookingStage_Boil ); break; } case eCookingStage_Cooling: { + // 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; + // A basic operation for a basic stage - xStageFirstRun( coolingTime, coolingTemperature, 255, eCookingStage_Cooling ); + xStageFirstRun( cleaningTime, cleaningTemperature, PUMP_SPEED_MAX, eCookingStage_Clean ); break; } case eCookingStage_Purge: { // A basic operation for a basic stage - xStageFirstRun( 0, 0, 255, eCookingStage_Purge ); + xStageFirstRun( 0, 0, PUMP_SPEED_MAX, eCookingStage_Purge ); xRegulatePumpSpeed(); @@ -1224,19 +1482,14 @@ void xTransitionIntoStage_GlobalVariables(eCookingStages nextStage) { } case eCookingStage_Done: { // A basic operation for a basic stage - xStageFirstRun( 0, 0, 0, eCookingStage_Done ); + xStageFirstRun( 0, 0, PUMP_SPEED_STOP, eCookingStage_Done ); break; } - default : { - // A basic operation for a basic stage - xStageFirstRun( 0, 0, 0, eCookingStage_Done ); - } } } void xTransitionIntoStage(eCookingStages nextStage) { - // Turn off all hardware that can damage itself if the machine is not cooking xSafeHardwarePowerOff(); @@ -1244,10 +1497,10 @@ void xTransitionIntoStage(eCookingStages nextStage) { xWarnClockEnded(); // Reset global stage variables - xTransitionIntoStage_GlobalVariables( nextStage ); + xSetupGlobalVariablesForStage( nextStage ); } -void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemperatureRange, eCookingStages nextStage ) { +void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemperatureRange, eCookingStages nextStage, boolean bAverageUpDown ) { if(bStageFirstRun) { // Don't run this again bStageFirstRun = false; @@ -1257,7 +1510,7 @@ void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemp // When the stage should be skipped if( iStageTime == 0) { // Continue to the next stage - //xTransitionIntoStage_GlobalVariables( nextStage ); + //xSetupGlobalVariablesForStage( nextStage ); // There is nothing to do, in this stage return; @@ -1268,16 +1521,24 @@ void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemp } } else { // Account for time spent at the target temperature | Input 1: range in ºC within which the target temperature is considered to be reached - xCountTheTime( iStageTemperatureRange ); + #ifdef DEBUG_OFF + xCountTheTime( iStageTemperatureRange, false ); + #else + xCountTheTime( iStageTemperatureRange, bAverageUpDown ); + #endif if( isTimeLeft() ) { // Do temperature control - xRegulateTemperature(); + xRegulateTemperature( bAverageUpDown ); // Do flow control xRegulatePumpSpeed(); } else { + #ifdef DEBUG_OFF + debugPrintFunction("xBasicStageOperation"); + debugPrintVar("clockCounter", clockCounter); + #endif // Continue to the next stage xTransitionIntoStage( nextStage ); @@ -1289,20 +1550,33 @@ void xBasicStageOperation( int iStageTime, int iStageTemperature, int iStageTemp return; } -void xWarnCookEnded() { - sing(MELODY_UNDERWORLD, PIEZO_PIN); -} +void xManageMachineSystems() { -void operateMachine() { + #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 // Measure temperature, for effect - //analogWrite(6, 0); - //delay(1); - basePT100.measure(); - upPT100.measure(); - downPT100.measure(); - Serial.println(" "); - //analogWrite(6, iPumpSpeed); + basePT100.measure1(false, false); + upPT100.measure1(false, false); + downPT100.measure1(true, false); // If cooking is done, return (this is a nice place to double check safety and ensure the cooking parts aren't on. if(!cooking) { @@ -1315,67 +1589,73 @@ void operateMachine() { switch(cookingStage) { case eCookingStage_Startpoint: { // A basic operation for a basic stage - xBasicStageOperation( startpointTime, startpointTemperature, 1, eCookingStage_BetaGlucanase ); + xBasicStageOperation( startpointTime, startpointTemperature, 1, eCookingStage_BetaGlucanase, false); break; } case eCookingStage_BetaGlucanase: { // A basic operation for a basic stage - xBasicStageOperation( betaGlucanaseTime, betaGlucanaseTemperature, 1, eCookingStage_Debranching ); + xBasicStageOperation( betaGlucanaseTime, betaGlucanaseTemperature, 0, eCookingStage_Debranching, true ); break; } case eCookingStage_Debranching: { // A basic operation for a basic stage - xBasicStageOperation( debranchingTime, debranchingTemperature, 1, eCookingStage_Proteolytic ); + xBasicStageOperation( debranchingTime, debranchingTemperature, 0, eCookingStage_Proteolytic, true ); break; } case eCookingStage_Proteolytic: { // A basic operation for a basic stage - xBasicStageOperation( proteolyticTime, proteolyticTemperature, 1, eCookingStage_BetaAmylase ); + xBasicStageOperation( proteolyticTime, proteolyticTemperature, 0, eCookingStage_BetaAmylase, true ); break; } case eCookingStage_BetaAmylase: { // A basic operation for a basic stage - xBasicStageOperation( betaAmylaseTime, betaAmylaseTemperature, 1, eCookingStage_AlphaAmylase ); + xBasicStageOperation( betaAmylaseTime, betaAmylaseTemperature, 0, eCookingStage_AlphaAmylase, true ); break; } case eCookingStage_AlphaAmylase: { // A basic operation for a basic stage - xBasicStageOperation( alphaAmylaseTime, alphaAmylaseTemperature, 1, eCookingStage_Mashout ); + xBasicStageOperation( alphaAmylaseTime, alphaAmylaseTemperature, 0, eCookingStage_Mashout, true ); break; } case eCookingStage_Mashout: { // A basic operation for a basic stage - xBasicStageOperation( mashoutTime, mashoutTemperature, 1, eCookingStage_Recirculation ); + xBasicStageOperation( mashoutTime, mashoutTemperature, 0, eCookingStage_Recirculation, true ); break; } case eCookingStage_Recirculation: { // A basic operation for a basic stage - xBasicStageOperation( recirculationTime, recirculationTemperature, 1, eCookingStage_Sparge ); + xBasicStageOperation( recirculationTime, recirculationTemperature, 0, eCookingStage_Sparge, true ); break; } case eCookingStage_Sparge: { // A basic operation for a basic stage - xBasicStageOperation( spargeTime, spargeTemperature, 1, eCookingStage_Boil ); + xBasicStageOperation( spargeTime, spargeTemperature, 1, eCookingStage_Boil, false ); break; } case eCookingStage_Boil: { // A basic operation for a basic stage - xBasicStageOperation( boilTime, boilTemperature, 1, eCookingStage_Cooling ); + xBasicStageOperation( boilTime, boilTemperature, 2, eCookingStage_Cooling, false ); break; } case eCookingStage_Cooling: { // A basic operation for a basic stage - xBasicStageOperation( coolingTime, coolingTemperature, 1, eCookingStage_Done ); + 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 ); break; } @@ -1388,12 +1668,17 @@ void operateMachine() { break; } - default : { - // Update state - cooking = false; + case eCookingStage_Done: { + // Update cooking state + stopBrewing(); + + // Ask for screen refresh + repaint = true; // Warn the user that the cooking is done xWarnCookEnded(); + + break; } } } @@ -1445,7 +1730,7 @@ int getTimer(int init) { break; } else { // Don't forget to keep an eye on the cooking - operateMachine(); + xManageMachineSystems(); } // display current timer @@ -1500,7 +1785,7 @@ int getTemperature(int init) { break; } else { // Don't forget to keep an eye on the cooking - operateMachine(); + xManageMachineSystems(); } // display current timer @@ -1555,7 +1840,7 @@ int xSetGenericValue(int init, int min, int max, char *valueName, char *unit) { break; } else { // Don't forget to keep an eye on the cooking - operateMachine(); + xManageMachineSystems(); } // Check if there was an update by the rotary encoder @@ -1584,40 +1869,43 @@ int xSetGenericValue(int init, int min, int max, char *valueName, char *unit) { // ###################### Set Variables ################################################## -void lcdPrint(String title, String message) { - int messageLength = message.length(); - - lcd.clear(); - - // print title - lcd.home(); - lcd.print(title); - - // print message - if(messageLength <= LCD_HORIZONTAL_RESOLUTION) { - lcd.setCursor(0,LCD_VERTICAL_RESOLUTION-1); - lcd.print(message); - delay(1000); - } - // print scrolling message - else { - String output_message = " "; - output_message += message; - messageLength = output_message.length(); - - // Adjust the message size for proper printing - if ( messageLength & 1 == 1 ) { - output_message+=" "; - messageLength+=2; - } - - // print scrolling message - for (int cursor = 0; cursor < messageLength - LCD_HORIZONTAL_RESOLUTION; cursor+=2) { - lcd.setCursor(0,LCD_VERTICAL_RESOLUTION-1); - lcd.print(output_message.substring(cursor, cursor+16)); - delay(500); +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; + } } } } +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 + } + return ret; +} + +void cleanSerialMonitor() { + for (int i = 0; i < 40; i++) { + Serial.println(); + } +} \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000000000000000000000000000000000000..96e758c9b5819e71f68f5d38d5f7be18bb525586 --- /dev/null +++ b/config.h @@ -0,0 +1,100 @@ +/* + debug.h - Debug functions. + Created by João Lino, September 25, 2015. + Released into the public domain. +*/ + +#ifndef CONFIG_h +#define CONFIG_h + +// ######################### SETTINGS ######################### +#define SETTING_CLEANING_TIME 1200 //1200 +#define SETTING_CLEANING_TEMPERATURE 45 //45 + +// ++++++++++++++++++++++++ Heating Element Relay ++++++++++++++++++++++++ +#define HEATING_ELEMENT_DEFAULT_WINDOW_SIZE 1000 +#define HEATING_ELEMENT_OUTPUT_PIN 24 +#define HEATING_ELEMENT_MAX_HEAT_PWM_INTEGER 5 +#define HEATING_ELEMENT_MAX_HEAT_PWM_FLOAT 5.0 +#define HEATING_ELEMENT_MAX_WATTAGE 3000.0 // Minimum = 2000.0 +#define HEATING_ELEMENT_AC_FREQUENCY_HZ 50.0 + +// ++++++++++++++++++++++++ Temperature ++++++++++++++++++++++++ +#define PT100_BASE_INPUT_PIN A4 +#define PT100_BASE_OUTPUT_PIN 32 +#define PT100_BASE_INPUT_R_PIN A7 +#define PT100_BASE_OUTPUT_R_PIN 7 +#define PT100_BASE_TIME_BETWEEN_READINGS 1 +#define PT100_UP_INPUT_PIN A5 +#define PT100_UP_OUTPUT_PIN 30 +#define PT100_UP_INPUT_R_PIN A8 +#define PT100_UP_OUTPUT_R_PIN 9 +#define PT100_UP_TIME_BETWEEN_READINGS 1 +#define PT100_DOWN_INPUT_PIN A6 +#define PT100_DOWN_OUTPUT_PIN 31 +#define PT100_DOWN_INPUT_R_PIN A9 +#define PT100_DOWN_OUTPUT_R_PIN 8 +#define PT100_DOWN_TIME_BETWEEN_READINGS 1 + +#define PT100_BASE_DEFAULT_ADC_VMAX 1.1 +#define PT100_BASE_DEFAULT_VS 5.0 +#define PT100_BASE_DEFAULT_R1_RESISTENCE 605.2 +#define PT100_BASE_DEFAULT_R2_RESISTENCE 605.2 +#define PT100_UP_DEFAULT_ADC_VMAX 1.1 +#define PT100_UP_DEFAULT_VS 5.0 +#define PT100_UP_DEFAULT_R1_RESISTENCE 630.0 //620.0 +#define PT100_UP_DEFAULT_R2_RESISTENCE 610.0 +#define PT100_DOWN_DEFAULT_ADC_VMAX 1.1 +#define PT100_DOWN_DEFAULT_VS 5.0 +#define PT100_DOWN_DEFAULT_R1_RESISTENCE 616.0 +#define PT100_DOWN_DEFAULT_R2_RESISTENCE 611.0 + +// ++++++++++++++++++++++++ Mixer ++++++++++++++++++++++++ +//#define MIXER_PIN 12 +//#define MIXER_MAX_POSITION 255 + +// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++ +#define PIEZO_PIN 25 + +// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++ +#define PUMP_PIN 6 +#define PUMP_TEMPERATURE_MAX_OPERATION 90 +#define PUMP_SPEED_STOP 0 +#define PUMP_SPEED_SLOW 64 +#define PUMP_SPEED_AVERAGE 128 +#define PUMP_SPEED_FAST 192 +#define PUMP_SPEED_MAX 255 + +// ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++ +#define ROTARY_ENCODER_INTERRUPT_NUMBER 1 // On Mega2560 boards, interrupt 1 is on pin 3 +#define ROTARY_ENCODER_CLK_PIN 3 // Used for generating interrupts using CLK signal +#define ROTARY_ENCODER_DT_PIN 22 // Used for reading DT signal +#define ROTARY_ENCODER_SW_PIN 23 // Used for the push button switch +#define ROTARY_ENCODER_DEBOUNCE_TIME 50 //20 // Number of miliseconds to ignore new signals a signal is received + +// ++++++++++++++++++++++++ State Machine ++++++++++++++++++++++++ +#define SETTING_WELCOME_TIMEOUT 100 +#define SETTING_MAX_INACTIVITY_TIME 3000 +#define MENU_SIZE_MAIN_MENU 12 +#define MENU_SIZE_PROFILES_MENU 9 +#define MENU_SIZE_STAGE_MENU 13 +#define MENU_SIZE_MALT_MENU 4 +#define MENU_SIZE_SETTINGS_MENU 5 +#define SETTING_SERIAL_MONITOR_BAUD_RATE 9600 +#define SETTING_SERIAL_MONITOR_WELCOME_MESSAGE "Let's start Brewing!" + +// ######################### LIBRARIES ######################### +// ++++++++++++++++++++++++ LiquidCrystal_I2C ++++++++++++++++++++++++ +#define LCD_I2C_ADDR 0x27 // <<----- Add your address here. Find it from I2C Scanner +#define LCD_HORIZONTAL_RESOLUTION 16 +#define LCD_VERTICAL_RESOLUTION 2 +#define LCD_BACKLIGHT_PIN 3 +#define LCD_EN_PIN 2 +#define LCD_RW_PIN 1 +#define LCD_RS_PIN 0 +#define LCD_D4_PIN 4 +#define LCD_D5_PIN 5 +#define LCD_D6_PIN 6 +#define LCD_D7_PIN 7 + +#endif \ No newline at end of file diff --git a/debug.h b/debug.h new file mode 100644 index 0000000000000000000000000000000000000000..30dd30db212d59f9591c57d7d7ec05d83922e896 --- /dev/null +++ b/debug.h @@ -0,0 +1,25 @@ +/* + debug.h - Debug functions. + Created by João Lino, September 25, 2015. + Released into the public domain. +*/ + +#ifndef DEBUG_h +#define DEBUB_h + +// ######################### TEMPLATES ######################### +template void debugPrintVar( char *name, const T& value ); +template void debugPrintVar( char *name, const T& value ) { + Serial.print("["); + Serial.print(name); + Serial.print(":"); + Serial.print(value); + Serial.println("]"); +} +void debugPrintFunction( char *name ) { + Serial.print("++++++++++++++++++++++++ "); + Serial.print(name); + Serial.println("++++++++++++++++++++++++"); +} + +#endif \ No newline at end of file