diff --git a/.gitignore b/.gitignore index b0ffa36..4e739ad 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ Arduino/Afterburner/src/* /Releases /webdev /case +/DieselHeaterV2.PcbDoc +/StandardResponse.txt +/HeaterHack-Tested.zip +/OTA_COM.txt diff --git a/Arduino/Afterburner/Afterburner.ino b/Arduino/Afterburner/Afterburner.ino index b033a45..872fce0 100644 --- a/Arduino/Afterburner/Afterburner.ino +++ b/Arduino/Afterburner/Afterburner.ino @@ -99,6 +99,7 @@ #include "src/Utility/helpers.h" #include "src/Utility/NVStorage.h" #include "src/Utility/DebugPort.h" +#include "src/Utility/macros.h" #include "src/Utility/UtilClasses.h" #include "src/Utility/BTC_JSON.h" #include "src/Utility/BTC_GPIO.h" @@ -446,7 +447,11 @@ void setup() { FilteredSamples.Fan.setRounding(10); FilteredSamples.Fan.setAlpha(0.7); FilteredSamples.AmbientTemp.reset(-100.0); - + FilteredSamples.FastipVolts.setRounding(0.1); + FilteredSamples.FastipVolts.setAlpha(0.7); + FilteredSamples.FastGlowAmps.setRounding(0.01); + FilteredSamples.FastGlowAmps.setAlpha(0.7); + RTC_Store.begin(); FuelGauge.init(RTC_Store.getFuelGauge()); // demandDegC = RTC_Store.getDesiredTemp(); @@ -792,9 +797,24 @@ void loop() ScreenManager.reqUpdate(); } + if(bHasHtrData) { + // apply exponential mean to the anlogue readings for some smoothing updateFilteredData(); - FuelGauge.Integrate(HeaterFrame2.getPump_Actual()); + + // integrate fuel pump activity for fuel gauge + FuelGauge.Integrate(getHeaterInfo().getPump_Actual()); + + // test for low volts shutdown during normal run + if(INBOUNDS(getHeaterInfo().getRunState(), 1, 5)) { // check for Low Voltage Cutout + SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); + } + + // trap being in state 0 with a heater error - cancel user on memory to avoid unexpected cyclic restarts + if(RTC_Store.getCyclicEngaged() && (getHeaterInfo().getRunState() == 0) && (getHeaterInfo().getErrState() > 1)) { + DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); + RTC_Store.setCyclicEngaged(false); + } } updateJSONclients(bReportJSONData); CommState.set(CommStates::Idle); @@ -822,7 +842,7 @@ void manageCyclicMode() const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic; if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over! - float deltaT = FilteredSamples.AmbientTemp.getValue() - getDemandDegC(); + float deltaT = getTemperatureSensor() - getDemandDegC(); // DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT); // ensure we cancel user ON mode if heater throws an error @@ -883,8 +903,10 @@ bool validateFrame(const CProtocol& frame, const char* name) void requestOn() { - heaterOn(); - RTC_Store.setCyclicEngaged(true); // for cyclic mode + if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) { + heaterOn(); + RTC_Store.setCyclicEngaged(true); // for cyclic mode + } } void requestOff() @@ -1041,7 +1063,7 @@ float getTemperatureDesired() float getTemperatureSensor() { - return FilteredSamples.AmbientTemp.getValue(); + return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempOfs; } void setPumpMin(float val) @@ -1449,12 +1471,15 @@ void simulateGPIOin(uint8_t newKey) GPIOin.simulateKey(newKey); } -float getBatteryVoltage() +float getBatteryVoltage(bool fast) { #ifdef RAW_SAMPLES return getHeaterInfo().getBattVoltage(); #else - return FilteredSamples.ipVolts.getValue(); + if(fast) + return FilteredSamples.FastipVolts.getValue(); + else + return FilteredSamples.ipVolts.getValue(); #endif } @@ -1487,13 +1512,16 @@ float getFanSpeed() void updateFilteredData() { - FilteredSamples.ipVolts.update(HeaterFrame2.getVoltage_Supply()); - FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage()); - FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current()); - FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual()); + FilteredSamples.ipVolts.update(getHeaterInfo().getBattVoltage()); + FilteredSamples.GlowVolts.update(getHeaterInfo().getGlow_Voltage()); + FilteredSamples.GlowAmps.update(getHeaterInfo().getGlow_Current()); + FilteredSamples.Fan.update(getHeaterInfo().getFan_Actual()); + FilteredSamples.FastipVolts.update(getHeaterInfo().getBattVoltage()); + FilteredSamples.FastGlowAmps.update(getHeaterInfo().getGlow_Current()); } int sysUptime() { return Clock.get().secondstime() - BootTime; -} \ No newline at end of file +} + diff --git a/Partitions.txt b/Partitions.txt new file mode 100644 index 0000000..3bd58a3 --- /dev/null +++ b/Partitions.txt @@ -0,0 +1,10 @@ + + PLATFORMIO MIN_SPIFFS PARTITION ARDUINO MIN_SPIFFS PARTITION + + # Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags +Same nvs, data, nvs, 0x9000, 0x5000, nvs, data, nvs, 0x9000, 0x5000, +Same otadata, data, ota, 0xe000, 0x2000, otadata, data, ota, 0xe000, 0x2000, +Same app0, app, ota_0, 0x10000, 0x1E0000, app0, app, ota_0, 0x10000, 0x1E0000, +Same app1, app, ota_1, 0x1F0000,0x1E0000, app1, app, ota_1, 0x1F0000,0x1E0000, +Diff eeprom, data, 0x99, 0x3D0000,0x1000, spiffs, data, spiffs, 0x3D0000,0x30000, +Diff spiffs, data, spiffs, 0x3D1000,0x2F000, diff --git a/README.md b/README.md index afa12d8..690a7e8 100644 --- a/README.md +++ b/README.md @@ -51,15 +51,15 @@ Working so far: Web browser upload new binary to controller (AP or STA mode) Direct discovery and download of updates from internet server (STA mode) * Factory default menu option +* "Fuel gauge" - Integrates pump frequency, assumes a repeatable dose of fuel per pump stroke. +* Low voltage cut out, definable threshold - auto adjusts for cable voltage drop during start +* Temperature probe offset to correct adverse readings. To be implemented -------------------------- * 433MHz Rx stream, 433MHz Tx stream connections. This would allow external timer units for example, or analogue temperature demand (which is still only 1 degree resolution with standard heater thermostat). -* Low voltage cut out -* Temperature probe offset or mapping to correct adverse readings. * MQTT pub/sub -* "fuel gauge" - Integrate pump frequency, assuming a repeatable dose of fuel per pump cycle... * Regular Hot Burn cycle (DPF mode!) * Hour meter - run time, glow time * list under construction..... diff --git a/icons/BatteryIcon.bmp b/icons/BatteryIcon.bmp index 5cf003a..fdd9ec9 100644 Binary files a/icons/BatteryIcon.bmp and b/icons/BatteryIcon.bmp differ diff --git a/icons/DegC.bmp b/icons/DegC.bmp new file mode 100644 index 0000000..dc64c53 Binary files /dev/null and b/icons/DegC.bmp differ diff --git a/icons/DegF.bmp b/icons/DegF.bmp new file mode 100644 index 0000000..67b841a Binary files /dev/null and b/icons/DegF.bmp differ diff --git a/icons/FuelIcon.bmp b/icons/FuelIcon.bmp index a590cf3..cd72aec 100644 Binary files a/icons/FuelIcon.bmp and b/icons/FuelIcon.bmp differ diff --git a/icons/FuelIconSmall.bmp b/icons/FuelIconSmall.bmp new file mode 100644 index 0000000..5dba1b2 Binary files /dev/null and b/icons/FuelIconSmall.bmp differ diff --git a/icons/Reset.bmp b/icons/Reset.bmp new file mode 100644 index 0000000..af9a953 Binary files /dev/null and b/icons/Reset.bmp differ diff --git a/icons/ThermostatC.bmp b/icons/ThermostatC.bmp new file mode 100644 index 0000000..a14076a Binary files /dev/null and b/icons/ThermostatC.bmp differ diff --git a/icons/ThermostatF.bmp b/icons/ThermostatF.bmp new file mode 100644 index 0000000..50d754b Binary files /dev/null and b/icons/ThermostatF.bmp differ diff --git a/icons/ThermostatHz.bmp b/icons/ThermostatHz.bmp new file mode 100644 index 0000000..0cd1576 Binary files /dev/null and b/icons/ThermostatHz.bmp differ diff --git a/platformio.ini b/platformio.ini index c1392d7..9d1c4c7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,6 +11,7 @@ [env:esp32dev] platform = espressif32 lib_extra_dirs = ~/Documents/Arduino/libraries +;lib_dir = src/Afterburner/src board = esp32dev framework = arduino board_build.partitions = min_spiffs.csv @@ -22,5 +23,6 @@ upload_flags = monitor_speed = 115200 extra_scripts = post:add_CRC.py ; replace shitty Arduino millis with a linear time version -build_flags = -Wl,--wrap,millis +build_flags = + -Wl,--wrap,millis diff --git a/src/Afterburner/Afterburner.cpp b/src/Afterburner/Afterburner.cpp index b033a45..872fce0 100644 --- a/src/Afterburner/Afterburner.cpp +++ b/src/Afterburner/Afterburner.cpp @@ -99,6 +99,7 @@ #include "src/Utility/helpers.h" #include "src/Utility/NVStorage.h" #include "src/Utility/DebugPort.h" +#include "src/Utility/macros.h" #include "src/Utility/UtilClasses.h" #include "src/Utility/BTC_JSON.h" #include "src/Utility/BTC_GPIO.h" @@ -446,7 +447,11 @@ void setup() { FilteredSamples.Fan.setRounding(10); FilteredSamples.Fan.setAlpha(0.7); FilteredSamples.AmbientTemp.reset(-100.0); - + FilteredSamples.FastipVolts.setRounding(0.1); + FilteredSamples.FastipVolts.setAlpha(0.7); + FilteredSamples.FastGlowAmps.setRounding(0.01); + FilteredSamples.FastGlowAmps.setAlpha(0.7); + RTC_Store.begin(); FuelGauge.init(RTC_Store.getFuelGauge()); // demandDegC = RTC_Store.getDesiredTemp(); @@ -792,9 +797,24 @@ void loop() ScreenManager.reqUpdate(); } + if(bHasHtrData) { + // apply exponential mean to the anlogue readings for some smoothing updateFilteredData(); - FuelGauge.Integrate(HeaterFrame2.getPump_Actual()); + + // integrate fuel pump activity for fuel gauge + FuelGauge.Integrate(getHeaterInfo().getPump_Actual()); + + // test for low volts shutdown during normal run + if(INBOUNDS(getHeaterInfo().getRunState(), 1, 5)) { // check for Low Voltage Cutout + SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); + } + + // trap being in state 0 with a heater error - cancel user on memory to avoid unexpected cyclic restarts + if(RTC_Store.getCyclicEngaged() && (getHeaterInfo().getRunState() == 0) && (getHeaterInfo().getErrState() > 1)) { + DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); + RTC_Store.setCyclicEngaged(false); + } } updateJSONclients(bReportJSONData); CommState.set(CommStates::Idle); @@ -822,7 +842,7 @@ void manageCyclicMode() const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic; if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over! - float deltaT = FilteredSamples.AmbientTemp.getValue() - getDemandDegC(); + float deltaT = getTemperatureSensor() - getDemandDegC(); // DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT); // ensure we cancel user ON mode if heater throws an error @@ -883,8 +903,10 @@ bool validateFrame(const CProtocol& frame, const char* name) void requestOn() { - heaterOn(); - RTC_Store.setCyclicEngaged(true); // for cyclic mode + if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) { + heaterOn(); + RTC_Store.setCyclicEngaged(true); // for cyclic mode + } } void requestOff() @@ -1041,7 +1063,7 @@ float getTemperatureDesired() float getTemperatureSensor() { - return FilteredSamples.AmbientTemp.getValue(); + return FilteredSamples.AmbientTemp.getValue() + NVstore.getHeaterTuning().tempOfs; } void setPumpMin(float val) @@ -1449,12 +1471,15 @@ void simulateGPIOin(uint8_t newKey) GPIOin.simulateKey(newKey); } -float getBatteryVoltage() +float getBatteryVoltage(bool fast) { #ifdef RAW_SAMPLES return getHeaterInfo().getBattVoltage(); #else - return FilteredSamples.ipVolts.getValue(); + if(fast) + return FilteredSamples.FastipVolts.getValue(); + else + return FilteredSamples.ipVolts.getValue(); #endif } @@ -1487,13 +1512,16 @@ float getFanSpeed() void updateFilteredData() { - FilteredSamples.ipVolts.update(HeaterFrame2.getVoltage_Supply()); - FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage()); - FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current()); - FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual()); + FilteredSamples.ipVolts.update(getHeaterInfo().getBattVoltage()); + FilteredSamples.GlowVolts.update(getHeaterInfo().getGlow_Voltage()); + FilteredSamples.GlowAmps.update(getHeaterInfo().getGlow_Current()); + FilteredSamples.Fan.update(getHeaterInfo().getFan_Actual()); + FilteredSamples.FastipVolts.update(getHeaterInfo().getBattVoltage()); + FilteredSamples.FastGlowAmps.update(getHeaterInfo().getGlow_Current()); } int sysUptime() { return Clock.get().secondstime() - BootTime; -} \ No newline at end of file +} + diff --git a/src/Afterburner/src/OLED/BasicScreen.cpp b/src/Afterburner/src/OLED/BasicScreen.cpp index 9538c88..d643eb3 100644 --- a/src/Afterburner/src/OLED/BasicScreen.cpp +++ b/src/Afterburner/src/OLED/BasicScreen.cpp @@ -50,7 +50,7 @@ CBasicScreen::CBasicScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen bool CBasicScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); char msg[20]; int xPos, yPos; diff --git a/src/Afterburner/src/OLED/ClockScreen.cpp b/src/Afterburner/src/OLED/ClockScreen.cpp index a16c40e..9fcb39a 100644 --- a/src/Afterburner/src/OLED/ClockScreen.cpp +++ b/src/Afterburner/src/OLED/ClockScreen.cpp @@ -43,10 +43,11 @@ CClockScreen::CClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen } + bool CClockScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); const BTCDateTime& now = Clock.get(); diff --git a/src/Afterburner/src/OLED/DetailedScreen.cpp b/src/Afterburner/src/OLED/DetailedScreen.cpp index 68308fe..4532436 100644 --- a/src/Afterburner/src/OLED/DetailedScreen.cpp +++ b/src/Afterburner/src/OLED/DetailedScreen.cpp @@ -29,8 +29,6 @@ #include "../Utility/NVStorage.h" #include "../Utility/FuelGauge.h" -extern CFuelGauge FuelGauge; - #define MINIFONT miniFontInfo @@ -84,7 +82,7 @@ CDetailedScreen::CDetailedScreen(C128x64_OLED& display, CScreenManager& mgr) : C bool CDetailedScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); int runstate = getHeaterInfo().getRunStateEx(); int errstate = getHeaterInfo().getErrState(); diff --git a/src/Afterburner/src/OLED/FuelCalScreen.h b/src/Afterburner/src/OLED/FuelCalScreen.h new file mode 100644 index 0000000..8bbe878 --- /dev/null +++ b/src/Afterburner/src/OLED/FuelCalScreen.h @@ -0,0 +1,48 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __FUELCALSCREEN_H__ +#define __FUELCALSCREEN_H__ + +#include +#include "PasswordScreen.h" + +class C128x64_OLED; +class CScreenManager; + +class CFuelCalScreen : public CPasswordScreen +{ + int _rowSel; + void _adjust(int dir); + float _mlPerStroke; + float _tOfs; + uint8_t _LVC; + int _animateCount; + void _initUI(); +public: + CFuelCalScreen(C128x64_OLED& display, CScreenManager& mgr); + bool show(); + bool animate(); + bool keyHandler(uint8_t event); + void onSelect(); +}; + +#endif diff --git a/src/Afterburner/src/OLED/FuelMixtureScreen.cpp b/src/Afterburner/src/OLED/FuelMixtureScreen.cpp index 0f12062..8347ffe 100644 --- a/src/Afterburner/src/OLED/FuelMixtureScreen.cpp +++ b/src/Afterburner/src/OLED/FuelMixtureScreen.cpp @@ -34,6 +34,7 @@ #include "../Utility/DebugPort.h" #include "../Utility/macros.h" #include "../Protocol/Protocol.h" +#include "fonts/Icons.h" CFuelMixtureScreen::CFuelMixtureScreen(C128x64_OLED& display, CScreenManager& mgr) : CPasswordScreen(display, mgr) @@ -47,6 +48,12 @@ CFuelMixtureScreen::onSelect() CPasswordScreen::onSelect(); _initUI(); + _load(); +} + +void +CFuelMixtureScreen::_load() +{ adjPump[0] = getHeaterInfo().getPump_Min(); adjPump[1] = getHeaterInfo().getPump_Max(); adjFan[0] = getHeaterInfo().getFan_Min(); @@ -58,6 +65,7 @@ CFuelMixtureScreen::_initUI() { _rowSel = 0; _colSel = 0; + _animateCount = 0; } bool @@ -67,34 +75,40 @@ CFuelMixtureScreen::show() int xPos, yPos; const int col3 = _display.width() - border; - _display.clearDisplay(); + _display.fillRect(70, 0, 58, 64, BLACK); // scrub variables + _display.fillRect(0, 50, 128, 14, BLACK); // scrub footer if(!CPasswordScreen::show()) { + switch(_rowSel) { case 0: case 1: case 2: case 3: case 4: + if(_animateCount == -1) { + _animateCount = 0; + _display.clearDisplay(); + } // Pump Minimum adjustment yPos = border + 36; - _printMenuText(80, yPos, "Min", false, eRightJustify); - sprintf(str, "%.1f", adjPump[0]); + _printMenuText(65, yPos, "Min", false, eRightJustify); + sprintf(str, "%.1f Hz", adjPump[0]); _printMenuText(col3, yPos, str, _rowSel == 1, eRightJustify); // Pump Maximum adjustment yPos = border + 24; - _printMenuText(80, yPos, "Pump Hz Max", false, eRightJustify); - sprintf(str, "%.1f", adjPump[1]); + _printMenuText(65, yPos, "Max", false, eRightJustify); + sprintf(str, "%.1f Hz", adjPump[1]); _printMenuText(col3, yPos, str, _rowSel == 2, eRightJustify); // Fan Minimum adjustment yPos = border + 12; - _printMenuText(80, yPos, "Min", false, eRightJustify); - sprintf(str, "%d", adjFan[0]); + _printMenuText(65, yPos, "Min", false, eRightJustify); + sprintf(str, "%d RPM", adjFan[0]); _printMenuText(col3, yPos, str, _rowSel == 3, eRightJustify); // Fan Maximum adjustment yPos = border; - _printMenuText(80, yPos, "Fan RPM Max", false, eRightJustify); - sprintf(str, "%d", adjFan[1]); + _printMenuText(65, yPos, "Max", false, eRightJustify); + sprintf(str, "%d RPM", adjFan[1]); _printMenuText(col3, yPos, str, _rowSel == 4, eRightJustify); // navigation line yPos = 53; @@ -128,6 +142,29 @@ CFuelMixtureScreen::show() return true; } +bool +CFuelMixtureScreen::animate() +{ + if(_animateCount >= 0) { + + int xPos = 20; + int yPos = 5; + int yFuel = 30; + _display.fillRect(xPos, yPos, FanIcon1Info.width, FanIcon1Info.height, BLACK); + _display.fillRect(xPos+5, yFuel, FuelIconInfo.width, FuelIconInfo.height + 4, BLACK); + _drawBitmap(xPos+5, yFuel+_animateCount, FuelIconInfo); + switch(_animateCount) { + case 0: _drawBitmap(xPos, yPos, FanIcon1Info); break; + case 1: _drawBitmap(xPos, yPos, FanIcon2Info); break; + case 2: _drawBitmap(xPos, yPos, FanIcon3Info); break; + case 3: _drawBitmap(xPos, yPos, FanIcon4Info); break; + } + + _animateCount++; + WRAPUPPERLIMIT(_animateCount, 3, 0); + } + return true; +} bool CFuelMixtureScreen::keyHandler(uint8_t event) @@ -143,6 +180,8 @@ CFuelMixtureScreen::keyHandler(uint8_t event) case 2: case 3: case 4: + _animateCount = -1; + _display.clearDisplay(); _rowSel = 5; // enter save confirm mode break; case 5: @@ -204,6 +243,8 @@ CFuelMixtureScreen::keyHandler(uint8_t event) UPPERLIMIT(_rowSel, 4); break; case 5: + _display.clearDisplay(); + _animateCount = -1; _showStoringMessage(); setPumpMin(adjPump[0]); setPumpMax(adjPump[1]); @@ -233,7 +274,10 @@ CFuelMixtureScreen::keyHandler(uint8_t event) _ScreenManager.reqUpdate(); } - + if(_rowSel == 0) { + _load(); // dispose of any changes, re-obtain current settings + } + if(event & keyRepeat) { switch(_rowSel) { case 1: diff --git a/src/Afterburner/src/OLED/FuelMixtureScreen.h b/src/Afterburner/src/OLED/FuelMixtureScreen.h index 44d5ea1..25f563f 100644 --- a/src/Afterburner/src/OLED/FuelMixtureScreen.h +++ b/src/Afterburner/src/OLED/FuelMixtureScreen.h @@ -33,12 +33,15 @@ class CFuelMixtureScreen : public CPasswordScreen { uint16_t adjFan[2]; int _rowSel; int _colSel; + int _animateCount; void _adjustSetting(int dir); void _initUI(); + void _load(); public: CFuelMixtureScreen(C128x64_OLED& display, CScreenManager& mgr); bool show(); + bool animate(); bool keyHandler(uint8_t event); void onSelect(); }; diff --git a/src/Afterburner/src/OLED/GPIOScreen.cpp b/src/Afterburner/src/OLED/GPIOScreen.cpp index f75d904..f160c95 100644 --- a/src/Afterburner/src/OLED/GPIOScreen.cpp +++ b/src/Afterburner/src/OLED/GPIOScreen.cpp @@ -291,12 +291,6 @@ CGPIOInfoScreen::CGPIOInfoScreen(C128x64_OLED& display, CScreenManager& mgr) : C _keyRepeatCount = -1; } -void -CGPIOInfoScreen::onSelect() -{ - CScreenHeader::onSelect(); -} - void CGPIOInfoScreen::_initUI() { @@ -305,7 +299,7 @@ CGPIOInfoScreen::_initUI() bool CGPIOInfoScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); char msg[16]; _display.writeFillRect(49, 18, 30, 12, WHITE); diff --git a/src/Afterburner/src/OLED/GPIOScreen.h b/src/Afterburner/src/OLED/GPIOScreen.h index fdcceb5..7ad720f 100644 --- a/src/Afterburner/src/OLED/GPIOScreen.h +++ b/src/Afterburner/src/OLED/GPIOScreen.h @@ -53,7 +53,6 @@ public: CGPIOInfoScreen(C128x64_OLED& display, CScreenManager& mgr); bool show(); bool keyHandler(uint8_t event); - void onSelect(); }; #endif diff --git a/src/Afterburner/src/OLED/HeaterSettingsScreen.cpp b/src/Afterburner/src/OLED/HeaterSettingsScreen.cpp index 5549e5a..f2c1c50 100644 --- a/src/Afterburner/src/OLED/HeaterSettingsScreen.cpp +++ b/src/Afterburner/src/OLED/HeaterSettingsScreen.cpp @@ -21,10 +21,13 @@ #include "128x64OLED.h" #include "HeaterSettingsScreen.h" +#include "FuelCalScreen.h" #include "KeyPad.h" #include "../Utility/helpers.h" #include "../Utility/macros.h" +#include "../Utility/NVStorage.h" #include "../Protocol/Protocol.h" +#include "fonts/Icons.h" /////////////////////////////////////////////////////////////////////////// // @@ -246,3 +249,280 @@ CHeaterSettingsScreen::_adjust(int dir) break; } } + + + + + + + + + + + + + +CFuelCalScreen::CFuelCalScreen(C128x64_OLED& display, CScreenManager& mgr) : CPasswordScreen(display, mgr) +{ + _initUI(); + _mlPerStroke = 0.02; + _LVC = 115; + _tOfs = 0; +} + +void +CFuelCalScreen::onSelect() +{ + CPasswordScreen::onSelect(); + _initUI(); + _mlPerStroke = NVstore.getHeaterTuning().pumpCal; + _LVC = NVstore.getHeaterTuning().lowVolts; + _tOfs = NVstore.getHeaterTuning().tempOfs; +} + +void +CFuelCalScreen::_initUI() +{ + _rowSel = 0; + _animateCount = 0; +} + +bool +CFuelCalScreen::show() +{ + char msg[20]; + const int col = 90; + + _display.fillRect(0, 50, 128, 14, BLACK); + _display.fillRect(col-border, Line3-border, 128-(col-1), 64-Line3-border, BLACK); + + if(!CPasswordScreen::show()) { // for showing "saving settings" + + if(_rowSel == 4) { + _printInverted(_display.xCentre(), 0, " Saving Settings ", true, eCentreJustify); + _printMenuText(_display.xCentre(), 35, "Press UP to", false, eCentreJustify); + _printMenuText(_display.xCentre(), 43, "confirm save", false, eCentreJustify); + } + else { + if(_animateCount < 0) { + _display.clearDisplay(); + _animateCount = 0; + } + _printInverted(_display.xCentre(), 0, " Special Features ", true, eCentreJustify); + // fuel calibration + int yPos = Line1; + _printMenuText(col, yPos, "mL/stroke : ", false, eRightJustify); + sprintf(msg, "%.03f", _mlPerStroke); + _printMenuText(col, yPos, msg, _rowSel == 1); + // low voltage cutout + yPos = Line2; + _printMenuText(col, yPos, "L.V.C. < ", false, eRightJustify); + if(_LVC) + sprintf(msg, "%.1fV", float(_LVC) * 0.1); + else + strcpy(msg, "OFF"); + _printMenuText(col, yPos, msg, _rowSel == 2); + // temp offset + yPos = Line3; + _printMenuText(col, yPos, "\367C offset : ", false, eRightJustify); + sprintf(msg, "%+.1f", _tOfs); + _printMenuText(col, yPos, msg, _rowSel == 3); + // navigation line + yPos = 53; + int xPos = _display.xCentre(); + + switch(_rowSel) { + case 0: + _printMenuText(xPos, yPos, " \021 Exit \020 ", true, eCentreJustify); + break; + default: + _display.drawFastHLine(0, 52, 128, WHITE); + _printMenuText(xPos, 56, "\030\031Sel \033\032 Adj", false, eCentreJustify); + _printMenuText(xPos, 56, "Save", false, eCentreJustify); + break; + } + } + } + + return true; +} + + +bool +CFuelCalScreen::animate() +{ + if(_animateCount >= 0) { + switch(_animateCount) { + case 0: + _display.fillRect(0, Line3-4, BatteryIconInfo.width, 40, BLACK); + _drawBitmap(6, Line1-3, FuelIconSmallInfo); + _drawBitmap(0, Line2-1 , BatteryIconInfo); + _drawBitmap(5, Line3-4, miniThermoIconInfo); + break; + case 2: + _display.fillRect(6, Line1-3, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip + _drawBitmap(6, Line1-2, FuelIconSmallInfo); // drip fuel + _display.fillRect(BatteryIconInfo.width - 4, Line2+2, 2, 5, BLACK); // deplete battery + _display.fillRect(7, Line3+2, 2, 2, WHITE); // grow thermometer + break; + case 4: + _display.fillRect(6, Line1-2, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip + _drawBitmap(6, Line1-1, FuelIconSmallInfo); // drip fuel + _display.fillRect(BatteryIconInfo.width - 7, Line2+2, 2, 5, BLACK); // deplete battery + _display.fillRect(7, Line3+1, 2, 1, WHITE); // grow thermometer + break; + case 6: + _display.fillRect(6, Line1-1, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip + _drawBitmap(6, Line1, FuelIconSmallInfo); // drip fuel + _display.fillRect(BatteryIconInfo.width - 10, Line2+2, 2, 5, BLACK); // deplete battery + _display.fillRect(7, Line3, 2, 1, WHITE); // grow thermometer + break; + case 8: + _display.fillRect(6, Line1, FuelIconSmallInfo.width, FuelIconSmallInfo.height, BLACK); // scrub prior drip + _drawBitmap(6, Line1+1, FuelIconSmallInfo); // drip fuel + _display.fillRect(BatteryIconInfo.width - 13, Line2+2, 2, 5, BLACK); // deplete battery + _display.fillRect(7, Line3-1, 2, 1, WHITE); // grow thermometer + break; + } + + _animateCount++; + WRAPUPPERLIMIT(_animateCount, 9, 0); + } + + return true; +} + + +bool +CFuelCalScreen::keyHandler(uint8_t event) +{ + sHeaterTuning tuning; + + if(event & keyRepeat) { + if(event & key_Left) { + _adjust(-1); + } + if(event & key_Right) { + _adjust(+1); + } + } + + if(event & keyPressed) { + // press LEFT to select previous screen + if(event & key_Left) { + switch(_rowSel) { + case 0: + _ScreenManager.prevMenu(); + break; + case 1: + case 2: + case 3: + _adjust(-1); + break; + case 4: + _rowSel = 0; // abort save + break; + } + } + // press RIGHT to select next screen + if(event & key_Right) { + switch(_rowSel) { + case 0: + _ScreenManager.nextMenu(); + break; + case 1: + case 2: + case 3: + _adjust(+1); + break; + case 4: + _rowSel = 0; // abort save + break; + } + } + if(event & key_Down) { + _rowSel--; + LOWERLIMIT(_rowSel, 0); + } + // UP press + if(event & key_Up) { + switch(_rowSel) { + case 0: + case 1: + case 2: + case 3: + _rowSel++; + UPPERLIMIT(_rowSel, 3); + break; + case 4: // confirmed save + _display.clearDisplay(); + _animateCount = -1; + _showStoringMessage(); + tuning = NVstore.getHeaterTuning(); + tuning.pumpCal = _mlPerStroke; + tuning.lowVolts = _LVC; + tuning.tempOfs = _tOfs; + NVstore.setHeaterTuning(tuning); + saveNV(); + _rowSel = 0; + break; + } + } + // CENTRE press + if(event & key_Centre) { + switch(_rowSel) { + case 0: + _ScreenManager.selectMenu(CScreenManager::RootMenuLoop); + break; + case 1: + case 2: + case 3: + _animateCount = -1; + _display.clearDisplay(); + _rowSel = 4; + break; + } + } + _ScreenManager.reqUpdate(); + } + + return true; +} + +void +CFuelCalScreen::_adjust(int dir) +{ + switch(_rowSel) { + case 1: + _mlPerStroke += dir * 0.001; + BOUNDSLIMIT(_mlPerStroke, 0.001, 1); + break; + case 2: + if(_LVC == 0) { + if(NVstore.getHeaterTuning().sysVoltage == 120) + _LVC = dir > 0 ? 115 : 0; + else + _LVC = dir > 0 ? 230 : 0; + } + else { + _LVC += dir; + if(NVstore.getHeaterTuning().sysVoltage == 120) { + if(_LVC < 100) + _LVC = 0; + else + UPPERLIMIT(_LVC, 125); + } + else { + if(_LVC < 200) + _LVC = 0; + else + UPPERLIMIT(_LVC, 250); + } + } + break; + case 3: + _tOfs += dir * 0.1; + BOUNDSLIMIT(_tOfs, -10, 10); + break; + } +} diff --git a/src/Afterburner/src/OLED/InheritSettingsScreen.cpp b/src/Afterburner/src/OLED/InheritSettingsScreen.cpp index d68e8f2..be5fb5a 100644 --- a/src/Afterburner/src/OLED/InheritSettingsScreen.cpp +++ b/src/Afterburner/src/OLED/InheritSettingsScreen.cpp @@ -57,7 +57,7 @@ CInheritSettingsScreen::_initUI() bool CInheritSettingsScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); _display.writeFillRect(0, 16, 96, 12, WHITE); _printInverted(3, 18, "Inherit Settings", true); diff --git a/src/Afterburner/src/OLED/PasswordScreen.cpp b/src/Afterburner/src/OLED/PasswordScreen.cpp index 8bb808d..f1d890d 100644 --- a/src/Afterburner/src/OLED/PasswordScreen.cpp +++ b/src/Afterburner/src/OLED/PasswordScreen.cpp @@ -41,6 +41,7 @@ CPasswordScreen::CPasswordScreen(C128x64_OLED& display, CScreenManager& mgr) : C void CPasswordScreen::onSelect() { + CScreenHeader::onSelect(); _initUI(); } diff --git a/src/Afterburner/src/OLED/PrimingScreen.cpp b/src/Afterburner/src/OLED/PrimingScreen.cpp index a6c685b..763bec4 100644 --- a/src/Afterburner/src/OLED/PrimingScreen.cpp +++ b/src/Afterburner/src/OLED/PrimingScreen.cpp @@ -23,6 +23,10 @@ #include "KeyPad.h" #include "../Utility/NVStorage.h" #include "../Protocol/Protocol.h" +#include "fonts/Icons.h" +#include "../RTC/Clock.h" +#include "../Utility/FuelGauge.h" + /////////////////////////////////////////////////////////////////////////// // @@ -42,6 +46,7 @@ CPrimingScreen::CPrimingScreen(C128x64_OLED& display, CScreenManager& mgr) : CSc void CPrimingScreen::onSelect() { + CScreenHeader::onSelect(); _stopPump(); _initUI(); } @@ -58,93 +63,147 @@ CPrimingScreen::_initUI() { _PrimeStop = 0; _PrimeCheck = 0; - _rowSel = 0; + _paramSel = 0; _colSel = 0; } bool CPrimingScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); + _display.fillRect(0, 15, 100, 3, BLACK); + CRect extents; int yPos = 53; // show next/prev menu navigation line - switch(_rowSel) { + switch(_paramSel) { case 0: - _printMenuText(_display.xCentre(), yPos, " \021 \030Edit \020 ", _rowSel == 0, eCentreJustify); + _printMenuText(_display.xCentre(), yPos, " \021 \030Edit \020 ", _paramSel == 0, eCentreJustify); break; case 1: case 2: _display.drawFastHLine(0, 53, 128, WHITE); - _printMenuText(_display.xCentre(), 57, "\030\031 Sel \033\032 Adj", false, eCentreJustify); + _printMenuText(_display.xCentre(), 57, "\030\031 Sel \033\032 Param", false, eCentreJustify); break; case 3: _display.drawFastHLine(0, 53, 128, WHITE); - if(_colSel == 2) { - _printMenuText(_display.xCentre(), 57, "\033\030\031 Stop", false, eCentreJustify); - } - else { - _printMenuText(_display.xCentre(), 57, "\032 Start \031 Sel", false, eCentreJustify); + switch(_colSel) { + case 1: + _printMenuText(_display.xCentre(), 57, "\033\030\031\032 Stop", false, eCentreJustify); + break; + case 0: + _printMenuText(_display.xCentre(), 57, "\030 Start \031 Sel", false, eCentreJustify); + break; + case -1: + _printMenuText(_display.xCentre(), 57, "\030 Sel Zero", false, eCentreJustify); + break; } break; } - yPos = 40; - if(_rowSel == 1) { + int topline = 19; + int midline = 29; + int botline = 35; + + CRect loc; + loc.height = ThermostatDegCIconInfo.height; + loc.width = ThermostatDegCIconInfo.width; + loc.xPos = border; + if(_paramSel == 1) { // follow user desired setting, heater info is laggy - _printMenuText(border, yPos, "Thermostat", _colSel == 0); - _printMenuText(_display.width()-border, yPos, "Fixed Hz", _colSel == 1, eRightJustify); + if(NVstore.getUserSettings().degF) + _drawBitmap(loc.xPos, topline-1, ThermostatDegFIconInfo); + else + _drawBitmap(loc.xPos, topline-1, ThermostatDegCIconInfo); + _drawBitmap(loc.xPos, botline+1, ThermostatHzIconInfo); + loc.yPos = (_colSel == 0) ? topline-1 : botline+1; + _drawMenuSelection(loc, border, radius); } else { // follow actual heater settings - // int col = getHeaterInfo().isThermostat() ? 0 : 1; - int col = getThermostatModeActive() ? 0 : 1; - _printInverted(border, yPos, "Thermostat", col == 0); - _printInverted(_display.width()-border, yPos, "Fixed Hz", col == 1, eRightJustify); + if(getThermostatModeActive()) { + _drawBitmap(loc.xPos, midline, NVstore.getUserSettings().degF ? ThermostatDegFIconInfo : ThermostatDegCIconInfo); + } + else { + _drawBitmap(loc.xPos, midline, ThermostatHzIconInfo); + } } - yPos = 28; - if(_rowSel == 2) { - _printMenuText(border, yPos, "degC", _colSel == 0); - _printMenuText(_display.width()-border, yPos, "degF", _colSel == 1, eRightJustify); + + loc.height = DegCIconInfo.height; + loc.width = DegCIconInfo.width; + loc.xPos = 35; + if(_paramSel == 2) { + loc.yPos = (_colSel == 0) ? topline : botline; + _drawMenuSelection(loc, border, radius); + _drawBitmap(loc.xPos, topline, DegCIconInfo); + _drawBitmap(loc.xPos, botline, DegFIconInfo); } else { - int col = NVstore.getUserSettings().degF ? 1 : 0; - _printInverted(border, yPos, "degC", col == 0); - _printInverted(_display.width()-border, yPos, "degF", col == 1, eRightJustify); + _drawBitmap(loc.xPos, midline, NVstore.getUserSettings().degF ? DegFIconInfo : DegCIconInfo); } // fuel pump priming menu - yPos = 16; - _printMenuText(border, yPos, "Pump"); - if(_rowSel == 3) { - _printMenuText(40, yPos, "OFF", _colSel == 1); - if(_colSel != 2) { - if(!getHeaterInfo().getRunState()) { // prevent option if heater is running - _printMenuText(70, yPos, "ON"); // becomes Hz when actually priming + loc.xPos = 66; + loc.width = BowserIconInfo.width; + loc.height = BowserIconInfo.height; + _drawBitmap(loc.xPos, midline, BowserIconInfo); + loc.xPos = 81; + if(_paramSel == 3) { + _drawBitmap(loc.xPos, topline, FuelIconInfo); + _drawBitmap(loc.xPos, botline, resetIconInfo); + + if(_colSel == -1) { + loc.yPos = botline; + loc.width = resetIconInfo.width; + loc.height = resetIconInfo.height; + _drawMenuSelection(loc, border, radius); + } + + loc.yPos = _colSel == -1 ? botline : topline; + loc.width = FuelIconInfo.width + StartIconInfo.width + 2; + loc.height = FuelIconInfo.height; + if(_colSel == 0) { + _drawMenuSelection(loc, border, radius); + } + loc.xPos += FuelIconInfo.width + 2; + if(_colSel != 1) { // only show start options if not priming already + if(getHeaterInfo().getRunState() == 0) { // prevent priming option if heater is running + _drawBitmap(loc.xPos, topline+2, StartIconInfo); // becomes Hz when actually priming + } + else { + _drawBitmap(loc.xPos, topline, CrossIconInfo); } } - else { + if(_colSel == 1) { float pumpHz = getHeaterInfo().getPump_Actual(); // recognise if heater has stopped pump, after an initial holdoff upon first starting long tDelta = millis() - _PrimeCheck; if(_PrimeCheck && tDelta > 0 && pumpHz < 0.1) { _stopPump(); + _paramSel = _colSel = 0; } // test if time is up, stop priming if so tDelta = millis() - _PrimeStop; if(_PrimeStop && tDelta > 0) { _stopPump(); + _paramSel = _colSel = 0; } if(_PrimeStop) { char msg[16]; sprintf(msg, "%.1fHz", pumpHz); - _printMenuText(70, yPos, msg, true); + _printMenuText(loc.xPos+1+border, topline+3, msg, true); + _ScreenManager.bumpTimeout(); // don't allow menu timeouts whilst priming is active } } } + else { + char msg[16]; + sprintf(msg, "%.02fL", FuelGauge.Used_mL() * 0.001); + _printMenuText(loc.xPos+1, midline+3, msg); + } return true; } @@ -157,47 +216,46 @@ CPrimingScreen::keyHandler(uint8_t event) if(event & keyPressed) { // press LEFT if(event & key_Left) { - switch(_rowSel) { + switch(_paramSel) { case 0: _ScreenManager.prevMenu(); break; - case 1: - _colSel = 0; - setThermostatMode(1); - saveNV(); + default: + _paramSel--; + LOWERLIMIT(_paramSel, 0); + _colSel = 0; + switch(_paramSel) { + case 1: + _colSel = getThermostatModeActive() ? 0 : 1; + break; + case 2: + _colSel = NVstore.getUserSettings().degF ? 1 : 0; + break; + } break; - case 2: - _colSel = 0; - setDegFMode(false); - saveNV(); - break; - case 3: - _colSel = 1; - break; - case 4: break; } } // press RIGHT if(event & key_Right) { - switch(_rowSel) { + switch(_paramSel) { case 0: _ScreenManager.nextMenu(); break; - case 1: - _colSel = 1; - setThermostatMode(0); - saveNV(); + default: + _paramSel++; + UPPERLIMIT(_paramSel, 3); + switch(_paramSel) { + case 3: + _colSel = 0; // select OFF upon entry to priming menu + break; + case 2: + _colSel = NVstore.getUserSettings().degF ? 1 : 0; + break; + case 1: + _colSel = getThermostatModeActive() ? 0 : 1; + break; + } break; - case 2: - _colSel = 1; - setDegFMode(true); - saveNV(); - break; - case 3: - if(!getHeaterInfo().getRunState()) - _colSel = 2; - break; - case 4: break; } } // press UP @@ -205,35 +263,90 @@ CPrimingScreen::keyHandler(uint8_t event) if(hasOEMcontroller()) _reqOEMWarning(); else { - _rowSel++; - UPPERLIMIT(_rowSel, 3); - if(_rowSel == 3) - _colSel = 1; // select OFF upon entry to priming menu - if(_rowSel == 2) - _colSel = NVstore.getUserSettings().degF ? 1 : 0; - if(_rowSel == 1) - _colSel = getThermostatModeActive() ? 0 : 1; + switch(_paramSel) { + case 0: + _paramSel = 1; + _colSel = getThermostatModeActive() ? 0 : 1; + break; + case 1: + _colSel++; + WRAPLIMITS(_colSel, 0, 1); + setThermostatMode(_colSel == 0); + saveNV(); + break; + case 2: + _colSel++; + WRAPLIMITS(_colSel, 0, 1); + setDegFMode(_colSel != 0); + saveNV(); + break; + case 3: + if(_colSel == 1) + _colSel = 0; + else { + _colSel++; + UPPERLIMIT(_colSel, (getHeaterInfo().getRunState() == 0) ? 1 : 0); // prevent priming if heater is running + } + break; + case 4: + break; + } } } // press DOWN if(event & key_Down) { - if(_rowSel == 0) { + if(_paramSel == 0) { _ScreenManager.selectMenu(CScreenManager::UserSettingsLoop, CScreenManager::VersionUI); // force return to main menu } else { - _rowSel--; - LOWERLIMIT(_rowSel, 0); - _colSel = 0; - if(_rowSel == 1) - // _colSel = getHeaterInfo().isThermostat() ? 0 : 1; - _colSel = getThermostatModeActive() ? 0 : 1; - if(_rowSel == 2) - _colSel = NVstore.getUserSettings().degF ? 1 : 0; + switch(_paramSel) { + case 0: + break; + case 1: + _colSel--; + WRAPLIMITS(_colSel, 0, 1); + setThermostatMode(_colSel == 0); + saveNV(); + break; + case 2: + _colSel--; + WRAPLIMITS(_colSel, 0, 1); + setDegFMode(_colSel != 0); + saveNV(); + break; + case 3: + _colSel--; + LOWERLIMIT(_colSel, -1); + break; + case 4: + break; + } + } + } + // press UP + if(event & key_Centre) { + if(_paramSel == 3) { + switch(_colSel) { + case 0: + if(getHeaterInfo().getRunState() == 0) + _colSel = 1; + break; + case 1: + _colSel = 0; + break; + case -1: + FuelGauge.reset(); + _paramSel = _colSel = 0; + break; + } + } + else { + _paramSel = _colSel = 0; } } // check if fuel priming was selected - if(_rowSel == 3 && _colSel == 2) { + if(_paramSel == 3 && _colSel == 1 ) { reqPumpPrime(true); _PrimeStop = millis() + 150000; // allow 2.5 minutes - much the same as the heater itself cuts out at _PrimeCheck = millis() + 3000; // holdoff upon start before testing for heater shutting off pump @@ -253,6 +366,7 @@ CPrimingScreen::_stopPump() reqPumpPrime(false); _PrimeCheck = 0; _PrimeStop = 0; - if(_colSel == 2) - _colSel = 1; + if(_paramSel == 3 && _colSel == 1) { + _colSel = 0; + } } diff --git a/src/Afterburner/src/OLED/PrimingScreen.h b/src/Afterburner/src/OLED/PrimingScreen.h index 27daae3..4798e92 100644 --- a/src/Afterburner/src/OLED/PrimingScreen.h +++ b/src/Afterburner/src/OLED/PrimingScreen.h @@ -31,7 +31,7 @@ class CScreenManager; class CPrimingScreen : public CScreenHeader { unsigned long _PrimeStop; unsigned long _PrimeCheck; - int _rowSel; + int _paramSel; int _colSel; void _stopPump(); void _initUI(); diff --git a/src/Afterburner/src/OLED/Screen.cpp b/src/Afterburner/src/OLED/Screen.cpp index bb6cac7..5bc8d52 100644 --- a/src/Afterburner/src/OLED/Screen.cpp +++ b/src/Afterburner/src/OLED/Screen.cpp @@ -63,6 +63,7 @@ CScreen::show() void CScreen::onSelect() { + _display.clearDisplay(); } void @@ -91,9 +92,19 @@ CScreen::_printMenuText(int x, int y, const char* str, bool selected, eJUSTIFY j void CScreen::_drawMenuSelection(CRect extents, const char* str, int border, int radius) { - _display.getTextExtents(str, extents); - extents.Expand(border); - _display.drawRoundRect(extents.xPos, extents.yPos, extents.width, extents.height, radius, WHITE); + CRect resize(extents); + _display.getTextExtents(str, resize); +// resize.Expand(border); +// _display.drawRoundRect(resize.xPos, resize.yPos, resize.width, resize.height, radius, WHITE); + _drawMenuSelection(resize, border, radius); +} + +void +CScreen::_drawMenuSelection(const CRect& extents, int border, int radius) +{ + CRect resize(extents); + resize.Expand(border); + _display.drawRoundRect(resize.xPos, resize.yPos, resize.width, resize.height, radius, WHITE); } void diff --git a/src/Afterburner/src/OLED/Screen.h b/src/Afterburner/src/OLED/Screen.h index e5a0dad..61c0740 100644 --- a/src/Afterburner/src/OLED/Screen.h +++ b/src/Afterburner/src/OLED/Screen.h @@ -52,6 +52,7 @@ protected: void _printInverted(int x, int y, const char* str, bool selected, eJUSTIFY justify = eLeftJustify); void _adjustExtents(CRect& rect, eJUSTIFY justify, const char* str); void _drawMenuSelection(CRect extents, const char* str, int border = 3, int radius = 4); + void _drawMenuSelection(const CRect& extents, int border, int radius); void _scrollMessage(int y, const char* str, int& charOffset); void _reqOEMWarning(); void _drawBitmap(int x, int y, const BITMAP_INFO& info, uint16_t color = WHITE, uint16_t bg = 0xffff); diff --git a/src/Afterburner/src/OLED/ScreenHeader.cpp b/src/Afterburner/src/OLED/ScreenHeader.cpp index 5f08d95..2d5c602 100644 --- a/src/Afterburner/src/OLED/ScreenHeader.cpp +++ b/src/Afterburner/src/OLED/ScreenHeader.cpp @@ -31,6 +31,8 @@ #include "fonts/Icons.h" #include "fonts/MiniFont.h" #include "../RTC/TimerManager.h" +#include "../Protocol/SmartError.h" +#include "../Utility/DataFilter.h" #define MINIFONT miniFontInfo @@ -65,9 +67,15 @@ CScreenHeader::CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr) : CScreen( } bool -CScreenHeader::show() +CScreenHeader::show(bool erase) { - _display.clearDisplay(); + if(erase) + _display.clearDisplay(); // erase everything + else { + _display.fillRect(0, 17, 128, 47, BLACK); // only erase below the header + _display.fillRect(119, 0, 9, 17, BLACK); // erase top of body thermo + _display.fillRect(0, 0, 9, 17, BLACK); // erase top of ambient thermo + } // standard header items // Bluetooth @@ -75,8 +83,7 @@ CScreenHeader::show() // WiFi icon is updated in animate() - // battery - showBatteryIcon(getBatteryVoltage()); + // Battery is updated in animate // clock showTime(); @@ -104,7 +111,9 @@ CScreenHeader::animate() // animate timer icon, // inserting an update icon if new firmware available from internet web server _animateCount++; - WRAPUPPERLIMIT(_animateCount, 10, 0); + _batteryCount++; + WRAPUPPERLIMIT(_animateCount, 9, 0); + WRAPUPPERLIMIT(_batteryCount, 5, 0); if(isUpdateAvailable(true)) { int xPos = X_TIMER_ICON - 3; int yPos = Y_TIMER_ICON; @@ -122,7 +131,22 @@ CScreenHeader::animate() } } else { + int xPos = X_BATT_ICON; + int yPos = Y_BATT_ICON; showTimers(); + switch(_batteryCount) { + case 3: + if(SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue())) { + showBatteryIcon(getBatteryVoltage(true)); + } + else { + _display.fillRect(xPos, yPos, BatteryIconInfo.width, BatteryIconInfo.height, BLACK); + } + break; + case 0: + showBatteryIcon(getBatteryVoltage(true)); + break; + } } showWifiIcon(); @@ -285,6 +309,7 @@ CScreenHeader::showTime() int xPos = X_WIFI_ICON + WifiIconInfo.width + WifiInIconInfo.width; // rhs of wifi conglomeration if(isWifiAP()) xPos += 4; // add more if an Access Point + _display.fillRect(xPos - 15, Y_CLOCK, 30, arial_8ptFontInfo.nBitsPerLine, BLACK); _printMenuText(X_CLOCK, Y_CLOCK, msg); } } diff --git a/src/Afterburner/src/OLED/ScreenHeader.h b/src/Afterburner/src/OLED/ScreenHeader.h index e263d8c..d9aeed1 100644 --- a/src/Afterburner/src/OLED/ScreenHeader.h +++ b/src/Afterburner/src/OLED/ScreenHeader.h @@ -44,7 +44,8 @@ class CScreenHeader : public CScreen { sScreenholdoff _UpAnnotation; sScreenholdoff _DnAnnotation; bool _colon; - int _animateCount; + uint8_t _animateCount; + uint8_t _batteryCount; protected: void showBTicon(); void showWifiIcon(); @@ -53,7 +54,7 @@ protected: virtual void showTime(); // x location depends upon how many timers are active public: CScreenHeader(C128x64_OLED& disp, CScreenManager& mgr); - bool show(); + bool show(bool erase); bool animate(); }; diff --git a/src/Afterburner/src/OLED/ScreenManager.cpp b/src/Afterburner/src/OLED/ScreenManager.cpp index f71dad6..15d2dc7 100644 --- a/src/Afterburner/src/OLED/ScreenManager.cpp +++ b/src/Afterburner/src/OLED/ScreenManager.cpp @@ -30,6 +30,7 @@ #include "ClockScreen.h" #include "RebootScreen.h" #include "HeaterSettingsScreen.h" +#include "FuelCalScreen.h" #include "SettingsScreen.h" #include "ThermostatModeScreen.h" #include "FontDumpScreen.h" @@ -290,8 +291,9 @@ CScreenManager::begin(bool bNoClock) // create heater tuning screens loop - password protected menuloop.clear(); - menuloop.push_back(new CFuelMixtureScreen(*_pDisplay, *this)); // tuning - menuloop.push_back(new CHeaterSettingsScreen(*_pDisplay, *this)); // tuning + menuloop.push_back(new CFuelMixtureScreen(*_pDisplay, *this)); // mixture tuning + menuloop.push_back(new CHeaterSettingsScreen(*_pDisplay, *this)); // heater system tuning + menuloop.push_back(new CFuelCalScreen(*_pDisplay, *this)); // fuel pump calibration _Screens.push_back(menuloop); // create User Settings screens loop @@ -514,26 +516,30 @@ CScreenManager::prevMenu() void CScreenManager::keyHandler(uint8_t event) { - long dimTime = NVstore.getUserSettings().dimTime; - if(_bDimmed) { if(event & keyReleased) { _dim(false); - _DimTime_ms = (millis() + abs(dimTime)) | 1; - _MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1; + bumpTimeout(); } return; // initial press when dimmed is always thrown away } -// _dim(false); - _DimTime_ms = (millis() + abs(dimTime)) | 1; - _MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1; + bumpTimeout(); // call key handler for active screen if(_menu >= 0) _Screens[_menu][_subMenu]->keyHandler(event); } +void +CScreenManager::bumpTimeout() +{ + long dimTime = NVstore.getUserSettings().dimTime; + _DimTime_ms = (millis() + abs(dimTime)) | 1; + _MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1; +} + + void CScreenManager::selectMenu(eUIMenuSets menuSet, int specific) { @@ -572,11 +578,8 @@ void CScreenManager::showOTAMessage(int percent, eOTAmodes updateType) { static int prevPercent = -1; - static long prevTime = millis(); - long tDelta = millis() - prevTime; if(percent != prevPercent/* && tDelta > 500*/) { - prevTime = millis(); _pDisplay->clearDisplay(); _pDisplay->setCursor(64,22); switch(updateType) { diff --git a/src/Afterburner/src/OLED/ScreenManager.h b/src/Afterburner/src/OLED/ScreenManager.h index f9d667f..1e0c5c4 100644 --- a/src/Afterburner/src/OLED/ScreenManager.h +++ b/src/Afterburner/src/OLED/ScreenManager.h @@ -49,7 +49,7 @@ public: enum eUIRootMenus { DetailedControlUI, BasicControlUI, ClockUI, ModeUI, CommsUI, GPIOInfoUI, SettingsUI }; enum eUITimerMenus { TimerOverviewUI, Timer1UI, Timer2UI, Timer3UI, Timer4UI, Timer5UI, Timer6UI, Timer7UI, Timer8UI, Timer9UI, Timer10UI, Timer11UI, Timer12UI, Timer13UI, Timer14UI }; - enum eUITuningMenus { MixtureUI, HeaterSettingsUI }; + enum eUITuningMenus { MixtureUI, HeaterSettingsUI, FuelCalUI }; enum eUIUserSettingsMenus { GPIOUI, ExThermostatUI, VersionUI, HomeMenuUI, TimeIntervalsUI }; enum eUIBranchMenus { SetClockUI, InheritSettingsUI, FontDumpUI }; public: @@ -66,6 +66,7 @@ public: void selectMenu(eUIMenuSets menuset, int specific = -1); // use to select loop menus, including the root or branches void showRebootMsg(const char* content[2], long delayTime); void showOTAMessage(int percent, eOTAmodes updateType); + void bumpTimeout(); }; #endif // __SCREEN_MANAGER_H__ diff --git a/src/Afterburner/src/OLED/SetClockScreen.cpp b/src/Afterburner/src/OLED/SetClockScreen.cpp index 7e6280e..9275d2b 100644 --- a/src/Afterburner/src/OLED/SetClockScreen.cpp +++ b/src/Afterburner/src/OLED/SetClockScreen.cpp @@ -43,6 +43,7 @@ CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : C void CSetClockScreen::onSelect() { + CScreenHeader::onSelect(); _initUI(); } @@ -67,7 +68,7 @@ CSetClockScreen::show() if(deltaT >= 0) { _nextT += 1000; - CScreenHeader::show(); + CScreenHeader::show(false); char str[16]; int xPos, yPos; diff --git a/src/Afterburner/src/OLED/SetTimerScreen.cpp b/src/Afterburner/src/OLED/SetTimerScreen.cpp index 482f36b..3dcf636 100644 --- a/src/Afterburner/src/OLED/SetTimerScreen.cpp +++ b/src/Afterburner/src/OLED/SetTimerScreen.cpp @@ -47,6 +47,7 @@ CSetTimerScreen::CSetTimerScreen(C128x64_OLED& display, CScreenManager& mgr, int void CSetTimerScreen::onSelect() { + CScreenHeader::onSelect(); _initUI(); NVstore.getTimerInfo(_timerID, _timerInfo); } @@ -62,7 +63,7 @@ CSetTimerScreen::_initUI() bool CSetTimerScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); char str[20]; int xPos, yPos; diff --git a/src/Afterburner/src/OLED/SettingsScreen.cpp b/src/Afterburner/src/OLED/SettingsScreen.cpp index 34a0ab9..fc65b93 100644 --- a/src/Afterburner/src/OLED/SettingsScreen.cpp +++ b/src/Afterburner/src/OLED/SettingsScreen.cpp @@ -66,10 +66,10 @@ CSettingsScreen::show() { char str[16]; - CScreenHeader::show(); + CScreenHeader::show(false); - _display.writeFillRect(0, 16, 96, 12, WHITE); - _printInverted(3, 18, "Heater Settings", true); + _display.writeFillRect(0, 16, 84, 12, WHITE); + _printInverted(3, 18, "Heater Tuning", true); if(!CPasswordScreen::show()) { diff --git a/src/Afterburner/src/OLED/WiFiScreen.cpp b/src/Afterburner/src/OLED/WiFiScreen.cpp index 083f4e4..c47fc3e 100644 --- a/src/Afterburner/src/OLED/WiFiScreen.cpp +++ b/src/Afterburner/src/OLED/WiFiScreen.cpp @@ -48,6 +48,7 @@ CWiFiScreen::CWiFiScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreenHe void CWiFiScreen::onSelect() { + CScreenHeader::onSelect(); _initUI(); } @@ -87,7 +88,7 @@ CWiFiScreen::_initUI() bool CWiFiScreen::show() { - CScreenHeader::show(); + CScreenHeader::show(false); int yPos = 18; diff --git a/src/Afterburner/src/OLED/fonts/Arial.c b/src/Afterburner/src/OLED/fonts/Arial.c index d06e8fa..48f80f5 100644 --- a/src/Afterburner/src/OLED/fonts/Arial.c +++ b/src/Afterburner/src/OLED/fonts/Arial.c @@ -662,11 +662,19 @@ const uint8_t PROGMEM arial_8ptBitmaps [] = 0x01, 0x00, // # // @870 '~' (5 pixels wide) - 0x03, 0x00, // ## + 0x01, 0x00, // # 0x02, 0x00, // # 0x03, 0x00, // ## 0x01, 0x00, // # 0x02, 0x00, // # + + // @880 '1' (5 pixels wide) + 0x00, 0x00, // + 0x04, 0x00, // # + 0x08, 0x00, // # + 0x1F, 0xE0, // ######## + 0x00, 0x00, // + }; // Character descriptors for Arial 8pt @@ -690,7 +698,7 @@ const FONT_CHAR_INFO PROGMEM arial_8ptDescriptors[] = {1, 14, 100}, // '.' {3, 14, 102}, // '/' {5, 14, 108}, // '0' - {3, 14, 118}, // '1' + {5, 14, 880}, // '1' {5, 14, 124}, // '2' {5, 14, 134}, // '3' {5, 14, 144}, // '4' diff --git a/src/Afterburner/src/OLED/fonts/Icons.cpp b/src/Afterburner/src/OLED/fonts/Icons.cpp index b852154..6ebf1aa 100644 --- a/src/Afterburner/src/OLED/fonts/Icons.cpp +++ b/src/Afterburner/src/OLED/fonts/Icons.cpp @@ -375,6 +375,25 @@ const uint8_t FuelIcon [] PROGMEM = { }; const BITMAP_INFO FuelIconInfo(7, 12, FuelIcon); +// +// Image data for FuelIconSmall +// + +const uint8_t PROGMEM FuelIconSmall[] = +{ + 0x20, // # + 0x20, // # + 0x70, // ### + 0x70, // ### + 0xF8, // ##### + 0xF8, // ##### + 0xF8, // ##### + 0xF8, // ##### + 0x70, // ### +}; +const BITMAP_INFO FuelIconSmallInfo(5, 9, FuelIconSmall); + + // 'Target', 13x13px const uint8_t TargetIcon [] PROGMEM = { 0x0f, 0x80, // ##### @@ -578,6 +597,7 @@ const uint8_t PROGMEM CrossIcon[] = 0x50, // # # 0x88, // # # }; +const BITMAP_INFO CrossIconInfo(5, 5, CrossIcon); const uint8_t PROGMEM TickIcon[] = { @@ -961,7 +981,146 @@ const uint8_t PROGMEM bowserIcon[] = 0x7E, 0x00, // ###### 0xFF, 0x00, // ######## }; - const BITMAP_INFO BowserIconInfo(10, 12, bowserIcon); +// +// Image data for degC +// +const uint8_t PROGMEM degCIcon[] = +{ + 0x07, 0x00, 0x00, // ### + 0x18, 0xC1, 0x8E, // ## ## ## ### + 0x27, 0x22, 0x51, // # ### # # # # # + 0x4F, 0x92, 0x50, // # ##### # # # # + 0x8F, 0x89, 0x90, // # ##### # ## # + 0x4F, 0x90, 0x10, // # ##### # # + 0x27, 0x20, 0x11, // # ### # # # + 0x18, 0xC0, 0x0E, // ## ## ### + 0x07, 0x00, 0x00, // ### +}; +const BITMAP_INFO DegCIconInfo(23, 9, degCIcon); + +const uint8_t PROGMEM degFIcon[] = +{ + 0x07, 0x00, 0x00, // ### + 0x18, 0xC1, 0x9E, // ## ## ## #### + 0x27, 0x22, 0x50, // # ### # # # # + 0x4F, 0x92, 0x50, // # ##### # # # # + 0x8F, 0x89, 0x9C, // # ##### # ## ### + 0x4F, 0x90, 0x10, // # ##### # # + 0x27, 0x20, 0x10, // # ### # # + 0x18, 0xC0, 0x10, // ## ## # + 0x07, 0x00, 0x00, // ### +}; +const BITMAP_INFO DegFIconInfo(23, 9, degFIcon); + +// +// Image data for thermostatC +// + +const uint8_t PROGMEM thermostatDegCIcon[] = +{ + 0x7F, 0xFF, 0xFC, // ##################### + 0xFF, 0xFF, 0xFE, // ####################### + 0xE0, 0x07, 0xDE, // ### ##### #### + 0xCC, 0x73, 0x8E, // ## ## ### ### ### + 0xD2, 0x8B, 0x06, // ## # # # # ## ## + 0xD2, 0x83, 0xFE, // ## # # # ######### + 0xCC, 0x83, 0xFE, // ## ## # ######### + 0xC0, 0x83, 0x06, // ## # ## ## + 0xC0, 0x8B, 0x8E, // ## # # ### ### + 0xC0, 0x73, 0xDE, // ## ### #### #### + 0xE0, 0x07, 0xFE, // ### ########## + 0xFF, 0xFF, 0xFE, // ####################### + 0x7F, 0xFF, 0xFC, // ##################### +}; +const BITMAP_INFO ThermostatDegCIconInfo(23, 13, thermostatDegCIcon); + + +// +// Image data for thermostatDegF +// + +const uint8_t PROGMEM thermostatDegFIcon[] = +{ + 0x7F, 0xFF, 0xFC, // ##################### + 0xFF, 0xFF, 0xFE, // ####################### + 0xE0, 0x07, 0xDE, // ### ##### #### + 0xCC, 0xF3, 0x8E, // ## ## #### ### ### + 0xD2, 0x83, 0x06, // ## # # # ## ## + 0xD2, 0x83, 0xFE, // ## # # # ######### + 0xCC, 0xE3, 0xFE, // ## ## ### ######### + 0xC0, 0x83, 0x06, // ## # ## ## + 0xC0, 0x83, 0x8E, // ## # ### ### + 0xC0, 0x83, 0xDE, // ## # #### #### + 0xE0, 0x07, 0xFE, // ### ########## + 0xFF, 0xFF, 0xFE, // ####################### + 0x7F, 0xFF, 0xFC, // ##################### +}; +const BITMAP_INFO ThermostatDegFIconInfo(23, 13, thermostatDegFIcon); + +// +// Image data for thermostatHz +// + +const uint8_t PROGMEM thermostatHzIcon[] = +{ + 0x7F, 0xFF, 0xFC, // ##################### + 0xFF, 0xFF, 0xFE, // ####################### + 0xE0, 0x07, 0xDE, // ### ##### #### + 0xC9, 0x03, 0x8E, // ## # # ### ### + 0xC9, 0x03, 0x06, // ## # # ## ## + 0xC9, 0x73, 0xFE, // ## # # ### ######### + 0xCF, 0x13, 0xFE, // ## #### # ######### + 0xC9, 0x23, 0x06, // ## # # # ## ## + 0xC9, 0x43, 0x8E, // ## # # # ### ### + 0xC9, 0x73, 0xDE, // ## # # ### #### #### + 0xE0, 0x07, 0xFE, // ### ########## + 0xFF, 0xFF, 0xFE, // ####################### + 0x7F, 0xFF, 0xFC, // ##################### +}; +const BITMAP_INFO ThermostatHzIconInfo(23, 13, thermostatHzIcon); + + +// +// Image data for reset +// + +const uint8_t PROGMEM resetIcon[] = +{ + 0x9E, 0x00, 0x00, // # #### + 0xA1, 0x07, 0x00, // # # # ### + 0xC0, 0x88, 0x80, // ## # # # + 0xF0, 0x49, 0x80, // #### # # ## + 0x00, 0x4A, 0x80, // # # # # + 0x00, 0x4C, 0x80, // # ## # + 0x00, 0x48, 0x80, // # # # + 0x40, 0x87, 0x00, // # # ### + 0x21, 0x00, 0x00, // # # + 0x1E, 0x00, 0x00, // #### +}; +const BITMAP_INFO resetIconInfo(17, 10, resetIcon); + +// +// Image data for miniThermo +// + +const uint8_t PROGMEM miniThermoIcon[] = +{ + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x78, // #### + 0xFC, // ###### + 0xFC, // ###### + 0xFC, // ###### + 0x78, // #### +}; +const BITMAP_INFO miniThermoIconInfo(6, 13, miniThermoIcon); + diff --git a/src/Afterburner/src/OLED/fonts/Icons.h b/src/Afterburner/src/OLED/fonts/Icons.h index 5d7fc44..a7a1c6c 100644 --- a/src/Afterburner/src/OLED/fonts/Icons.h +++ b/src/Afterburner/src/OLED/fonts/Icons.h @@ -65,8 +65,8 @@ extern const BITMAP_INFO FanIcon4Info; // 'FuelIcon', 7x12px extern const BITMAP_INFO FuelIconInfo; +extern const BITMAP_INFO FuelIconSmallInfo; -// 'Target', 13x13px extern const BITMAP_INFO TargetIconInfo; extern const BITMAP_INFO RepeatIconInfo; @@ -77,6 +77,8 @@ extern const BITMAP_INFO LargeTimerIconInfo; extern const BITMAP_INFO VerticalRepeatIconInfo; extern const BITMAP_INFO CrossLgIconInfo; +extern const BITMAP_INFO CrossIconInfo +; // Bitmap for open extern const BITMAP_INFO OpenIconInfo; @@ -129,4 +131,11 @@ extern const BITMAP_INFO UpdateIconInfo; // Bitmap sizes for www extern const BITMAP_INFO WWWIconInfo; -extern const BITMAP_INFO BowserIconInfo; \ No newline at end of file +extern const BITMAP_INFO BowserIconInfo; +extern const BITMAP_INFO DegCIconInfo; +extern const BITMAP_INFO DegFIconInfo; +extern const BITMAP_INFO ThermostatDegCIconInfo; +extern const BITMAP_INFO ThermostatDegFIconInfo; +extern const BITMAP_INFO ThermostatHzIconInfo; +extern const BITMAP_INFO resetIconInfo; +extern const BITMAP_INFO miniThermoIconInfo; diff --git a/src/Afterburner/src/Protocol/Protocol.cpp b/src/Afterburner/src/Protocol/Protocol.cpp index b97bb2f..5426403 100644 --- a/src/Afterburner/src/Protocol/Protocol.cpp +++ b/src/Afterburner/src/Protocol/Protocol.cpp @@ -368,7 +368,7 @@ const char* Errstates [] PROGMEM = { "Flame out", // [9] E-08 "Temp sense", // [10] E-09 "Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5 - "Failed 1st ignition attempt", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3 + "Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3 "Unknown error?" // mystery code! }; @@ -385,7 +385,7 @@ const char* ErrstatesEx [] PROGMEM = { "E-08: Flame out", // [9] E-08 "E-09: Temp sense", // [10] E-09 "E-10: Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5 - "E-11: Failed 1st ignition attempt", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3 + "E-11: Failed 1st ignite", // [12] E-11 SmartError manufactured state - sensing runstate 2 -> 3 "Unknown error?" // mystery code! }; diff --git a/src/Afterburner/src/Protocol/SmartError.cpp b/src/Afterburner/src/Protocol/SmartError.cpp index 57e6174..723cd20 100644 --- a/src/Afterburner/src/Protocol/SmartError.cpp +++ b/src/Afterburner/src/Protocol/SmartError.cpp @@ -2,7 +2,7 @@ * This file is part of the "bluetoothheater" distribution * (https://gitlab.com/mrjones.id.au/bluetoothheater) * - * Copyright (C) 2018 Ray Jones + * Copyright (C) 2019 Ray Jones * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,10 @@ #include "SmartError.h" #include "TxManage.h" +#include "../Utility/helpers.h" +#include "../Utility/macros.h" +#include "../Utility/NVStorage.h" +#include "../Utility/DataFilter.h" CSmartError::CSmartError() { @@ -83,12 +87,12 @@ CSmartError::monitor(uint8_t newRunState) else if(newRunState > 5) { // transitioned from preheat to post glow // - second ignition attempt failed, heater is shutting down - m_Error = 11; + m_Error = 11; // +1 over displayed error code } else if(newRunState == 3) { // transitioned from preheat to retry // - first ignition attempt failed, heater will retry - m_Error = 12; + m_Error = 12; // +1 over displayed error code } } } @@ -106,7 +110,24 @@ CSmartError::monitor(uint8_t newRunState) } } - m_prevRunState = newRunState; + m_prevRunState = newRunState; +} + +bool +CSmartError::checkVolts(float ipVolts, float glowI) +{ + // check for low voltage + // values here are x10 integers + if(NVstore.getHeaterTuning().lowVolts) { // only if enabled + float cableComp = glowI * 0.1; // allow 1V drop for 10A current (bit naive but better than no compensation) + float Thresh = NVstore.getHeaterTuning().getLVC() - cableComp; // NVstore + if(ipVolts < Thresh) { + m_Error = 2; // +1 over displayed error code + requestOff(); + return false; + } + } + return true; } // return our smart error, if it exists, as the registered code diff --git a/src/Afterburner/src/Protocol/SmartError.h b/src/Afterburner/src/Protocol/SmartError.h index 90ad624..0c91d09 100644 --- a/src/Afterburner/src/Protocol/SmartError.h +++ b/src/Afterburner/src/Protocol/SmartError.h @@ -31,6 +31,7 @@ public: void inhibit(); void monitor(const CProtocol& heaterFrame); void monitor(uint8_t runstate); + bool checkVolts(float volts, float plugI); uint8_t getError(); }; diff --git a/src/Afterburner/src/RTC/Clock.cpp b/src/Afterburner/src/RTC/Clock.cpp index 1e1aeb7..0951f25 100644 --- a/src/Afterburner/src/RTC/Clock.cpp +++ b/src/Afterburner/src/RTC/Clock.cpp @@ -187,7 +187,7 @@ RTC_DS3231Ex::readData(uint8_t* pData, int len, int ofs) { Wire.endTransmission(); } -bool +void RTC_DS3231Ex::resetLostPower() { Wire.beginTransmission(DS3231_ADDRESS); diff --git a/src/Afterburner/src/RTC/Clock.h b/src/Afterburner/src/RTC/Clock.h index f79b7c6..3c9b0a5 100644 --- a/src/Afterburner/src/RTC/Clock.h +++ b/src/Afterburner/src/RTC/Clock.h @@ -30,7 +30,7 @@ class RTC_DS3231Ex : public RTC_DS3231 { public: void writeData(uint8_t* pData, int len, int ofs=0); void readData(uint8_t* pData, int len, int ofs=0); - bool resetLostPower(); + void resetLostPower(); }; diff --git a/src/Afterburner/src/Utility/BTC_JSON.cpp b/src/Afterburner/src/Utility/BTC_JSON.cpp index a3e4a90..5a6ed56 100644 --- a/src/Afterburner/src/Utility/BTC_JSON.cpp +++ b/src/Afterburner/src/Utility/BTC_JSON.cpp @@ -277,7 +277,29 @@ void interpretJsonCommand(char* pLine) sHeaterTuning ht = NVstore.getHeaterTuning(); ht.pumpCal = fCal; NVstore.setHeaterTuning(ht); - NVstore.save(); + } + } + else if(strcmp("TempOffset", it->key) == 0) { + float fCal = it->value.as(); + if(INBOUNDS(fCal, -10.0, +10.0)) { + sHeaterTuning ht = NVstore.getHeaterTuning(); + ht.tempOfs = fCal; + NVstore.setHeaterTuning(ht); + } + } + else if(strcmp("LowVoltCutout", it->key) == 0) { + float fCal = it->value.as(); + if(fCal != 0) { + bool bOK = false; + if(NVstore.getHeaterTuning().sysVoltage == 120) + bOK = INBOUNDS(fCal, 10.0, 12.5); + else + bOK = INBOUNDS(fCal, 20.0, 25.0); + if(bOK) { + sHeaterTuning ht = NVstore.getHeaterTuning(); + ht.lowVolts = uint8_t(fCal * 10); + NVstore.setHeaterTuning(ht); + } } } } @@ -325,7 +347,7 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len) bSend |= moderator.addJson("FanRPM", getFanSpeed(), root ); bSend |= moderator.addJson("FanVoltage", getHeaterInfo().getFan_Voltage(), root ); bSend |= moderator.addJson("FanSensor", getHeaterInfo().getFan_Sensor(), root ); - bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(), root ); + bSend |= moderator.addJson("InputVoltage", getBatteryVoltage(false), root ); bSend |= moderator.addJson("SystemVoltage", getHeaterInfo().getSystemVoltage(), root ); bSend |= moderator.addJson("GlowVoltage", getGlowVolts(), root ); bSend |= moderator.addJson("GlowCurrent", getGlowCurrent(), root ); @@ -354,9 +376,11 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len) bSend |= moderator.addJson("ThermostatUndertemp", NVstore.getUserSettings().cyclic.Start, root); 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("PumpCount", RTC_Store.getFuelGauge(), root); // running count of pump strokes - bSend |= moderator.addJson("PumpCal", NVstore.getHeaterTuning().pumpCal, root); // ml/stroke + bSend |= moderator.addJson("CyclicOn", NVstore.getUserSettings().cyclic.Start, root); // threshold of under temp for cyclic mode + 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("TempOffset", NVstore.getHeaterTuning().tempOfs, root); // degC offset + bSend |= moderator.addJson("LowVoltCutout", NVstore.getHeaterTuning().getLVC(), root); // low volatge cutout if(bSend) { root.printTo(opStr, len); diff --git a/src/Afterburner/src/Utility/DataFilter.h b/src/Afterburner/src/Utility/DataFilter.h index 218e4c0..b25e31a 100644 --- a/src/Afterburner/src/Utility/DataFilter.h +++ b/src/Afterburner/src/Utility/DataFilter.h @@ -2,7 +2,7 @@ * This file is part of the "bluetoothheater" distribution * (https://gitlab.com/mrjones.id.au/bluetoothheater) * - * Copyright (C) 2018 Ray Jones + * Copyright (C) 2019 Ray Jones * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +44,11 @@ struct sFilteredData { CExpMean GlowAmps; CExpMean Fan; CExpMean AmbientTemp; + CExpMean FastipVolts; + CExpMean FastGlowAmps; }; +extern sFilteredData FilteredSamples; + + #endif diff --git a/src/Afterburner/src/Utility/FuelGauge.cpp b/src/Afterburner/src/Utility/FuelGauge.cpp index 0434632..2d985ba 100644 --- a/src/Afterburner/src/Utility/FuelGauge.cpp +++ b/src/Afterburner/src/Utility/FuelGauge.cpp @@ -43,6 +43,13 @@ CFuelGauge::init(float fuelUsed) _lastStoredVal = _pumpStrokes; } +void +CFuelGauge::reset() +{ + _pumpStrokes = 0; + _lastStoredVal = _pumpStrokes; + RTC_Store.setFuelGauge(_pumpStrokes); // uses RTC registers to store this +} void CFuelGauge::Integrate(float Hz) @@ -66,5 +73,11 @@ CFuelGauge::Integrate(float Hz) float CFuelGauge::Used_mL() { - return _pumpStrokes * _pumpCal; // strokes * mL / stroke + return _pumpStrokes * _pumpCal; // strokes * millilitre / stroke +} + +float +CFuelGauge::Used_strokes() +{ + return _pumpStrokes; } diff --git a/src/Afterburner/src/Utility/FuelGauge.h b/src/Afterburner/src/Utility/FuelGauge.h index 912dc1d..88c0af9 100644 --- a/src/Afterburner/src/Utility/FuelGauge.h +++ b/src/Afterburner/src/Utility/FuelGauge.h @@ -32,9 +32,12 @@ class CFuelGauge { public: CFuelGauge(); void init(float fuelUsed = 0); + void reset(); void Integrate(float Hz); float Used_mL(); + float Used_strokes(); }; +extern CFuelGauge FuelGauge; #endif diff --git a/src/Afterburner/src/Utility/NVStorage.cpp b/src/Afterburner/src/Utility/NVStorage.cpp index bc508c6..c3f6925 100644 --- a/src/Afterburner/src/Utility/NVStorage.cpp +++ b/src/Afterburner/src/Utility/NVStorage.cpp @@ -97,6 +97,11 @@ sHeaterTuning::setSysVoltage(float fVal) } } +float +sHeaterTuning::getLVC() const +{ + return lowVolts * 0.1; +} void CHeaterStorage::getTimerInfo(int idx, sTimer& timerInfo) @@ -235,7 +240,12 @@ sHeaterTuning::load() validatedLoad("systemVoltage", sysVoltage, 120, u8Match2, 120, 240); validatedLoad("fanSensor", fanSensor, 1, u8inBounds, 1, 2); validatedLoad("glowDrive", glowDrive, 5, u8inBounds, 1, 6); + if(sysVoltage == 120) + validatedLoad("lowVolts", lowVolts, 115, u8inBounds, 100, 125); + else + validatedLoad("lowVolts", lowVolts, 230, u8inBounds, 200, 250); validatedLoad("pumpCal", pumpCal, 0.02, 0.001, 1); + validatedLoad("tempOfs", tempOfs, 0.0, -10.0, +10.0); preferences.end(); } @@ -252,7 +262,9 @@ sHeaterTuning::save() preferences.putUChar("systemVoltage", sysVoltage); preferences.putUChar("fanSensor", fanSensor); preferences.putUChar("glowDrive", glowDrive); + preferences.putUChar("lowVolts", lowVolts); preferences.putFloat("pumpCal", pumpCal); + preferences.putFloat("tempOfs", tempOfs); preferences.end(); } diff --git a/src/Afterburner/src/Utility/NVStorage.h b/src/Afterburner/src/Utility/NVStorage.h index 37bd258..f68bdcc 100644 --- a/src/Afterburner/src/Utility/NVStorage.h +++ b/src/Afterburner/src/Utility/NVStorage.h @@ -37,10 +37,12 @@ struct sHeaterTuning : public CESP32_NVStorage { uint8_t Pmax; uint16_t Fmin; uint16_t Fmax; - uint8_t sysVoltage; + uint8_t sysVoltage; // x10 uint8_t fanSensor; uint8_t glowDrive; + uint8_t lowVolts; // x10 float pumpCal; + float tempOfs; bool valid() { bool retval = true; @@ -52,6 +54,11 @@ struct sHeaterTuning : public CESP32_NVStorage { retval &= fanSensor == 1 || fanSensor == 2; retval &= INBOUNDS(glowDrive, 1, 6); retval &= INBOUNDS(pumpCal, 0.001, 1.0); + if(sysVoltage == 120) + retval &= INBOUNDS(lowVolts, 100, 125); + else + retval &= INBOUNDS(lowVolts, 200, 250); + retval &= INBOUNDS(tempOfs, -10, +10); return retval; }; void init() { @@ -63,6 +70,8 @@ struct sHeaterTuning : public CESP32_NVStorage { fanSensor = 1; glowDrive = 5; pumpCal = 0.02; + lowVolts = 115; + tempOfs = 0; }; void load(); void save(); @@ -75,6 +84,8 @@ struct sHeaterTuning : public CESP32_NVStorage { fanSensor = rhs.fanSensor; glowDrive = rhs.glowDrive; pumpCal = rhs.pumpCal; + lowVolts = rhs.lowVolts; + tempOfs = rhs.tempOfs; return *this; } float getPmin() const; @@ -82,6 +93,7 @@ struct sHeaterTuning : public CESP32_NVStorage { void setPmin(float val); void setPmax(float val); void setSysVoltage(float val); + float getLVC() const; }; struct sHomeMenuActions { @@ -108,6 +120,11 @@ struct sHomeMenuActions { } }; +struct sHourMeter { + unsigned long RunTime; + unsigned long GlowTime; +}; + struct sCyclicThermostat { int8_t Stop; int8_t Start; diff --git a/src/Afterburner/src/Utility/UtilClasses.h b/src/Afterburner/src/Utility/UtilClasses.h index 6eb1da1..de92e1b 100644 --- a/src/Afterburner/src/Utility/UtilClasses.h +++ b/src/Afterburner/src/Utility/UtilClasses.h @@ -152,6 +152,12 @@ struct CRect { CRect() { xPos = yPos = width = height = 0; } + CRect(const CRect& a) { + xPos = a.xPos; + yPos = a.yPos; + width = a.width; + height = a.height; + } void Expand(int val) { xPos -= val; yPos -= val; diff --git a/src/Afterburner/src/Utility/helpers.h b/src/Afterburner/src/Utility/helpers.h index 95f07a7..11265f7 100644 --- a/src/Afterburner/src/Utility/helpers.h +++ b/src/Afterburner/src/Utility/helpers.h @@ -78,19 +78,12 @@ extern void setUploadSize(long val); extern void getGPIOinfo(sGPIO& info); extern void simulateGPIOin(uint8_t newKey); extern void setDegFMode(bool state); -extern float getBatteryVoltage(); +extern float getBatteryVoltage(bool fast); extern float getGlowVolts(); extern float getGlowCurrent(); extern float getFanSpeed(); extern int sysUptime(); -/* extern void setFuelGauge_RTC(float val); -extern void getFuelGauge_RTC(float& val); -extern void setDesiredTemp_RTC(uint8_t val); -extern void getDesiredTemp_RTC(uint8_t& val); -extern void setDesiredPump_RTC(uint8_t val); -extern void getDesiredPump_RTC(uint8_t& val);*/ - extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal); diff --git a/src/Afterburner/src/Utility/macros.h b/src/Afterburner/src/Utility/macros.h index 8f1701a..04d9ee8 100644 --- a/src/Afterburner/src/Utility/macros.h +++ b/src/Afterburner/src/Utility/macros.h @@ -2,7 +2,7 @@ * This file is part of the "bluetoothheater" distribution * (https://gitlab.com/mrjones.id.au/bluetoothheater) * - * Copyright (C) 2018 Ray Jones + * Copyright (C) 2019 Ray Jones * Copyright (C) 2018 James Clark * * This program is free software: you can redistribute it and/or modify @@ -23,12 +23,12 @@ #ifndef __MACROS_H__ #define __MACROS_H__ -#define LOWERLIMIT(A, B) if((A) < (B)) (A) = (B) -#define UPPERLIMIT(A, B) if((A) > (B)) (A) = (B) -#define WRAPUPPERLIMIT(A, B, C) if((A) > (B)) (A) = (C) -#define WRAPLOWERLIMIT(A, B, C) if((A) < (B)) (A) = (C) -#define WRAPLIMITS(A, B, C) if((A) < (B)) (A) = (C) ; if((A) > (C)) (A) = (B) +#define LOWERLIMIT(A, B) { if((A) < (B)) (A) = (B); } +#define UPPERLIMIT(A, B) { if((A) > (B)) (A) = (B); } +#define WRAPUPPERLIMIT(A, B, C) { if((A) > (B)) (A) = (C); } +#define WRAPLOWERLIMIT(A, B, C) { if((A) < (B)) (A) = (C); } +#define WRAPLIMITS(A, B, C) { if((A) < (B)) (A) = (C) ; if((A) > (C)) (A) = (B); } #define INBOUNDS(TST, MIN, MAX) (((TST) >= (MIN)) && ((TST) <= (MAX))) -#define BOUNDSLIMIT(A, B, C) if((A) < (B)) (A) = (B); if((A) > (C)) (A) = (C) +#define BOUNDSLIMIT(A, B, C) { if((A) < (B)) (A) = (B); if((A) > (C)) (A) = (C); } #endif \ No newline at end of file