diff --git a/Arduino/Afterburner/Afterburner.ino b/Arduino/Afterburner/Afterburner.ino index 7e06a01..450095c 100644 --- a/Arduino/Afterburner/Afterburner.ino +++ b/Arduino/Afterburner/Afterburner.ino @@ -119,7 +119,7 @@ const int FirmwareRevision = 23; const int FirmwareSubRevision = 6; -const char* FirmwareDate = "3 Jul 2019"; +const char* FirmwareDate = "15 Jul 2019"; #ifdef ESP32 @@ -197,6 +197,7 @@ unsigned long moderator; bool bUpdateDisplay = false; bool bHaveWebClient = false; bool bBTconnected = false; +long BootTime; hw_timer_t *watchdogTimer = NULL; @@ -269,6 +270,35 @@ void interruptReboot() esp_restart(); } +//************************************************************************************************** +//** ** +//** WORKAROUND for crap ESP32 millis() standard function ** +//** ** +//************************************************************************************************** +// +// Substitute shitfull ESP32 millis() with a true and proper ms counter +// The standard millis() on ESP32 is actually micros()/1000. +// This wraps every 71.5 minutes in a **very non linear fashion**. +// +// The FreeRTOS Tick Counter however does increment each ms, and rolls naturally past 0 every 49days. +// With this proper linear behaviour you can use valid timeout calcualtions even through wrap around. +// This elegance breaks using the standard library function, leading to many weird and obtuse issues. +// +// *** IMPORTANT *** +// +// You **MUST** use --wrap millis in the linker command, or -Wl,--wrap,millis in the GCC command. +// platformio.ini file for this project defines the latter as a build_flags entry. +// +// The linker will now link to __wrap_millis() instead of millis() for *any* usage of millis(). +// Best of all this includes any library usages of millis() :-D +// If you really must call the shitty ESP32 Arduino millis(), you must call __real_millis() +// from your dubious code ;-) - basically DON'T do this. + +extern "C" unsigned long __wrap_millis() { + return xTaskGetTickCount(); +} + + void setup() { // ensure cyclic mode is disabled after power on @@ -332,6 +362,7 @@ void setup() { initMQTTJSONmoderator(); // prevents JSON for MQTT unless requested initIPJSONmoderator(); // prevents JSON for IP unless requested initTimerJSONmoderator(); // prevents JSON for timers unless requested + initSysModerator(); KeyPad.begin(keyLeft_pin, keyRight_pin, keyCentre_pin, keyUp_pin, keyDown_pin); @@ -344,6 +375,7 @@ void setup() { const BTCDateTime& now = Clock.get(); if(now.day() != 0xa5) bNoClock = false; + BootTime = Clock.get().secondstime(); ScreenManager.begin(bNoClock); @@ -452,7 +484,7 @@ void loop() CommState.is(CommStates::HeaterRx2) ) { if(RxTimeElapsed >= moderator) { - moderator += 10; + moderator += 10; if(bReportRecyleEvents) { DebugPort.printf("%ldms - ", RxTimeElapsed); } @@ -496,7 +528,7 @@ void loop() doStreaming(); // do wifi, BT tx etc when NOT in midst of handling blue wire // this especially avoids E-07 faults due to larger data transfers - moderator = 50; + moderator = 50; #if RX_LED == 1 digitalWrite(LED_Pin, LOW); @@ -520,7 +552,7 @@ void loop() } #if SUPPORT_OEM_CONTROLLER == 1 - if(BlueWireData.available() && (RxTimeElapsed > RX_DATA_TIMOUT+10)) { + if(BlueWireData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) { if(bReportOEMresync) { DebugPort.printf("Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed); @@ -719,7 +751,7 @@ void loop() // update temperature reading, // synchronised with serial reception as interrupts do get disabled in the OneWire library tDelta = timenow - lastTemperatureTime; - if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period + if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period lastTemperatureTime = millis(); // reset time to observe temeprature if(TempSensor.readTemperature(fTemperature)) { @@ -928,7 +960,7 @@ void checkDisplayUpdate() { // only update OLED when not processing blue wire if(ScreenManager.checkUpdate()) { - lastAnimationTime = millis() + 100; + lastAnimationTime = millis() + 100; ScreenManager.animate(); ScreenManager.refresh(); // always refresh post major update } @@ -936,7 +968,7 @@ void checkDisplayUpdate() long tDelta = millis() - lastAnimationTime; if(tDelta >= 100) { - lastAnimationTime = millis() + 100; + lastAnimationTime = millis() + 100; if(ScreenManager.animate()) ScreenManager.refresh(); } @@ -1436,4 +1468,9 @@ void updateFilteredData() FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage()); FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current()); FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual()); +} + +int sysUptime() +{ + return Clock.get().secondstime() - BootTime; } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index ae83de3..c1392d7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,4 +21,6 @@ upload_flags = --port=3232 monitor_speed = 115200 extra_scripts = post:add_CRC.py +; replace shitty Arduino millis with a linear time version +build_flags = -Wl,--wrap,millis diff --git a/src/Afterburner/Afterburner.cpp b/src/Afterburner/Afterburner.cpp index 7e06a01..450095c 100644 --- a/src/Afterburner/Afterburner.cpp +++ b/src/Afterburner/Afterburner.cpp @@ -119,7 +119,7 @@ const int FirmwareRevision = 23; const int FirmwareSubRevision = 6; -const char* FirmwareDate = "3 Jul 2019"; +const char* FirmwareDate = "15 Jul 2019"; #ifdef ESP32 @@ -197,6 +197,7 @@ unsigned long moderator; bool bUpdateDisplay = false; bool bHaveWebClient = false; bool bBTconnected = false; +long BootTime; hw_timer_t *watchdogTimer = NULL; @@ -269,6 +270,35 @@ void interruptReboot() esp_restart(); } +//************************************************************************************************** +//** ** +//** WORKAROUND for crap ESP32 millis() standard function ** +//** ** +//************************************************************************************************** +// +// Substitute shitfull ESP32 millis() with a true and proper ms counter +// The standard millis() on ESP32 is actually micros()/1000. +// This wraps every 71.5 minutes in a **very non linear fashion**. +// +// The FreeRTOS Tick Counter however does increment each ms, and rolls naturally past 0 every 49days. +// With this proper linear behaviour you can use valid timeout calcualtions even through wrap around. +// This elegance breaks using the standard library function, leading to many weird and obtuse issues. +// +// *** IMPORTANT *** +// +// You **MUST** use --wrap millis in the linker command, or -Wl,--wrap,millis in the GCC command. +// platformio.ini file for this project defines the latter as a build_flags entry. +// +// The linker will now link to __wrap_millis() instead of millis() for *any* usage of millis(). +// Best of all this includes any library usages of millis() :-D +// If you really must call the shitty ESP32 Arduino millis(), you must call __real_millis() +// from your dubious code ;-) - basically DON'T do this. + +extern "C" unsigned long __wrap_millis() { + return xTaskGetTickCount(); +} + + void setup() { // ensure cyclic mode is disabled after power on @@ -332,6 +362,7 @@ void setup() { initMQTTJSONmoderator(); // prevents JSON for MQTT unless requested initIPJSONmoderator(); // prevents JSON for IP unless requested initTimerJSONmoderator(); // prevents JSON for timers unless requested + initSysModerator(); KeyPad.begin(keyLeft_pin, keyRight_pin, keyCentre_pin, keyUp_pin, keyDown_pin); @@ -344,6 +375,7 @@ void setup() { const BTCDateTime& now = Clock.get(); if(now.day() != 0xa5) bNoClock = false; + BootTime = Clock.get().secondstime(); ScreenManager.begin(bNoClock); @@ -452,7 +484,7 @@ void loop() CommState.is(CommStates::HeaterRx2) ) { if(RxTimeElapsed >= moderator) { - moderator += 10; + moderator += 10; if(bReportRecyleEvents) { DebugPort.printf("%ldms - ", RxTimeElapsed); } @@ -496,7 +528,7 @@ void loop() doStreaming(); // do wifi, BT tx etc when NOT in midst of handling blue wire // this especially avoids E-07 faults due to larger data transfers - moderator = 50; + moderator = 50; #if RX_LED == 1 digitalWrite(LED_Pin, LOW); @@ -520,7 +552,7 @@ void loop() } #if SUPPORT_OEM_CONTROLLER == 1 - if(BlueWireData.available() && (RxTimeElapsed > RX_DATA_TIMOUT+10)) { + if(BlueWireData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) { if(bReportOEMresync) { DebugPort.printf("Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed); @@ -719,7 +751,7 @@ void loop() // update temperature reading, // synchronised with serial reception as interrupts do get disabled in the OneWire library tDelta = timenow - lastTemperatureTime; - if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period + if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period lastTemperatureTime = millis(); // reset time to observe temeprature if(TempSensor.readTemperature(fTemperature)) { @@ -928,7 +960,7 @@ void checkDisplayUpdate() { // only update OLED when not processing blue wire if(ScreenManager.checkUpdate()) { - lastAnimationTime = millis() + 100; + lastAnimationTime = millis() + 100; ScreenManager.animate(); ScreenManager.refresh(); // always refresh post major update } @@ -936,7 +968,7 @@ void checkDisplayUpdate() long tDelta = millis() - lastAnimationTime; if(tDelta >= 100) { - lastAnimationTime = millis() + 100; + lastAnimationTime = millis() + 100; if(ScreenManager.animate()) ScreenManager.refresh(); } @@ -1436,4 +1468,9 @@ void updateFilteredData() FilteredSamples.GlowVolts.update(HeaterFrame2.getGlowPlug_Voltage()); FilteredSamples.GlowAmps.update(HeaterFrame2.getGlowPlug_Current()); FilteredSamples.Fan.update(HeaterFrame2.getFan_Actual()); +} + +int sysUptime() +{ + return Clock.get().secondstime() - BootTime; } \ No newline at end of file diff --git a/src/Afterburner/src/Bluetooth/BluetoothAbstract.h b/src/Afterburner/src/Bluetooth/BluetoothAbstract.h index c67f839..ea39a60 100644 --- a/src/Afterburner/src/Bluetooth/BluetoothAbstract.h +++ b/src/Afterburner/src/Bluetooth/BluetoothAbstract.h @@ -23,7 +23,6 @@ #define __BLUETOOTHABSTRACT_H__ #include "../Utility/UtilClasses.h" -//#include "../Utility/DebugPort.h" #include "../Utility/helpers.h" class CProtocol; diff --git a/src/Afterburner/src/Libraries/WiFiManager-dev/WiFiManager.cpp b/src/Afterburner/src/Libraries/WiFiManager-dev/WiFiManager.cpp index 508f031..f9d53ce 100644 --- a/src/Afterburner/src/Libraries/WiFiManager-dev/WiFiManager.cpp +++ b/src/Afterburner/src/Libraries/WiFiManager-dev/WiFiManager.cpp @@ -427,7 +427,7 @@ void WiFiManager::stopWebPortal() { boolean WiFiManager::configPortalHasTimeout(){ if(_configPortalTimeout == 0 || (_apClientCheck && (WiFi_softap_num_stations() > 0))){ - if(millis() - timer > 30000){ + if(millis() - timer > 30000){ timer = millis(); DEBUG_WM(DEBUG_VERBOSE,"NUM CLIENTS: " + (String)WiFi_softap_num_stations()); } @@ -443,7 +443,7 @@ boolean WiFiManager::configPortalHasTimeout(){ } else if(_debugLevel > 0) { // log timeout if(_debug){ - uint16_t logintvl = 30000; // how often to emit timeing out counter logging + uint32_t logintvl = 30000; // how often to emit timing out counter logging if((millis() - timer) > logintvl){ timer = millis(); DEBUG_WM(DEBUG_VERBOSE,F("Portal Timeout In"),(String)((_configPortalStart + _configPortalTimeout-millis())/1000) + (String)F(" seconds")); @@ -861,7 +861,7 @@ uint8_t WiFiManager::waitForConnectResult(uint16_t timeout) { DEBUG_WM(DEBUG_VERBOSE,timeout,F("ms timeout, waiting for connect...")); uint8_t status = WiFi.status(); - while(millis() < timeoutmillis) { + while((millis() - timeoutmillis) < 0) { status = WiFi.status(); // @todo detect additional states, connect happens, then dhcp then get ip, there is some delay here, make sure not to timeout if waiting on IP if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) { diff --git a/src/Afterburner/src/Libraries/arduinoWebSockets/src/WebSockets.h b/src/Afterburner/src/Libraries/arduinoWebSockets/src/WebSockets.h index f7b9649..46768b3 100644 --- a/src/Afterburner/src/Libraries/arduinoWebSockets/src/WebSockets.h +++ b/src/Afterburner/src/Libraries/arduinoWebSockets/src/WebSockets.h @@ -81,7 +81,7 @@ #endif -#define WEBSOCKETS_TCP_TIMEOUT (2000) +#define WEBSOCKETS_TCP_TIMEOUT (2000) #define NETWORK_ESP8266_ASYNC (0) #define NETWORK_ESP8266 (1) diff --git a/src/Afterburner/src/Libraries/esp32FOTA/src/esp32fota.cpp b/src/Afterburner/src/Libraries/esp32FOTA/src/esp32fota.cpp index 80492b8..6c605f7 100644 --- a/src/Afterburner/src/Libraries/esp32FOTA/src/esp32fota.cpp +++ b/src/Afterburner/src/Libraries/esp32FOTA/src/esp32fota.cpp @@ -54,7 +54,7 @@ void esp32FOTA::execOTA() unsigned long timeout = millis(); while (client.available() == 0) { - if (millis() - timeout > 5000) + if (millis() - timeout > 5000) { Serial.println("Client Timeout !"); client.stop(); diff --git a/src/Afterburner/src/OLED/ScreenManager.cpp b/src/Afterburner/src/OLED/ScreenManager.cpp index ba1f7ca..63e5e27 100644 --- a/src/Afterburner/src/OLED/ScreenManager.cpp +++ b/src/Afterburner/src/OLED/ScreenManager.cpp @@ -520,14 +520,14 @@ CScreenManager::keyHandler(uint8_t event) if(event & keyReleased) { _dim(false); _DimTime_ms = (millis() + abs(dimTime)) | 1; - _MenuTimeout = millis() + NVstore.getUserSettings().menuTimeout; + _MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1; } return; // initial press when dimmed is always thrown away } // _dim(false); _DimTime_ms = (millis() + abs(dimTime)) | 1; - _MenuTimeout = millis() + NVstore.getUserSettings().menuTimeout; + _MenuTimeout = (millis() + NVstore.getUserSettings().menuTimeout) | 1; // call key handler for active screen if(_menu >= 0) diff --git a/src/Afterburner/src/Protocol/TxManage.cpp b/src/Afterburner/src/Protocol/TxManage.cpp index c4d76b7..9cf8e7c 100644 --- a/src/Afterburner/src/Protocol/TxManage.cpp +++ b/src/Afterburner/src/Protocol/TxManage.cpp @@ -200,7 +200,6 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) break; } } -// m_TxFrame.setThermostatMode(NVstore.getThermostatMode()); m_TxFrame.Controller.OperatingVoltage = NVstore.getHeaterTuning().sysVoltage; m_TxFrame.Controller.FanSensor = NVstore.getHeaterTuning().fanSensor; diff --git a/src/Afterburner/src/Protocol/TxManage.h b/src/Afterburner/src/Protocol/TxManage.h index d6dcfca..60a139f 100644 --- a/src/Afterburner/src/Protocol/TxManage.h +++ b/src/Afterburner/src/Protocol/TxManage.h @@ -23,9 +23,9 @@ class CTxManage { - const int m_nStartDelay = 20; - const int m_nFrameTime = 14; - const int m_nFrontPorch = 2; + const int m_nStartDelay = 20; + const int m_nFrameTime = 14; + const int m_nFrontPorch = 2; public: CTxManage(int TxGatePin, HardwareSerial& serial); diff --git a/src/Afterburner/src/Utility/BTC_JSON.cpp b/src/Afterburner/src/Utility/BTC_JSON.cpp index 570b18a..30f88e3 100644 --- a/src/Afterburner/src/Utility/BTC_JSON.cpp +++ b/src/Afterburner/src/Utility/BTC_JSON.cpp @@ -22,6 +22,7 @@ #include "BTC_JSON.h" #include "DebugPort.h" #include "NVStorage.h" +#include "../RTC/Clock.h" #include "../RTC/BTCDateTime.h" #include "../RTC/Timers.h" #include "../RTC/TimerManager.h" @@ -39,9 +40,17 @@ int timerConflict = 0; CModerator MQTTmoderator; CModerator IPmoderator; CModerator GPIOmoderator; +CModerator SysModerator; void validateTimer(int ID); void Expand(std::string& str); +bool makeJSONString(CModerator& moderator, char* opStr, int len); +bool makeJSONStringEx(CModerator& moderator, char* opStr, int len); +bool makeJSONTimerString(int channel, char* opStr, int len); +bool makeJSONStringGPIO( CModerator& moderator, char* opStr, int len); +bool makeJSONStringSysInfo(CModerator& moderator, char* opStr, int len); +bool makeJSONStringMQTT(CModerator& moderator, char* opStr, int len); +bool makeJSONStringIP(CModerator& moderator, char* opStr, int len); void interpretJsonCommand(char* pLine) { @@ -183,6 +192,10 @@ void interpretJsonCommand(char* pLine) else if(strcmp("IQuery", it->key) == 0) { IPmoderator.reset(); // force IP params to be sent } + // system info + else if(strcmp("SQuery", it->key) == 0) { + SysModerator.reset(); // force MQTT params to be sent + } // MQTT parameters else if(strcmp("MQuery", it->key) == 0) { MQTTmoderator.reset(); // force MQTT params to be sent @@ -399,6 +412,34 @@ bool makeJSONStringMQTT(CModerator& moderator, char* opStr, int len) return bSend; } + +bool makeJSONStringSysInfo(CModerator& moderator, char* opStr, int len) +{ + StaticJsonBuffer<800> jsonBuffer; // create a JSON buffer on the stack + JsonObject& root = jsonBuffer.createObject(); // create object to add JSON commands to + + bool bSend = false; // reset should send flag + + const BTCDateTime& now = Clock.get(); + + char str[32]; + sprintf(str, "%02d:%02d:%02d", now.hour(), now.minute(), now.second()); + bSend |= moderator.addJson("Time", str, root); + sprintf(str, "%d %s %d", now.day(), now.monthStr(), now.year()); + bSend |= moderator.addJson("Date", str, root); + bSend |= moderator.addJson("UpTime", sysUptime(), root); + bSend |= moderator.addJson("SysVer", getVersionStr(), root); + bSend |= moderator.addJson("SysDate", getVersionDate(), root); + bSend |= moderator.addJson("SysFreeMem", ESP.getFreeHeap(), root); +// bSend |= moderator.addJson("TickCount", millis(), root); // ms! + + if(bSend) { + root.printTo(opStr, len); + } + + return bSend; +} + bool makeJSONStringIP(CModerator& moderator, char* opStr, int len) { StaticJsonBuffer<800> jsonBuffer; // create a JSON buffer on the stack @@ -501,7 +542,7 @@ void updateJSONclients(bool report) } } - // report MQTT params + // report IP params { if(makeJSONStringIP(IPmoderator, jsonStr, sizeof(jsonStr))) { if (report) { @@ -514,6 +555,19 @@ void updateJSONclients(bool report) } } + // report System info + { + if(makeJSONStringSysInfo(SysModerator, jsonStr, sizeof(jsonStr))) { + if (report) { + DebugPort.printf("JSON send: %s\r\n", jsonStr); + } + sendWebSocketString( jsonStr ); + std::string expand = jsonStr; + Expand(expand); + getBluetoothClient().send( expand.c_str() ); + } + } + { if(makeJSONStringGPIO(GPIOmoderator, jsonStr, sizeof(jsonStr))) { if (report) { @@ -539,6 +593,7 @@ void resetJSONmoderator() #endif initMQTTJSONmoderator(); initIPJSONmoderator(); + initSysModerator(); GPIOmoderator.reset(); } @@ -554,6 +609,12 @@ void initIPJSONmoderator() makeJSONStringIP(IPmoderator, jsonStr, sizeof(jsonStr)); } +void initSysModerator() +{ + char jsonStr[800]; + makeJSONStringSysInfo(SysModerator, jsonStr, sizeof(jsonStr)); +} + void initTimerJSONmoderator() { char jsonStr[800]; diff --git a/src/Afterburner/src/Utility/BTC_JSON.h b/src/Afterburner/src/Utility/BTC_JSON.h index b42efa9..29dfd3f 100644 --- a/src/Afterburner/src/Utility/BTC_JSON.h +++ b/src/Afterburner/src/Utility/BTC_JSON.h @@ -27,16 +27,11 @@ extern char defaultJSONstr[64]; -bool makeJSONString(CModerator& moderator, char* opStr, int len); -bool makeJSONStringEx(CModerator& moderator, char* opStr, int len); -bool makeJSONTimerString(int channel, char* opStr, int len); -bool makeJSONStringGPIO( CModerator& moderator, char* opStr, int len); void updateJSONclients(bool report); -bool makeJSONStringMQTT(CModerator& moderator, char* opStr, int len); -bool makeJSONStringIP(CModerator& moderator, char* opStr, int len); void initMQTTJSONmoderator(); void initIPJSONmoderator(); void initTimerJSONmoderator(); +void initSysModerator(); template const char* createJSON(const char* name, T value) diff --git a/src/Afterburner/src/Utility/Moderator.h b/src/Afterburner/src/Utility/Moderator.h index 07ad61a..3c5f2c9 100644 --- a/src/Afterburner/src/Utility/Moderator.h +++ b/src/Afterburner/src/Utility/Moderator.h @@ -106,6 +106,7 @@ void TModerator::reset(const char* name) } class CModerator { + TModerator u32Moderator; TModerator iModerator; TModerator fModerator; TModerator ucModerator; @@ -115,6 +116,12 @@ public: bool addJson(const char* name, int value, JsonObject& root) { return iModerator.addJson(name, value, root); }; + bool addJson(const char* name, uint32_t value, JsonObject& root) { + return u32Moderator.addJson(name, value, root); + }; + bool addJson(const char* name, unsigned long value, JsonObject& root) { + return u32Moderator.addJson(name, value, root); + }; // float values bool addJson(const char* name, float value, JsonObject& root) { return fModerator.addJson(name, value, root); @@ -133,12 +140,14 @@ public: fModerator.reset(); ucModerator.reset(); szModerator.reset(); + u32Moderator.reset(); }; void reset(const char* name) { iModerator.reset(name); fModerator.reset(name); ucModerator.reset(name); szModerator.reset(name); + u32Moderator.reset(name); }; }; diff --git a/src/Afterburner/src/Utility/helpers.h b/src/Afterburner/src/Utility/helpers.h index 9303091..ba38223 100644 --- a/src/Afterburner/src/Utility/helpers.h +++ b/src/Afterburner/src/Utility/helpers.h @@ -82,6 +82,7 @@ extern float getBatteryVoltage(); extern float getGlowVolts(); extern float getGlowCurrent(); extern float getFanSpeed(); +extern int sysUptime(); extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal); diff --git a/src/Afterburner/src/WiFi/BTCWebServer.cpp b/src/Afterburner/src/WiFi/BTCWebServer.cpp index dd11bf6..50df0ad 100644 --- a/src/Afterburner/src/WiFi/BTCWebServer.cpp +++ b/src/Afterburner/src/WiFi/BTCWebServer.cpp @@ -46,6 +46,7 @@ extern const char* stdHeader; extern const char* formatIndex; extern const char* updateIndex; extern const char* formatDoneContent; +extern const char* rebootIndex; File fsUploadFile; // a File object to temporarily store the received file int SPIFFSupload = 0; @@ -71,11 +72,14 @@ void onErase(); void onFormatSPIFFS(); void onFormatNow(); void onFormatDone(); +void onReboot(); +void onDoReboot(); void onWMConfig(); void onResetWifi(); void onUploadBegin(); void onUploadCompletion(); void onUploadProgression(); +void onRename(); void build404Response(String& content, String file); void build500Response(String& content, String file); @@ -121,7 +125,12 @@ void initWebServer(void) { }); server.on("/formatnow", HTTP_POST, onFormatNow); // access via POST is legal, but only if bFormatAccess == true - // NOTE: this serves the default home page, and favicon.ico + server.on("/reboot", HTTP_GET, onReboot); // access via POST is legal, but only if bFormatAccess == true + server.on("/reboot", HTTP_POST, onDoReboot); // access via POST is legal, but only if bFormatAccess == true + + server.on("/rename", HTTP_POST, onRename); // access via POST is legal, but only if bFormatAccess == true + +// NOTE: this serves the default home page, and favicon.ico server.onNotFound([]() { // If the client requests any URI if (!handleFileRead(server.uri())) { // send it if it exists @@ -153,6 +162,8 @@ String getContentType(String filename) { // convert the file extension to the MI else if (filename.endsWith(".js")) return "application/javascript"; else if (filename.endsWith(".ico")) return "image/x-icon"; else if (filename.endsWith(".bin")) return "application/octet-stream"; + else if (filename.endsWith(".zip")) return "application/x-zip"; + else if (filename.endsWith(".gz")) return "application/x-gzip"; return "text/plain"; } @@ -160,7 +171,10 @@ bool handleFileRead(String path) { // send the right file to the client (if it e DebugPort.println("handleFileRead: " + path); if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file String contentType = getContentType(path); // Get the MIME type - if (SPIFFS.exists(path)) { // If the file exists + String pathWithGz = path + ".gz"; + if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists as a compressed archive, or normal + if (SPIFFS.exists(pathWithGz)) // If the compressed file exists + path += ".gz"; File file = SPIFFS.open(path, "r"); // Open it if(!checkFile(file)) { // check it is readable file.close(); // if not, close the file @@ -200,6 +214,7 @@ button { background-color: #016ABC; color: #fff; border-radius: 25px; + height: 30px; } .del { color: white; @@ -209,7 +224,7 @@ button { height: 30px; width: 30px; } -.fmt { +.redbutton { color: white; font-weight: bold; background-color: red; @@ -226,6 +241,12 @@ th { } } + + )====="; const char* updateIndex = R"=====( @@ -269,10 +290,6 @@ var ws; var timeDown; var timeUp; -function _(el) { - return document.getElementById(el); -} - function onWebSocket(event) { var response = JSON.parse(event.data); var key; @@ -352,6 +369,19 @@ function onErase(fn) { } } +function onRename(fn) { + var newname = prompt("Enter new file name", fn); + if(newname != null && newname != "") { + var formdata = new FormData(); + formdata.append("oldname", fn); + formdata.append("newname", newname); + var ajax = new XMLHttpRequest(); + ajax.open("POST", "/rename"); + ajax.send(formdata); + setTimeout(function () { location.reload(); }, 500); + } +} + function onBrowseChange() { _("uploaddiv").hidden = false; _("upload").hidden = false; @@ -359,7 +389,7 @@ function onBrowseChange() { _("loaded_n_total").hidden = false; _("spacer").hidden = false; var file = _("file1").files[0]; - document.getElementById('filename').innerHTML = file.name; + _('filename').innerHTML = file.name; } function onformatClick() { @@ -538,6 +568,7 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi Name Size + )====="; File file = root.openNextFile(); @@ -548,6 +579,7 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi addTableData(HTMLreport, file.name()); addTableData(HTMLreport, ""); addTableData(HTMLreport, ""); + addTableData(HTMLreport, ""); sprintf(msg, " DIR : %s", file.name()); DebugPort.println(msg); @@ -558,18 +590,28 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi } else { String fn = file.name(); String ers; - if(withHTMLanchors == 2) + String rename; + if(withHTMLanchors == 2) { + rename = ""; ers = ""; + } if(withHTMLanchors) { + String fn2; if(fn.endsWith(".html") || fn.endsWith(".htm")) { - String fn2(fn); - fn = "" + fn2 + ""; + fn2 = fn; + } + else if(fn.endsWith(".html.gz") || fn.endsWith(".htm.gz")) { + fn2 = fn.substring(0, fn.length()-3); + } + if(fn2.length() != 0) { + fn = "" + file.name() + ""; } } String sz; sz += int(file.size()); addTableData(HTMLreport, ""); addTableData(HTMLreport, fn); addTableData(HTMLreport, sz); + addTableData(HTMLreport, rename); addTableData(HTMLreport, ers); sprintf(msg, " FILE: %s SIZE: %d", fn.c_str(), file.size()); @@ -662,7 +704,7 @@ void onUploadBegin() listSPIFFS("/", 2, SPIFFSinfo, 2); String content = stdHeader; content += updateIndex + SPIFFSinfo; - content += "

"; + content += "

"; content += ""; server.send(200, "text/html", content ); #else @@ -741,7 +783,7 @@ void onUploadProgression() Update.printError(DebugPort); } } - DebugPort.setDebugOutput(false); +// DebugPort.setDebugOutput(false); bUpdateAccessed = false; } else { DebugPort.printf("Update Failed Unexpectedly (likely broken connection): status=%d\r\n", upload.status); @@ -826,7 +868,7 @@ function init() { function onFormat() { var formdata = new FormData(); if(confirm('Do you really want to reformat the SPIFFS partition ?')) { - document.getElementById('throb').innerHTML = 'FORMATTING - Please wait'; + _('throb').innerHTML = 'FORMATTING - Please wait'; formdata.append('confirm', 'yes'); setTimeout(function () { location.reload(); }, 200); } @@ -844,7 +886,7 @@ function onFormat() {

Format SPIFFS partition

CAUTION! This will erase all web content

-


+


@@ -868,6 +910,71 @@ void onFormatNow() } } +void onReboot() +{ + DebugPort.println("WEB: GET /reboot"); + String content = stdHeader; + content += rebootIndex; + server.send(200, "text/html", content ); +} + +void onDoReboot() +{ + // HTTP POST handler, do not need to return a web page! + DebugPort.println("WEB: POST /reboot"); + String confirm = server.arg("reboot"); // get request argument value by name + if(confirm == "yes") { // confirm user agrees, and we did pass thru /formatspiffs first + DebugPort.println("Rebooting via /reboot"); + ESP.restart(); + } +} + +const char* rebootIndex = R"=====( + + +Afterburner Reboot + + +

Reboot Afterburner

+

+

+ +     + + +)====="; + + +void onRename() +{ + // HTTP POST handler, do not need to return a web page! + DebugPort.println("WEB: POST /reboot"); + String oldname = server.arg("oldname"); // get request argument value by name + String newname = server.arg("newname"); // get request argument value by name + if(oldname != "" && newname != "") { + DebugPort.printf("Renaming %s to %s\r\n", oldname.c_str(), newname.c_str()); + SPIFFS.rename(oldname.c_str(), newname.c_str()); + } +} /*************************************************************************************** @@ -919,7 +1026,7 @@ content += R"=====(" exists, but cannot be streamed?

Recommended remedy is to re-format the SPIFFS partition, then reload the web content files.
Latest web content can be downloaded from http://www.mrjones.id.au/afterburner/firmware.html (opens in new page). -

To format the SPIFFS partition, press +

To format the SPIFFS partition, press

You will then need to upload each file of the web content by using the subsequent "Upload" button.


Please ensure you unzip the web page content, then upload all the files contained.

diff --git a/src/Afterburner/src/WiFi/BTCWifi.cpp b/src/Afterburner/src/WiFi/BTCWifi.cpp index 80ad9d0..fcb9e2e 100644 --- a/src/Afterburner/src/WiFi/BTCWifi.cpp +++ b/src/Afterburner/src/WiFi/BTCWifi.cpp @@ -47,6 +47,7 @@ int TRIG_PIN; // pin that triggers the configuration portal unsigned restartServer = 0; // set to time of portal reconfig - will cause reboot a while later char MACstr[2][20]; // MACstr[0] STA, MACstr[1] = AP int wifiButtonState = 0; +unsigned long WifiReconnectHoldoff = 0; extern CScreenManager ScreenManager; @@ -87,8 +88,10 @@ bool initWifi(int initpin,const char *failedssid, const char *failedpassword) DebugPort.println("Attempting to start STA mode (or config portal) via WifiManager..."); wm.setHostname(failedssid); + wm.setDebugOutput(true); wm.setConfigPortalTimeout(20); wm.setConfigPortalBlocking(false); + wm.setWiFiAutoReconnect(true); wm.setSaveParamsCallback(saveParamsCallback); // ensure our webserver gets awoken when IP config changes to STA wm.setAPCallback(APstartedCallback); wm.setEnableConfigPortal(shouldBootIntoConfigPortal()); @@ -143,6 +146,26 @@ void doWiFiManager() { wm.process(); +/* if(WiFi.status() != WL_CONNECTED) { + if(WifiReconnectHoldoff) { + long tDelta = millis() - WifiReconnectHoldoff; + if(tDelta >= 0) { + WifiReconnectHoldoff = 0; + WiFi.disconnect(); + WiFi.mode(WIFI_AP_STA); + wifi_config_t conf; + esp_wifi_get_config(WIFI_IF_STA, &conf); + WiFi.begin((char*)conf.sta.ssid, (char*)conf.sta.password); + } + } + else { + WifiReconnectHoldoff = millis() + 10000; + } + } + else { + WifiReconnectHoldoff = 0; + }*/ + #if USE_PORTAL_TRIGGER_PIN == 1 // manage handling of pin to enter WiFManager config portal // we typically use the BOOT pin for this (pins.h) @@ -270,21 +293,17 @@ void APstartedCallback(WiFiManager*) const char* getWifiAPAddrStr() { -// noInterrupts(); IPAddress IPaddr = WiFi.softAPIP(); // use stepping stone - function returns an automatic stack var - LAME! static char APIPaddr[16]; sprintf(APIPaddr, "%d.%d.%d.%d", IPaddr[0], IPaddr[1], IPaddr[2], IPaddr[3]); -// interrupts(); return APIPaddr; } const char* getWifiSTAAddrStr() { -// noInterrupts(); IPAddress IPaddr = WiFi.localIP(); // use stepping stone - function returns an automatic stack var - LAME! static char STAIPaddr[16]; sprintf(STAIPaddr, "%d.%d.%d.%d", IPaddr[0], IPaddr[1], IPaddr[2], IPaddr[3]); -// interrupts(); return STAIPaddr; }