From d2116fc18cf7331c9cabd3dcdf454fb550785734 Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Fri, 26 Jul 2019 22:13:46 +1000 Subject: [PATCH] Cleaner hourmeter handling using a new class for each counter. Added a warning animation to LVC, ether <12V, or 0.5V > LVC if that result is over 12V --- src/Afterburner.cpp | 15 ++--- src/OLED/ScreenHeader.cpp | 37 +++++++++++- src/OLED/ScreenHeader.h | 1 + src/Protocol/SmartError.cpp | 23 ++++++- src/Protocol/SmartError.h | 2 +- src/RTC/RTCStore.cpp | 3 +- src/Utility/HourMeter.cpp | 117 ++++++++++++++++++++---------------- src/Utility/HourMeter.h | 61 ++++++++++++++----- 8 files changed, 174 insertions(+), 85 deletions(-) diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index c7abbb3..95a57bd 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -458,8 +458,8 @@ void setup() { // bCyclicEngaged = RTC_Store.getCyclicEngaged(); DebugPort.printf("Previous cyclic active = %d\r\n", RTC_Store.getCyclicEngaged()); // state flag required for cyclic mode to persist properly after a WD reboot :-) - pHourMeter = new CHourMeter(persistentRunTime, persistentGlowTime); // persistent vars are only trustworthy with SW reboots - pHourMeter->init(bESP32PowerUpInit || RTC_Store.getBootInit()); // ensure persistent memory variable are reset after powerup, or OTA update + pHourMeter = new CHourMeter(persistentRunTime, persistentGlowTime); // persistent vars passed by reference so they can be valid after SW reboots + pHourMeter->init(bESP32PowerUpInit || RTC_Store.getBootInit()); // ensure persistent memory variable are reset after powerup, or OTA update RTC_Store.setBootInit(false); delay(1000); // just to hold the splash screeen for while @@ -908,7 +908,7 @@ bool validateFrame(const CProtocol& frame, const char* name) void requestOn() { - if(bHasHtrData && SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) { + if(bHasHtrData && (0 == SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()))) { heaterOn(); RTC_Store.setCyclicEngaged(true); // for cyclic mode } @@ -1249,14 +1249,7 @@ void checkDebugCommands() ESP.restart(); // reset the esp } else if(rxVal == ('h' & 0x1f)) { // CTRL-H hourmeter reset - sHourMeter FLASHmem = NVstore.getHourMeter(); - FLASHmem.RunTime = 0; - FLASHmem.GlowTime = 0; - NVstore.setHourMeter(FLASHmem); - RTC_Store.resetRunTime(); - RTC_Store.resetGlowTime(); - persistentRunTime = 0; - persistentGlowTime = 0; + pHourMeter->resetHard(); } } #ifdef PROTOCOL_INVESTIGATION diff --git a/src/OLED/ScreenHeader.cpp b/src/OLED/ScreenHeader.cpp index e6f3f29..ac2e958 100644 --- a/src/OLED/ScreenHeader.cpp +++ b/src/OLED/ScreenHeader.cpp @@ -134,9 +134,43 @@ CScreenHeader::animate() int xPos = X_BATT_ICON; int yPos = Y_BATT_ICON; showTimers(); + + switch(_batteryCount) { + case 0: + // establish battery icon flash pattern + // > 0.5 over LVC - solid + // < 0.5 over LVC - slow flash + // < LVC - fast flash + _batteryWarn = SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue(), false); + + showBatteryIcon(getBatteryVoltage(true)); + break; + case 1: + if(_batteryWarn == 2) + _display.fillRect(xPos, yPos, BatteryIconInfo.width, BatteryIconInfo.height, BLACK); + break; + case 2: + if(_batteryWarn == 2) + showBatteryIcon(getBatteryVoltage(true)); + break; + case 3: + if(_batteryWarn) // works for either < LVC, or < LVC+0.5 + _display.fillRect(xPos, yPos, BatteryIconInfo.width, BatteryIconInfo.height, BLACK); + break; + case 4: + if(_batteryWarn == 2) + showBatteryIcon(getBatteryVoltage(true)); + break; + case 5: + if(_batteryWarn == 2) + _display.fillRect(xPos, yPos, BatteryIconInfo.width, BatteryIconInfo.height, BLACK); + break; + + } +/* switch(_batteryCount) { case 3: - if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue(), false)) { // check but do not fault + if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue(), false) == 0) { // check but do not fault showBatteryIcon(getBatteryVoltage(true)); } else { @@ -147,6 +181,7 @@ CScreenHeader::animate() showBatteryIcon(getBatteryVoltage(true)); break; } +*/ } showWifiIcon(); diff --git a/src/OLED/ScreenHeader.h b/src/OLED/ScreenHeader.h index d9aeed1..d1eda34 100644 --- a/src/OLED/ScreenHeader.h +++ b/src/OLED/ScreenHeader.h @@ -46,6 +46,7 @@ class CScreenHeader : public CScreen { bool _colon; uint8_t _animateCount; uint8_t _batteryCount; + uint8_t _batteryWarn; protected: void showBTicon(); void showWifiIcon(); diff --git a/src/Protocol/SmartError.cpp b/src/Protocol/SmartError.cpp index ca8171a..3f1fd1d 100644 --- a/src/Protocol/SmartError.cpp +++ b/src/Protocol/SmartError.cpp @@ -113,7 +113,12 @@ CSmartError::monitor(uint8_t newRunState) _prevRunState = newRunState; } -bool +// +// retval: +// 0 - OK +// 1 - Warning less than 12/24 (or .5V over LVC for higher LVC levels) +// 2 - Warning less than LVC +int CSmartError::checkVolts(float ipVolts, float glowI, bool throwfault) { // check for low voltage @@ -126,10 +131,22 @@ CSmartError::checkVolts(float ipVolts, float glowI, bool throwfault) _Error = 2; // +1 over displayed error code requestOff(); } - return false; + return 2; + } + int alert = Thresh + 0.5; + if(NVstore.getHeaterTuning().sysVoltage == 120) { + if(alert < 12) + alert = 12; + } + else { + if(alert < 24) + alert = 24; + } + if(ipVolts < alert) { + return 1; } } - return true; + return 0; } diff --git a/src/Protocol/SmartError.h b/src/Protocol/SmartError.h index 6beab7c..c59f63b 100644 --- a/src/Protocol/SmartError.h +++ b/src/Protocol/SmartError.h @@ -31,7 +31,7 @@ public: void inhibit(); void monitor(const CProtocol& heaterFrame); void monitor(uint8_t runstate); - bool checkVolts(float volts, float plugI, bool throwfault=true); + int checkVolts(float volts, float plugI, bool throwfault=true); // 0 = OK, 1 = within 0.5V of LVC, 2 = under LVC uint8_t getError(); }; diff --git a/src/RTC/RTCStore.cpp b/src/RTC/RTCStore.cpp index 03f19ee..6e04015 100644 --- a/src/RTC/RTCStore.cpp +++ b/src/RTC/RTCStore.cpp @@ -245,6 +245,7 @@ CRTC_Store::_PackAndSaveByte5() void CRTC_Store::_PackAndSaveByte6() { + DebugPort.printf("RTC_Store - save byte 6: Run=%d, Glow=%d\r\n", _RunTime, _GlowTime); uint8_t NVval = ((_GlowTime & 0x07)<<5) | (_RunTime & 0x1f); Clock.saveData((uint8_t*)&NVval, 1, 6); } @@ -258,7 +259,7 @@ CRTC_Store::_ReadAndUnpackByte6() _GlowTime = (NVval >> 5) & 0x07; _RunTime = NVval & 0x1f; _accessed[3] = true; - DebugPort.printf("RTC_Store - read byte6: glow=%d, run=%d\r\n", _GlowTime, _RunTime); + DebugPort.printf("RTC_Store - read byte6: Run=%d, Glow=%d\r\n", _RunTime, _GlowTime); } } diff --git a/src/Utility/HourMeter.cpp b/src/Utility/HourMeter.cpp index 57b90ad..c38039c 100644 --- a/src/Utility/HourMeter.cpp +++ b/src/Utility/HourMeter.cpp @@ -31,11 +31,13 @@ void CHourMeter::init(bool poweron) { - if(poweron) { - _RunTime = 0; // definitely untrustworthy after power on or OTA updates - _GlowTime = 0; + // power on reset or OTA update - cannot trust persistent values - they are likely un-initialised + if(poweron) { + RunTime.reset(); + GlowTime.reset(); } - if(_RunTime || _GlowTime || RTC_Store.getRunTime() || RTC_Store.getGlowTime()) { + // if there is a remnant time held, add it to the real NV stored value + if(RunTime.get() || GlowTime.get() || RTC_Store.getRunTime() || RTC_Store.getGlowTime()) { store(); } } @@ -45,101 +47,110 @@ CHourMeter::reset() { RTC_Store.resetRunTime(); RTC_Store.resetGlowTime(); - _RunTime = 0; - _GlowTime = 0; + RunTime.reset(); + GlowTime.reset(); +} + +void +CHourMeter::resetHard() +{ + reset(); + sHourMeter NV = NVstore.getHourMeter(); + NV.RunTime = 0; + NV.GlowTime = 0; + NVstore.setHourMeter(NV); } void CHourMeter::store() { sHourMeter NV = NVstore.getHourMeter(); - NV.RunTime += _RunTime + baseSeconds * RTC_Store.getRunTime(); // add any residual to the real NV stored value - NV.GlowTime += _GlowTime + baseSeconds * RTC_Store.getGlowTime(); - NVstore.setHourMeter(NV); - reset(); + NV.RunTime += RunTime.get() + RTC_storageInterval * RTC_Store.getRunTime(); // add any residual to the real NV stored value + NV.GlowTime += GlowTime.get() + RTC_storageInterval * RTC_Store.getGlowTime(); + NVstore.setHourMeter(NV); // stage new values, and setup for save (if changed) + reset(); // zero time tracked in this class } void CHourMeter::monitor(const CProtocol& frame) { -// long now = Clock.get().secondstime(); - unsigned long now = millis(); if(frame.getRunState() == 0) { - // heater is stopped - save remnant times to flash - if(_lastRunTime) { - store(); // heater hass stopped - save remnant times to flash + // heater is stopped + if(RunTime.active()) { + store(); // initial stop of heater - save residual times to NV } - _lastRunTime = 0; - _lastGlowTime = 0; + RunTime.stop(); // cancel time tracking + GlowTime.stop(); } else { // heater is running - if(_lastRunTime != 0) { - // first sample after start is ignored - float tDelta = float(now - _lastRunTime) * 0.001; - _RunTime += tDelta; - if(frame.getGlowPlug_Voltage() != 0) { - if(_lastGlowTime != 0) { - _GlowTime += tDelta; - } - _lastGlowTime = now; - } - else { - _lastGlowTime = 0; - } + unsigned long now = millis(); + RunTime.recordTime(now); // track run time of heater + if(frame.getGlowPlug_Voltage() != 0) { + GlowTime.recordTime(now); // track on time of glow plug } - _lastRunTime = now; + else { + GlowTime.stop(); + } + // check for RAM counters time interval rollover sHourMeter NV = NVstore.getHourMeter(); - // test for rollover of our local run time tracking - if(_RunTime > baseSeconds) { - _RunTime -= baseSeconds; - if(RTC_Store.incRunTime()) { // returns true if rolled back to zero + // rollover run time tracking? + if(RunTime.get() > RTC_storageInterval) { + RunTime.offset(-RTC_storageInterval); + if(RTC_Store.incRunTime()) { // returns true if RTC counter rolled back to zero // rolled RTC intermediate store - push into FLASH - NV.RunTime += baseSeconds * RTC_Store.getMaxRunTime(); // bump by our maximum storable time + NV.RunTime += RTC_storageInterval * RTC_Store.getMaxRunTime(); // bump NV by our maximum storable time } } - // test for rollover of our local glow time tracking - if(_GlowTime > baseSeconds) { - _GlowTime -= baseSeconds; + // rollover glow time tracking? + if(GlowTime.get() > RTC_storageInterval) { + GlowTime.offset(-RTC_storageInterval); if(RTC_Store.incGlowTime()) { // returns true if rolled back to zero // rolled RTC intermediate store - push into FLASH - NV.GlowTime += baseSeconds * RTC_Store.getMaxGlowTime(); // bump by our maximum storable time + NV.GlowTime += RTC_storageInterval * RTC_Store.getMaxGlowTime(); // bump NV by our maximum storable time } } - NVstore.setHourMeter(NV); // internally moderated, only actually saves if values have changed + NVstore.setHourMeter(NV); // internally moderated, will only actually save if a value has changed } -// DebugPort.printf("CHourMeter %f %f\r\n", _RunTime, _GlowTime); } uint32_t CHourMeter::_getLclRunTime() { - uint32_t rt = (uint32_t)_RunTime; + uint32_t rt = RunTime.get(); +#ifdef DEBUG_HOURMETER DebugPort.printf("HrMtr _GetLclRunTime(): %d %d\r\n", rt, RTC_Store.getRunTime()); - return rt + baseSeconds * RTC_Store.getRunTime(); -} - -uint32_t -CHourMeter::_getLclGlowTime() -{ - uint32_t gt = (uint32_t)_GlowTime; - DebugPort.printf("HrMtr _GetLclGlowTime(): %d %d\r\n", gt, RTC_Store.getGlowTime()); - return gt + baseSeconds * RTC_Store.getGlowTime(); +#endif + return rt + RTC_storageInterval * RTC_Store.getRunTime(); } uint32_t CHourMeter::getRunTime() { uint32_t rt = _getLclRunTime(); +#ifdef DEBUG_HOURMETER DebugPort.printf("HrMtr GetRunTime(): %d %d\r\n", rt, NVstore.getHourMeter().RunTime); +#endif return rt + NVstore.getHourMeter().RunTime; } +uint32_t +CHourMeter::_getLclGlowTime() +{ + uint32_t gt = GlowTime.get(); +#ifdef DEBUG_HOURMETER + DebugPort.printf("HrMtr _GetLclGlowTime(): %d %d\r\n", gt, RTC_Store.getGlowTime()); +#endif + return gt + RTC_storageInterval * RTC_Store.getGlowTime(); +} + uint32_t CHourMeter::getGlowTime() { uint32_t gt = _getLclGlowTime(); - DebugPort.printf("HrMtr GetGlowTime(): %d %d\r\n", gt, NVstore.getHourMeter().RunTime); +#ifdef DEBUG_HOURMETER + DebugPort.printf("HrMtr GetGlowTime(): %d %d\r\n", gt, NVstore.getHourMeter().GlowTime); +#endif return gt + NVstore.getHourMeter().GlowTime; } diff --git a/src/Utility/HourMeter.h b/src/Utility/HourMeter.h index 9e56412..7975f8c 100644 --- a/src/Utility/HourMeter.h +++ b/src/Utility/HourMeter.h @@ -23,29 +23,59 @@ #include "../RTC/RTCStore.h" #include "NVStorage.h" +#define DEBUG_HOURMETER + + class CProtocol; -class CHourMeter { - const int baseSeconds = 60 * 15; // 15 minutes +class sRunTime { + float& persistentVal; + unsigned long lastSampleTime; +public: + sRunTime(float& refVal) : persistentVal(refVal) { + lastSampleTime = 0; + }; + void reset() { + persistentVal = 0; + }; + uint32_t get() const { + return (uint32_t)persistentVal; + } + bool active() const { + return lastSampleTime != 0; + } + void stop() { + lastSampleTime = 0; + } + float recordTime(unsigned long now) { + float rVal = 0; + if(lastSampleTime) + rVal = float((unsigned long)(now - lastSampleTime)) * 0.001; + lastSampleTime = now; + persistentVal += rVal; + return rVal; + } + void offset(float ofs) { + persistentVal += ofs; + } - float& _RunTime; - float& _GlowTime; - unsigned long _lastRunTime; - unsigned long _lastGlowTime; +}; + +class CHourMeter { + const int RTC_storageInterval = 60 * 10; // 10 minutes + + sRunTime RunTime; + sRunTime GlowTime; uint32_t _getLclRunTime(); // volatile persistent variable + RTC stored rollovers uint32_t _getLclGlowTime(); // volatile persistent variable + RTC stored rollovers public: CHourMeter(float &runtime, float& glowtime) : - _RunTime(runtime), - _GlowTime(glowtime) + RunTime(runtime), + GlowTime(glowtime) { - _lastRunTime = 0; - _lastGlowTime = 0; - DebugPort.printf("CHourMeter %f %f\r\n", _RunTime, _GlowTime); - }; - void associate(float &runtime, float& glowtime) { - _RunTime = runtime; - _GlowTime = glowtime; +#ifdef DEBUG_HOURMETER + DebugPort.printf("CHourMeter %d %d\r\n", RunTime.get(), GlowTime.get()); +#endif }; void init(bool poweron); void reset(); @@ -53,6 +83,7 @@ public: void monitor(const CProtocol& frame); uint32_t getRunTime(); // total time, local tracked + last NV stored value uint32_t getGlowTime(); // total time, local tracked + last NV stored value + void resetHard(); }; extern CHourMeter* pHourMeter; \ No newline at end of file