#include #include #include "Boiler.h" #define HIBERNATE_DELAY 5000 #define TEMP_TRIGGER 10 #define LCD_CHAR_SENSOR 1 #define PIN_BTN_MODE 11 #define PIN_BTN_MINUS 10 #define PIN_BTN_PLUS 9 #define PIN_LCD_LED 8 #define PIN_LCD_RS 7 #define PIN_LCD_ENABLE 6 #define PIN_LCD_D0 5 #define PIN_LCD_D1 4 #define PIN_LCD_D2 3 #define PIN_LCD_D3 2 byte g_sensorChar[8] = { B00100, B01110, B01110, B01110, B01110, B11111, B11111, B01110, }; Button g_btnMode(PIN_BTN_MODE); Button g_btnMinus(PIN_BTN_MINUS); Button g_btnPlus(PIN_BTN_PLUS); Button* g_buttons[3] = {&g_btnMode, &g_btnMinus, &g_btnPlus}; #define BUTTONS_COUNT (sizeof(g_buttons) / sizeof(*g_buttons)) LiquidCrystal g_lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3); UiStateEnum g_modeSequence[3] = {WaterSetting, HeaterSetting, Lighting}; #define MODE_SEQUENCE_COUNT (sizeof(g_modeSequence) / sizeof(*g_modeSequence)) BoilerState g_boilerState = { .water = { .current = 44, .setting = 44, .isActive = false }, .heater = { .current = 44, .setting = 44, .isActive = false }, .uiState = { .state = Lighting, .lastOpMs = 0, .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1 } }; void printStateLine(char prefix, const BoilerLineState& boilerLineState, bool isModifying, bool isActive, LiquidCrystal lcd) { char curTmp[5], setTmp[5], tmp[17]; dtostrf(boilerLineState.current / 2.0f, 2, 1, curTmp); dtostrf(boilerLineState.setting / 2.0f, 2, 1, setTmp); int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ', isActive ? LCD_CHAR_SENSOR : ' '); for (; count < 17; ++count) { tmp[count] = ' '; } tmp[count] = 0; lcd.print(tmp); } void printState(const BoilerState& boilerState, LiquidCrystal lcd) { lcd.setCursor(0, 0); printStateLine('S', boilerState.water, boilerState.uiState.state == WaterSetting, boilerState.water.isActive, lcd); lcd.setCursor(0, 1); printStateLine('C', boilerState.heater, boilerState.uiState.state == HeaterSetting, boilerState.heater.isActive, lcd); } void setup() { for (unsigned i = 0; i < BUTTONS_COUNT; ++i) { g_buttons[i]->begin(); } pinMode(PIN_LCD_LED, OUTPUT); digitalWrite(PIN_LCD_LED, 1); g_lcd.begin(16, 2); g_lcd.createChar(LCD_CHAR_SENSOR, g_sensorChar); printState(g_boilerState, g_lcd); Serial.begin(9600); } void loop() { bool needLcdUpdate = false; unsigned long currentMs = millis(); for (unsigned i = 0; i < BUTTONS_COUNT; ++i) { g_buttons[i]->read(); if (g_buttons[i]->isPressed()) { g_boilerState.uiState.lastOpMs = currentMs; } } if (g_boilerState.uiState.state == Hibernate) { for (unsigned i = 0; i < BUTTONS_COUNT; ++i) { if (g_buttons[i]->wasReleased()) { g_boilerState.uiState.state = Lighting; digitalWrite(PIN_LCD_LED, 1); break; } } needLcdUpdate = true; } else { if (currentMs - g_boilerState.uiState.lastOpMs >= HIBERNATE_DELAY) { g_boilerState.uiState.state = Hibernate; digitalWrite(PIN_LCD_LED, 0); g_boilerState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1; needLcdUpdate = true; } else { if (g_btnMode.wasPressed()) { g_boilerState.uiState.modeSequenceIndex = (g_boilerState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT; g_boilerState.uiState.state = g_modeSequence[g_boilerState.uiState.modeSequenceIndex]; needLcdUpdate = true; } else { temp_t* setting = NULL; if (g_boilerState.uiState.state == WaterSetting) { setting = &g_boilerState.water.setting; } else if (g_boilerState.uiState.state == HeaterSetting) { setting = &g_boilerState.heater.setting; } if (setting) { if (g_btnMinus.wasReleased()) { --(*setting); } else if (g_btnPlus.wasReleased()) { ++(*setting); } needLcdUpdate = true; } } } } if (!g_boilerState.water.isActive && (g_boilerState.water.current <= g_boilerState.water.setting - TEMP_TRIGGER)) { g_boilerState.water.isActive = true; needLcdUpdate = true; } else if (g_boilerState.water.isActive && (g_boilerState.water.current >= g_boilerState.water.setting)) { g_boilerState.water.isActive = false; needLcdUpdate = true; } if (needLcdUpdate) { printState(g_boilerState, g_lcd); } }