From 7081391f63b4b0b81574f76f17a0dfef01eb2d6b Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Fri, 1 Nov 2019 09:24:58 +1100 Subject: [PATCH] Added core frost functionality --- src/Afterburner.cpp | 33 +++++++++++++++++++++++++++++++-- src/OLED/BasicScreen.cpp | 3 ++- src/OLED/ClockScreen.cpp | 3 ++- src/OLED/DetailedScreen.cpp | 3 ++- src/OLED/ScreenHeader.cpp | 19 +++++++++++++++++-- src/OLED/ScreenHeader.h | 1 + src/OLED/fonts/Icons.cpp | 23 +++++++++++++++++++++++ src/OLED/fonts/Icons.h | 2 ++ src/RTC/RTCStore.cpp | 17 +++++++++++++++++ src/RTC/RTCStore.h | 3 +++ src/RTC/TimerManager.cpp | 5 ++++- src/Utility/BTC_GPIO.cpp | 3 ++- src/Utility/BTC_JSON.cpp | 4 ++++ src/Utility/NVStorage.cpp | 4 ++++ src/Utility/NVStorage.h | 6 ++++++ src/Utility/UtilClasses.cpp | 32 ++++++++++++++++++++++++++++++++ 16 files changed, 152 insertions(+), 9 deletions(-) diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index de44b5b..93316e0 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -124,8 +124,8 @@ #define RX_DATA_TIMOUT 50 const int FirmwareRevision = 31; -const int FirmwareSubRevision = 6; -const char* FirmwareDate = "27 Oct 2019"; +const int FirmwareSubRevision = 7; +const char* FirmwareDate = "30 Oct 2019"; #ifdef ESP32 @@ -149,6 +149,7 @@ bool validateFrame(const CProtocol& frame, const char* name); void checkDisplayUpdate(); void checkDebugCommands(); void manageCyclicMode(); +void manageFrostMode(); bool preemptCyclicMode(); void doStreaming(); void heaterOn(); @@ -851,6 +852,7 @@ void loop() FilteredSamples.AmbientTemp.update(fTemperature); manageCyclicMode(); + manageFrostMode(); } } else { @@ -941,6 +943,31 @@ void manageCyclicMode() } } + +void manageFrostMode() +{ + uint8_t engage = NVstore.getUserSettings().FrostOn; + if(engage) { + float deltaT = getTemperatureSensor() - engage; + int heaterState = getHeaterInfo().getRunState(); + if(deltaT < 0) { + if(heaterState == 0) { + RTC_Store.setFrostOn(true); + DebugPort.printf("FROST MODE: Starting heater, < %d`C\r\n", engage); + heaterOn(); + } + } + uint8_t rise = NVstore.getUserSettings().FrostRise; + if(deltaT > rise) { + if(RTC_Store.getFrostOn()) { + DebugPort.printf("FROST MODE: Stopping heater, > %d`C\r\n", engage+rise); + heaterOff(); + RTC_Store.setFrostOn(false); // cancel active frost mode + } + } + } +} + bool preemptCyclicMode() { const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic; @@ -993,6 +1020,7 @@ void requestOn() { if(bHasHtrData && (0 == SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()))) { RTC_Store.setCyclicEngaged(true); // for cyclic mode + RTC_Store.setFrostOn(false); // cancel frost mode if(!preemptCyclicMode()) { // only start if below cyclic threshold when enabled heaterOn(); } @@ -1003,6 +1031,7 @@ void requestOff() { heaterOff(); RTC_Store.setCyclicEngaged(false); // for cyclic mode + RTC_Store.setFrostOn(false); // cancel active frost mode } void heaterOn() diff --git a/src/OLED/BasicScreen.cpp b/src/OLED/BasicScreen.cpp index ded7c37..9a950f4 100644 --- a/src/OLED/BasicScreen.cpp +++ b/src/OLED/BasicScreen.cpp @@ -29,6 +29,7 @@ #include "../Utility/NVStorage.h" #include "../Protocol/Protocol.h" #include "../Utility/TempSense.h" +#include "../RTC/RTCStore.h" #define MAXIFONT tahoma_24ptFontInfo @@ -270,7 +271,7 @@ CBasicScreen::keyHandler(uint8_t event) if(event & key_Centre) { if(NVstore.getUserSettings().menuMode < 2) { int runstate = getHeaterInfo().getRunStateEx(); - if(runstate) { // running, including cyclic mode idle + if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle if(repeatCount > 5) { repeatCount = -1; requestOff(); diff --git a/src/OLED/ClockScreen.cpp b/src/OLED/ClockScreen.cpp index 3808955..801d0dd 100644 --- a/src/OLED/ClockScreen.cpp +++ b/src/OLED/ClockScreen.cpp @@ -28,6 +28,7 @@ #include "../RTC/Clock.h" #include "../Protocol/Protocol.h" #include "../Utility/NVStorage.h" +#include "../RTC/RTCStore.h" /////////////////////////////////////////////////////////////////////////// // @@ -135,7 +136,7 @@ CClockScreen::keyHandler(uint8_t event) if(event & key_Centre) { if(NVstore.getUserSettings().menuMode < 2) { int runstate = getHeaterInfo().getRunStateEx(); - if(runstate) { // running, including cyclic mode idle + if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle if(_keyRepeatCount > 5) { _keyRepeatCount = -1; requestOff(); diff --git a/src/OLED/DetailedScreen.cpp b/src/OLED/DetailedScreen.cpp index 77e7f1e..5818ed4 100644 --- a/src/OLED/DetailedScreen.cpp +++ b/src/OLED/DetailedScreen.cpp @@ -28,6 +28,7 @@ #include "../Protocol/Protocol.h" #include "../Utility/NVStorage.h" #include "../Utility/FuelGauge.h" +#include "../RTC/RTCStore.h" #define MINIFONT miniFontInfo @@ -217,7 +218,7 @@ CDetailedScreen::keyHandler(uint8_t event) if(event & key_Centre) { int runstate = getHeaterInfo().getRunStateEx(); - if(runstate) { // running, including cyclic mode idle + if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle if(_keyRepeatCount > 5) { _keyRepeatCount = -1; // prevent double handling requestOff(); diff --git a/src/OLED/ScreenHeader.cpp b/src/OLED/ScreenHeader.cpp index 5997e8c..5e435b6 100644 --- a/src/OLED/ScreenHeader.cpp +++ b/src/OLED/ScreenHeader.cpp @@ -33,6 +33,7 @@ #include "../RTC/TimerManager.h" #include "../Protocol/SmartError.h" #include "../Utility/DataFilter.h" +#include "../RTC/RTCStore.h" #define MINIFONT miniFontInfo @@ -329,6 +330,11 @@ CScreenHeader::showBatteryIcon(float voltage) int CScreenHeader::showTimers() { + if(RTC_Store.getFrostOn()) { + int xPos = X_TIMER_ICON; + _drawBitmap(xPos, Y_TIMER_ICON, frostIconInfo); + return 0; + } int nextTimer = CTimerManager::getNextTimer(); // timer ID and repeat flag info of next scheduled timer if(nextTimer) { int xPos = X_TIMER_ICON; @@ -358,12 +364,21 @@ CScreenHeader::showTimers() } return 1; } - _display.fillRect(X_TIMER_ICON-3, Y_TIMER_ICON, TimerIconInfo.width+3, TimerIconInfo.height, BLACK); // erase icon + _display.fillRect(X_TIMER_ICON-3, Y_TIMER_ICON, TimerIconInfo.width+3, 13, BLACK); // erase icon _display.fillRect(X_TIMER_ICON-5, Y_TIMER_ICON+12, 21, 5, BLACK); // erase annotation return 0; } - +bool +CScreenHeader::showFrost() +{ + if(RTC_Store.getFrostOn()) { + int xPos = X_TIMER_ICON; + _drawBitmap(xPos, Y_TIMER_ICON, frostIconInfo); + return true; + } + return false; +} void CScreenHeader::showTime() diff --git a/src/OLED/ScreenHeader.h b/src/OLED/ScreenHeader.h index 989ffd9..230ed4f 100644 --- a/src/OLED/ScreenHeader.h +++ b/src/OLED/ScreenHeader.h @@ -54,6 +54,7 @@ protected: void showBatteryIcon(float voltage); int showTimers(); virtual void showTime(); // x location depends upon how many timers are active + bool showFrost(); void showHeaderDetail(bool state) { _hdrDetail = state; }; public: CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr); diff --git a/src/OLED/fonts/Icons.cpp b/src/OLED/fonts/Icons.cpp index b56774e..12191fe 100644 --- a/src/OLED/fonts/Icons.cpp +++ b/src/OLED/fonts/Icons.cpp @@ -1392,3 +1392,26 @@ const uint8_t PROGMEM threshIcon[] = const BITMAP_INFO threshIconInfo(9, 9, threshIcon); +// +// Image data for frost +// + +const uint8_t PROGMEM frostIcon[] = +{ + 0x15, 0x00, // # # # + 0x0A, 0x00, // # # + 0xA4, 0xA0, // # # # # # + 0x44, 0x40, // # # # + 0xA4, 0xA0, // # # # # # + 0x15, 0x00, // # # # + 0x0E, 0x00, // ### + 0x15, 0x00, // # # # + 0xA4, 0xA0, // # # # # # + 0x44, 0x40, // # # # + 0xA4, 0xA0, // # # # # # + 0x0A, 0x00, // # # + 0x15, 0x00, // # # # +}; + +const BITMAP_INFO frostIconInfo(11, 13, frostIcon); + diff --git a/src/OLED/fonts/Icons.h b/src/OLED/fonts/Icons.h index c55e569..2426396 100644 --- a/src/OLED/fonts/Icons.h +++ b/src/OLED/fonts/Icons.h @@ -159,3 +159,5 @@ extern const BITMAP_INFO algIconInfo; extern const BITMAP_INFO passwordIconInfo; extern const BITMAP_INFO threshIconInfo; +extern const BITMAP_INFO frostIconInfo; + diff --git a/src/RTC/RTCStore.cpp b/src/RTC/RTCStore.cpp index 6e04015..09e43d7 100644 --- a/src/RTC/RTCStore.cpp +++ b/src/RTC/RTCStore.cpp @@ -154,6 +154,20 @@ CRTC_Store::getDesiredPump() return _demandPump; } +void +CRTC_Store::setFrostOn(bool state) +{ + _frostOn = state; + _PackAndSaveByte5(); +} + +bool +CRTC_Store::getFrostOn() +{ + _ReadAndUnpackByte5(); + return _frostOn; +} + void CRTC_Store::resetRunTime() { @@ -230,6 +244,7 @@ CRTC_Store::_ReadAndUnpackByte5() uint8_t NVval = 0; Clock.readData((uint8_t*)&NVval, 1, 5); _demandPump = NVval & 0x3f; + _frostOn = (NVval & 0x40) != 0; _accessed[2] = true; DebugPort.printf("RTC_Store - read byte5: pump=%d\r\n", _demandPump); } @@ -239,6 +254,8 @@ void CRTC_Store::_PackAndSaveByte5() { uint8_t NVval = (_demandPump & 0x3f); + NVval |= _frostOn ? 0x40 : 0; + Clock.saveData((uint8_t*)&NVval, 1, 5); } diff --git a/src/RTC/RTCStore.h b/src/RTC/RTCStore.h index 6532cc9..7ec0ba8 100644 --- a/src/RTC/RTCStore.h +++ b/src/RTC/RTCStore.h @@ -32,6 +32,7 @@ class CRTC_Store { bool _BootInit; // Byte4[6] bool _CyclicEngaged; // Byte4[7] uint8_t _demandPump; // Byte5[0..5] + bool _frostOn; // Byte5[6] uint8_t _RunTime; // Byte6[0..4] uint8_t _GlowTime; // Byte6[5..7] void _ReadAndUnpackByte4(); @@ -61,6 +62,8 @@ public: int getGlowTime(); int getMaxGlowTime() const { return 8; }; int getMaxRunTime() const { return 32; }; + void setFrostOn(bool state); + bool getFrostOn(); }; extern CRTC_Store RTC_Store; diff --git a/src/RTC/TimerManager.cpp b/src/RTC/TimerManager.cpp index 1eef1c0..c7c82f6 100644 --- a/src/RTC/TimerManager.cpp +++ b/src/RTC/TimerManager.cpp @@ -33,6 +33,7 @@ #include "Clock.h" #include "../Utility/NVStorage.h" #include "../Utility/helpers.h" +#include "../RTC/RTCStore.h" // main array to hold information of which timer is active at any particular minute of the week // LSBs are used for the timerID + 1 @@ -271,7 +272,9 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow) } else { DebugPort.println("End of timer interval, stopping heater"); - requestOff(); +// if(!RTC_Store.getFrostOn() && !RTC_Store.getCyclicEngaged()) + if(!RTC_Store.getFrostOn()) + requestOff(); retval = 2; } _activeTimer = newID; diff --git a/src/Utility/BTC_GPIO.cpp b/src/Utility/BTC_GPIO.cpp index 9046586..c7b91a6 100644 --- a/src/Utility/BTC_GPIO.cpp +++ b/src/Utility/BTC_GPIO.cpp @@ -25,6 +25,7 @@ #include "DebugPort.h" #include "../Protocol/Protocol.h" #include "../Utility/NVStorage.h" +#include "../RTC/RTCStore.h" const int BREATHINTERVAL = 45; const int FADEAMOUNT = 3; @@ -122,7 +123,7 @@ void CGPIOin1::_doStartStop(bool active) { if(active) { - if(getHeaterInfo().getRunStateEx()) + if(getHeaterInfo().getRunStateEx() && !RTC_Store.getFrostOn()) requestOff(); else requestOn(); diff --git a/src/Utility/BTC_JSON.cpp b/src/Utility/BTC_JSON.cpp index 69d3fb7..da29284 100644 --- a/src/Utility/BTC_JSON.cpp +++ b/src/Utility/BTC_JSON.cpp @@ -216,6 +216,8 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len) bSend |= moderator.addJson("CyclicTemp", getDemandDegC(), root); // actual pivot point for cyclic mode bSend |= moderator.addJson("CyclicOff", stop, root); // threshold of over temp for cyclic mode bSend |= moderator.addJson("CyclicOn", NVstore.getUserSettings().cyclic.Start, root); // threshold of under temp for cyclic mode + bSend |= moderator.addJson("FrostOn", NVstore.getUserSettings().FrostOn, root); // temp drops below this, auto start - 0 = disable + bSend |= moderator.addJson("FrostRise", NVstore.getUserSettings().FrostRise, root); // temp rise in frost mode till auto off bSend |= moderator.addJson("PumpCount", RTC_Store.getFuelGauge(), root); // running count of pump strokes bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // mL/stroke bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low voltage cutout @@ -283,6 +285,8 @@ bool makeJSONStringGPIO(CModerator& moderator, char* opStr, int len) bSend |= moderator.addJson("GPmodeIn2", GPIOin2Names[info.in2Mode], root); bSend |= moderator.addJson("GPmodeOut1", GPIOout1Names[info.out1Mode], root); bSend |= moderator.addJson("GPmodeOut2", GPIOout2Names[info.out2Mode], root); + bSend |= moderator.addJson("GPOutThr1", NVstore.getUserSettings().GPIO.thresh[0], root); + bSend |= moderator.addJson("GPOutThr2", NVstore.getUserSettings().GPIO.thresh[1], root); bSend |= moderator.addJson("GPmodeAnlg", GPIOalgNames[info.algMode], root); bSend |= moderator.addJson("ExtThermoTmout", (uint32_t)NVstore.getUserSettings().ExtThermoTimeout, root); const char* stop = getExternalThermostatHoldTime(); diff --git a/src/Utility/NVStorage.cpp b/src/Utility/NVStorage.cpp index 44ac6c9..3051b41 100644 --- a/src/Utility/NVStorage.cpp +++ b/src/Utility/NVStorage.cpp @@ -379,6 +379,8 @@ sUserSettings::load() } validatedLoad("thermoWindow", ThermostatWindow, 1.0f, 0.2f, 10.f); DebugPort.printf("2) Window = %f\r\n", ThermostatWindow); + validatedLoad("frostOn", FrostOn, 0, u8inBounds, 0, 10); + validatedLoad("frostRise", FrostRise, 5, u8inBounds, 0, 20); validatedLoad("enableWifi", enableWifi, 1, u8inBounds, 0, 1); validatedLoad("enableOTA", enableOTA, 0, u8inBounds, 0, 1); validatedLoad("cyclicStop", cyclic.Stop, 0, s8inBounds, 0, 10); @@ -444,6 +446,8 @@ sUserSettings::save() preferences.putUChar("degF", degF); preferences.putUChar("thermoMethod", ThermostatMethod); preferences.putFloat("thermoWindow", ThermostatWindow); + preferences.putUChar("frostOn", FrostOn); + preferences.putUChar("frostRise", FrostRise); preferences.putUChar("enableWifi", enableWifi); preferences.putUChar("enableOTA", enableOTA); preferences.putChar("cyclicStop", cyclic.Stop); diff --git a/src/Utility/NVStorage.h b/src/Utility/NVStorage.h index 5b6a37d..c7ef53e 100644 --- a/src/Utility/NVStorage.h +++ b/src/Utility/NVStorage.h @@ -284,6 +284,8 @@ struct sUserSettings : public CESP32_NVStorage { uint8_t degF; uint8_t ThermostatMethod; // 0: standard heater, 1: Narrow Hysterisis, 2:Managed Hz mode float ThermostatWindow; + uint8_t FrostOn; + uint8_t FrostRise; uint8_t useThermostat; uint8_t enableWifi; uint8_t enableOTA; @@ -324,6 +326,8 @@ struct sUserSettings : public CESP32_NVStorage { degF = 0; ThermostatMethod = 0; ThermostatWindow = 1.0; + FrostOn = 0; + FrostRise = 5; useThermostat = 1; enableWifi = 1; enableOTA = 0; @@ -351,6 +355,8 @@ struct sUserSettings : public CESP32_NVStorage { degF = rhs.degF; ThermostatMethod = rhs.ThermostatMethod; ThermostatWindow = rhs.ThermostatWindow; + FrostOn = rhs.FrostOn; + FrostRise = rhs.FrostRise; useThermostat = rhs.useThermostat; enableWifi = rhs.enableWifi; enableOTA = rhs.enableOTA; diff --git a/src/Utility/UtilClasses.cpp b/src/Utility/UtilClasses.cpp index a1b0f46..e4bbe4f 100644 --- a/src/Utility/UtilClasses.cpp +++ b/src/Utility/UtilClasses.cpp @@ -309,6 +309,22 @@ void DecodeCmd(const char* cmd, String& payload) else if(strcmp("GPin2", cmd) == 0) { simulateGPIOin(payload.toInt() ? 0x02 : 0x00); // simulate key 2 press } + else if(strcmp("GPOutThr1", cmd) == 0) { + sUserSettings us = NVstore.getUserSettings(); + us.GPIO.thresh[0] = payload.toInt(); // 0 = disable; < 0, active if less than abs(val); > 0, active if over val + if(INBOUNDS(us.GPIO.thresh[0], -50, 50)) { + NVstore.setUserSettings(us); + NVstore.save(); + } + } + else if(strcmp("GPOutThr2", cmd) == 0) { + sUserSettings us = NVstore.getUserSettings(); + us.GPIO.thresh[1] = payload.toInt(); // 0 = disable; < 0, active if less than abs(val); > 0, active if over val + if(INBOUNDS(us.GPIO.thresh[1], -50, 50)) { + NVstore.setUserSettings(us); + NVstore.save(); + } + } else if(strcmp("JSONpack", cmd) == 0) { sUserSettings us = NVstore.getUserSettings(); uint8_t packed = payload.toInt() ? 0x00 : 0x01; @@ -395,5 +411,21 @@ void DecodeCmd(const char* cmd, String& payload) else if(strcmp("SysHourMeters", cmd) == 0) { pHourMeter->resetHard(); } + else if(strcmp("FrostOn", cmd) == 0) { + sUserSettings us = NVstore.getUserSettings(); + us.FrostOn = payload.toInt(); + if(INBOUNDS(us.FrostOn, 0, 30)) { + NVstore.setUserSettings(us); + NVstore.save(); + } + } + else if(strcmp("FrostRise", cmd) == 0) { + sUserSettings us = NVstore.getUserSettings(); + us.FrostRise = payload.toInt(); + if(INBOUNDS(us.FrostRise, 1, 30)) { + NVstore.setUserSettings(us); + NVstore.save(); + } + } }