Added core frost functionality

This commit is contained in:
Ray Jones 2019-11-01 09:24:58 +11:00
parent f2af9c3fda
commit 7081391f63
16 changed files with 152 additions and 9 deletions

View file

@ -124,8 +124,8 @@
#define RX_DATA_TIMOUT 50 #define RX_DATA_TIMOUT 50
const int FirmwareRevision = 31; const int FirmwareRevision = 31;
const int FirmwareSubRevision = 6; const int FirmwareSubRevision = 7;
const char* FirmwareDate = "27 Oct 2019"; const char* FirmwareDate = "30 Oct 2019";
#ifdef ESP32 #ifdef ESP32
@ -149,6 +149,7 @@ bool validateFrame(const CProtocol& frame, const char* name);
void checkDisplayUpdate(); void checkDisplayUpdate();
void checkDebugCommands(); void checkDebugCommands();
void manageCyclicMode(); void manageCyclicMode();
void manageFrostMode();
bool preemptCyclicMode(); bool preemptCyclicMode();
void doStreaming(); void doStreaming();
void heaterOn(); void heaterOn();
@ -851,6 +852,7 @@ void loop()
FilteredSamples.AmbientTemp.update(fTemperature); FilteredSamples.AmbientTemp.update(fTemperature);
manageCyclicMode(); manageCyclicMode();
manageFrostMode();
} }
} }
else { 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() bool preemptCyclicMode()
{ {
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic; const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
@ -993,6 +1020,7 @@ void requestOn()
{ {
if(bHasHtrData && (0 == SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()))) { if(bHasHtrData && (0 == SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()))) {
RTC_Store.setCyclicEngaged(true); // for cyclic mode RTC_Store.setCyclicEngaged(true); // for cyclic mode
RTC_Store.setFrostOn(false); // cancel frost mode
if(!preemptCyclicMode()) { // only start if below cyclic threshold when enabled if(!preemptCyclicMode()) { // only start if below cyclic threshold when enabled
heaterOn(); heaterOn();
} }
@ -1003,6 +1031,7 @@ void requestOff()
{ {
heaterOff(); heaterOff();
RTC_Store.setCyclicEngaged(false); // for cyclic mode RTC_Store.setCyclicEngaged(false); // for cyclic mode
RTC_Store.setFrostOn(false); // cancel active frost mode
} }
void heaterOn() void heaterOn()

View file

@ -29,6 +29,7 @@
#include "../Utility/NVStorage.h" #include "../Utility/NVStorage.h"
#include "../Protocol/Protocol.h" #include "../Protocol/Protocol.h"
#include "../Utility/TempSense.h" #include "../Utility/TempSense.h"
#include "../RTC/RTCStore.h"
#define MAXIFONT tahoma_24ptFontInfo #define MAXIFONT tahoma_24ptFontInfo
@ -270,7 +271,7 @@ CBasicScreen::keyHandler(uint8_t event)
if(event & key_Centre) { if(event & key_Centre) {
if(NVstore.getUserSettings().menuMode < 2) { if(NVstore.getUserSettings().menuMode < 2) {
int runstate = getHeaterInfo().getRunStateEx(); int runstate = getHeaterInfo().getRunStateEx();
if(runstate) { // running, including cyclic mode idle if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle
if(repeatCount > 5) { if(repeatCount > 5) {
repeatCount = -1; repeatCount = -1;
requestOff(); requestOff();

View file

@ -28,6 +28,7 @@
#include "../RTC/Clock.h" #include "../RTC/Clock.h"
#include "../Protocol/Protocol.h" #include "../Protocol/Protocol.h"
#include "../Utility/NVStorage.h" #include "../Utility/NVStorage.h"
#include "../RTC/RTCStore.h"
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// //
@ -135,7 +136,7 @@ CClockScreen::keyHandler(uint8_t event)
if(event & key_Centre) { if(event & key_Centre) {
if(NVstore.getUserSettings().menuMode < 2) { if(NVstore.getUserSettings().menuMode < 2) {
int runstate = getHeaterInfo().getRunStateEx(); int runstate = getHeaterInfo().getRunStateEx();
if(runstate) { // running, including cyclic mode idle if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle
if(_keyRepeatCount > 5) { if(_keyRepeatCount > 5) {
_keyRepeatCount = -1; _keyRepeatCount = -1;
requestOff(); requestOff();

View file

@ -28,6 +28,7 @@
#include "../Protocol/Protocol.h" #include "../Protocol/Protocol.h"
#include "../Utility/NVStorage.h" #include "../Utility/NVStorage.h"
#include "../Utility/FuelGauge.h" #include "../Utility/FuelGauge.h"
#include "../RTC/RTCStore.h"
#define MINIFONT miniFontInfo #define MINIFONT miniFontInfo
@ -217,7 +218,7 @@ CDetailedScreen::keyHandler(uint8_t event)
if(event & key_Centre) { if(event & key_Centre) {
int runstate = getHeaterInfo().getRunStateEx(); int runstate = getHeaterInfo().getRunStateEx();
if(runstate) { // running, including cyclic mode idle if(runstate && !RTC_Store.getFrostOn()) { // running, including cyclic mode idle
if(_keyRepeatCount > 5) { if(_keyRepeatCount > 5) {
_keyRepeatCount = -1; // prevent double handling _keyRepeatCount = -1; // prevent double handling
requestOff(); requestOff();

View file

@ -33,6 +33,7 @@
#include "../RTC/TimerManager.h" #include "../RTC/TimerManager.h"
#include "../Protocol/SmartError.h" #include "../Protocol/SmartError.h"
#include "../Utility/DataFilter.h" #include "../Utility/DataFilter.h"
#include "../RTC/RTCStore.h"
#define MINIFONT miniFontInfo #define MINIFONT miniFontInfo
@ -329,6 +330,11 @@ CScreenHeader::showBatteryIcon(float voltage)
int int
CScreenHeader::showTimers() 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 int nextTimer = CTimerManager::getNextTimer(); // timer ID and repeat flag info of next scheduled timer
if(nextTimer) { if(nextTimer) {
int xPos = X_TIMER_ICON; int xPos = X_TIMER_ICON;
@ -358,12 +364,21 @@ CScreenHeader::showTimers()
} }
return 1; 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 _display.fillRect(X_TIMER_ICON-5, Y_TIMER_ICON+12, 21, 5, BLACK); // erase annotation
return 0; 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 void
CScreenHeader::showTime() CScreenHeader::showTime()

View file

@ -54,6 +54,7 @@ protected:
void showBatteryIcon(float voltage); void showBatteryIcon(float voltage);
int showTimers(); int showTimers();
virtual void showTime(); // x location depends upon how many timers are active virtual void showTime(); // x location depends upon how many timers are active
bool showFrost();
void showHeaderDetail(bool state) { _hdrDetail = state; }; void showHeaderDetail(bool state) { _hdrDetail = state; };
public: public:
CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr); CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr);

View file

@ -1392,3 +1392,26 @@ const uint8_t PROGMEM threshIcon[] =
const BITMAP_INFO threshIconInfo(9, 9, 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);

View file

@ -159,3 +159,5 @@ extern const BITMAP_INFO algIconInfo;
extern const BITMAP_INFO passwordIconInfo; extern const BITMAP_INFO passwordIconInfo;
extern const BITMAP_INFO threshIconInfo; extern const BITMAP_INFO threshIconInfo;
extern const BITMAP_INFO frostIconInfo;

View file

@ -154,6 +154,20 @@ CRTC_Store::getDesiredPump()
return _demandPump; return _demandPump;
} }
void
CRTC_Store::setFrostOn(bool state)
{
_frostOn = state;
_PackAndSaveByte5();
}
bool
CRTC_Store::getFrostOn()
{
_ReadAndUnpackByte5();
return _frostOn;
}
void void
CRTC_Store::resetRunTime() CRTC_Store::resetRunTime()
{ {
@ -230,6 +244,7 @@ CRTC_Store::_ReadAndUnpackByte5()
uint8_t NVval = 0; uint8_t NVval = 0;
Clock.readData((uint8_t*)&NVval, 1, 5); Clock.readData((uint8_t*)&NVval, 1, 5);
_demandPump = NVval & 0x3f; _demandPump = NVval & 0x3f;
_frostOn = (NVval & 0x40) != 0;
_accessed[2] = true; _accessed[2] = true;
DebugPort.printf("RTC_Store - read byte5: pump=%d\r\n", _demandPump); DebugPort.printf("RTC_Store - read byte5: pump=%d\r\n", _demandPump);
} }
@ -239,6 +254,8 @@ void
CRTC_Store::_PackAndSaveByte5() CRTC_Store::_PackAndSaveByte5()
{ {
uint8_t NVval = (_demandPump & 0x3f); uint8_t NVval = (_demandPump & 0x3f);
NVval |= _frostOn ? 0x40 : 0;
Clock.saveData((uint8_t*)&NVval, 1, 5); Clock.saveData((uint8_t*)&NVval, 1, 5);
} }

View file

@ -32,6 +32,7 @@ class CRTC_Store {
bool _BootInit; // Byte4[6] bool _BootInit; // Byte4[6]
bool _CyclicEngaged; // Byte4[7] bool _CyclicEngaged; // Byte4[7]
uint8_t _demandPump; // Byte5[0..5] uint8_t _demandPump; // Byte5[0..5]
bool _frostOn; // Byte5[6]
uint8_t _RunTime; // Byte6[0..4] uint8_t _RunTime; // Byte6[0..4]
uint8_t _GlowTime; // Byte6[5..7] uint8_t _GlowTime; // Byte6[5..7]
void _ReadAndUnpackByte4(); void _ReadAndUnpackByte4();
@ -61,6 +62,8 @@ public:
int getGlowTime(); int getGlowTime();
int getMaxGlowTime() const { return 8; }; int getMaxGlowTime() const { return 8; };
int getMaxRunTime() const { return 32; }; int getMaxRunTime() const { return 32; };
void setFrostOn(bool state);
bool getFrostOn();
}; };
extern CRTC_Store RTC_Store; extern CRTC_Store RTC_Store;

View file

@ -33,6 +33,7 @@
#include "Clock.h" #include "Clock.h"
#include "../Utility/NVStorage.h" #include "../Utility/NVStorage.h"
#include "../Utility/helpers.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 // main array to hold information of which timer is active at any particular minute of the week
// LSBs are used for the timerID + 1 // LSBs are used for the timerID + 1
@ -271,7 +272,9 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow)
} }
else { else {
DebugPort.println("End of timer interval, stopping heater"); DebugPort.println("End of timer interval, stopping heater");
requestOff(); // if(!RTC_Store.getFrostOn() && !RTC_Store.getCyclicEngaged())
if(!RTC_Store.getFrostOn())
requestOff();
retval = 2; retval = 2;
} }
_activeTimer = newID; _activeTimer = newID;

View file

@ -25,6 +25,7 @@
#include "DebugPort.h" #include "DebugPort.h"
#include "../Protocol/Protocol.h" #include "../Protocol/Protocol.h"
#include "../Utility/NVStorage.h" #include "../Utility/NVStorage.h"
#include "../RTC/RTCStore.h"
const int BREATHINTERVAL = 45; const int BREATHINTERVAL = 45;
const int FADEAMOUNT = 3; const int FADEAMOUNT = 3;
@ -122,7 +123,7 @@ void
CGPIOin1::_doStartStop(bool active) CGPIOin1::_doStartStop(bool active)
{ {
if(active) { if(active) {
if(getHeaterInfo().getRunStateEx()) if(getHeaterInfo().getRunStateEx() && !RTC_Store.getFrostOn())
requestOff(); requestOff();
else else
requestOn(); requestOn();

View file

@ -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("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("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("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("PumpCount", RTC_Store.getFuelGauge(), root); // running count of pump strokes
bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // mL/stroke bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // mL/stroke
bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low voltage cutout 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("GPmodeIn2", GPIOin2Names[info.in2Mode], root);
bSend |= moderator.addJson("GPmodeOut1", GPIOout1Names[info.out1Mode], root); bSend |= moderator.addJson("GPmodeOut1", GPIOout1Names[info.out1Mode], root);
bSend |= moderator.addJson("GPmodeOut2", GPIOout2Names[info.out2Mode], 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("GPmodeAnlg", GPIOalgNames[info.algMode], root);
bSend |= moderator.addJson("ExtThermoTmout", (uint32_t)NVstore.getUserSettings().ExtThermoTimeout, root); bSend |= moderator.addJson("ExtThermoTmout", (uint32_t)NVstore.getUserSettings().ExtThermoTimeout, root);
const char* stop = getExternalThermostatHoldTime(); const char* stop = getExternalThermostatHoldTime();

View file

@ -379,6 +379,8 @@ sUserSettings::load()
} }
validatedLoad("thermoWindow", ThermostatWindow, 1.0f, 0.2f, 10.f); validatedLoad("thermoWindow", ThermostatWindow, 1.0f, 0.2f, 10.f);
DebugPort.printf("2) Window = %f\r\n", ThermostatWindow); 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("enableWifi", enableWifi, 1, u8inBounds, 0, 1);
validatedLoad("enableOTA", enableOTA, 0, u8inBounds, 0, 1); validatedLoad("enableOTA", enableOTA, 0, u8inBounds, 0, 1);
validatedLoad("cyclicStop", cyclic.Stop, 0, s8inBounds, 0, 10); validatedLoad("cyclicStop", cyclic.Stop, 0, s8inBounds, 0, 10);
@ -444,6 +446,8 @@ sUserSettings::save()
preferences.putUChar("degF", degF); preferences.putUChar("degF", degF);
preferences.putUChar("thermoMethod", ThermostatMethod); preferences.putUChar("thermoMethod", ThermostatMethod);
preferences.putFloat("thermoWindow", ThermostatWindow); preferences.putFloat("thermoWindow", ThermostatWindow);
preferences.putUChar("frostOn", FrostOn);
preferences.putUChar("frostRise", FrostRise);
preferences.putUChar("enableWifi", enableWifi); preferences.putUChar("enableWifi", enableWifi);
preferences.putUChar("enableOTA", enableOTA); preferences.putUChar("enableOTA", enableOTA);
preferences.putChar("cyclicStop", cyclic.Stop); preferences.putChar("cyclicStop", cyclic.Stop);

View file

@ -284,6 +284,8 @@ struct sUserSettings : public CESP32_NVStorage {
uint8_t degF; uint8_t degF;
uint8_t ThermostatMethod; // 0: standard heater, 1: Narrow Hysterisis, 2:Managed Hz mode uint8_t ThermostatMethod; // 0: standard heater, 1: Narrow Hysterisis, 2:Managed Hz mode
float ThermostatWindow; float ThermostatWindow;
uint8_t FrostOn;
uint8_t FrostRise;
uint8_t useThermostat; uint8_t useThermostat;
uint8_t enableWifi; uint8_t enableWifi;
uint8_t enableOTA; uint8_t enableOTA;
@ -324,6 +326,8 @@ struct sUserSettings : public CESP32_NVStorage {
degF = 0; degF = 0;
ThermostatMethod = 0; ThermostatMethod = 0;
ThermostatWindow = 1.0; ThermostatWindow = 1.0;
FrostOn = 0;
FrostRise = 5;
useThermostat = 1; useThermostat = 1;
enableWifi = 1; enableWifi = 1;
enableOTA = 0; enableOTA = 0;
@ -351,6 +355,8 @@ struct sUserSettings : public CESP32_NVStorage {
degF = rhs.degF; degF = rhs.degF;
ThermostatMethod = rhs.ThermostatMethod; ThermostatMethod = rhs.ThermostatMethod;
ThermostatWindow = rhs.ThermostatWindow; ThermostatWindow = rhs.ThermostatWindow;
FrostOn = rhs.FrostOn;
FrostRise = rhs.FrostRise;
useThermostat = rhs.useThermostat; useThermostat = rhs.useThermostat;
enableWifi = rhs.enableWifi; enableWifi = rhs.enableWifi;
enableOTA = rhs.enableOTA; enableOTA = rhs.enableOTA;

View file

@ -309,6 +309,22 @@ void DecodeCmd(const char* cmd, String& payload)
else if(strcmp("GPin2", cmd) == 0) { else if(strcmp("GPin2", cmd) == 0) {
simulateGPIOin(payload.toInt() ? 0x02 : 0x00); // simulate key 2 press 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) { else if(strcmp("JSONpack", cmd) == 0) {
sUserSettings us = NVstore.getUserSettings(); sUserSettings us = NVstore.getUserSettings();
uint8_t packed = payload.toInt() ? 0x00 : 0x01; uint8_t packed = payload.toInt() ? 0x00 : 0x01;
@ -395,5 +411,21 @@ void DecodeCmd(const char* cmd, String& payload)
else if(strcmp("SysHourMeters", cmd) == 0) { else if(strcmp("SysHourMeters", cmd) == 0) {
pHourMeter->resetHard(); 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();
}
}
} }