Added Stop/Start thermostat mode

FuelUsage & FuelRate added as basic MQTT status topics
This commit is contained in:
Ray Jones 2020-05-13 10:37:31 +10:00
parent 24d8a4a7f1
commit 775f235ba8
22 changed files with 292 additions and 103 deletions

View File

@ -32,6 +32,7 @@ extra_scripts = post:add_CRC.py
; replace shitty Arduino millis with a linear time version ; replace shitty Arduino millis with a linear time version
build_flags = build_flags =
-Wl,--wrap,millis -Wl,--wrap,millis
-DHTTPS_LOGLEVEL=2
debug_tool = esp-prog debug_tool = esp-prog
;upload_protocol = esp-prog ;upload_protocol = esp-prog
debug_init_break = debug_init_break =

View File

@ -134,7 +134,7 @@
const int FirmwareRevision = 32; const int FirmwareRevision = 32;
const int FirmwareSubRevision = 0; const int FirmwareSubRevision = 0;
const int FirmwareMinorRevision = 6; const int FirmwareMinorRevision = 6;
const char* FirmwareDate = "26 Apr 2020"; const char* FirmwareDate = "12 May 2020";
/* /*
* Macro to check the outputs of TWDT functions and trigger an abort if an * Macro to check the outputs of TWDT functions and trigger an abort if an
@ -158,6 +158,7 @@ const char* FirmwareDate = "26 Apr 2020";
bool validateFrame(const CProtocol& frame, const char* name); bool validateFrame(const CProtocol& frame, const char* name);
void checkDisplayUpdate(); void checkDisplayUpdate();
void checkDebugCommands(); void checkDebugCommands();
void manageStopStartMode();
void manageCyclicMode(); void manageCyclicMode();
void manageFrostMode(); void manageFrostMode();
void manageHumidity(); void manageHumidity();
@ -315,10 +316,10 @@ void checkBlueWireEvents()
} }
// trap being in state 0 with a heater error - cancel user on memory to avoid unexpected cyclic restarts // trap being in state 0 with a heater error - cancel user on memory to avoid unexpected cyclic restarts
if(RTC_Store.getCyclicEngaged() && (BlueWireRxData.getRunState() == 0) && (BlueWireRxData.getErrState() > 1)) { if(RTC_Store.getUserStart() && (BlueWireRxData.getRunState() == 0) && (BlueWireRxData.getErrState() > 1)) {
DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); DebugPort.println("Forcing cyclic cancel due to error induced shutdown");
// DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); // DebugPort.println("Forcing cyclic cancel due to error induced shutdown");
RTC_Store.setCyclicEngaged(false); RTC_Store.setUserStart(false);
} }
pHourMeter->monitor(BlueWireRxData); pHourMeter->monitor(BlueWireRxData);
@ -553,8 +554,7 @@ void setup() {
RTC_Store.begin(); RTC_Store.begin();
FuelGauge.init(RTC_Store.getFuelGauge()); FuelGauge.init(RTC_Store.getFuelGauge());
// bCyclicEngaged = RTC_Store.getCyclicEngaged(); DebugPort.printf("Previous user start = %d\r\n", RTC_Store.getUserStart()); // state flag required for cyclic mode to persist properly after a WD reboot :-)
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 passed by reference so they can be valid after SW reboots 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 pHourMeter->init(bESP32PowerUpInit || RTC_Store.getBootInit()); // ensure persistent memory variable are reset after powerup, or OTA update
@ -665,6 +665,7 @@ bool checkTemperatureSensors()
manageCyclicMode(); manageCyclicMode();
manageFrostMode(); manageFrostMode();
manageHumidity(); manageHumidity();
manageStopStartMode();
} }
} }
else { else {
@ -679,10 +680,31 @@ bool checkTemperatureSensors()
return false; return false;
} }
void manageStopStartMode()
{
if(NVstore.getUserSettings().ThermostatMethod == 4 && RTC_Store.getUserStart() ) {
float deltaT = getTemperatureSensor() - CDemandManager::getDegC();
float thresh = NVstore.getUserSettings().ThermostatWindow/2;
int heaterState = getHeaterInfo().getRunState(); // native heater state
if(deltaT > thresh) {
if(heaterState > 0 && heaterState <= 5) {
DebugPort.printf("STOP START MODE: Stopping heater, deltaT > +%.1f\r\n", thresh);
heaterOff(); // over temp - request heater stop
}
}
if(deltaT < -thresh) {
if(heaterState == 0) {
DebugPort.printf("STOP START MODE: Restarting heater, deltaT <%.1f\r\n", thresh);
heaterOn(); // under temp, start heater again
}
}
}
}
void manageCyclicMode() void manageCyclicMode()
{ {
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic; const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater if(cyclic.Stop && RTC_Store.getUserStart()) { // 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! int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over!
float deltaT = getTemperatureSensor() - CDemandManager::getDegC(); float deltaT = getTemperatureSensor() - CDemandManager::getDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT); // DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
@ -725,7 +747,7 @@ void manageFrostMode()
RTC_Store.setFrostOn(true); RTC_Store.setFrostOn(true);
DebugPort.printf("FROST MODE: Starting heater, < %d`C\r\n", engage); DebugPort.printf("FROST MODE: Starting heater, < %d`C\r\n", engage);
if(NVstore.getUserSettings().FrostRise == 0) if(NVstore.getUserSettings().FrostRise == 0)
RTC_Store.setCyclicEngaged(true); // enable cyclic mode if user stop RTC_Store.setUserStart(true); // enable cyclic mode if user stop
heaterOn(); heaterOn();
} }
} }
@ -735,7 +757,7 @@ void manageFrostMode()
DebugPort.printf("FROST MODE: Stopping heater, > %d`C\r\n", engage+rise); DebugPort.printf("FROST MODE: Stopping heater, > %d`C\r\n", engage+rise);
heaterOff(); heaterOff();
RTC_Store.setFrostOn(false); // cancel active frost mode RTC_Store.setFrostOn(false); // cancel active frost mode
RTC_Store.setCyclicEngaged(false); // for cyclic mode RTC_Store.setUserStart(false); // for cyclic mode
} }
} }
} }
@ -768,7 +790,7 @@ requestOn()
} }
bool LVCOK = 2 != SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); bool LVCOK = 2 != SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
if(hasHtrData() && LVCOK) { if(hasHtrData() && LVCOK) {
RTC_Store.setCyclicEngaged(true); // for cyclic mode RTC_Store.setUserStart(true); // for cyclic mode
RTC_Store.setFrostOn(false); // cancel frost mode RTC_Store.setFrostOn(false); // cancel frost mode
// only start if below appropriate temperature threshold, raised for cyclic mode // only start if below appropriate temperature threshold, raised for cyclic mode
// int denied = checkStartTemp(); // int denied = checkStartTemp();
@ -795,7 +817,7 @@ void requestOff()
{ {
DebugPort.println("Stop Request!"); DebugPort.println("Stop Request!");
heaterOff(); heaterOff();
RTC_Store.setCyclicEngaged(false); // for cyclic mode RTC_Store.setUserStart(false); // for cyclic mode
RTC_Store.setFrostOn(false); // cancel active frost mode RTC_Store.setFrostOn(false); // cancel active frost mode
CTimerManager::cancelActiveTimer(); CTimerManager::cancelActiveTimer();
} }
@ -1022,9 +1044,9 @@ int getSmartError()
return SmartError.getError(); return SmartError.getError();
} }
bool isCyclicActive() bool isCyclicStopStartActive()
{ {
return RTC_Store.getCyclicEngaged() && NVstore.getUserSettings().cyclic.isEnabled(); return RTC_Store.getUserStart() && (NVstore.getUserSettings().cyclic.isEnabled() || NVstore.getUserSettings().ThermostatMethod == 4);
} }
void setupGPIO() void setupGPIO()

View File

@ -292,6 +292,7 @@ CScreenManager::CScreenManager()
_pRebootScreen = NULL; _pRebootScreen = NULL;
_bDimmed = false; _bDimmed = false;
_bReload = true; _bReload = true;
_OTAholdoff = 0;
} }
CScreenManager::~CScreenManager() CScreenManager::~CScreenManager()
@ -471,9 +472,25 @@ CScreenManager::_loadScreens()
showSplash(); showSplash();
} }
bool
CScreenManager::_checkOTAholdoff()
{
if(_OTAholdoff) {
long tDelta = millis() - _OTAholdoff;
if(tDelta < 0)
return false;
_pDisplay->clearDisplay();
_pDisplay->display(); // blank screen
_OTAholdoff = 0;
}
return true;
}
bool bool
CScreenManager::checkUpdate() CScreenManager::checkUpdate()
{ {
if(!_checkOTAholdoff())
return false;
if(_bReload) if(_bReload)
_loadScreens(); _loadScreens();
@ -585,6 +602,9 @@ CScreenManager::reqUpdate()
bool bool
CScreenManager::animate() CScreenManager::animate()
{ {
if(!_checkOTAholdoff())
return false;
if(_pRebootScreen) if(_pRebootScreen)
return false; return false;
@ -733,6 +753,7 @@ CScreenManager::showOTAMessage(int percent, eOTAmodes updateType)
static int prevPercent = -1; static int prevPercent = -1;
if(percent != prevPercent) { if(percent != prevPercent) {
DebugPort.printf("%d%%\r\n", percent);
prevPercent = percent; prevPercent = percent;
_pDisplay->clearDisplay(); _pDisplay->clearDisplay();
if(percent < 0) if(percent < 0)
@ -759,6 +780,7 @@ CScreenManager::showOTAMessage(int percent, eOTAmodes updateType)
} }
_pDisplay->display(); _pDisplay->display();
} }
_OTAholdoff = millis() + 1000;
} }
void void

View File

@ -33,6 +33,7 @@ class CScreenManager {
std::vector<std::vector<CScreen*>> _Screens; std::vector<std::vector<CScreen*>> _Screens;
CRebootScreen* _pRebootScreen; CRebootScreen* _pRebootScreen;
C128x64_OLED* _pDisplay; C128x64_OLED* _pDisplay;
unsigned long _OTAholdoff;
int _menu; int _menu;
int _subMenu; int _subMenu;
int _rootMenu; int _rootMenu;
@ -49,6 +50,7 @@ class CScreenManager {
void _dim(bool state); void _dim(bool state);
void _loadScreens(); void _loadScreens();
void _unloadScreens(); void _unloadScreens();
bool _checkOTAholdoff();
public: public:
enum eUIMenuSets { RootMenuLoop, TimerMenuLoop, UserSettingsLoop, SystemSettingsLoop, TuningMenuLoop, BranchMenu }; enum eUIMenuSets { RootMenuLoop, TimerMenuLoop, UserSettingsLoop, SystemSettingsLoop, TuningMenuLoop, BranchMenu };
enum eUIRootMenus { DetailedControlUI, BasicControlUI, ClockUI, ModeUI, GPIOInfoUI, TrunkUI }; enum eUIRootMenus { DetailedControlUI, BasicControlUI, ClockUI, ModeUI, GPIOInfoUI, TrunkUI };

View File

@ -90,6 +90,7 @@ CThermostatModeScreen::show()
case 1: modeStr = "Deadband"; break; case 1: modeStr = "Deadband"; break;
case 2: modeStr = "Linear Hz"; break; case 2: modeStr = "Linear Hz"; break;
case 3: modeStr = "Ext thermostat"; break; case 3: modeStr = "Ext thermostat"; break;
case 4: modeStr = "Stop Start"; break;
} }
if(modeStr) if(modeStr)
_printMenuText(Column, Line3, modeStr, _rowSel == 4); _printMenuText(Column, Line3, modeStr, _rowSel == 4);
@ -170,6 +171,9 @@ CThermostatModeScreen::animate()
case 3: case 3:
pMsg = " The heater runs according to GPIO input #2: Open:minimum, Closed:maximum. "; pMsg = " The heater runs according to GPIO input #2: Open:minimum, Closed:maximum. ";
break; break;
case 4:
pMsg = " The heater is stopped then started according to the defined window. ";
break;
} }
if(pMsg) if(pMsg)
_scrollMessage(56, pMsg, _scrollChar); _scrollMessage(56, pMsg, _scrollChar);
@ -287,7 +291,6 @@ CThermostatModeScreen::keyHandler(uint8_t event)
void void
CThermostatModeScreen::_adjust(int dir) CThermostatModeScreen::_adjust(int dir)
{ {
int wrap;
switch(_rowSel) { switch(_rowSel) {
case 1: case 1:
_cyclicMode.Stop += dir; _cyclicMode.Stop += dir;
@ -304,12 +307,14 @@ CThermostatModeScreen::_adjust(int dir)
case 4: // thermostat mode case 4: // thermostat mode
_thermoMode += dir; _thermoMode += dir;
#if USE_JTAG == 0 #if USE_JTAG == 0
wrap = GPIOin.usesExternalThermostat() ? 3 : 2; if(_thermoMode == 3 && !GPIOin.usesExternalThermostat())
_thermoMode += dir;
#else #else
//CANNOT USE GPIO WITH JTAG DEBUG // CANNOT USE GPIO WITH JTAG DEBUG
wrap = 2; if(_thermoMode == 3
_thermoMode += dir;
#endif #endif
WRAPLIMITS(_thermoMode, 0, wrap); WRAPLIMITS(_thermoMode, 0, 4);
break; break;
} }
} }

View File

@ -328,7 +328,7 @@ CProtocol::setSystemVoltage(float fVal)
int CProtocolPackage::getRunStateEx() const int CProtocolPackage::getRunStateEx() const
{ {
int runstate = getRunState(); int runstate = getRunState();
if(isCyclicActive()) { if(isCyclicStopStartActive()) {
// special states for cyclic suspended // special states for cyclic suspended
switch(runstate) { switch(runstate) {
case 0: runstate = 10; break; // standby, awaiting temperature drop case 0: runstate = 10; break; // standby, awaiting temperature drop
@ -405,7 +405,7 @@ const char* ErrstatesEx [] PROGMEM = {
"E-09: Temp sense", // [10] E-09 "E-09: Temp sense", // [10] E-09
"E-10: Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5 "E-10: Ignition fail", // [11] E-10 SmartError manufactured state - sensing runstate 2 -> >5
"E-11: Failed 1st ignite", // [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
"E-12 Excess fuel shutdown", // [13] E-12 SmartError manufactured state - excess fuel consumed "E-12: Excess fuel shutdown", // [13] E-12 SmartError manufactured state - excess fuel consumed
"Unknown error?" // mystery code! "Unknown error?" // mystery code!
}; };

View File

@ -290,6 +290,12 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
} }
#endif #endif
break; break;
case 4:
m_TxFrame.setThermostatModeProtocol(0); // direct heater to use Hz Mode
m_TxFrame.setTemperature_Actual(0); // must force actual to 0 for Hz mode
m_TxFrame.setHeaterDemand(m_TxFrame.getTemperature_Max()); // maximum Hz
break;
} }
} }

View File

@ -52,7 +52,7 @@ CRTC_Store::CRTC_Store()
_fuelgauge = 0; _fuelgauge = 0;
_demandDegC = 22; _demandDegC = 22;
_demandPump = 22; _demandPump = 22;
_CyclicEngaged = false; _userStart = false;
_BootInit = true; _BootInit = true;
_RunTime = 0; _RunTime = 0;
_GlowTime = 0; _GlowTime = 0;
@ -65,7 +65,7 @@ CRTC_Store::begin()
// RTC lost power - reset internal NV values to defaults // RTC lost power - reset internal NV values to defaults
DebugPort.println("CRTC_Store::begin() RTC lost power, re-initialising NV aspect"); DebugPort.println("CRTC_Store::begin() RTC lost power, re-initialising NV aspect");
_demandPump = _demandDegC = 22; _demandPump = _demandDegC = 22;
_CyclicEngaged = false; _userStart = false;
setFuelGauge(0); setFuelGauge(0);
setDesiredTemp(_demandDegC); setDesiredTemp(_demandDegC);
setDesiredPump(_demandPump); setDesiredPump(_demandPump);
@ -127,16 +127,16 @@ CRTC_Store::setBootInit(bool val)
} }
bool bool
CRTC_Store::getCyclicEngaged() CRTC_Store::getUserStart()
{ {
_ReadAndUnpackByte4(); _ReadAndUnpackByte4();
return _CyclicEngaged; return _userStart;
} }
void void
CRTC_Store::setCyclicEngaged(bool active) CRTC_Store::setUserStart(bool active)
{ {
_CyclicEngaged = active; _userStart = active;
_PackAndSaveByte4(); _PackAndSaveByte4();
} }
@ -168,6 +168,20 @@ CRTC_Store::getFrostOn()
return _frostOn; return _frostOn;
} }
void
CRTC_Store::setSpare(bool state)
{
_spare = state;
_PackAndSaveByte5();
}
bool
CRTC_Store::getSpare()
{
_ReadAndUnpackByte5();
return _spare;
}
void void
CRTC_Store::resetRunTime() CRTC_Store::resetRunTime()
{ {
@ -221,17 +235,17 @@ CRTC_Store::_ReadAndUnpackByte4()
uint8_t NVval = 0; uint8_t NVval = 0;
Clock.readData((uint8_t*)&NVval, 1, 4); Clock.readData((uint8_t*)&NVval, 1, 4);
_demandDegC = NVval & 0x3f; _demandDegC = NVval & 0x3f;
_CyclicEngaged = (NVval & 0x80) != 0; _userStart = (NVval & 0x80) != 0;
_BootInit = (NVval & 0x40) != 0; _BootInit = (NVval & 0x40) != 0;
_accessed[1] = true; _accessed[1] = true;
DebugPort.printf("RTC_Store - read byte4: degC=%d, CyclicOn=%d, BootInit=%d\r\n", _demandDegC, _CyclicEngaged, _BootInit); DebugPort.printf("RTC_Store - read byte4: degC=%d, UserStart=%d, BootInit=%d\r\n", _demandDegC, _userStart, _BootInit);
} }
} }
void void
CRTC_Store::_PackAndSaveByte4() CRTC_Store::_PackAndSaveByte4()
{ {
uint8_t NVval = (_CyclicEngaged ? 0x80 : 0x00) uint8_t NVval = (_userStart ? 0x80 : 0x00)
| (_BootInit ? 0x40 : 0x00) | (_BootInit ? 0x40 : 0x00)
| (_demandDegC & 0x3f); | (_demandDegC & 0x3f);
Clock.saveData((uint8_t*)&NVval, 1, 4); Clock.saveData((uint8_t*)&NVval, 1, 4);
@ -245,6 +259,7 @@ CRTC_Store::_ReadAndUnpackByte5()
Clock.readData((uint8_t*)&NVval, 1, 5); Clock.readData((uint8_t*)&NVval, 1, 5);
_demandPump = NVval & 0x3f; _demandPump = NVval & 0x3f;
_frostOn = (NVval & 0x40) != 0; _frostOn = (NVval & 0x40) != 0;
_spare = (NVval & 0x80) != 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);
} }
@ -255,6 +270,7 @@ CRTC_Store::_PackAndSaveByte5()
{ {
uint8_t NVval = (_demandPump & 0x3f); uint8_t NVval = (_demandPump & 0x3f);
NVval |= _frostOn ? 0x40 : 0; NVval |= _frostOn ? 0x40 : 0;
NVval |= _spare ? 0x80 : 0;
Clock.saveData((uint8_t*)&NVval, 1, 5); Clock.saveData((uint8_t*)&NVval, 1, 5);
} }

View File

@ -30,9 +30,10 @@ class CRTC_Store {
float _fuelgauge; // Byte0..Byte3 float _fuelgauge; // Byte0..Byte3
uint8_t _demandDegC; // Byte4[0..5] uint8_t _demandDegC; // Byte4[0..5]
bool _BootInit; // Byte4[6] bool _BootInit; // Byte4[6]
bool _CyclicEngaged; // Byte4[7] bool _userStart; // Byte4[7]
uint8_t _demandPump; // Byte5[0..5] uint8_t _demandPump; // Byte5[0..5]
bool _frostOn; // Byte5[6] bool _frostOn; // Byte5[6]
bool _spare; // Byte5[7]
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();
@ -51,12 +52,12 @@ public:
void resetGlowTime(); void resetGlowTime();
bool incRunTime(); bool incRunTime();
bool incGlowTime(); bool incGlowTime();
void setCyclicEngaged(bool _CyclicEngaged); void setUserStart(bool state);
void setBootInit(bool val = true); void setBootInit(bool val = true);
float getFuelGauge(); float getFuelGauge();
uint8_t getDesiredTemp(); uint8_t getDesiredTemp();
uint8_t getDesiredPump(); uint8_t getDesiredPump();
bool getCyclicEngaged(); bool getUserStart();
bool getBootInit(); bool getBootInit();
int getRunTime(); int getRunTime();
int getGlowTime(); int getGlowTime();
@ -64,6 +65,8 @@ public:
int getMaxRunTime() const { return 32; }; int getMaxRunTime() const { return 32; };
void setFrostOn(bool state); void setFrostOn(bool state);
bool getFrostOn(); bool getFrostOn();
void setSpare(bool state);
bool getSpare();
}; };
extern CRTC_Store RTC_Store; extern CRTC_Store RTC_Store;

View File

@ -283,7 +283,6 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow)
} }
} }
else { else {
// if(!RTC_Store.getFrostOn() && !RTC_Store.getCyclicEngaged())
if(!RTC_Store.getFrostOn()) if(!RTC_Store.getFrostOn())
requestOff(); requestOff();
retval = 2; retval = 2;

View File

@ -415,17 +415,13 @@ void updateJSONclients(bool report)
char jsonStr[800]; char jsonStr[800];
{ {
if(makeJSONString(JSONmoderator, jsonStr, sizeof(jsonStr))) { if(makeJSONString(JSONmoderator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
// update extended params // update extended params
{ {
if(makeJSONStringEx(JSONmoderator, jsonStr, sizeof(jsonStr))) { if(makeJSONStringEx(JSONmoderator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
// update timer parameters // update timer parameters
@ -433,9 +429,7 @@ void updateJSONclients(bool report)
for(int tmr=0; tmr<14; tmr++) for(int tmr=0; tmr<14; tmr++)
{ {
if(makeJSONTimerString(tmr, jsonStr, sizeof(jsonStr))) { if(makeJSONTimerString(tmr, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println("Done");
bNewTimerInfo = true; bNewTimerInfo = true;
} }
} }
@ -451,43 +445,33 @@ void updateJSONclients(bool report)
root.set("TimerRefresh", 1); root.set("TimerRefresh", 1);
root.printTo(jsonStr, 800); root.printTo(jsonStr, 800);
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
// report MQTT params // report MQTT params
{ {
if(makeJSONStringMQTT(MQTTJSONmoderator, jsonStr, sizeof(jsonStr))) { if(makeJSONStringMQTT(MQTTJSONmoderator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
// report IP params // report IP params
{ {
if(makeJSONStringIP(IPmoderator, jsonStr, sizeof(jsonStr))) { if(makeJSONStringIP(IPmoderator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
// report System info // report System info
{ {
if(makeJSONStringSysInfo(SysModerator, jsonStr, sizeof(jsonStr))) { if(makeJSONStringSysInfo(SysModerator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
{ {
if(makeJSONStringGPIO(GPIOmoderator, jsonStr, sizeof(jsonStr))) { if(makeJSONStringGPIO(GPIOmoderator, jsonStr, sizeof(jsonStr))) {
if (report) DebugPort.printf(" %ld JSON send: %s", millis(), jsonStr); sendJSONtext(jsonStr, report);
sendJSONtext(jsonStr);
if (report) DebugPort.println(" Done");
} }
} }
@ -508,7 +492,7 @@ void resetAllJSONmoderators()
GPIOmoderator.reset(); GPIOmoderator.reset();
// create and send a validation code (then client knows AB is capable of reboot over JSON) // create and send a validation code (then client knows AB is capable of reboot over JSON)
doJSONreboot(0); doJSONreboot(0);
sendJSONtext("{\"LoadWebContent\":\"Supported\"}"); sendJSONtext("{\"LoadWebContent\":\"Supported\"}", false);
} }
void initJSONMQTTmoderator() void initJSONMQTTmoderator()
@ -562,8 +546,10 @@ void Expand(std::string& str)
} }
} }
void sendJSONtext(const char* jsonStr) void sendJSONtext(const char* jsonStr, bool report)
{ {
if (report) DebugPort.printf("JSON send: %s\r\n", jsonStr);
#ifdef REPORT_JSONSENDS #ifdef REPORT_JSONSENDS
std::string dest; std::string dest;
DebugPort.print("1"); DebugPort.print("1");
@ -601,11 +587,11 @@ void doJSONreboot(uint16_t PIN)
char jsonStr[20]; char jsonStr[20];
sprintf(jsonStr, "{\"Reboot\":\"%04d\"}", validate); sprintf(jsonStr, "{\"Reboot\":\"%04d\"}", validate);
sendJSONtext( jsonStr ); sendJSONtext( jsonStr, false );
} }
else if(PIN == validate) { else if(PIN == validate) {
strcpy(jsonStr, "{\"Reboot\":\"-1\"}"); strcpy(jsonStr, "{\"Reboot\":\"-1\"}");
sendJSONtext( jsonStr ); sendJSONtext( jsonStr, false );
// initate reboot // initate reboot
const char* content[2]; const char* content[2];

View File

@ -41,7 +41,7 @@ void resetJSONSysModerator();
void resetJSONMQTTmoderator(); void resetJSONMQTTmoderator();
void validateTimer(int ID); void validateTimer(int ID);
void doJSONreboot(uint16_t code); void doJSONreboot(uint16_t code);
void sendJSONtext(const char* JSONstr); void sendJSONtext(const char* JSONstr, bool report);
template<class T> template<class T>
const char* createJSON(const char* name, T value) const char* createJSON(const char* name, T value)

View File

@ -452,11 +452,13 @@ sUserSettings::load()
validatedLoad("thermostat", useThermostat, 1, u8inBounds, 0, 1); validatedLoad("thermostat", useThermostat, 1, u8inBounds, 0, 1);
validatedLoad("thermoMethod", ThermostatMethod, 0, u8inBounds, 0, 255); validatedLoad("thermoMethod", ThermostatMethod, 0, u8inBounds, 0, 255);
// catch and migrate old combined method & window // catch and migrate old combined method & window
if(ThermostatMethod & 0xFC) { // if(ThermostatMethod & 0xFC) {
float defVal = float(ThermostatMethod>>2) * 0.1f; // float defVal = float(ThermostatMethod>>2) * 0.1f;
validatedLoad("thermoWindow", ThermostatWindow, defVal, 0.2f, 10.0f); // validatedLoad("thermoWindow", ThermostatWindow, defVal, 0.2f, 10.0f);
preferences.putUChar("thermoMethod", ThermostatMethod & 0x03); // strip old window // preferences.putUChar("thermoMethod", ThermostatMethod & 0x03); // strip old window
} // }
if(ThermostatMethod > 4)
ThermostatMethod = 0;
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("frostOn", FrostOn, 0, u8inBounds, 0, 10);

View File

@ -310,7 +310,7 @@ struct sUserSettings : public CESP32_NVStorage {
long menuTimeout; long menuTimeout;
long ExtThermoTimeout; long ExtThermoTimeout;
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, 3: External contact, 4: Stop/Start
float ThermostatWindow; float ThermostatWindow;
uint8_t FrostOn; uint8_t FrostOn;
uint8_t FrostRise; uint8_t FrostRise;
@ -333,7 +333,7 @@ struct sUserSettings : public CESP32_NVStorage {
retval &= INBOUNDS(menuTimeout, 0, 300000); // 5 mins retval &= INBOUNDS(menuTimeout, 0, 300000); // 5 mins
retval &= INBOUNDS(ExtThermoTimeout, 0, 3600000); // 1 hour retval &= INBOUNDS(ExtThermoTimeout, 0, 3600000); // 1 hour
retval &= (degF == 0) || (degF == 1); retval &= (degF == 0) || (degF == 1);
retval &= ThermostatMethod <= 3; // only modes 0, 1 or 2, 3 retval &= ThermostatMethod <= 4; // only modes 0, 1, 2, 3 or 4
retval &= INBOUNDS(ThermostatWindow, 0.2f, 10.f); retval &= INBOUNDS(ThermostatWindow, 0.2f, 10.f);
retval &= useThermostat < 2; retval &= useThermostat < 2;
retval &= INBOUNDS(wifiMode, 0, 3); retval &= INBOUNDS(wifiMode, 0, 3);

View File

@ -131,11 +131,11 @@ void DecodeCmd(const char* cmd, String& payload)
if(payload.toInt()) { if(payload.toInt()) {
CDemandManager::eStartCode result = requestOn(); CDemandManager::eStartCode result = requestOn();
switch(result) { switch(result) {
case CDemandManager::eStartOK: sendJSONtext("{\"StartString\":\"\"}"); break; case CDemandManager::eStartOK: sendJSONtext("{\"StartString\":\"\"}", true); break;
case CDemandManager::eStartTooWarm: sendJSONtext("{\"StartString\":\"Ambient too warm!\"}"); break; case CDemandManager::eStartTooWarm: sendJSONtext("{\"StartString\":\"Ambient too warm!\"}", true); break;
case CDemandManager::eStartSuspend: sendJSONtext("{\"StartString\":\"Immediate Cyclic suspension!\"}"); break; case CDemandManager::eStartSuspend: sendJSONtext("{\"StartString\":\"Immediate Cyclic suspension!\"}", true); break;
case CDemandManager::eStartLVC: sendJSONtext("{\"StartString\":\"Battery below LVC!\"}"); break; case CDemandManager::eStartLVC: sendJSONtext("{\"StartString\":\"Battery below LVC!\"}", true); break;
case CDemandManager::eStartLowFuel: sendJSONtext("{\"StartString\":\"Fuel Empty!\"}"); break; case CDemandManager::eStartLowFuel: sendJSONtext("{\"StartString\":\"Fuel Empty!\"}", true); break;
} }
} }
else { else {
@ -185,7 +185,7 @@ void DecodeCmd(const char* cmd, String& payload)
else if(strcmp("ThermostatMethod", cmd) == 0) { else if(strcmp("ThermostatMethod", cmd) == 0) {
sUserSettings settings = NVstore.getUserSettings(); sUserSettings settings = NVstore.getUserSettings();
settings.ThermostatMethod = payload.toInt(); settings.ThermostatMethod = payload.toInt();
if(INBOUNDS(settings.ThermostatMethod, 0, 3)) if(INBOUNDS(settings.ThermostatMethod, 0, 4))
NVstore.setUserSettings(settings); NVstore.setUserSettings(settings);
} }
else if(strcmp("ThermostatWindow", cmd) == 0) { else if(strcmp("ThermostatWindow", cmd) == 0) {

View File

@ -48,7 +48,7 @@ extern bool hasOEMLCDcontroller();
extern bool hasHtrData(); extern bool hasHtrData();
extern int getBlueWireStat(); extern int getBlueWireStat();
extern int getSmartError(); extern int getSmartError();
extern bool isCyclicActive(); extern bool isCyclicStopStartActive();
extern float getVersion(); extern float getVersion();
const char* getVersionStr(bool beta=false); const char* getVersionStr(bool beta=false);
extern const char* getVersionDate(); extern const char* getVersionDate();

View File

@ -39,6 +39,9 @@
#include "../Utility/BTC_JSON.h" #include "../Utility/BTC_JSON.h"
#include "../Utility/TempSense.h" #include "../Utility/TempSense.h"
#include "../Utility/DemandManager.h" #include "../Utility/DemandManager.h"
#include "../Utility/FuelGauge.h"
#include "../Utility/BoardDetect.h"
#include "../Utility/NVStorage.h"
#include <FreeRTOS.h> #include <FreeRTOS.h>
extern void DecodeCmd(const char* cmd, String& payload); extern void DecodeCmd(const char* cmd, String& payload);
@ -348,9 +351,15 @@ void updateMQTT()
pubTopic("InputVoltage", getBatteryVoltage(false)); pubTopic("InputVoltage", getBatteryVoltage(false));
pubTopic("GlowVoltage", getGlowVolts()); pubTopic("GlowVoltage", getGlowVolts());
pubTopic("GlowCurrent", getGlowCurrent()); pubTopic("GlowCurrent", getGlowCurrent());
sGPIO info; if(getBoardRevision() != BRD_V2_GPIO_NOALG && getBoardRevision() != BRD_V3_GPIO_NOALG) { // has GPIO support
getGPIOinfo(info); sGPIO info;
pubTopic("GPanlg", info.algVal * 100 / 4096); getGPIOinfo(info);
pubTopic("GPanlg", info.algVal * 100 / 4096);
}
pubTopic("FuelUsage", FuelGauge.Used_mL());
float fuelRate = getHeaterInfo().getPump_Actual() * NVstore.getHeaterTuning().pumpCal * 60 * 60;
pubTopic("FuelRate", fuelRate);
} }
void refreshMQTT() void refreshMQTT()

View File

@ -21,6 +21,7 @@
*/ */
#define USE_EMBEDDED_WEBUPDATECODE #define USE_EMBEDDED_WEBUPDATECODE
#define HTTPS_LOGLEVEL 2
#include <Arduino.h> #include <Arduino.h>
#include "BTCWifi.h" #include "BTCWifi.h"
@ -53,7 +54,7 @@
#include <WebsocketHandler.hpp> #include <WebsocketHandler.hpp>
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include "../OLED/ScreenManager.h" #include "../OLED/ScreenManager.h"
// #include "../Utility/ABpreferences.h" #include "esp_task_wdt.h"
// Max clients to be connected to the JSON handler // Max clients to be connected to the JSON handler
#define MAX_CLIENTS 4 #define MAX_CLIENTS 4
@ -77,6 +78,7 @@ void streamFileCoreSSL(const size_t fileSize, const String & fileName, const Str
void processWebsocketQueue(); void processWebsocketQueue();
QueueHandle_t webSocketQueue = NULL; QueueHandle_t webSocketQueue = NULL;
QueueHandle_t JSONcommandQueue = NULL;
TaskHandle_t handleWebServerTask; TaskHandle_t handleWebServerTask;
#if USE_HTTPS == 1 #if USE_HTTPS == 1
SSLCert* pCert; SSLCert* pCert;
@ -121,6 +123,8 @@ void doDefaultWebHandler(HTTPRequest * req, HTTPResponse * res);
void build404Response(HTTPRequest * req, String& content, String file); void build404Response(HTTPRequest * req, String& content, String file);
void build500Response(String& content, String file); void build500Response(String& content, String file);
bool checkAuthentication(HTTPRequest * req, HTTPResponse * res, int credID=0); bool checkAuthentication(HTTPRequest * req, HTTPResponse * res, int credID=0);
bool addRxJSONcommand(const char* str);
bool checkRxJSONcommand();
// As websockets are more complex, they need a custom class that is derived from WebsocketHandler // As websockets are more complex, they need a custom class that is derived from WebsocketHandler
@ -176,14 +180,31 @@ JSONHandler::onMessage(WebsocketInputStreambuf * inbuf) {
bRxWebData = true; bRxWebData = true;
char cmd[256]; // use a queue to hand over messages - ensures any commands that affect the I2C bus
memset(cmd, 0, 256); // (typ. various RTC operations) are performed in line with all other accesses
if(msg.length() < 256) { addRxJSONcommand(msg.c_str());
strcpy(cmd, msg.c_str()); }
// TODO: use a queue to hand over message
interpretJsonCommand(cmd); // send to the main heater controller decode routine bool addRxJSONcommand(const char* str)
{
if(JSONcommandQueue) {
char *pMsg = new char[strlen(str)+1];
strcpy(pMsg, str);
xQueueSend(JSONcommandQueue, &pMsg, 0);
return true;
} }
return false;
}
bool checkRxJSONcommand()
{
char* pMsg = NULL;
if(xQueueReceive(JSONcommandQueue, &pMsg, 0)) {
interpretJsonCommand(pMsg);
delete[] pMsg;
return true;
}
return false;
} }
@ -470,10 +491,10 @@ void initWebServer(void) {
DebugPort.println("HTTPS started"); DebugPort.println("HTTPS started");
JSONcommandQueue = xQueueCreate(50, sizeof(char*) );
// setup task to handle webserver // setup task to handle webserver
webSocketQueue = xQueueCreate(50, sizeof(char*) ); webSocketQueue = xQueueCreate(50, sizeof(char*) );
bStopWebServer = false; bStopWebServer = false;
xTaskCreate(SSLloopTask, xTaskCreate(SSLloopTask,
"Web server task", "Web server task",
@ -512,6 +533,8 @@ void SSLloopTask(void *) {
bool doWebServer(void) bool doWebServer(void)
{ {
GetWebContent.manage(); GetWebContent.manage();
BrowserUpload.queueProcess(); // manage data queued from web update
checkRxJSONcommand();
return true; return true;
} }
@ -1061,9 +1084,6 @@ void onWMConfig(HTTPRequest * req, httpsserver::HTTPResponse * res)
newMode.eraseCreds = false; newMode.eraseCreds = false;
newMode.delay = 500; newMode.delay = 500;
scheduleWMreboot(newMode); scheduleWMreboot(newMode);
// delay(500);
// wifiEnterConfigPortal(true, false, 10000);
} }
@ -1077,8 +1097,6 @@ void onResetWifi(HTTPRequest * req, httpsserver::HTTPResponse * res)
newMode.eraseCreds = true; newMode.eraseCreds = true;
newMode.delay = 500; newMode.delay = 500;
scheduleWMreboot(newMode); scheduleWMreboot(newMode);
// delay(500);
// wifiEnterConfigPortal(true, true, 3000);
} }
@ -1118,7 +1136,7 @@ void processWebsocketQueue()
// DebugPort.println("->"); // DebugPort.println("->");
} }
} }
delete pMsg; delete[] pMsg;
} }
} }
} }
@ -1461,19 +1479,40 @@ void onUploadProgression(HTTPRequest * req, httpsserver::HTTPResponse * res)
} }
while (!parser->endOfField()) { while (!parser->endOfField()) {
// file upload and writing to SPIFFS is not a happy combination as the web server is running at an elevated level here
// best to pass the data to the normal Arduino processing task via a queue, but maintain synchronism with the processing
// by spinning here until ready.
while(!BrowserUpload.Ready()) {
taskYIELD();
}
esp_task_wdt_reset();
upload.currentSize = parser->read(upload.buf, HTTP_UPLOAD_BUFLEN); upload.currentSize = parser->read(upload.buf, HTTP_UPLOAD_BUFLEN);
sts = BrowserUpload.fragment(upload, res);
BrowserUpload.queueFragment(upload); // let user task process the fresh data
while(!BrowserUpload.Ready()) {
taskYIELD();
}
esp_task_wdt_reset();
// sts = BrowserUpload.fragment(upload, res);
sts = BrowserUpload.queueResult();
if(sts < 0) { if(sts < 0) {
if(pUpdateHandler) { if(pUpdateHandler) {
sprintf(JSON, "{\"updateProgress\":%d}", sts); sprintf(JSON, "{\"updateProgress\":%d}", sts);
pUpdateHandler->send(JSON, WebsocketHandler::SEND_TYPE_TEXT); pUpdateHandler->send(JSON, WebsocketHandler::SEND_TYPE_TEXT);
} }
DebugPort.printf("Upload code %d\r\n", sts);
break; break;
} }
else { else {
// upload still in progress? // upload still in progress?
if(BrowserUpload.bUploadActive) { // show progress unless a write error has occured if(BrowserUpload.bUploadActive) { // show progress unless a write error has occured
DebugPort.print("."); // DebugPort.printf(" p%d ", uxTaskPriorityGet(NULL));
// DebugPort.print(".");
if(upload.totalSize) { if(upload.totalSize) {
// feed back bytes received over web socket for progressbar update on browser (via javascript) // feed back bytes received over web socket for progressbar update on browser (via javascript)
if(pUpdateHandler) { if(pUpdateHandler) {
@ -1481,13 +1520,14 @@ void onUploadProgression(HTTPRequest * req, httpsserver::HTTPResponse * res)
pUpdateHandler->send(JSON, WebsocketHandler::SEND_TYPE_TEXT); pUpdateHandler->send(JSON, WebsocketHandler::SEND_TYPE_TEXT);
} }
} }
// show percentage on OLED /* // show percentage on OLED
int percent = 0; int percent = 0;
if(_SuppliedFileSize) if(_SuppliedFileSize)
percent = 100 * upload.totalSize / _SuppliedFileSize; percent = 100 * upload.totalSize / _SuppliedFileSize;
#if USE_SSL_LOOP_TASK != 1 #if USE_SSL_LOOP_TASK != 1
ShowOTAScreen(percent, eOTAbrowser); // browser update ShowOTAScreen(percent, eOTAbrowser); // browser update
#endif #endif
*/
} }
} }
} }

View File

@ -122,7 +122,7 @@ void doOTA()
if ((WiFi.status() == WL_CONNECTED)) // bug workaround in FOTA where execHTTPcheck does not return false in this condition if ((WiFi.status() == WL_CONNECTED)) // bug workaround in FOTA where execHTTPcheck does not return false in this condition
{ {
#endif #endif
FOTA.onProgress(onWebProgress); // important - keeps watchdog fed FOTA.onProgress(NULL); // important - keeps watchdog fed
FOTA.onComplete(CheckFirmwareCRC); // upload complete, but not yet verified FOTA.onComplete(CheckFirmwareCRC); // upload complete, but not yet verified
FOTA.onSuccess(onSuccess); FOTA.onSuccess(onSuccess);
#ifdef TESTFOTA #ifdef TESTFOTA
@ -137,16 +137,20 @@ void doOTA()
if(FOTA.execHTTPcheck()) { if(FOTA.execHTTPcheck()) {
DebugPort.println("New firmware available on web server!"); DebugPort.println("New firmware available on web server!");
if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.) if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.)
FOTA.onProgress(onWebProgress); // important - keeps watchdog fed
FOTA.execOTA(); // go ahead and do the update, reading new file from web server FOTA.execOTA(); // go ahead and do the update, reading new file from web server
FOTA.onProgress(NULL); // avoid rogue web update pop ups during browser update!
FOTAauth = 0; // and we're done. FOTAauth = 0; // and we're done.
} }
else else {
FOTAauth = 1; // flag that new firmware is available FOTAauth = 1; // flag that new firmware is available
}
} }
else { else {
FOTAauth = 0; // cancel FOTAauth = 0; // cancel
} }
} // Wifi (STA) Connected } // Wifi (STA) Connected
FOTA.onProgress(NULL);
#else #else
@ -161,7 +165,9 @@ void doOTA()
// version number is collected asynchronously after initiating the update check // version number is collected asynchronously after initiating the update check
if(FOTA.getNewVersion()) { if(FOTA.getNewVersion()) {
if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.) if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.)
FOTA.onProgress(onWebProgress); // important - keeps watchdog fed
FOTA.execOTA(); // go ahead and do the update, reading new file from web server FOTA.execOTA(); // go ahead and do the update, reading new file from web server
FOTA.onProgress(NULL); // avoid rogue web update pop ups during browser update!
FOTAauth = 0; // and we're done. FOTAauth = 0; // and we're done.
} }
else { else {
@ -247,8 +253,6 @@ void onWebProgress(size_t progress, size_t total)
static int prevPC = 0; static int prevPC = 0;
if(percent != prevPC) { if(percent != prevPC) {
prevPC = percent; prevPC = percent;
DebugPort.printf("Web progress: %u%%\r\n", percent);
DebugPort.handle(); // keep telnet spy alive
ShowOTAScreen(percent, eOTAWWW); ShowOTAScreen(percent, eOTAWWW);
} }
} }

View File

@ -29,6 +29,9 @@
#include "BTCota.h" #include "BTCota.h"
#include "../Utility/helpers.h" #include "../Utility/helpers.h"
QueueHandle_t webUpdateQueue = NULL;
void void
sBrowserUpload::init() sBrowserUpload::init()
{ {
@ -103,7 +106,7 @@ sBrowserUpload::begin(String& filename, int filesize)
} }
int int
sBrowserUpload::fragment(HTTPUpload& upload, httpsserver::HTTPResponse * res) sBrowserUpload::doFragment(HTTPUpload& upload, httpsserver::HTTPResponse * res)
{ {
if(isSPIFFSupload()) { if(isSPIFFSupload()) {
// SPIFFS update (may be error state) // SPIFFS update (may be error state)
@ -129,6 +132,7 @@ sBrowserUpload::fragment(HTTPUpload& upload, httpsserver::HTTPResponse * res)
} }
} }
else { else {
// DebugPort.print(".");
// Firmware update, add new fragment to OTA partition // Firmware update, add new fragment to OTA partition
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
// ERROR ! // ERROR !
@ -138,6 +142,13 @@ sBrowserUpload::fragment(HTTPUpload& upload, httpsserver::HTTPResponse * res)
if(res) if(res)
upload.totalSize += upload.currentSize; upload.totalSize += upload.currentSize;
} }
// show percentage on OLED
int percent = 0;
if(SrcFile.size) {
percent = 100 * upload.totalSize / SrcFile.size;
}
ShowOTAScreen(percent, eOTAbrowser); // browser update
return upload.totalSize; return upload.totalSize;
} }
@ -187,3 +198,47 @@ sBrowserUpload::isOK() const
else else
return !Update.hasError(); return !Update.hasError();
} }
bool
sBrowserUpload::Ready() const
{
return _bProcessed;
}
int
sBrowserUpload::queueFragment(HTTPUpload& upload)
{
_bProcessed = false;
sUpdateFragment fragment;
fragment.pUploadInfo = &upload;
xQueueSend(webUpdateQueue, &fragment, 0);
return upload.currentSize;
}
bool
sBrowserUpload::queueProcess()
{
sUpdateFragment fragment;
if(webUpdateQueue && xQueueReceive(webUpdateQueue, &fragment, 0)) {
HTTPUpload& upload = *fragment.pUploadInfo;
_queueResult = doFragment(upload, (httpsserver::HTTPResponse *)1);
_bProcessed = true;
return true;
}
return false;
}
int
sBrowserUpload::queueResult()
{
return _queueResult;
}
void
sBrowserUpload::createQueue()
{
if(webUpdateQueue == NULL) {
webUpdateQueue = xQueueCreate(2, sizeof(sUpdateFragment) );
}
}

View File

@ -37,10 +37,16 @@ struct sBrowserUpload{
int state; int state;
} DstFile; } DstFile;
bool bUploadActive; bool bUploadActive;
int _queueResult;
volatile bool _bProcessed;
//methods //methods
sBrowserUpload() { sBrowserUpload() {
reset(); reset();
createQueue();
_bProcessed = true;
} }
void createQueue();
void reset() { void reset() {
if(DstFile.file) { if(DstFile.file) {
DstFile.file.close(); DstFile.file.close();
@ -50,10 +56,21 @@ struct sBrowserUpload{
} }
void init(); void init();
int begin(String& filename, int filesize = -1); int begin(String& filename, int filesize = -1);
int fragment(HTTPUpload& upload, httpsserver::HTTPResponse * res = NULL); int doFragment(HTTPUpload& upload, httpsserver::HTTPResponse * res = NULL);
int end(HTTPUpload& upload); int end(HTTPUpload& upload);
bool isSPIFFSupload() const { return DstFile.state != 0; }; bool isSPIFFSupload() const { return DstFile.state != 0; };
bool isOK() const; bool isOK() const;
bool Ready() const;
int queueFragment(HTTPUpload& upload);
bool queueProcess();
int queueResult();
};
struct sUpdateFragment {
// uint16_t len;
// uint8_t buf[1500];
HTTPUpload *pUploadInfo;
}; };

View File

@ -395,6 +395,6 @@ CGetWebContent::_sendJSON(const char* name)
else else
JSONmsg += name; JSONmsg += name;
JSONmsg += "\"}"; JSONmsg += "\"}";
sendJSONtext(JSONmsg.c_str()); sendJSONtext(JSONmsg.c_str(), false);
} }