From 28bfb28ff62f9caaf8b3cbd02f851afc3928395b Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Wed, 22 Apr 2020 16:42:24 +1000 Subject: [PATCH 1/4] Adding web authorisation --- src/Afterburner.cpp | 2 +- src/Utility/NVStorage.cpp | 4 ++++ src/Utility/NVStorage.h | 6 ++++++ src/WiFi/BTCWebServer.cpp | 8 ++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index 9ec3163..7962fe6 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -1623,7 +1623,7 @@ void feedWatchdog() #if USE_SW_WATCHDOG == 1 && USE_JTAG == 0 // BEST NOT USE WATCHDOG WITH JTAG DEBUG :-) // DebugPort.printf("\r %ld Watchdog fed", millis()); - DebugPort.print("~"); + // DebugPort.print("~"); WatchdogTick = 1500; #else WatchdogTick = -1; diff --git a/src/Utility/NVStorage.cpp b/src/Utility/NVStorage.cpp index 5175b63..2191f6d 100644 --- a/src/Utility/NVStorage.cpp +++ b/src/Utility/NVStorage.cpp @@ -600,6 +600,8 @@ sCredentials::load() validatedLoad("APpassword", APpassword, 31, "thereisnospoon"); validatedLoad("webUpdateUser", webUpdateUsername, 31, "Afterburner"); validatedLoad("webUpdatePass", webUpdatePassword, 31, "BurnBabyBurn"); + validatedLoad("webUser", webUsername, 31, "Afterburner"); + validatedLoad("webPass", webPassword, 31, "WebAccess"); preferences.end(); } @@ -612,6 +614,8 @@ sCredentials::save() preferences.putString("APpassword", APpassword); preferences.putString("webUpdateUser", webUpdateUsername); preferences.putString("webUpdatePass", webUpdatePassword); + preferences.putString("webUser", webUsername); + preferences.putString("webPass", webPassword); preferences.end(); } diff --git a/src/Utility/NVStorage.h b/src/Utility/NVStorage.h index 490c2de..b902adb 100644 --- a/src/Utility/NVStorage.h +++ b/src/Utility/NVStorage.h @@ -235,11 +235,15 @@ struct sCredentials : public CESP32_NVStorage { char APpassword[32]; char webUpdateUsername[32]; char webUpdatePassword[32]; + char webUsername[32]; + char webPassword[32]; void init() { strcpy(APSSID, "Afterburner"); strcpy(APpassword, "thereisnospoon"); strcpy(webUpdateUsername, "Afterburner"); strcpy(webUpdatePassword, "BurnBabyBurn"); + strcpy(webUsername, "Afterburner"); + strcpy(webPassword, "WebAccess"); }; void load(); void save(); @@ -249,6 +253,8 @@ struct sCredentials : public CESP32_NVStorage { strcpy(APpassword, rhs.APpassword); strcpy(webUpdateUsername, rhs.webUpdateUsername); strcpy(webUpdatePassword, rhs.webUpdatePassword); + strcpy(webUsername, rhs.webUsername); + strcpy(webPassword, rhs.webPassword); return *this; } }; diff --git a/src/WiFi/BTCWebServer.cpp b/src/WiFi/BTCWebServer.cpp index 53f1d10..70d987c 100644 --- a/src/WiFi/BTCWebServer.cpp +++ b/src/WiFi/BTCWebServer.cpp @@ -177,6 +177,14 @@ String getContentType(String filename) { // convert the file extension to the MI bool handleFileRead(String path) { // send the right file to the client (if it exists) DebugPort.println("handleFileRead: " + path); if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file + if(path.indexOf("index.html") >= 0) { + sCredentials creds = NVstore.getCredentials(); + if (!server.authenticate(creds.webUsername, creds.webPassword)) { + server.requestAuthentication(); + return true; // not entirely correct, but avoids 404 response + } + } + path.replace("%20", " "); // convert HTML spaces to normal spaces String contentType = getContentType(path); // Get the MIME type String pathWithGz = path + ".gz"; From 5cdc5c95a5ddb331b43d4c40851543196eb149c6 Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Sat, 25 Apr 2020 09:23:16 +1000 Subject: [PATCH 2/4] Moved Blue Wire Comms to a separate task --- src/Afterburner.cpp | 850 +++++++++++++--------------------- src/Protocol/BlueWireTask.cpp | 478 +++++++++++++++++++ src/Protocol/BlueWireTask.h | 40 ++ src/Protocol/Protocol.cpp | 21 +- src/Protocol/Protocol.h | 4 +- src/Protocol/SmartError.cpp | 3 +- src/Protocol/TxManage.cpp | 35 +- src/Protocol/TxManage.h | 2 + src/Utility/DebugPort.h | 2 +- src/Utility/TempSense.cpp | 26 +- src/Utility/TempSense.h | 10 +- src/Utility/UtilClasses.cpp | 28 +- src/Utility/UtilClasses.h | 31 +- src/Utility/helpers.h | 5 +- src/WiFi/BTCWifi.cpp | 2 +- src/WiFi/BrowserUpload.cpp | 6 - 16 files changed, 958 insertions(+), 585 deletions(-) create mode 100644 src/Protocol/BlueWireTask.cpp create mode 100644 src/Protocol/BlueWireTask.h diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index 7962fe6..8cb0e24 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -120,12 +120,13 @@ #include "RTC/TimerManager.h" #include "Utility/GetLine.h" #include "Utility/DemandManager.h" +#include "Protocol/BlueWireTask.h" // SSID & password now stored in NV storage - these are still the default values. //#define AP_SSID "Afterburner" //#define AP_PASSWORD "thereisnospoon" -#define RX_DATA_TIMOUT 50 +// #define RX_DATA_TIMOUT 50 const int FirmwareRevision = 32; const int FirmwareSubRevision = 0; @@ -139,17 +140,6 @@ const char* FirmwareDate = "11 Apr 2020"; #include "Bluetooth/BluetoothHC05.h" #endif -// Setup Serial Port Definitions -#if defined(__arm__) -// Required for Arduino Due, UARTclass is derived from HardwareSerial -static UARTClass& BlueWireSerial(Serial1); -#else -// for ESP32, Mega -// HardwareSerial is it for these boards -static HardwareSerial& BlueWireSerial(Serial1); -#endif - -void initBlueWireSerial(); bool validateFrame(const CProtocol& frame, const char* name); void checkDisplayUpdate(); void checkDebugCommands(); @@ -159,7 +149,7 @@ void manageHumidity(); void doStreaming(); void heaterOn(); void heaterOff(); -void updateFilteredData(); +void updateFilteredData(CProtocol& HeaterInfo); bool HandleMQTTsetup(char rxVal); void showMainmenu(); @@ -178,16 +168,11 @@ bool bReportStack = false; unsigned long lastAnimationTime; // used to sequence updates to LCD for animation sFilteredData FilteredSamples; -CommStates CommState; -CTxManage TxManage(TxEnbPin, BlueWireSerial); -CModeratedFrame OEMCtrlFrame; // data packet received from heater in response to OEM controller packet -CModeratedFrame HeaterFrame1; // data packet received from heater in response to OEM controller packet -CProtocol HeaterFrame2; // data packet received from heater in response to our packet -CProtocol DefaultBTCParams(CProtocol::CtrlMode); // defines the default parameters, used in case of no OEM controller CSmartError SmartError; CKeyPad KeyPad; CScreenManager ScreenManager; ABTelnetSpy DebugPort; + #if USE_JTAG == 0 //CANNOT USE GPIO WITH JTAG DEBUG CGPIOin GPIOin; @@ -199,11 +184,6 @@ CMQTTsetup MQTTmenu; -long lastRxTime; // used to observe inter character delays -bool bHasOEMController = false; -bool bHasOEMLCDController = false; -bool bHasHtrData = false; - // these variables will persist over a soft reboot. __NOINIT_ATTR float persistentRunTime; __NOINIT_ATTR float persistentGlowTime; @@ -217,10 +197,10 @@ bool bReportJSONData = REPORT_JSON_TRANSMIT; bool bReportRecyleEvents = REPORT_BLUEWIRE_RECYCLES; bool bReportOEMresync = REPORT_OEM_RESYNC; -CProtocolPackage reportHeaterData; -CProtocolPackage primaryHeaterData; +CProtocol BlueWireRxData; +CProtocol BlueWireTxData; +CProtocolPackage BlueWireData; -unsigned long moderator; bool bUpdateDisplay = false; bool bHaveWebClient = false; bool bBTconnected = false; @@ -281,6 +261,14 @@ CBluetoothAbstract& getBluetoothClient() return Bluetooth; } +// collect and report any debug messages from the blue wire task +char taskMsg[BLUEWIRE_MSGQUEUESIZE]; +void checkBlueWireDebugMsgs() +{ + if(BlueWireMsgBuf && xQueueReceive(BlueWireMsgBuf, taskMsg, 0)) + DebugPort.print(taskMsg); +} + // callback function for Keypad events. // must be an absolute function, cannot be a class member due the "this" element! void parentKeyHandler(uint8_t event) @@ -462,31 +450,11 @@ void setup() { pinMode(LED_Pin, OUTPUT); // On board LED indicator digitalWrite(LED_Pin, LOW); - initBlueWireSerial(); - - // prepare for first long delay detection - lastRxTime = millis(); - - TxManage.begin(); // ensure Tx enable pin is setup - - // define defaults should OEM controller be missing - DefaultBTCParams.setHeaterDemand(23); - DefaultBTCParams.setTemperature_Actual(22); - DefaultBTCParams.setSystemVoltage(12.0); - DefaultBTCParams.setPump_Min(1.6f); - DefaultBTCParams.setPump_Max(5.5f); - DefaultBTCParams.setFan_Min(1680); - DefaultBTCParams.setFan_Max(4500); - DefaultBTCParams.Controller.FanSensor = 1; - bBTconnected = false; Bluetooth.begin(); setupGPIO(); -// pinMode(0, OUTPUT); -// digitalWrite(0, LOW); - #if USE_SW_WATCHDOG == 1 && USE_JTAG == 0 // create a high priority FreeRTOS task as a watchdog monitor TaskHandle_t wdTask; @@ -553,6 +521,15 @@ void setup() { TempSensor.getDS18B20().mapSensor(1, NVstore.getHeaterTuning().DS18B20probe[1].romCode); TempSensor.getDS18B20().mapSensor(2, NVstore.getHeaterTuning().DS18B20probe[2].romCode); + // create task to run blue wire interface + TaskHandle_t bwTask; + xTaskCreate(BlueWireTask, + "BlueWireTask", + 2000, + NULL, + 3, + &bwTask); + delay(1000); // just to hold the splash screeen for while } @@ -570,386 +547,89 @@ void loop() // DebugPort.handle(); // keep telnet spy alive - ////////////////////////////////////////////////////////////////////////////////////// - // Blue wire data reception - // Reads data from the "blue wire" Serial port, (to/from heater) - // If an OEM controller exists we will also see it's data frames - // Note that the data is read now, then held for later use in the state machine - // - sRxData BlueWireData; + // report any debug messages from the blue wire task + checkBlueWireDebugMsgs(); - // calc elapsed time since last rxd byte - // used to detect no OEM controller, or the start of an OEM frame sequence - unsigned long RxTimeElapsed = timenow - lastRxTime; - - if (BlueWireSerial.available()) { - // Data is available, read and store it now, use it later - // Note that if not in a recognised data receive frame state, the data - // will be deliberately lost! - BlueWireData.setValue(BlueWireSerial.read()); // read hex byte, store for later use + feedWatchdog(); // feed watchdog - lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes - } + doStreaming(); // do wifi, BT tx etc + Clock.update(); + checkDisplayUpdate(); - // precautionary state machine action if all 24 bytes were not received - // whilst expecting a frame from the blue wire - if(RxTimeElapsed > RX_DATA_TIMOUT) { + long tDelta = timenow - lastTemperatureTime; + if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period + lastTemperatureTime = millis(); // reset time to observe temeprature - if(NVstore.getUserSettings().menuMode == 2) - bReportRecyleEvents = false; - - if( CommState.is(CommStates::OEMCtrlRx) || - CommState.is(CommStates::HeaterRx1) || - CommState.is(CommStates::HeaterRx2) ) { - - if(RxTimeElapsed >= moderator) { - moderator += 10; - if(bReportRecyleEvents) { - DebugPort.printf("%ldms - ", RxTimeElapsed); - } - if(CommState.is(CommStates::OEMCtrlRx)) { - bHasOEMController = false; - bHasOEMLCDController = false; - if(bReportRecyleEvents) - DebugPort.println("Timeout collecting OEM controller data, returning to Idle State"); - } - else if(CommState.is(CommStates::HeaterRx1)) { - bHasHtrData = false; - if(bReportRecyleEvents) - DebugPort.println("Timeout collecting OEM heater response data, returning to Idle State"); - } - else { - bHasHtrData = false; - if(bReportRecyleEvents) - DebugPort.println("Timeout collecting BTC heater response data, returning to Idle State"); - } - } - - if(bReportRecyleEvents) - DebugPort.println("Recycling blue wire serial interface"); - initBlueWireSerial(); - CommState.set(CommStates::TemperatureRead); // revert to idle mode, after passing thru temperature mode + if(bReportStack) { + int stackdepth = uxTaskGetStackHighWaterMark(NULL); + DebugPort.printf("Stack : %d\r\n", stackdepth); } + + TempSensor.readSensors(); + if(TempSensor.getTemperature(0, fTemperature)) { // get Primary sensor temperature + if(DS18B20holdoff) { + DS18B20holdoff--; + DebugPort.printf("Skipped initial DS18B20 reading: %f\r\n", fTemperature); + } // first value upon sensor connect is bad + else { + // exponential mean to stabilse readings + FilteredSamples.AmbientTemp.update(fTemperature); + + manageCyclicMode(); + manageFrostMode(); + manageHumidity(); + } + } + else { + DS18B20holdoff = 3; + FilteredSamples.AmbientTemp.reset(-100.0); + } + + TempSensor.startConvert(); // request a new conversion, will be ready by the time we loop back around + + ScreenManager.reqUpdate(); } - /////////////////////////////////////////////////////////////////////////////////////////// - // do our state machine to track the reception and delivery of blue wire data - - long tDelta; - switch(CommState.get()) { - - case CommStates::Idle: - - feedWatchdog(); // feed watchdog - - 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; - -#if RX_LED == 1 - digitalWrite(LED_Pin, LOW); -#endif - // Detect the possible start of a new frame sequence from an OEM controller - // This will be the first activity for considerable period on the blue wire - // The heater always responds to a controller frame, but otherwise never by itself - - if(RxTimeElapsed >= (NVstore.getUserSettings().FrameRate - 60)) { // compensate for the time spent just doing things in this state machine - // have not seen any receive data for a second. - // OEM controller is probably not connected. - // Skip state machine immediately to BTC_Tx, sending our own settings. - bHasHtrData = false; - bHasOEMController = false; - bHasOEMLCDController = false; - bool isBTCmaster = true; - TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values - TxManage.Start(timenow); - CommState.set(CommStates::BTC_Tx); - break; - } - -#if SUPPORT_OEM_CONTROLLER == 1 - if(BlueWireData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) { - - if(bReportOEMresync) { - DebugPort.printf("Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed); - } - - bHasHtrData = false; - bHasOEMController = true; - CommState.set(CommStates::OEMCtrlRx); // we must add this new byte! - // - // ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skipping break) ** - // ** otherwise the first byte will be lost! ** - // - } - else { - Clock.update(); - checkDisplayUpdate(); - break; // only break if we fail all Idle state tests - } -#else - Clock.update(); - checkDisplayUpdate(); - break; -#endif - - - case CommStates::OEMCtrlRx: - -#if RX_LED == 1 - digitalWrite(LED_Pin, HIGH); -#endif - // collect OEM controller frame - if(BlueWireData.available()) { - if(CommState.collectData(OEMCtrlFrame, BlueWireData.getValue()) ) { - CommState.set(CommStates::OEMCtrlValidate); // collected 24 bytes, move on! - } - } - break; - - - case CommStates::OEMCtrlValidate: -#if RX_LED == 1 - digitalWrite(LED_Pin, LOW); -#endif - // test for valid CRC, abort and restarts Serial1 if invalid - if(!validateFrame(OEMCtrlFrame, "OEM")) { - break; - } - - // filled OEM controller frame - OEMCtrlFrame.setTime(); - // LCD controllers use 0x76 as first byte, rotary knobs use 0x78 - bHasOEMLCDController = (OEMCtrlFrame.Controller.Byte0 != 0x78); - - CommState.set(CommStates::HeaterRx1); - break; - - - case CommStates::HeaterRx1: -#if RX_LED == 1 - digitalWrite(LED_Pin, HIGH); -#endif - // collect heater frame, always in response to an OEM controller frame - if(BlueWireData.available()) { - if( CommState.collectData(HeaterFrame1, BlueWireData.getValue()) ) { - CommState.set(CommStates::HeaterValidate1); - } - } - break; - - - case CommStates::HeaterValidate1: -#if RX_LED == 1 - digitalWrite(LED_Pin, LOW); -#endif - - // test for valid CRC, abort and restarts Serial1 if invalid - if(!validateFrame(HeaterFrame1, "RX1")) { - bHasHtrData = false; - break; - } - bHasHtrData = true; - - // received heater frame (after controller message), report - - // do some monitoring of the heater state variable - // if abnormal transitions, introduce a smart error! - // This routine also cancels ON/OFF requests if runstate in startup/shutdown periods - SmartError.monitor(HeaterFrame1); - - HeaterFrame1.setTime(); - - while(BlueWireSerial.available()) { - DebugPort.println("DUMPED ROGUE RX DATA"); - BlueWireSerial.read(); - } - BlueWireSerial.flush(); - - primaryHeaterData.set(HeaterFrame1, OEMCtrlFrame); // OEM is always *the* controller - if(bReportBlueWireData) { - primaryHeaterData.reportFrames(true); - CommState.setDelay(20); // let serial get sent before we send blue wire - } - else { - CommState.setDelay(0); - } - CommState.set(CommStates::HeaterReport1); - break; - - - case CommStates::HeaterReport1: - if(CommState.delayExpired()) { - bool isBTCmaster = false; - TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes - TxManage.Start(timenow); - CommState.set(CommStates::BTC_Tx); - } - break; - - - case CommStates::BTC_Tx: - // Handle time interval where we send data to the blue wire - lastRxTime = timenow; // *we* are pumping onto blue wire, track this activity! - if(TxManage.CheckTx(timenow) ) { // monitor progress of our data delivery - CommState.set(CommStates::HeaterRx2); // then await heater repsonse - } - break; - - - case CommStates::HeaterRx2: -#if RX_LED == 1 - digitalWrite(LED_Pin, HIGH); -#endif - // collect heater frame, in response to our control frame - if(BlueWireData.available()) { -#ifdef BADSTARTCHECK - if(!CommState.checkValidStart(BlueWireData.getValue())) { - DebugPort.println("***** Invalid start of frame - restarting Serial port *****"); - initBlueWireSerial(); - CommState.set(CommStates::Idle); - } - else { - if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) { - CommState.set(CommStates::HeaterValidate2); - } - } -#else - if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) { - CommState.set(CommStates::HeaterValidate2); - } -#endif - } - break; - - - case CommStates::HeaterValidate2: -#if RX_LED == 1 - digitalWrite(LED_Pin, LOW); -#endif - - // test for valid CRC, abort and restart Serial1 if invalid - if(!validateFrame(HeaterFrame2, "RX2")) { - bHasHtrData = false; - break; - } - bHasHtrData = true; - - // received heater frame (after our control message), report - - // do some monitoring of the heater state variables - // if abnormal transitions, introduce a smart error! - SmartError.monitor(HeaterFrame2); - - if(!bHasOEMController) // no OEM controller - BTC is *the* controller - primaryHeaterData.set(HeaterFrame2, TxManage.getFrame()); - - if(bReportBlueWireData) { - reportHeaterData.set(HeaterFrame2, TxManage.getFrame()); - reportHeaterData.reportFrames(false); - CommState.setDelay(20); // let serial get sent before we send blue wire - } - else { - CommState.setDelay(0); - } - CommState.set(CommStates::HeaterReport2); - break; - - - case CommStates::HeaterReport2: - if(CommState.delayExpired()) { - CommState.set(CommStates::TemperatureRead); - } - break; - - - case CommStates::TemperatureRead: - // 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 - lastTemperatureTime = millis(); // reset time to observe temeprature - - if(bReportStack) { - int stackdepth = uxTaskGetStackHighWaterMark(NULL); - DebugPort.printf("Stack : %d\r\n", stackdepth); - } - - TempSensor.readSensors(); - if(TempSensor.getTemperature(0, fTemperature)) { // get Primary sensor temperature - if(DS18B20holdoff) { - DS18B20holdoff--; - DebugPort.printf("Skipped initial DS18B20 reading: %f\r\n", fTemperature); - } // first value upon sensor connect is bad - else { - // exponential mean to stabilse readings - FilteredSamples.AmbientTemp.update(fTemperature); - - manageCyclicMode(); - manageFrostMode(); - manageHumidity(); - } - } - else { - DS18B20holdoff = 3; - FilteredSamples.AmbientTemp.reset(-100.0); - } - - TempSensor.startConvert(); // request a new conversion, will be ready by the time we loop back around - - ScreenManager.reqUpdate(); - } - - if(bHasHtrData) { - // apply exponential mean to the anlogue readings for some smoothing - updateFilteredData(); - - // 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()); - SmartError.checkfuelUsage(); - } - - // 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); - } - - pHourMeter->monitor(HeaterFrame2); - } - updateJSONclients(bReportJSONData); - updateMQTT(); - CommState.set(CommStates::Idle); - NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity - break; - } // switch(CommState) - - BlueWireData.reset(); // ensure we flush any used data - -// 21/11/19 vTaskDelay() causes E-07 errors when OEM controller is attached. -// may look at a specific freertos task to handle the blue wire.... - if(!bHasOEMController) { - vTaskDelay(1); // give up for now - allow power lowering... + if(BlueWireSemaphore && xSemaphoreTake(BlueWireSemaphore, 0)) { + updateJSONclients(bReportJSONData); + updateMQTT(); + NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity } + // collect transmitted heater data from blue wire task + if(BlueWireTxQueue && xQueueReceive(BlueWireTxQueue, BlueWireTxData.Data, 0)) { + } + + // collect and process received heater data from blue wire task + if(BlueWireRxQueue && xQueueReceive(BlueWireRxQueue, BlueWireRxData.Data, 0)) { + BlueWireData.set(BlueWireRxData, BlueWireTxData); + SmartError.monitor(BlueWireRxData); + + updateFilteredData(BlueWireRxData); + + FuelGauge.Integrate(BlueWireRxData.getPump_Actual()); + + if(INBOUNDS(BlueWireRxData.getRunState(), 1, 5)) { // check for Low Voltage Cutout + SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); + SmartError.checkfuelUsage(); + } + + // 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)) { + const char* msg = "Forcing cyclic cancel due to error induced shutdown\r\n"; + xQueueSend(BlueWireMsgBuf, msg, 0); + // DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); + RTC_Store.setCyclicEngaged(false); + } + + pHourMeter->monitor(BlueWireRxData); + + } + + } // loop -void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr) -{ - DebugPort.print(hdr); // header - for(int i=0; i<24; i++) { - char str[16]; - sprintf(str, " %02X", Frame.Data[i]); // build 2 dig hex values - DebugPort.print(str); // and print - } - DebugPort.print(ftr); // footer -} void manageCyclicMode() { @@ -1029,36 +709,6 @@ void manageHumidity() } -void initBlueWireSerial() -{ - // initialize serial port to interact with the "blue wire" - // 25000 baud, Tx and Rx channels of Chinese heater comms interface: - // Tx/Rx data to/from heater, - // Note special baud rate for Chinese heater controllers -#if defined(__arm__) || defined(__AVR__) - BlueWireSerial.begin(25000); - pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly -#elif ESP32 - // ESP32 - BlueWireSerial.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin); // need to explicitly specify pins for pin multiplexer! - pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly -#endif -} - -bool validateFrame(const CProtocol& frame, const char* name) -{ - if(!frame.verifyCRC()) { - // Bad CRC - restart blue wire Serial port - DebugPort.printf("\007Bad CRC detected for %s frame - restarting blue wire's serial port\r\n", name); - DebugReportFrame("BAD CRC:", frame, "\r\n"); - initBlueWireSerial(); - CommState.set(CommStates::TemperatureRead); - return false; - } - return true; -} - - CDemandManager::eStartCode requestOn() { @@ -1068,7 +718,7 @@ requestOn() return CDemandManager::eStartLowFuel; } bool LVCOK = 2 != SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); - if(bHasHtrData && LVCOK) { + if(hasHtrData() && LVCOK) { RTC_Store.setCyclicEngaged(true); // for cyclic mode RTC_Store.setFrostOn(false); // cancel frost mode // only start if below appropriate temperature threshold, raised for cyclic mode @@ -1130,11 +780,6 @@ void checkDisplayUpdate() } } -void reqPumpPrime(bool on) -{ - DefaultBTCParams.setPump_Prime(on); -} - void forceBootInit() { RTC_Store.setBootInit(); @@ -1150,11 +795,6 @@ float getTemperatureSensor(int source) } -const CProtocolPackage& getHeaterInfo() -{ - return primaryHeaterData; -} - bool isWebClientConnected() { return bHaveWebClient; @@ -1199,10 +839,22 @@ void checkDebugCommands() } switch(nGetConf) { case 1: - setSSID(line.getString()); + setName(line.getString(), 0); break; case 2: - setAPpassword(pw2.c_str()); + setPassword(pw2.c_str(), 0); + break; + case 3: + setName(line.getString(), 1); + break; + case 4: + setPassword(pw2.c_str(), 1); + break; + case 5: + setName(line.getString(), 2); + break; + case 6: + setPassword(pw2.c_str(), 2); break; } nGetConf = 0; @@ -1276,6 +928,122 @@ void checkDebugCommands() } nGetString = 0; return; + case 10: + if(line.getLen() <= 31) { + nGetConf = 3; + DebugPort.printf("\r\nSet Web page username to %s? (y/n) - ", line.getString()); + } + else { + DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING"); + } + nGetString = 0; + return; + case 11: + pw1 = line.getString(); + pw2 = NVstore.getCredentials().webPassword; + if(pw1 != pw2) { + DebugPort.println("\r\nPassword does not match existing - ABORTING"); + nGetString = 0; + } + else { + nGetString = 12; + DebugPort.print("\r\nPlease enter new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + line.reset(); + line.maskEntry(); + return; + case 12: + pw1 = line.getString(); + if(line.getLen() < 8) { + // ABORT - too short + DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); + nGetString = 0; + } + else if(line.getLen() > 31) { + // ABORT - too long! + DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); + nGetString = 0; + } + else { + nGetString = 13; + DebugPort.print("\r\nPlease confirm new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + line.reset(); + line.maskEntry(); + return; + case 13: + pw2 = line.getString(); + line.reset(); + if(pw1 != pw2) { + DebugPort.println("\r\nNew passwords do not match - ABORTING"); + } + else { + nGetConf = 4; + DebugPort.print("\r\nSet new password (y/n) - "); + } + nGetString = 0; + return; + + case 20: + if(line.getLen() <= 31) { + nGetConf = 5; + DebugPort.printf("\r\nSet Web /update username to %s? (y/n) - ", line.getString()); + } + else { + DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING"); + } + nGetString = 0; + return; + + case 21: + pw1 = line.getString(); + pw2 = NVstore.getCredentials().webUpdatePassword; + if(pw1 != pw2) { + DebugPort.println("\r\nPassword does not match existing - ABORTING"); + nGetString = 0; + } + else { + nGetString = 22; + DebugPort.print("\r\nPlease enter new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + line.reset(); + line.maskEntry(); + return; + case 22: + pw1 = line.getString(); + if(line.getLen() < 8) { + // ABORT - too short + DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); + nGetString = 0; + } + else if(line.getLen() > 31) { + // ABORT - too long! + DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); + nGetString = 0; + } + else { + nGetString = 23; + DebugPort.print("\r\nPlease confirm new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + line.reset(); + line.maskEntry(); + return; + case 23: + pw2 = line.getString(); + line.reset(); + if(pw1 != pw2) { + DebugPort.println("\r\nNew passwords do not match - ABORTING"); + } + else { + nGetConf = 6; + DebugPort.print("\r\nSet new password (y/n) - "); + } + nGetString = 0; + return; } } DebugPort.enable(false); @@ -1347,8 +1115,10 @@ void checkDebugCommands() bReportJSONData = !bReportJSONData; DebugPort.printf("Toggled JSON data reporting %s\r\n", bReportJSONData ? "ON" : "OFF"); } - else if(rxVal == 'w') { + else if(rxVal == ('w' & 0x1f)) { bReportRecyleEvents = !bReportRecyleEvents; + if(NVstore.getUserSettings().menuMode == 2) + bReportRecyleEvents = false; DebugPort.printf("Toggled blue wire recycling event reporting %s\r\n", bReportRecyleEvents ? "ON" : "OFF"); } else if(rxVal == 'n') { @@ -1360,7 +1130,7 @@ void checkDebugCommands() else if(rxVal == 'm') { MQTTmenu.setActive(); } - else if(rxVal == 'o') { + else if(rxVal == ('o' & 0x1f)) { bReportOEMresync = !bReportOEMresync; DebugPort.printf("Toggled OEM resync event reporting %s\r\n", bReportOEMresync ? "ON" : "OFF"); } @@ -1371,7 +1141,31 @@ void checkDebugCommands() nGetString = 2; DebugPort.enable(false); // block other debug msgs whilst we get strings } - else if(rxVal == 's') { + else if(rxVal == 'u') { + DebugPort.print("Please enter username for Web page access (CTRL-X to disable) - "); + line.reset(); + nGetString = 10; + DebugPort.enable(false); // block other debug msgs whilst we get strings + } + else if(rxVal == 'w') { + DebugPort.print("Please enter current Web page password - "); + line.reset(); + nGetString = 11; + DebugPort.enable(false); // block other debug msgs whilst we get strings + } + else if(rxVal == 'y') { + DebugPort.print("Please enter username for /update Web page access - "); + line.reset(); + nGetString = 20; + DebugPort.enable(false); // block other debug msgs whilst we get strings + } + else if(rxVal == 'z') { + DebugPort.print("Please enter current /update Web page password - "); + line.reset(); + nGetString = 21; + DebugPort.enable(false); // block other debug msgs whilst we get strings + } + else if(rxVal == ('c' & 0x1f)) { CommState.toggleReporting(); } else if(rxVal == '+') { @@ -1393,7 +1187,7 @@ void checkDebugCommands() else if(rxVal == ('r' & 0x1f)) { // CTRL-R reboot ESP.restart(); // reset the esp } - else if(rxVal == ('s' & 0x1f)) { // CTRL-B Test Mode: bluetooth module route + else if(rxVal == ('s' & 0x1f)) { // CTRL-S Test Mode: bluetooth module route bReportStack = !bReportStack; } } @@ -1422,38 +1216,6 @@ void checkDebugCommands() } } -// 0x00 - Normal: BTC, with heater responding -// 0x01 - Error: BTC, heater not responding -// 0x02 - Special: OEM controller & heater responding -// 0x03 - Error: OEM controller, heater not responding -int getBlueWireStat() -{ - int stat = 0; - if(!bHasHtrData) { - stat |= 0x01; - } - if(bHasOEMController) { - stat |= 0x02; - } - return stat; -} - -const char* getBlueWireStatStr() -{ - static const char* BlueWireStates[] = { "BTC,Htr", "BTC", "OEM,Htr", "OEM" }; - - return BlueWireStates[getBlueWireStat()]; -} - -bool hasOEMcontroller() -{ - return bHasOEMController; -} - -bool hasOEMLCDcontroller() -{ - return bHasOEMLCDController; -} int getSmartError() { @@ -1756,14 +1518,14 @@ int getFanSpeed() #endif } -void updateFilteredData() +void updateFilteredData(CProtocol& HeaterInfo) { - 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()); + FilteredSamples.ipVolts.update(HeaterInfo.getVoltage_Supply()); + FilteredSamples.GlowVolts.update(HeaterInfo.getGlowPlug_Voltage()); + FilteredSamples.GlowAmps.update(HeaterInfo.getGlowPlug_Current()); + FilteredSamples.Fan.update(HeaterInfo.getFan_Actual()); + FilteredSamples.FastipVolts.update(HeaterInfo.getVoltage_Supply()); + FilteredSamples.FastGlowAmps.update(HeaterInfo.getGlowPlug_Current()); } int sysUptime() @@ -1776,32 +1538,72 @@ void resetFuelGauge() FuelGauge.reset(); } -void setSSID(const char* name) +void setName(const char* name, int type) { sCredentials creds = NVstore.getCredentials(); - strncpy(creds.APSSID, name, 31); - creds.APSSID[31] = 0; + char* pDest = NULL; + switch (type) { + case 0: pDest = creds.APSSID; break; + case 1: pDest = creds.webUsername; break; + case 2: pDest = creds.webUpdateUsername; break; + } + if(pDest) { + strncpy(pDest, name, 31); + pDest[31] = 0; + } NVstore.setCredentials(creds); NVstore.save(); NVstore.doSave(); // ensure NV storage - DebugPort.println("Restarting ESP to invoke new network credentials"); - DebugPort.handle(); - delay(1000); - ESP.restart(); + if(type == 0) { + DebugPort.println("Restarting ESP to invoke new network credentials"); + DebugPort.handle(); + delay(1000); + ESP.restart(); + } } -void setAPpassword(const char* name) +void setPassword(const char* name, int type) { sCredentials creds = NVstore.getCredentials(); - strncpy(creds.APpassword, name, 31); - creds.APpassword[31] = 0; + char* pDest = NULL; + switch (type) { + case 0: pDest = creds.APpassword; break; + case 1: pDest = creds.webPassword; break; + case 2: pDest = creds.webUpdatePassword; break; + } + if(pDest) { + strncpy(pDest, name, 31); + pDest[31] = 0; + } + NVstore.setCredentials(creds); + NVstore.save(); + NVstore.doSave(); // ensure NV storage + if(type == 0) { + DebugPort.println("Restarting ESP to invoke new network credentials"); + DebugPort.handle(); + delay(1000); + ESP.restart(); + } +} + +void setWebUsername(const char* name) +{ + sCredentials creds = NVstore.getCredentials(); + strncpy(creds.webUsername, name, 31); + creds.webUsername[31] = 0; + NVstore.setCredentials(creds); + NVstore.save(); + NVstore.doSave(); // ensure NV storage +} + +void setWebPassword(const char* name) +{ + sCredentials creds = NVstore.getCredentials(); + strncpy(creds.webPassword, name, 31); + creds.webPassword[31] = 0; NVstore.setCredentials(creds); NVstore.save(); NVstore.doSave(); // ensure NV storage - DebugPort.println("Restarting ESP to invoke new network credentials"); - DebugPort.handle(); - delay(1000); - ESP.restart(); } @@ -1812,15 +1614,19 @@ void showMainmenu() DebugPort.println(""); DebugPort.printf(" - toggle raw blue wire data reporting, currently %s\r\n", bReportBlueWireData ? "ON" : "OFF"); DebugPort.printf(" - toggle output JSON reporting, currently %s\r\n", bReportJSONData ? "ON" : "OFF"); - DebugPort.printf(" - toggle reporting of blue wire timeout/recycling event, currently %s\r\n", bReportRecyleEvents ? "ON" : "OFF"); - DebugPort.printf(" - toggle reporting of OEM resync event, currently %s\r\n", bReportOEMresync ? "ON" : "OFF"); - DebugPort.printf(" - toggle reporting of state machine transits %s\r\n", CommState.isReporting() ? "ON" : "OFF"); DebugPort.printf(" - change AP SSID, currently \"%s\"\r\n", NVstore.getCredentials().APSSID); DebugPort.println("

- change AP password"); DebugPort.println(" - configure MQTT"); + DebugPort.println(" - change Web page username"); + DebugPort.println(" - change Web page password"); + DebugPort.println(" - change Web /update username"); + DebugPort.println(" - change Web /update password"); DebugPort.println(" <+> - request heater turns ON"); DebugPort.println(" <-> - request heater turns OFF"); - DebugPort.println(" - restart the ESP"); + DebugPort.println(" - restart the ESP"); + DebugPort.printf(" - toggle reporting of state machine transits %s\r\n", CommState.isReporting() ? "ON" : "OFF"); + DebugPort.printf(" - toggle reporting of OEM resync event, currently %s\r\n", bReportOEMresync ? "ON" : "OFF"); + DebugPort.printf(" - toggle reporting of blue wire timeout/recycling event, currently %s\r\n", bReportRecyleEvents ? "ON" : "OFF"); DebugPort.println(""); DebugPort.println(""); DebugPort.println(""); @@ -1844,3 +1650,9 @@ void reqHeaterCalUpdate() { TxManage.queueSysUpdate(); } + +const CProtocolPackage& getHeaterInfo() +{ + return BlueWireData; +} + diff --git a/src/Protocol/BlueWireTask.cpp b/src/Protocol/BlueWireTask.cpp new file mode 100644 index 0000000..381bf3d --- /dev/null +++ b/src/Protocol/BlueWireTask.cpp @@ -0,0 +1,478 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * Copyright (C) 2018 James Clark + * + * 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 . + * + */ + +#include "BlueWireTask.h" +#include "../cfg/BTCConfig.h" +#include "../cfg/pins.h" +#include "Protocol.h" +#include "TxManage.h" +// #include "SmartError.h" +#include "../Utility/UtilClasses.h" +#include "../Utility/DataFilter.h" +#include "../Utility/FuelGauge.h" +#include "../Utility/HourMeter.h" +#include "../Utility/macros.h" + +// Setup Serial Port Definitions +#if defined(__arm__) +// Required for Arduino Due, UARTclass is derived from HardwareSerial +static UARTClass& BlueWireSerial(Serial1); +#else +// for ESP32, Mega +// HardwareSerial is it for these boards +static HardwareSerial& BlueWireSerial(Serial1); +#endif + +#define RX_DATA_TIMOUT 50 + +CommStates CommState; +CTxManage TxManage(TxEnbPin, BlueWireSerial); +CProtocol DefaultBTCParams(CProtocol::CtrlMode); // defines the default parameters, used in case of no OEM controller +CModeratedFrame OEMCtrlFrame; // data packet received from heater in response to OEM controller packet +CModeratedFrame HeaterFrame1; // data packet received from heater in response to OEM controller packet +CProtocol HeaterFrame2; // data packet received from heater in response to our packet +// CSmartError SmartError; +CProtocolPackage reportHeaterData; +CProtocolPackage primaryHeaterData; + +static bool bHasOEMController = false; +static bool bHasOEMLCDController = false; +static bool bHasHtrData = false; + +extern bool bReportRecyleEvents; +extern bool bReportOEMresync; +extern bool bReportBlueWireData; +extern sFilteredData FilteredSamples; + +QueueHandle_t BlueWireMsgBuf = NULL; // cannot use general Serial.print etc from this task without causing conflicts +QueueHandle_t BlueWireRxQueue = NULL; // queue to pass down heater receive data +QueueHandle_t BlueWireTxQueue = NULL; // queue to pass down heater transmit data +SemaphoreHandle_t BlueWireSemaphore = NULL; // flag to indicate completion of heater data exchange + +bool validateFrame(const CProtocol& frame, const char* name); +void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg); +// void updateFilteredData(); +void initBlueWireSerial(); + +void pushDebugMsg(const char* msg) { + if(BlueWireMsgBuf) + xQueueSend(BlueWireMsgBuf, msg, 0); +} + +void BlueWireTask(void*) { + ////////////////////////////////////////////////////////////////////////////////////// + // Blue wire data reception + // Reads data from the "blue wire" Serial port, (to/from heater) + // If an OEM controller exists we will also see it's data frames + // Note that the data is read now, then held for later use in the state machine + // + static unsigned long lastRxTime = 0; // used to observe inter character delays + static unsigned long moderator = 50; + bool isBTCmaster = false; + + BlueWireMsgBuf = xQueueCreate(4, BLUEWIRE_MSGQUEUESIZE); + BlueWireRxQueue = xQueueCreate(4, BLUEWIRE_DATAQUEUESIZE); + BlueWireTxQueue = xQueueCreate(4, BLUEWIRE_DATAQUEUESIZE); + BlueWireSemaphore = xSemaphoreCreateBinary(); + + TxManage.begin(); // ensure Tx enable pin is setup + + // define defaults should OEM controller be missing + DefaultBTCParams.setHeaterDemand(23); + DefaultBTCParams.setTemperature_Actual(22); + DefaultBTCParams.setSystemVoltage(12.0); + DefaultBTCParams.setPump_Min(1.6f); + DefaultBTCParams.setPump_Max(5.5f); + DefaultBTCParams.setFan_Min(1680); + DefaultBTCParams.setFan_Max(4500); + DefaultBTCParams.Controller.FanSensor = 1; + + initBlueWireSerial(); + + CommState.setCallback(pushDebugMsg); + TxManage.setCallback(pushDebugMsg); + + for(;;) { + + sRxData BlueWireRxData; + unsigned long timenow = millis(); + + // calc elapsed time since last rxd byte + // used to detect no OEM controller, or the start of an OEM frame sequence + unsigned long RxTimeElapsed = timenow - lastRxTime; + + if (BlueWireSerial.available()) { + // Data is available, read and store it now, use it later + // Note that if not in a recognised data receive frame state, the data + // will be deliberately lost! + BlueWireRxData.setValue(BlueWireSerial.read()); // read hex byte, store for later use + + lastRxTime = timenow; // tickle last rx time, for rx data timeout purposes + } + + + // precautionary state machine action if all 24 bytes were not received + // whilst expecting a frame from the blue wire + if(RxTimeElapsed > RX_DATA_TIMOUT) { + + + if( CommState.is(CommStates::OEMCtrlRx) || + CommState.is(CommStates::HeaterRx1) || + CommState.is(CommStates::HeaterRx2) ) { + + if(RxTimeElapsed >= moderator) { + moderator += 10; + if(bReportRecyleEvents) { + char msg[32]; + sprintf(msg, "%ldms - ", RxTimeElapsed); + pushDebugMsg(msg); + } + if(CommState.is(CommStates::OEMCtrlRx)) { + bHasOEMController = false; + bHasOEMLCDController = false; + if(bReportRecyleEvents) { + pushDebugMsg("Timeout collecting OEM controller data, returning to Idle State\r\n"); + } + } + else if(CommState.is(CommStates::HeaterRx1)) { + bHasHtrData = false; + if(bReportRecyleEvents) { + pushDebugMsg("Timeout collecting OEM heater response data, returning to Idle State\r\n"); + } + } + else { + bHasHtrData = false; + if(bReportRecyleEvents) { + pushDebugMsg("Timeout collecting BTC heater response data, returning to Idle State\r\n"); + } + } + } + + if(bReportRecyleEvents) { + pushDebugMsg("Recycling blue wire serial interface\r\n"); + } + #ifdef REBOOT_BLUEWIRE + initBlueWireSerial(); + #endif + CommState.set(CommStates::ExchangeComplete); // revert to idle mode, after passing thru exchange complete mode + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // do our state machine to track the reception and delivery of blue wire data + + switch(CommState.get()) { + + case CommStates::Idle: + + moderator = 50; + + digitalWrite(LED_Pin, LOW); + // Detect the possible start of a new frame sequence from an OEM controller + // This will be the first activity for considerable period on the blue wire + // The heater always responds to a controller frame, but otherwise never by itself + + if(RxTimeElapsed >= (NVstore.getUserSettings().FrameRate - 60)) { // compensate for the time spent just doing things in this state machine + // have not seen any receive data for a second (typ.). + // OEM controller is probably not connected. + // Skip state machine immediately to BTC_Tx, sending our own settings. + bHasHtrData = false; + bHasOEMController = false; + bHasOEMLCDController = false; + isBTCmaster = true; + TxManage.PrepareFrame(DefaultBTCParams, isBTCmaster); // use our parameters, and mix in NV storage values + TxManage.Start(timenow); + CommState.set(CommStates::TxStart); + break; + } + + if(BlueWireRxData.available() && (RxTimeElapsed > (RX_DATA_TIMOUT+10))) { + + if(bReportOEMresync) { + char msg[64]; + sprintf(msg, "Re-sync'd with OEM Controller. %ldms Idle time.\r\n", RxTimeElapsed); + pushDebugMsg(msg); + } + + bHasHtrData = false; + bHasOEMController = true; + CommState.set(CommStates::OEMCtrlRx); // we must add this new byte! + // + // ** IMPORTANT - we must drop through to OEMCtrlRx *NOW* (skipping break) ** + // ** otherwise the first byte will be lost! ** + // + } + else { + break; // only break if we fail all Idle state tests + } + + + case CommStates::OEMCtrlRx: + + digitalWrite(LED_Pin, HIGH); + + // collect OEM controller frame + if(BlueWireRxData.available()) { + if(CommState.collectData(OEMCtrlFrame, BlueWireRxData.getValue()) ) { + CommState.set(CommStates::OEMCtrlValidate); // collected 24 bytes, move on! + } + } + break; + + + case CommStates::OEMCtrlValidate: + + digitalWrite(LED_Pin, LOW); + + // test for valid CRC, abort and restarts Serial1 if invalid + if(!validateFrame(OEMCtrlFrame, "OEM")) { + break; + } + + // filled OEM controller frame + OEMCtrlFrame.setTime(); + // LCD controllers use 0x76 as first byte, rotary knobs use 0x78 + bHasOEMLCDController = (OEMCtrlFrame.Controller.Byte0 != 0x78); + + // xQueueSend(BlueWireTxQueue, OEMCtrlFrame.Data, 0); + + CommState.set(CommStates::HeaterRx1); + break; + + + case CommStates::HeaterRx1: + + digitalWrite(LED_Pin, HIGH); + + // collect heater frame, always in response to an OEM controller frame + if(BlueWireRxData.available()) { + if( CommState.collectData(HeaterFrame1, BlueWireRxData.getValue()) ) { + CommState.set(CommStates::HeaterValidate1); + } + } + break; + + + case CommStates::HeaterValidate1: + + digitalWrite(LED_Pin, LOW); + + // test for valid CRC, abort and restarts Serial1 if invalid + if(!validateFrame(HeaterFrame1, "RX1")) { + bHasHtrData = false; + break; + } + bHasHtrData = true; + + HeaterFrame1.setTime(); + + while(BlueWireSerial.available()) { + pushDebugMsg("DUMPED ROGUE RX DATA\r\n"); + BlueWireSerial.read(); + } + BlueWireSerial.flush(); + + // received heater frame (after controller message), report + primaryHeaterData.set(HeaterFrame1, OEMCtrlFrame); // OEM is always *the* controller + if(bReportBlueWireData) { + primaryHeaterData.reportFrames(true, pushDebugMsg); + } + isBTCmaster = false; + TxManage.PrepareFrame(OEMCtrlFrame, isBTCmaster); // parrot OEM parameters, but block NV modes + CommState.set(CommStates::TxStart); + break; + + + case CommStates::TxStart: + xQueueSend(BlueWireTxQueue, TxManage.getFrame().Data, 0); + TxManage.Start(timenow); + CommState.set(CommStates::TxInterval); + break; + + + case CommStates::TxInterval: + // Handle time interval where we send data to the blue wire + lastRxTime = timenow; // *we* are pumping onto blue wire, track this activity! + if(TxManage.CheckTx(timenow) ) { // monitor progress of our data delivery + CommState.set(CommStates::HeaterRx2); // then await heater repsonse + } + break; + + + case CommStates::HeaterRx2: + + digitalWrite(LED_Pin, HIGH); + + // collect heater frame, in response to our control frame + if(BlueWireRxData.available()) { +#ifdef BADSTARTCHECK + if(!CommState.checkValidStart(BlueWireData.getValue())) { + DebugPort.println("***** Invalid start of frame - restarting Serial port *****"); + initBlueWireSerial(); + CommState.set(CommStates::Idle); + } + else { + if( CommState.collectData(HeaterFrame2, BlueWireData.getValue()) ) { + CommState.set(CommStates::HeaterValidate2); + } + } +#else + if( CommState.collectData(HeaterFrame2, BlueWireRxData.getValue()) ) { + CommState.set(CommStates::HeaterValidate2); + } +#endif + } + break; + + + case CommStates::HeaterValidate2: + + digitalWrite(LED_Pin, LOW); + + // test for valid CRC, abort and restart Serial1 if invalid + if(!validateFrame(HeaterFrame2, "RX2")) { + bHasHtrData = false; + break; + } + bHasHtrData = true; + + // received heater frame (after our control message), report + + xQueueSend(BlueWireRxQueue, HeaterFrame2.Data, 0); + + // do some monitoring of the heater state variables + // if abnormal transitions, introduce a smart error! + // SmartError.monitor(HeaterFrame2); + + if(!bHasOEMController) // no OEM controller - BTC is *the* controller + primaryHeaterData.set(HeaterFrame2, TxManage.getFrame()); + + if(bReportBlueWireData) { // debug or investigation purposes + reportHeaterData.set(HeaterFrame2, TxManage.getFrame()); + reportHeaterData.reportFrames(false, pushDebugMsg); + } + CommState.set(CommStates::ExchangeComplete); + break; + + + case CommStates::ExchangeComplete: + xSemaphoreGive(BlueWireSemaphore); + CommState.set(CommStates::Idle); + break; + } // switch(CommState) + + vTaskDelay(1); + } +} + + +bool validateFrame(const CProtocol& frame, const char* name) +{ + if(!frame.verifyCRC(pushDebugMsg)) { + // Bad CRC - restart blue wire Serial port + char msg[128]; + sprintf(msg, "\007Bad CRC detected for %s frame - restarting blue wire's serial port\r\n", name); + pushDebugMsg(msg); + msg[0] = 0; // empty string + DebugReportFrame("BAD CRC:", frame, "\r\n", msg); + pushDebugMsg(msg); +#ifdef REBOOT_BLUEWIRE + initBlueWireSerial(); +#endif + CommState.set(CommStates::ExchangeComplete); + return false; + } + return true; +} + + +void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg) +{ + strcat(msg, hdr); // header + for(int i=0; i<24; i++) { + char str[16]; + sprintf(str, " %02X", Frame.Data[i]); // build 2 dig hex values + strcat(msg, str); // and print + } + strcat(msg, ftr); // footer +} + +bool hasOEMcontroller() +{ + return bHasOEMController; +} + +bool hasOEMLCDcontroller() +{ + return bHasOEMLCDController; +} + +bool hasHtrData() +{ + return bHasHtrData; +} + +void initBlueWireSerial() +{ + // initialize serial port to interact with the "blue wire" + // 25000 baud, Tx and Rx channels of Chinese heater comms interface: + // Tx/Rx data to/from heater, + // Note special baud rate for Chinese heater controllers +#if defined(__arm__) || defined(__AVR__) + BlueWireSerial.begin(25000); + pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly +#elif ESP32 + // ESP32 + BlueWireSerial.begin(25000, SERIAL_8N1, Rx1Pin, Tx1Pin); // need to explicitly specify pins for pin multiplexer! + pinMode(Rx1Pin, INPUT_PULLUP); // required for MUX to work properly +#endif +} + +// 0x00 - Normal: BTC, with heater responding +// 0x01 - Error: BTC, heater not responding +// 0x02 - Special: OEM controller & heater responding +// 0x03 - Error: OEM controller, heater not responding +int getBlueWireStat() +{ + int stat = 0; + if(!bHasHtrData) { + stat |= 0x01; + } + if(bHasOEMController) { + stat |= 0x02; + } + return stat; +} + +const char* getBlueWireStatStr() +{ + static const char* BlueWireStates[] = { "BTC,Htr", "BTC", "OEM,Htr", "OEM" }; + + return BlueWireStates[getBlueWireStat()]; +} + +void reqPumpPrime(bool on) +{ + DefaultBTCParams.setPump_Prime(on); +} + + diff --git a/src/Protocol/BlueWireTask.h b/src/Protocol/BlueWireTask.h new file mode 100644 index 0000000..bc457d3 --- /dev/null +++ b/src/Protocol/BlueWireTask.h @@ -0,0 +1,40 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * Copyright (C) 2018 James Clark + * + * 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 __BLUEWIRETASK_H__ +#define __BLUEWIRETASK_H__ + +#include +#include "../Utility/UtilClasses.h" + +extern QueueHandle_t BlueWireMsgBuf; // cannot use general Serial.print etc from this task without causing conflicts +extern QueueHandle_t BlueWireRxQueue; // queue to pass down heater receive data +extern QueueHandle_t BlueWireTxQueue; // queue to pass down heater transmit data +extern SemaphoreHandle_t BlueWireSemaphore; // flag to indicate completion of heater data exchange + +const int BLUEWIRE_MSGQUEUESIZE = 192; +const int BLUEWIRE_DATAQUEUESIZE = 24; + +extern void BlueWireTask(void*); +extern CommStates CommState; + + +#endif \ No newline at end of file diff --git a/src/Protocol/Protocol.cpp b/src/Protocol/Protocol.cpp index c221a1e..1a2f607 100644 --- a/src/Protocol/Protocol.cpp +++ b/src/Protocol/Protocol.cpp @@ -54,15 +54,17 @@ CProtocol::getCRC() const // return true for CRC match bool -CProtocol::verifyCRC(bool bSilent) const +CProtocol::verifyCRC(std::function pushMsg) const { + char errmsg[32]; CModBusCRC16 CRCengine; uint16_t CRC = CRCengine.process(22, Data); // calculate CRC based on first 22 bytes of our data buffer uint16_t FrameCRC = getCRC(); bool bOK = (FrameCRC == CRC); - if(!bOK && !bSilent) { - DebugPort.printf("verifyCRC FAILED: calc: %04X data: %04X\r\n", CRC, FrameCRC); + if(!bOK) { + sprintf(errmsg, "verifyCRC FAILED: calc: %04X data: %04X\r\n", CRC, FrameCRC); + pushMsg(errmsg); } return bOK; // does it match the stored values? } @@ -430,16 +432,19 @@ CProtocolPackage::setRefTime() }*/ void -CProtocolPackage::reportFrames(bool isOEM) +CProtocolPackage::reportFrames(bool isOEM, std::function pushMsg) { - _timeStamp.report(); // absolute time + char msg[192]; + msg[0] = 0; + _timeStamp.report(msg); // absolute time if(isOEM) { - DebugReportFrame("OEM:", Controller, TERMINATE_OEM_LINE ? "\r\n" : " "); + DebugReportFrame("OEM:", Controller, TERMINATE_OEM_LINE ? "\r\n" : " ", msg); } else { - DebugReportFrame("BTC:", Controller, TERMINATE_BTC_LINE ? "\r\n" : " "); + DebugReportFrame("BTC:", Controller, TERMINATE_BTC_LINE ? "\r\n" : " ", msg); } - DebugReportFrame("HTR:", Heater, "\r\n"); + DebugReportFrame("HTR:", Heater, "\r\n", msg); + pushMsg(msg); } int diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index fa6503b..0176122 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -95,7 +95,7 @@ public: void setCRC(); // calculate and set the CRC in the buffer void setCRC(uint16_t CRC); // set the CRC in the buffer uint16_t getCRC() const; // extract CRC value from buffer - bool verifyCRC(bool silent=false) const; // return true for CRC match + bool verifyCRC(std::function pushMsg) const; // return true for CRC match void setActiveMode() { Controller.Byte0 = 0x76; }; // this allows heater to save tuning params to EEPROM void setPassiveMode() { Controller.Byte0 = 0x78; }; // this prevents heater saving tuning params to EEPROM @@ -214,7 +214,7 @@ public: int getAltitude() const { return Controller.getAltitude(); }; // void setRefTime(); - void reportFrames(bool isOEM); + void reportFrames(bool isOEM, std::function pushMsg); }; extern const CProtocolPackage& getHeaterInfo(); diff --git a/src/Protocol/SmartError.cpp b/src/Protocol/SmartError.cpp index a4f5e21..c9d771a 100644 --- a/src/Protocol/SmartError.cpp +++ b/src/Protocol/SmartError.cpp @@ -59,8 +59,7 @@ CSmartError::inhibit(bool reseterror) void CSmartError::monitor(const CProtocol& heaterFrame) { - bool bSilent = true; - if(heaterFrame.verifyCRC(bSilent)) { // check but don't report dodgy frames to debug + if(heaterFrame.verifyCRC(NULL)) { // check but don't report dodgy frames to debug // only accept valid heater frames! monitor(heaterFrame.getRunState()); } diff --git a/src/Protocol/TxManage.cpp b/src/Protocol/TxManage.cpp index 1676327..3fc4403 100644 --- a/src/Protocol/TxManage.cpp +++ b/src/Protocol/TxManage.cpp @@ -28,8 +28,6 @@ //#define DEBUG_THERMOSTAT -extern void DebugReportFrame(const char* hdr, const CProtocol&, const char* ftr); - // CTxManage is used to send a data frame to the blue wire // // As the blue wire is bidirectional, we need to only allow our transmit data @@ -71,6 +69,7 @@ CTxManage::CTxManage(int TxGatePin, HardwareSerial& serial) : m_nTxGatePin = TxGatePin; _rawCommand = 0; m_HWTimer = NULL; + _callback = NULL; } // static function used for the tx gate termination @@ -198,7 +197,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) float tDelta = tCurrent - tDesired; float fTemp; #ifdef DEBUG_THERMOSTAT - DebugPort.printf("Window=%.1f tCurrent=%.1f tDesired=%.1f tDelta=%.1f\r\n", Window, tCurrent, tDesired, tDelta); + if(_callback) { + char msg[80]; + sprintf(msg, "Window=%.1f tCurrent=%.1f tDesired=%.1f tDelta=%.1f\r\n", Window, tCurrent, tDesired, tDelta); + _callback(msg); + } #endif Window /= 2; switch(ThermoMode) { @@ -224,7 +227,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) s8Temp = (int8_t)(tActual + 0.5); m_TxFrame.setTemperature_Actual(s8Temp); #ifdef DEBUG_THERMOSTAT - DebugPort.printf("Conventional thermostat mode: tActual = %d\r\n", u8Temp); + if(_callback) { + char msg[80]; + sprintf(msg, "Conventional thermostat mode: tActual = %d\r\n", u8Temp); + _callback(msg); + } #endif break; @@ -241,7 +248,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) } m_TxFrame.setTemperature_Actual(s8Temp); #ifdef DEBUG_THERMOSTAT - DebugPort.printf("Heater controlled windowed thermostat mode: tActual=%d\r\n", u8Temp); + if(_callback) { + char msg[80]; + sprintf(msg, "Heater controlled windowed thermostat mode: tActual=%d\r\n", u8Temp); + _callback(msg); + } #endif break; @@ -251,7 +262,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) // so create a desired "temp" according the the current hystersis tDelta /= Window; // convert tDelta to fraction of window (CAUTION - may be > +-1 !) #ifdef DEBUG_THERMOSTAT - DebugPort.printf("Linear window thermostat mode: Fraction=%f", tDelta); + if(_callback) { + char msg[80]; + DebugPort.printf("Linear window thermostat mode: Fraction=%f", tDelta); + _callback(msg); + } #endif fTemp = (m_TxFrame.getTemperature_Max() + m_TxFrame.getTemperature_Min()) * 0.5; // midpoint - tDelta = 0 hinges here tDelta *= (m_TxFrame.getTemperature_Max() - fTemp); // linear offset from setpoint @@ -264,8 +279,12 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster) m_TxFrame.setHeaterDemand(s8Temp); m_TxFrame.setThermostatModeProtocol(0); // direct heater to use Hz Mode m_TxFrame.setTemperature_Actual(0); // must force actual to 0 for Hz mode -#ifdef DEBUG_THERMOSTAT - DebugPort.printf(" tDesired (pseudo Hz demand) = %d\r\n", u8Temp); +#ifdef DEBUG_THERMOSTAT + if(_callback) { + char msg[80]; + sprintf(msg, " tDesired (pseudo Hz demand) = %d\r\n", u8Temp); + _callback(msg); + } #endif break; } diff --git a/src/Protocol/TxManage.h b/src/Protocol/TxManage.h index cb2fcc7..820efed 100644 --- a/src/Protocol/TxManage.h +++ b/src/Protocol/TxManage.h @@ -27,6 +27,7 @@ class CTxManage const int m_nFrameTime = 14; const int m_nFrontPorch = 0; int m_sysUpdate; + std::function _callback; public: CTxManage(int TxGatePin, HardwareSerial& serial); @@ -40,6 +41,7 @@ public: const CProtocol& getFrame() const { return m_TxFrame; }; static void IRAM_ATTR callbackGateTerminate(); void queueSysUpdate(); // use to implant NV settings into heater + void setCallback(std::function fn) { _callback = fn; }; private: HardwareSerial& m_BlueWireSerial; diff --git a/src/Utility/DebugPort.h b/src/Utility/DebugPort.h index 417fd6e..d2df013 100644 --- a/src/Utility/DebugPort.h +++ b/src/Utility/DebugPort.h @@ -30,6 +30,6 @@ class CProtocol; extern ABTelnetSpy DebugPort; -void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr); +void DebugReportFrame(const char* hdr, const CProtocol& Frame, const char* ftr, char* msg); #endif // __DEBUGPORT_H__ diff --git a/src/Utility/TempSense.cpp b/src/Utility/TempSense.cpp index d602db7..dd15beb 100644 --- a/src/Utility/TempSense.cpp +++ b/src/Utility/TempSense.cpp @@ -438,16 +438,22 @@ CBME280Sensor::getTemperature(float& tempReading, bool filtered) } bool -CBME280Sensor::getAltitude(float& reading) +CBME280Sensor::getAltitude(float& reading, bool fresh) { - reading = _bme.readAltitude(1013.25); //use standard atmosphere as reference + if(fresh) { + _fAltitude = _bme.readAltitude(1013.25); //use standard atmosphere as reference + } + reading = _fAltitude; return true; } bool -CBME280Sensor::getHumidity(float& reading) +CBME280Sensor::getHumidity(float& reading, bool fresh) { - reading = _bme.readHumidity(); + if(fresh) { + _fHumidity = _bme.readHumidity(); + } + reading = _fHumidity; return true; } @@ -489,6 +495,10 @@ CTempSense::startConvert() bool CTempSense::readSensors() { + float fDummy; + getAltitude(fDummy, true); + getHumidity(fDummy, true); + return DS18B20.readSensors(); } @@ -628,19 +638,19 @@ CTempSense::getTemperatureBME280(float& reading) } bool -CTempSense::getAltitude(float& reading) +CTempSense::getAltitude(float& reading, bool fresh) { if(BME280.getCount()) - return BME280.getAltitude(reading); + return BME280.getAltitude(reading, fresh); else return false; } bool -CTempSense::getHumidity(float& reading) +CTempSense::getHumidity(float& reading, bool fresh) { if(BME280.getCount()) - return BME280.getHumidity(reading); + return BME280.getHumidity(reading, fresh); else return false; } \ No newline at end of file diff --git a/src/Utility/TempSense.h b/src/Utility/TempSense.h index 8a35875..33429af 100644 --- a/src/Utility/TempSense.h +++ b/src/Utility/TempSense.h @@ -89,13 +89,15 @@ public: class CBME280Sensor : public CSensor { Adafruit_BME280 _bme; // I2C long _lastSampleTime; + float _fAltitude; + float _fHumidity; int _count; public: CBME280Sensor(); bool begin(int ID); bool getTemperature(float& tempReading, bool filtered) ; - bool getAltitude(float& reading); - bool getHumidity(float& reading); + bool getAltitude(float& reading, bool fresh=false); + bool getHumidity(float& reading, bool fresh=false); const char* getID(); int getCount() const { return _count; }; }; @@ -119,8 +121,8 @@ public: bool getTemperatureBME280(float& tempReading) ; // index is sensor discovery order on one-wire bus bool getTemperatureDS18B20Idx(int sensIdx, float& tempReading) ; // index is sensor discovery order on one-wire bus int getNumSensors() const; - bool getAltitude(float& reading); - bool getHumidity(float& reading); + bool getAltitude(float& reading, bool fresh=false); + bool getHumidity(float& reading, bool fresh=false); CBME280Sensor& getBME280() { return BME280; }; CDS18B20SensorSet& getDS18B20() { return DS18B20; }; static void format(char* msg, float fTemp); diff --git a/src/Utility/UtilClasses.cpp b/src/Utility/UtilClasses.cpp index 313e17b..88042f8 100644 --- a/src/Utility/UtilClasses.cpp +++ b/src/Utility/UtilClasses.cpp @@ -38,13 +38,16 @@ CommStates::set(eCS eState) { _State = eState; _Count = 0; - if(_report) { + if(_report && _callback != NULL) { static const char* stateNames[] = { - "Idle", "OEMCtrlRx", "OEMCtrlValidate", "HeaterRx1", "HeaterValidate1", "HeaterReport1", - "BTC_Tx", "HeaterRx2", "HeaterValidate2", "HeaterReport2", "TemperatureRead" + "Idle", "OEMCtrlRx", "OEMCtrlValidate", "HeaterRx1", "HeaterValidate1", "TxStart", + "TxInterval", "HeaterRx2", "HeaterValidate2", "ExchangeComplete" }; - if(_State == Idle) DebugPort.println(""); // clear screen - DebugPort.printf("State: %s\r\n", stateNames[_State]); + if(_State == Idle) + _callback("\r\n"); + char msg[32]; + sprintf(msg, "State: %s\r\n", stateNames[_State]); + _callback(msg); } } @@ -54,17 +57,6 @@ CommStates::collectData(CProtocol& Frame, uint8_t val, int limit) { // returns return _Count >= limit; } -bool -CommStates::collectDataEx(CProtocol& Frame, uint8_t val, int limit) { // returns true when buffer filled - // guarding against rogue rx kernel buffer stutters.... - if((_Count == 0) && (val != 0x76)) { - DebugPort.println("First heater byte not 0x76 - SKIPPING"); - return false; - } - Frame.Data[_Count++] = val; - return _Count >= limit; -} - bool CommStates::checkValidStart(uint8_t val) { @@ -87,6 +79,8 @@ CommStates::delayExpired() return(test >= 0); } + + CProfile::CProfile() { tStart = millis(); @@ -103,6 +97,8 @@ CProfile::elapsed(bool reset/* = false*/) return retval; } + + void DecodeCmd(const char* cmd, String& payload) { int val; diff --git a/src/Utility/UtilClasses.h b/src/Utility/UtilClasses.h index cc07859..9d4fe96 100644 --- a/src/Utility/UtilClasses.h +++ b/src/Utility/UtilClasses.h @@ -29,11 +29,14 @@ class CProtocol; // a class to track the blue wire receive / transmit states + +#define COMMSTATES_CALLBACK_SIGNATURE std::function CScallback + class CommStates { public: // comms states enum eCS { - Idle, OEMCtrlRx, OEMCtrlValidate, HeaterRx1, HeaterValidate1, HeaterReport1, BTC_Tx, HeaterRx2, HeaterValidate2, HeaterReport2,TemperatureRead + Idle, OEMCtrlRx, OEMCtrlValidate, HeaterRx1, HeaterValidate1, TxStart, TxInterval, HeaterRx2, HeaterValidate2, ExchangeComplete }; private: @@ -41,12 +44,14 @@ private: int _Count; unsigned long _delay; bool _report; + std::function _callback; public: CommStates() { _State = Idle; _Count = 0; _delay = millis(); - _report = REPORT_STATE_MACHINE_TRANSITIONS; + _report = false; + _callback = NULL; } void set(eCS eState); eCS get() { @@ -56,7 +61,6 @@ public: return _State == eState; } bool collectData(CProtocol& Frame, uint8_t val, int limit = 24); - bool collectDataEx(CProtocol& Frame, uint8_t val, int limit = 24); bool checkValidStart(uint8_t val); void setDelay(int ms); bool delayExpired(); @@ -67,6 +71,7 @@ public: bool isReporting() { return _report != 0; }; + void setCallback(std::function fn) { _callback = fn; }; }; @@ -128,19 +133,29 @@ public: void setRefTime() { refTime = millis(); }; - void report(bool isDelta) { + void report(bool isDelta, char* msg=NULL) { if(isDelta) { long delta = millis() - prevTime; - DebugPort.printf("%+8ldms ", delta); + if(msg) + sprintf(msg, "%+8ldms ", delta); + else + DebugPort.printf("%+8ldms ", delta); } else { prevTime = millis(); - DebugPort.printf("%8ldms ", prevTime - refTime); + if(msg) + sprintf(msg, "%8ldms ", prevTime - refTime); + else + DebugPort.printf("%8ldms ", prevTime - refTime); } }; - void report() { + void report(char* msg = NULL) { prevTime = millis(); - DebugPort.printf("%8ldms ", prevTime - refTime); + if(msg) { + sprintf(msg, "%8ldms ", prevTime - refTime); + } + else + DebugPort.printf("%8ldms ", prevTime - refTime); }; }; diff --git a/src/Utility/helpers.h b/src/Utility/helpers.h index fd530ac..679f640 100644 --- a/src/Utility/helpers.h +++ b/src/Utility/helpers.h @@ -45,6 +45,7 @@ extern void resetFuelGauge(); extern const char* getBlueWireStatStr(); extern bool hasOEMcontroller(); extern bool hasOEMLCDcontroller(); +extern bool hasHtrData(); extern int getBlueWireStat(); extern int getSmartError(); extern bool isCyclicActive(); @@ -73,8 +74,8 @@ extern CTempSense& getTempSensor() ; extern void reqHeaterCalUpdate(); -void setSSID(const char* name); -void setAPpassword(const char* name); +void setName(const char* name, int type); +void setPassword(const char* name, int type); extern void ShowOTAScreen(int percent=0, eOTAmodes updateType=eOTAnormal); diff --git a/src/WiFi/BTCWifi.cpp b/src/WiFi/BTCWifi.cpp index 1f80be2..77e896a 100644 --- a/src/WiFi/BTCWifi.cpp +++ b/src/WiFi/BTCWifi.cpp @@ -150,7 +150,7 @@ bool initWifi() } // WiFi.setTxPower(WIFI_POWER_MINUS_1dBm); - // WiFi.setTxPower(WIFI_POWER_19_5dBm); + WiFi.setTxPower(WIFI_POWER_19_5dBm); return retval; } diff --git a/src/WiFi/BrowserUpload.cpp b/src/WiFi/BrowserUpload.cpp index b006d93..7172079 100644 --- a/src/WiFi/BrowserUpload.cpp +++ b/src/WiFi/BrowserUpload.cpp @@ -118,9 +118,6 @@ sBrowserUpload::fragment(HTTPUpload& upload) ::SPIFFS.remove(SrcFile.name.c_str()); // remove the bad file from SPIFFS return -2; } -#ifdef SSL_SERVER - upload.totalSize += upload.currentSize; -#endif } } else { @@ -130,9 +127,6 @@ sBrowserUpload::fragment(HTTPUpload& upload) Update.printError(DebugPort); return -3; } -#ifdef SSL_SERVER - upload.totalSize += upload.currentSize; -#endif } return upload.totalSize; } From 9ff2d9410b8463e25b3c27b35c7ef5623a0fc5ab Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Sun, 26 Apr 2020 16:15:08 +1000 Subject: [PATCH 3/4] Split websocket handling into a task Read all added to BME-280 (MariusZ) New security menu in debug/telnet --- .../Adafruit_BME280.cpp | 123 ++++++++ lib/Adafruit_BME280_Library/Adafruit_BME280.h | 29 ++ lib/AsyncTCP/src/AsyncTCP.cpp | 20 +- src/Afterburner.cpp | 182 ++++++------ src/OLED/BME280Screen.cpp | 20 ++ src/RTC/Clock.cpp | 2 +- src/Utility/GetLine.cpp | 51 +++- src/Utility/MQTTsetup.cpp | 272 ++++++++++++++++++ src/Utility/MQTTsetup.h | 23 ++ src/Utility/TempSense.cpp | 19 +- src/Utility/TempSense.h | 1 + src/WiFi/BTCWebServer.cpp | 60 +++- src/WiFi/BTCWebServer.h | 1 + src/cfg/BTCConfig.h | 3 + 14 files changed, 695 insertions(+), 111 deletions(-) diff --git a/lib/Adafruit_BME280_Library/Adafruit_BME280.cpp b/lib/Adafruit_BME280_Library/Adafruit_BME280.cpp index 87ce92a..a8892f1 100644 --- a/lib/Adafruit_BME280_Library/Adafruit_BME280.cpp +++ b/lib/Adafruit_BME280_Library/Adafruit_BME280.cpp @@ -423,6 +423,129 @@ bool Adafruit_BME280::isReadingCalibration(void) { return (rStatus & (1 << 0)) != 0; } + +/*! + * Pressure and humidity readings require precise temperature for correctness. + * For this reason both readPressure() and readHumidity() call readTemperature() internally, + * which results in 2 SPI/I2C transactions for those readings. + * + * If user code calls a sequence of individual read*() methods to get all sensed values + * it may make sense to replace it with a call to this method + * and get all readings in 3 SPI/I2C transactions, instead of 5. + * + * @brief Returns all environmental values sensed + * @returns 0 on failure, otherwise a bitwise OR of BME280_{T,P,H}_OK flags + * @param readings reference to bme280_readings structure to be filled with data + */ +int Adafruit_BME280::readAll(bme280_readings& readings) +{ + int retval = 0; + + readings.temperature = readTemperature(); // will set t_fine attribute, for immediate reuse + + if (readings.temperature == NAN) // temperature is required for other measurements, abort + { + readings.humidity = NAN; + readings.pressure = NAN; + readings.altitude = NAN; + return retval; + } + + retval |= BME280_T_OK; // temperature read OK + + // t_fine attribute has just been updated by readTemperature(), proceed + // below code copied almost verbatim from readPressure() + + int64_t var1, var2, p; + int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); + + // less readable code, but reading humidity register could be moved here to minimise + // time before obtaining t_fine and applying it to humidity calculations + // int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + + if (adc_P == 0x800000) // value in case pressure measurement was disabled + { + readings.pressure = NAN; + } + else + { + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) << 17); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) >> 8) + + ((var1 * (int64_t)_bme280_calib.dig_P2) << 12); + var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)_bme280_calib.dig_P1) >> 33; + + if (var1 == 0) + { + readings.pressure = (float) 0.0; // avoid exception caused by division by zero + } + else + { + p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)_bme280_calib.dig_P8) * p) >> 19; + + p = ((p + var1 + var2) >> 8) + (((int64_t)_bme280_calib.dig_P7) << 4); + + readings.pressure = (float) p / 256; + retval |= BME280_P_OK; // pressure read OK + + // calculate altitude ref to std sea level pressure + readings.altitude = readAltitude(1013.25); + } + } + + // proceed with reading humidity + // again, code copied almost verbatim from readHumidity() + // t_fine attribute is assumed to be valid + + int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + + if (adc_H == 0x8000) + { // value in case humidity measurement was disabled + readings.humidity = NAN; + } + else + { + int32_t v_x1_u32r; + + v_x1_u32r = (t_fine - ((int32_t)76800)); + + v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bme280_calib.dig_H4) << 20) - + (((int32_t)_bme280_calib.dig_H5) * v_x1_u32r)) + + ((int32_t)16384)) >> + 15) * + (((((((v_x1_u32r * ((int32_t)_bme280_calib.dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)_bme280_calib.dig_H3)) >> 11) + + ((int32_t)32768))) >> + 10) + + ((int32_t)2097152)) * + ((int32_t)_bme280_calib.dig_H2) + + 8192) >> + 14)); + + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)_bme280_calib.dig_H1)) >> + 4)); + + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + + float h = (v_x1_u32r >> 12); + readings.humidity = h / 1024.0; + + retval |= BME280_H_OK; // humidity reading OK + } + + return retval; +} + + /*! * @brief Returns the temperature from the sensor * @returns the temperature read from the device diff --git a/lib/Adafruit_BME280_Library/Adafruit_BME280.h b/lib/Adafruit_BME280_Library/Adafruit_BME280.h index 2b10fe3..42009db 100644 --- a/lib/Adafruit_BME280_Library/Adafruit_BME280.h +++ b/lib/Adafruit_BME280_Library/Adafruit_BME280.h @@ -127,13 +127,27 @@ class Adafruit_BME280_Unified : public Adafruit_Sensor */ + /***********************************************************/ + /*! + @brief environment readings combined + */ + /***********************************************************/ + typedef struct { + float temperature; // temperature sensed [C] + float humidity; // humidity sensed [%Rh] + float pressure; // pressure sensed [Pa] + float altitude; // [m], referenced to std. sea level pressure + } bme280_readings; /**************************************************************************/ + /*! @brief Class that stores state and functions for interacting with BME280 IC */ /**************************************************************************/ class Adafruit_BME280 { public: + /*=========================================================*/ + /**************************************************************************/ /*! @brief sampling rates @@ -188,6 +202,19 @@ public: STANDBY_MS_1000 = 0b101 }; + + /*******************************************************/ + /*! + @brief read status for readAll() + */ + /*******************************************************/ + enum read_success { + BME280_T_OK = 0x01, + BME280_P_OK = 0x02, + BME280_H_OK = 0x04 + }; + + // constructors Adafruit_BME280(); Adafruit_BME280(int8_t cspin, SPIClass *theSPI = &SPI); @@ -214,6 +241,8 @@ public: float readAltitude(float seaLevel); float seaLevelForAltitude(float altitude, float pressure); + int readAll(bme280_readings& readings); + uint32_t sensorID(void); protected: diff --git a/lib/AsyncTCP/src/AsyncTCP.cpp b/lib/AsyncTCP/src/AsyncTCP.cpp index b6036bc..83651bf 100644 --- a/lib/AsyncTCP/src/AsyncTCP.cpp +++ b/lib/AsyncTCP/src/AsyncTCP.cpp @@ -85,10 +85,10 @@ const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; static uint32_t _closed_slots[_number_of_closed_slots]; static uint32_t _closed_index = []() { _slots_lock = xSemaphoreCreateBinary(); - xSemaphoreGive(_slots_lock); for (int i = 0; i < _number_of_closed_slots; ++ i) { - _closed_slots[i] = 1; + _closed_slots[i] = 1; // slot available } + xSemaphoreGive(_slots_lock); return 1; }(); @@ -849,21 +849,25 @@ void AsyncClient::_allocate_closed_slot(){ } } if (_closed_slot != -1) { - _closed_slots[_closed_slot] = 0; + _closed_slots[_closed_slot] = 0; // slot in use! } xSemaphoreGive(_slots_lock); } void AsyncClient::_free_closed_slot(){ + xSemaphoreTake(_slots_lock, portMAX_DELAY); if(_closed_slot >= 16 || _closed_slot < -1) { Serial.printf("CLOSED SLOTS BOUNDS!! free_closed_slot (%d)\r\n", _closed_slot); + xSemaphoreGive(_slots_lock); return; } if (_closed_slot != -1) { - _closed_slots[_closed_slot] = _closed_index; + _closed_slots[_closed_slot] = _closed_index; // slot released by index _closed_slot = -1; ++ _closed_index; + if(_closed_index == 0) _closed_index = 1; } + xSemaphoreGive(_slots_lock); } /* @@ -888,12 +892,12 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){ void AsyncClient::_error(int8_t err) { if(_pcb){ tcp_arg(_pcb, NULL); - if(_pcb->state == LISTEN) { + // if(_pcb->state == LISTEN) { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); - } + // } _pcb = NULL; } if(_error_cb) { @@ -911,12 +915,12 @@ int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { return ERR_OK; } tcp_arg(_pcb, NULL); - if(_pcb->state == LISTEN) { + // if(_pcb->state == LISTEN) { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); - } + // } if(tcp_close(_pcb) != ERR_OK) { tcp_abort(_pcb); } diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index 8cb0e24..90741e0 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -152,6 +152,8 @@ void heaterOff(); void updateFilteredData(CProtocol& HeaterInfo); bool HandleMQTTsetup(char rxVal); void showMainmenu(); +bool checkTemperatureSensors(); +void checkBlueWireEvents(); // DS18B20 temperature sensor support // Uses the RMT timeslot driver to operate as a one-wire bus @@ -181,6 +183,7 @@ CGPIOalg GPIOalg; #endif CMQTTsetup MQTTmenu; +CSecuritySetup SecurityMenu; @@ -261,14 +264,52 @@ CBluetoothAbstract& getBluetoothClient() return Bluetooth; } -// collect and report any debug messages from the blue wire task char taskMsg[BLUEWIRE_MSGQUEUESIZE]; -void checkBlueWireDebugMsgs() + +void checkBlueWireEvents() { +// collect and report any debug messages from the blue wire task if(BlueWireMsgBuf && xQueueReceive(BlueWireMsgBuf, taskMsg, 0)) DebugPort.print(taskMsg); + + // check for complted data exchange from the blue wire task + if(BlueWireSemaphore && xSemaphoreTake(BlueWireSemaphore, 0)) { + updateJSONclients(bReportJSONData); + updateMQTT(); + NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity + } + + // collect transmitted heater data from blue wire task + if(BlueWireTxQueue && xQueueReceive(BlueWireTxQueue, BlueWireTxData.Data, 0)) { + } + + // collect and process received heater data from blue wire task + if(BlueWireRxQueue && xQueueReceive(BlueWireRxQueue, BlueWireRxData.Data, 0)) { + BlueWireData.set(BlueWireRxData, BlueWireTxData); + SmartError.monitor(BlueWireRxData); + + updateFilteredData(BlueWireRxData); + + FuelGauge.Integrate(BlueWireRxData.getPump_Actual()); + + if(INBOUNDS(BlueWireRxData.getRunState(), 1, 5)) { // check for Low Voltage Cutout + SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); + SmartError.checkfuelUsage(); + } + + // 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)) { + const char* msg = "Forcing cyclic cancel due to error induced shutdown\r\n"; + xQueueSend(BlueWireMsgBuf, msg, 0); + // DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); + RTC_Store.setCyclicEngaged(false); + } + + pHourMeter->monitor(BlueWireRxData); + } } + // callback function for Keypad events. // must be an absolute function, cannot be a class member due the "this" element! void parentKeyHandler(uint8_t event) @@ -308,6 +349,19 @@ void WatchdogTask(void * param) } } +void webSocketTask(void*) +{ + for(;;) { +#if USE_WEBSERVER == 1 +#ifndef OLD_WEBSOCKETHANDLER + bHaveWebClient = doWebServer(); +#endif +#endif //USE_WEBSERVER + checkWebSocketSend(); + + vTaskDelay(1); + } +} //************************************************************************************************** //** ** @@ -340,6 +394,8 @@ extern "C" unsigned long __wrap_millis() { void setup() { + vTaskPrioritySet(NULL, TASK_PRIORITY_ARDUINO); // elevate normal ardion loop etc higher than the usual '1' + // ensure cyclic mode is disabled after power on bool bESP32PowerUpInit = false; if(rtc_get_reset_reason(0) == 1/* || bForceInit*/) { @@ -522,13 +578,24 @@ void setup() { TempSensor.getDS18B20().mapSensor(2, NVstore.getHeaterTuning().DS18B20probe[2].romCode); // create task to run blue wire interface - TaskHandle_t bwTask; + TaskHandle_t Task; xTaskCreate(BlueWireTask, "BlueWireTask", 2000, NULL, - 3, - &bwTask); + TASK_PRIORITY_BLUEWIRE, + &Task); + + // create task to run the websockets + xTaskCreate(webSocketTask, + "WebSocketTask", + 6000, + NULL, + TASK_PRIORITY_ARDUINO, + &Task); + + + delay(1000); // just to hold the splash screeen for while } @@ -541,23 +608,28 @@ void setup() { void loop() { - - float fTemperature; - unsigned long timenow = millis(); - // DebugPort.handle(); // keep telnet spy alive - // report any debug messages from the blue wire task - checkBlueWireDebugMsgs(); - feedWatchdog(); // feed watchdog doStreaming(); // do wifi, BT tx etc Clock.update(); + + if(checkTemperatureSensors()) + ScreenManager.reqUpdate(); + checkDisplayUpdate(); - long tDelta = timenow - lastTemperatureTime; + checkBlueWireEvents(); + + vTaskDelay(1); +} // loop + + +bool checkTemperatureSensors() +{ + long tDelta = millis() - lastTemperatureTime; if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period lastTemperatureTime = millis(); // reset time to observe temeprature @@ -567,6 +639,8 @@ void loop() } TempSensor.readSensors(); + + float fTemperature; if(TempSensor.getTemperature(0, fTemperature)) { // get Primary sensor temperature if(DS18B20holdoff) { DS18B20holdoff--; @@ -588,48 +662,10 @@ void loop() TempSensor.startConvert(); // request a new conversion, will be ready by the time we loop back around - ScreenManager.reqUpdate(); + return true; } - - if(BlueWireSemaphore && xSemaphoreTake(BlueWireSemaphore, 0)) { - updateJSONclients(bReportJSONData); - updateMQTT(); - NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity - } - - // collect transmitted heater data from blue wire task - if(BlueWireTxQueue && xQueueReceive(BlueWireTxQueue, BlueWireTxData.Data, 0)) { - } - - // collect and process received heater data from blue wire task - if(BlueWireRxQueue && xQueueReceive(BlueWireRxQueue, BlueWireRxData.Data, 0)) { - BlueWireData.set(BlueWireRxData, BlueWireTxData); - SmartError.monitor(BlueWireRxData); - - updateFilteredData(BlueWireRxData); - - FuelGauge.Integrate(BlueWireRxData.getPump_Actual()); - - if(INBOUNDS(BlueWireRxData.getRunState(), 1, 5)) { // check for Low Voltage Cutout - SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue()); - SmartError.checkfuelUsage(); - } - - // 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)) { - const char* msg = "Forcing cyclic cancel due to error induced shutdown\r\n"; - xQueueSend(BlueWireMsgBuf, msg, 0); - // DebugPort.println("Forcing cyclic cancel due to error induced shutdown"); - RTC_Store.setCyclicEngaged(false); - } - - pHourMeter->monitor(BlueWireRxData); - - } - - -} // loop - + return false; +} void manageCyclicMode() { @@ -827,6 +863,12 @@ void checkDebugCommands() } return; } + if(SecurityMenu.Handle(rxVal)) { + if(rxVal == 0) { + showMainmenu(); + } + return; + } if(nGetConf) { DebugPort.print(rxVal); @@ -1130,6 +1172,9 @@ void checkDebugCommands() else if(rxVal == 'm') { MQTTmenu.setActive(); } + else if(rxVal == 's') { + SecurityMenu.setActive(); + } else if(rxVal == ('o' & 0x1f)) { bReportOEMresync = !bReportOEMresync; DebugPort.printf("Toggled OEM resync event reporting %s\r\n", bReportOEMresync ? "ON" : "OFF"); @@ -1413,7 +1458,9 @@ void doStreaming() doOTA(); #endif // USE_OTA #if USE_WEBSERVER == 1 +#ifdef OLD_WEBSOCKETHANDLER bHaveWebClient = doWebServer(); +#endif #endif //USE_WEBSERVER #if USE_MQTT == 1 // most MQTT is managed via callbacks, but need some sundry housekeeping @@ -1586,26 +1633,6 @@ void setPassword(const char* name, int type) } } -void setWebUsername(const char* name) -{ - sCredentials creds = NVstore.getCredentials(); - strncpy(creds.webUsername, name, 31); - creds.webUsername[31] = 0; - NVstore.setCredentials(creds); - NVstore.save(); - NVstore.doSave(); // ensure NV storage -} - -void setWebPassword(const char* name) -{ - sCredentials creds = NVstore.getCredentials(); - strncpy(creds.webPassword, name, 31); - creds.webPassword[31] = 0; - NVstore.setCredentials(creds); - NVstore.save(); - NVstore.doSave(); // ensure NV storage -} - void showMainmenu() { @@ -1614,13 +1641,8 @@ void showMainmenu() DebugPort.println(""); DebugPort.printf(" - toggle raw blue wire data reporting, currently %s\r\n", bReportBlueWireData ? "ON" : "OFF"); DebugPort.printf(" - toggle output JSON reporting, currently %s\r\n", bReportJSONData ? "ON" : "OFF"); - DebugPort.printf(" - change AP SSID, currently \"%s\"\r\n", NVstore.getCredentials().APSSID); - DebugPort.println("

- change AP password"); DebugPort.println(" - configure MQTT"); - DebugPort.println(" - change Web page username"); - DebugPort.println(" - change Web page password"); - DebugPort.println(" - change Web /update username"); - DebugPort.println(" - change Web /update password"); + DebugPort.println(" - configure Security"); DebugPort.println(" <+> - request heater turns ON"); DebugPort.println(" <-> - request heater turns OFF"); DebugPort.println(" - restart the ESP"); diff --git a/src/OLED/BME280Screen.cpp b/src/OLED/BME280Screen.cpp index 348303b..f836d0f 100644 --- a/src/OLED/BME280Screen.cpp +++ b/src/OLED/BME280Screen.cpp @@ -61,6 +61,26 @@ CBME280Screen::show() _printMenuText(80, 26, msg, false); sprintf(msg, "%.0fm", altitude); _printMenuText(80, 36, msg, false); + +// int rv = getTempSensor().getBME280().getAllReadings(readings); +// if(rv & Adafruit_BME280::BME280_T_OK) +// sprintf(msg, "%.1f`C", readings.temperature); +// else +// strcpy(msg, "n/a"); +// _printMenuText(80, 16, msg, false); +// +// if(rv & Adafruit_BME280::BME280_H_OK) +// sprintf(msg, "%.1f%%", readings.humidity); +// else +// strcpy(msg, "n/a"); +// _printMenuText(80, 26, msg, false); +// +// if(rv & Adafruit_BME280::BME280_P_OK) +// sprintf(msg, "%.0fm", readings.altitude); +// else +// strcpy(msg, "n/a"); +// _printMenuText(80, 36, msg, false); + } else { _printMenuText(64, 16, "Sensor not found", false, eCentreJustify); diff --git a/src/RTC/Clock.cpp b/src/RTC/Clock.cpp index 34295a1..f381d14 100644 --- a/src/RTC/Clock.cpp +++ b/src/RTC/Clock.cpp @@ -84,7 +84,7 @@ CClock::update() _currentTime = _rtc.now(); // moderate I2C accesses Wire.setClock(origClock); _nextRTCfetch = millis() + 500; -// _checkTimers(); + // check timers upon minute rollovers if(_currentTime.minute() != _prevMinute) { CTimerManager::manageTime(_currentTime.hour(), _currentTime.minute(), _currentTime.dayOfTheWeek()); diff --git a/src/Utility/GetLine.cpp b/src/Utility/GetLine.cpp index 18fe37c..a365fb8 100644 --- a/src/Utility/GetLine.cpp +++ b/src/Utility/GetLine.cpp @@ -66,21 +66,30 @@ CGetLine::handle(char rxVal) if(rxVal < ' ') { if(_idx == 0) { if(_pTarget) - strcpy(_buffer, _pTarget); + strcpy(_buffer, _pTarget); // copy buffer if control upon first key, may well be a enter } - if(rxVal == ('x' & 0x1f)) { // CTRL-X - erase string, return done - memset(_buffer, 0, sizeof(_buffer)); + // CTRL-H (backspace) + if(rxVal == ('h' & 0x1f)) { + if(_idx) + _idx--; + } + // CTRL-X + if(rxVal == ('x' & 0x1f)) { + memset(_buffer, 0, sizeof(_buffer)); // erase string, return done _zeroTarget(); return true; } - if(rxVal == '\r') // ignore CR - return false; - if(rxVal == '\n') { // accept buffered string upon LF, return done - _copyTarget(); + // CR (ala CTRL-M) + if(rxVal == '\r') + return false; // do nothing upon CR + // LF (ala CTRL-J) + if(rxVal == '\n') { + _copyTarget(); // accept buffered string upon LF, return done return true; } - if(rxVal == 0x1b) { // abort, no change upon ESC, return done - return true; + // ESC + if(rxVal == 0x1b) { + return true; // abort, no change upon ESC, return done } } else { @@ -89,10 +98,16 @@ CGetLine::handle(char rxVal) DebugPort.print('*'); else DebugPort.print(rxVal); - _buffer[_idx++] = rxVal; - if(_idx == _maxlen) { - _copyTarget(); - return true; + if(rxVal == 0x7f) { // backspace + if(_idx) + _idx--; + } + else { + _buffer[_idx++] = rxVal; + if(_idx == _maxlen) { + _copyTarget(); + return true; + } } } return false; @@ -109,6 +124,11 @@ CGetLine::_doNum(char rxVal) _Numeric = val; return true; } + // CTRL-H (backspace) + if(rxVal == ('h' & 0x1f)) { + if(_idx) + _idx--; + } if(rxVal == 0x1b) { return true; } @@ -125,6 +145,11 @@ CGetLine::_doNum(char rxVal) } } else { + if(rxVal == 0x7f) { // backspace + if(_idx) + _idx--; + return false; + } return true; } return false; diff --git a/src/Utility/MQTTsetup.cpp b/src/Utility/MQTTsetup.cpp index 39a381a..a7e6059 100644 --- a/src/Utility/MQTTsetup.cpp +++ b/src/Utility/MQTTsetup.cpp @@ -183,3 +183,275 @@ CMQTTsetup::HandleMQTTsetup(char rxVal) return true; } + +CSecuritySetup::CSecuritySetup() +{ + _active = false; + _password.Idx = 0; + _password.State = 0; +} + +void +CSecuritySetup::setActive() +{ + _active = true; + _showMenu(true); +} + +void insertDummy(int len); + +void +CSecuritySetup::_showMenu(bool init) +{ + _mode = 0; + _password.Idx = 0; + _password.State = 0; + + DebugPort.enable(true); + if(init) + _credsSetup = NVstore.getCredentials(); + + int len; + + DebugPort.print("\014"); + DebugPort.println("Security configuration"); + DebugPort.println(""); + DebugPort.println(" Access Point credentials"); + DebugPort.printf(" <1> - set SSID, currently \"%s\"\r\n", _credsSetup.APSSID); + DebugPort.print(" <2> - set password"); + len = strlen(_credsSetup.APpassword); + if(len == 0) + DebugPort.print(", currently UNRESTRICTED\r\n"); + else { + insertDummy(len); + } + DebugPort.println(""); + + DebugPort.println(" Web page credentials"); + DebugPort.printf(" <3> - set username, currently \"%s\"\r\n", _credsSetup.webUsername); + DebugPort.print(" <4> - set password"); + len = strlen(_credsSetup.webPassword); + if(len == 0) + DebugPort.printf(", currently UNRESTRICTED\r\n"); + else { + insertDummy(len); + } + DebugPort.println(""); + + DebugPort.println(" /update web page credentials"); + DebugPort.printf(" <5> - set username, currently \"%s\"\r\n", _credsSetup.webUpdateUsername); + DebugPort.printf(" <6> - set password"); + len = strlen(_credsSetup.webUpdatePassword); + if(len == 0) + DebugPort.printf(", UNRESTRICTED!\r\n"); + else { + insertDummy(len); + } + DebugPort.println(""); + DebugPort.printf(" - save and exit\r\n"); + DebugPort.printf(" - abort\r\n"); + + DebugPort.enable(false); // suppress sundry debug whilst Security menu is active +} + +void insertDummy(int len) { + char dummy[32]; + memset(dummy, 0, 32); + if(len > 31) + len = 31; + for(int i = 0; i < len; i++) + dummy[i] = '*'; + DebugPort.printf(" (%s)\r\n", dummy); +} + + +bool +CSecuritySetup::Handle(char& rxVal) +{ + if(_active) { + DebugPort.enable(true); + _active = _handle(rxVal); + if(_active) + DebugPort.enable(false); + else + rxVal = 0; + return true; + } + return false; +} + +bool +CSecuritySetup::_handle(char rxVal) +{ + bool bJumptoMenuRoot = false; + if(_getPassword()) { + if(_handlePassword(rxVal)) { + if(!_getPassword()) + _showMenu(); + } + return true; + } + switch(_mode) { + case 0: // initial menu entry selection + if(rxVal == 0x1b) { + _credsSetup = NVstore.getCredentials(); + return false; + } + if(rxVal == '\n') { + NVstore.setCredentials(_credsSetup); + NVstore.save(); + return false; + } + if(rxVal >= '1' && rxVal <= '6') { + _mode = rxVal - '0'; + DebugPort.print("\014"); + switch(_mode) { + case 1: + DebugPort.printf("Enter new AP SSID (%s)", _credsSetup.APSSID); + _lineInput.reset(_credsSetup.APSSID, 31); + break; + case 2: + DebugPort.print("Enter current AP password"); + _initPassword(0); + break; + case 3: + DebugPort.printf("Enter new Web page access username (currently '%s', CTRL-X to erase)", _credsSetup.webUsername); + _lineInput.reset(_credsSetup.webUsername, 31); + break; + case 4: + DebugPort.print("Enter current web page access password"); + _initPassword(1); + break; + case 5: + DebugPort.printf("Enter new /update web page access username (currently '%s', CTRL-X to erase)", _credsSetup.webUpdateUsername); + _lineInput.reset(_credsSetup.webUpdateUsername, 31); + break; + case 6: + DebugPort.print("Enter current /update web page access password"); + _initPassword(2); + break; + } + DebugPort.print("... "); + } + else { + _showMenu(); + } + return true; + case 1: // enter AP SSID + case 2: // enter AP password + case 3: // enter web page username + case 4: // enter web page password + case 5: // enter /update username + case 6: // enter /update password + if(_lineInput.handle(rxVal)) { + bJumptoMenuRoot = true; + } + break; + } + if(bJumptoMenuRoot) { + _showMenu(); + } + return true; +} + +void +CSecuritySetup::_initPassword(int idx) +{ + _lineInput.reset(); + _lineInput.maskEntry(); + _password.Idx = idx; + _password.State = 1; +} + +bool +CSecuritySetup::_getPassword() +{ + return _password.State != 0; +} + +bool +CSecuritySetup::_handlePassword(char rxVal) +{ + switch(_password.State) { + case 1: + if(_lineInput.handle(rxVal)) { + _password.str1 = _lineInput.getString(); + _password.str2 = _getCurrentPassword(); + if(_password.str1 != _password.str2) { + DebugPort.println("\r\nPassword does not match existing - ABORTING"); + _password.State = 0; + } + else { + _password.State = 2; + DebugPort.print("\r\nPlease enter new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + _lineInput.reset(); + _lineInput.maskEntry(); + } + return true; + case 2: + if(_lineInput.handle(rxVal)) { + _password.str1 = _lineInput.getString(); + if(_lineInput.getLen() < 8) { + // ABORT - too short + DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); + _password.State = 0; + } + else if(_lineInput.getLen() > 31) { + // ABORT - too long! + DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); + _password.State = 0; + } + else { + _password.State = 3; + DebugPort.print("\r\nPlease confirm new password - "); + DebugPort.enable(false); // block other debug msgs whilst we get the password + } + _lineInput.reset(); + _lineInput.maskEntry(); + } + return true; + case 3: + if(_lineInput.handle(rxVal)) { + _password.str2 = _lineInput.getString(); + _lineInput.reset(); + if(_password.str1 != _password.str2) { + DebugPort.println("\r\nNew passwords do not match - ABORTING"); + _password.State = 0; + } + else { + _password.State = 4; + DebugPort.print("\r\nSet new password (y/n) - "); + } + } + return true; + case 4: + if(rxVal == 'y' || rxVal == 'Y') { + _setPassword(_password.str2.c_str()); + } + _password.State = 0; + return true; + } + return false; +} + +const char* +CSecuritySetup::_getCurrentPassword() { + switch(_password.Idx) { + case 0: return _credsSetup.APpassword; + case 1: return _credsSetup.webPassword; + case 2: return _credsSetup.webUpdatePassword; + } + return ""; +} + +void +CSecuritySetup::_setPassword(const char* newPW) +{ + switch(_password.Idx) { + case 0: strcpy(_credsSetup.APpassword, newPW); break; + case 1: strcpy(_credsSetup.webPassword, newPW); break; + case 2: strcpy(_credsSetup.webUpdatePassword, newPW); break; + } +} diff --git a/src/Utility/MQTTsetup.h b/src/Utility/MQTTsetup.h index 047db45..e78c8a7 100644 --- a/src/Utility/MQTTsetup.h +++ b/src/Utility/MQTTsetup.h @@ -35,3 +35,26 @@ public: bool Handle(char& rxVal); void setActive(); }; + +class CSecuritySetup { + CGetLine _lineInput; + int _mode; + bool _active; + struct { + int Idx; + int State; + String str1, str2; + } _password; + sCredentials _credsSetup; + bool _handle(char rxVal); + void _showMenu(bool init = false); + void _initPassword(int idx); + bool _getPassword(); + bool _handlePassword(char rxVal); + const char* _getCurrentPassword(); + void _setPassword(const char* newPW); +public: + CSecuritySetup(); + bool Handle(char& rxVal); + void setActive(); +}; diff --git a/src/Utility/TempSense.cpp b/src/Utility/TempSense.cpp index dd15beb..e6ea313 100644 --- a/src/Utility/TempSense.cpp +++ b/src/Utility/TempSense.cpp @@ -429,6 +429,7 @@ CBME280Sensor::getTemperature(float& tempReading, bool filtered) float temperature = _bme.readTemperature(); update(temperature); _lastSampleTime = millis() + 1000; + DebugPort.println("Forced BME sensor reading"); } CSensor::getTemperature(tempReading, filtered); @@ -457,6 +458,19 @@ CBME280Sensor::getHumidity(float& reading, bool fresh) return true; } +int +CBME280Sensor::getAllReadings(bme280_readings& readings) +{ + int retval = _bme.readAll(readings); + _fAltitude = readings.altitude; + _fHumidity = readings.humidity; + update(readings.temperature); + + _lastSampleTime = millis() + 1000; + + return retval; +} + const char* CBME280Sensor::getID() { @@ -495,9 +509,8 @@ CTempSense::startConvert() bool CTempSense::readSensors() { - float fDummy; - getAltitude(fDummy, true); - getHumidity(fDummy, true); + bme280_readings readings; + getBME280().getAllReadings(readings); return DS18B20.readSensors(); } diff --git a/src/Utility/TempSense.h b/src/Utility/TempSense.h index 33429af..0bf14ee 100644 --- a/src/Utility/TempSense.h +++ b/src/Utility/TempSense.h @@ -98,6 +98,7 @@ public: bool getTemperature(float& tempReading, bool filtered) ; bool getAltitude(float& reading, bool fresh=false); bool getHumidity(float& reading, bool fresh=false); + int getAllReadings(bme280_readings& readings); const char* getID(); int getCount() const { return _count; }; }; diff --git a/src/WiFi/BTCWebServer.cpp b/src/WiFi/BTCWebServer.cpp index 70d987c..42b69de 100644 --- a/src/WiFi/BTCWebServer.cpp +++ b/src/WiFi/BTCWebServer.cpp @@ -42,6 +42,7 @@ #include "BrowserUpload.h" #include #include "WebContentDL.h" +#include extern WiFiManager wm; extern const char* stdHeader; @@ -50,6 +51,8 @@ extern const char* updateIndex; extern const char* formatDoneContent; extern const char* rebootIndex; +QueueHandle_t webSocketQueue = NULL; + extern void checkSplashScreenUpdate(); sBrowserUpload BrowserUpload; @@ -85,6 +88,7 @@ void onUploadProgression(); void onRename(); void build404Response(String& content, String file); void build500Response(String& content, String file); +bool checkWebSocketSend(); const char* getWebContent(bool start) { @@ -99,6 +103,8 @@ const char* getWebContent(bool start) { void initWebServer(void) { + webSocketQueue = xQueueCreate(10, sizeof(char*)); + if (MDNS.begin("Afterburner")) { DebugPort.println("MDNS responder started"); } @@ -175,13 +181,18 @@ String getContentType(String filename) { // convert the file extension to the MI } bool handleFileRead(String path) { // send the right file to the client (if it exists) + DebugPort.println("handleFileRead: " + path); - if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file - if(path.indexOf("index.html") >= 0) { + + if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file in that folder + + if(path.indexOf("index.html") >= 0) { // if referencing sCredentials creds = NVstore.getCredentials(); - if (!server.authenticate(creds.webUsername, creds.webPassword)) { - server.requestAuthentication(); - return true; // not entirely correct, but avoids 404 response + if(strlen(creds.webPassword)) { // optionally present an authentication prompt for /index.html + if (!server.authenticate(creds.webUsername, creds.webPassword)) { + server.requestAuthentication(); + return true; // not entirely correct, but avoids 404 response + } } } @@ -495,13 +506,18 @@ void rootRedirect() server.send(303); } + bool sendWebSocketString(const char* Str) { #ifdef WEBTIMES CProfile profile; #endif - if(webSocket.connectedClients()) { + char* pMsg = new char[strlen(Str)+1]; + strcpy(pMsg, Str); + if(webSocketQueue) xQueueSend(webSocketQueue, &pMsg, 0); + +/* if(webSocket.connectedClients()) { #ifdef WEBTIMES unsigned long tCon = profile.elapsed(true); @@ -516,6 +532,38 @@ bool sendWebSocketString(const char* Str) #endif feedWatchdog(); return retval; + }*/ + return false; +} + +bool checkWebSocketSend() +{ + bool retval = false; +#ifdef WEBTIMES + CProfile profile; +#endif + + char* pMsg = NULL; + if(webSocketQueue && xQueueReceive(webSocketQueue, &pMsg, 0)) { + + if(webSocket.connectedClients()) { + +#ifdef WEBTIMES + unsigned long tCon = profile.elapsed(true); +#endif + + bTxWebData = true; // OLED tx data animation flag + if(pMsg) + retval = webSocket.broadcastTXT(pMsg); + +#ifdef WEBTIMES + unsigned long tWeb = profile.elapsed(true); + DebugPort.printf("Websend times : %ld,%ld\r\n", tCon, tWeb); +#endif + // feedWatchdog(); + delete pMsg; + } + return retval; } return false; } diff --git a/src/WiFi/BTCWebServer.h b/src/WiFi/BTCWebServer.h index 5ba26de..d76a708 100644 --- a/src/WiFi/BTCWebServer.h +++ b/src/WiFi/BTCWebServer.h @@ -36,6 +36,7 @@ void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int wi const char* getWebContent(bool start); void getWebContent(const char* filename); +bool checkWebSocketSend(); #endif diff --git a/src/cfg/BTCConfig.h b/src/cfg/BTCConfig.h index dc80c6c..dc4e06a 100644 --- a/src/cfg/BTCConfig.h +++ b/src/cfg/BTCConfig.h @@ -117,3 +117,6 @@ // #define USE_SW_WATCHDOG 1 + +#define TASK_PRIORITY_ARDUINO 3 +#define TASK_PRIORITY_BLUEWIRE 5 From b75254220cc5ed800963f9eebfcdf40dafcd0f22 Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Sun, 26 Apr 2020 19:28:31 +1000 Subject: [PATCH 4/4] Allow entry of no password --- src/Afterburner.cpp | 270 +------------------------------------- src/Utility/MQTTsetup.cpp | 58 ++++---- src/Utility/MQTTsetup.h | 2 +- 3 files changed, 34 insertions(+), 296 deletions(-) diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index 90741e0..658be2d 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -130,8 +130,8 @@ const int FirmwareRevision = 32; const int FirmwareSubRevision = 0; -const int FirmwareMinorRevision = 5; -const char* FirmwareDate = "11 Apr 2020"; +const int FirmwareMinorRevision = 6; +const char* FirmwareDate = "26 Apr 2020"; #ifdef ESP32 @@ -838,10 +838,6 @@ bool isWebClientConnected() void checkDebugCommands() { - static uint8_t nGetString = 0; - static uint8_t nGetConf = 0; - static String pw1; - static String pw2; static CGetLine line; // check for test commands received over Debug serial port or telnet @@ -870,229 +866,6 @@ void checkDebugCommands() return; } - if(nGetConf) { - DebugPort.print(rxVal); - bool bSave = (rxVal == 'y') || (rxVal == 'Y'); - DebugPort.println(""); - if(!bSave) { - DebugPort.println(" ABORTED!"); - nGetConf = 0; - return; - } - switch(nGetConf) { - case 1: - setName(line.getString(), 0); - break; - case 2: - setPassword(pw2.c_str(), 0); - break; - case 3: - setName(line.getString(), 1); - break; - case 4: - setPassword(pw2.c_str(), 1); - break; - case 5: - setName(line.getString(), 2); - break; - case 6: - setPassword(pw2.c_str(), 2); - break; - } - nGetConf = 0; - return; - } - else if(nGetString) { - DebugPort.enable(true); - - if(rxVal == 0x1b) { // ESCAPE - nGetString = 0; - DebugPort.println("\r\nABORTED!"); - return; - } - - if(line.handle(rxVal)) { - switch(nGetString) { - case 1: - if(line.getLen() <= 31) { - nGetConf = 1; - DebugPort.printf("\r\nSet AP SSID to %s? (y/n) - ", line.getString()); - } - else { - DebugPort.println("\r\nNew name is longer than 31 characters - ABORTING"); - } - nGetString = 0; - return; - case 2: - pw1 = line.getString(); - pw2 = NVstore.getCredentials().APpassword; - if(pw1 != pw2) { - DebugPort.println("\r\nPassword does not match existing - ABORTING"); - nGetString = 0; - } - else { - nGetString = 3; - DebugPort.print("\r\nPlease enter new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 3: - pw1 = line.getString(); - if(line.getLen() < 8) { - // ABORT - too short - DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); - nGetString = 0; - } - else if(line.getLen() > 31) { - // ABORT - too long! - DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); - nGetString = 0; - } - else { - nGetString = 4; - DebugPort.print("\r\nPlease confirm new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 4: - pw2 = line.getString(); - line.reset(); - if(pw1 != pw2) { - DebugPort.println("\r\nNew passwords do not match - ABORTING"); - } - else { - nGetConf = 2; - DebugPort.print("\r\nSet new password (y/n) - "); - } - nGetString = 0; - return; - case 10: - if(line.getLen() <= 31) { - nGetConf = 3; - DebugPort.printf("\r\nSet Web page username to %s? (y/n) - ", line.getString()); - } - else { - DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING"); - } - nGetString = 0; - return; - case 11: - pw1 = line.getString(); - pw2 = NVstore.getCredentials().webPassword; - if(pw1 != pw2) { - DebugPort.println("\r\nPassword does not match existing - ABORTING"); - nGetString = 0; - } - else { - nGetString = 12; - DebugPort.print("\r\nPlease enter new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 12: - pw1 = line.getString(); - if(line.getLen() < 8) { - // ABORT - too short - DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); - nGetString = 0; - } - else if(line.getLen() > 31) { - // ABORT - too long! - DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); - nGetString = 0; - } - else { - nGetString = 13; - DebugPort.print("\r\nPlease confirm new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 13: - pw2 = line.getString(); - line.reset(); - if(pw1 != pw2) { - DebugPort.println("\r\nNew passwords do not match - ABORTING"); - } - else { - nGetConf = 4; - DebugPort.print("\r\nSet new password (y/n) - "); - } - nGetString = 0; - return; - - case 20: - if(line.getLen() <= 31) { - nGetConf = 5; - DebugPort.printf("\r\nSet Web /update username to %s? (y/n) - ", line.getString()); - } - else { - DebugPort.println("\r\nNew username is longer than 31 characters - ABORTING"); - } - nGetString = 0; - return; - - case 21: - pw1 = line.getString(); - pw2 = NVstore.getCredentials().webUpdatePassword; - if(pw1 != pw2) { - DebugPort.println("\r\nPassword does not match existing - ABORTING"); - nGetString = 0; - } - else { - nGetString = 22; - DebugPort.print("\r\nPlease enter new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 22: - pw1 = line.getString(); - if(line.getLen() < 8) { - // ABORT - too short - DebugPort.println("\r\nNew password must be at least 8 characters - ABORTING"); - nGetString = 0; - } - else if(line.getLen() > 31) { - // ABORT - too long! - DebugPort.println("\r\nNew password is longer than 31 characters - ABORTING"); - nGetString = 0; - } - else { - nGetString = 23; - DebugPort.print("\r\nPlease confirm new password - "); - DebugPort.enable(false); // block other debug msgs whilst we get the password - } - line.reset(); - line.maskEntry(); - return; - case 23: - pw2 = line.getString(); - line.reset(); - if(pw1 != pw2) { - DebugPort.println("\r\nNew passwords do not match - ABORTING"); - } - else { - nGetConf = 6; - DebugPort.print("\r\nSet new password (y/n) - "); - } - nGetString = 0; - return; - } - } - DebugPort.enable(false); - return; - - } - rxVal = toLowerCase(rxVal); #ifdef PROTOCOL_INVESTIGATION @@ -1163,12 +936,6 @@ void checkDebugCommands() bReportRecyleEvents = false; DebugPort.printf("Toggled blue wire recycling event reporting %s\r\n", bReportRecyleEvents ? "ON" : "OFF"); } - else if(rxVal == 'n') { - DebugPort.print("Please enter new SSID name for Access Point - "); - line.reset(); - nGetString = 1; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } else if(rxVal == 'm') { MQTTmenu.setActive(); } @@ -1179,37 +946,6 @@ void checkDebugCommands() bReportOEMresync = !bReportOEMresync; DebugPort.printf("Toggled OEM resync event reporting %s\r\n", bReportOEMresync ? "ON" : "OFF"); } - else if(rxVal == 'p') { - DebugPort.print("Please enter current AP password - "); - line.reset(); - line.maskEntry(); - nGetString = 2; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } - else if(rxVal == 'u') { - DebugPort.print("Please enter username for Web page access (CTRL-X to disable) - "); - line.reset(); - nGetString = 10; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } - else if(rxVal == 'w') { - DebugPort.print("Please enter current Web page password - "); - line.reset(); - nGetString = 11; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } - else if(rxVal == 'y') { - DebugPort.print("Please enter username for /update Web page access - "); - line.reset(); - nGetString = 20; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } - else if(rxVal == 'z') { - DebugPort.print("Please enter current /update Web page password - "); - line.reset(); - nGetString = 21; - DebugPort.enable(false); // block other debug msgs whilst we get strings - } else if(rxVal == ('c' & 0x1f)) { CommState.toggleReporting(); } @@ -1222,7 +958,7 @@ void checkDebugCommands() else if(rxVal == 'h') { getWebContent(true); } - else if(rxVal == ('b' & 0x1f)) { // CTRL-B Tst Mdoe: bluetooth module route + else if(rxVal == ('b' & 0x1f)) { // CTRL-B Tst Mode: bluetooth module route bTestBTModule = !bTestBTModule; Bluetooth.test(bTestBTModule ? 0xff : 0x00); // special enter or leave BT test commands } diff --git a/src/Utility/MQTTsetup.cpp b/src/Utility/MQTTsetup.cpp index a7e6059..692aa17 100644 --- a/src/Utility/MQTTsetup.cpp +++ b/src/Utility/MQTTsetup.cpp @@ -220,33 +220,21 @@ CSecuritySetup::_showMenu(bool init) DebugPort.printf(" <1> - set SSID, currently \"%s\"\r\n", _credsSetup.APSSID); DebugPort.print(" <2> - set password"); len = strlen(_credsSetup.APpassword); - if(len == 0) - DebugPort.print(", currently UNRESTRICTED\r\n"); - else { - insertDummy(len); - } + insertDummy(len); DebugPort.println(""); DebugPort.println(" Web page credentials"); DebugPort.printf(" <3> - set username, currently \"%s\"\r\n", _credsSetup.webUsername); DebugPort.print(" <4> - set password"); len = strlen(_credsSetup.webPassword); - if(len == 0) - DebugPort.printf(", currently UNRESTRICTED\r\n"); - else { - insertDummy(len); - } + insertDummy(len); DebugPort.println(""); DebugPort.println(" /update web page credentials"); DebugPort.printf(" <5> - set username, currently \"%s\"\r\n", _credsSetup.webUpdateUsername); DebugPort.printf(" <6> - set password"); len = strlen(_credsSetup.webUpdatePassword); - if(len == 0) - DebugPort.printf(", UNRESTRICTED!\r\n"); - else { - insertDummy(len); - } + insertDummy(len); DebugPort.println(""); DebugPort.printf(" - save and exit\r\n"); DebugPort.printf(" - abort\r\n"); @@ -255,13 +243,17 @@ CSecuritySetup::_showMenu(bool init) } void insertDummy(int len) { - char dummy[32]; - memset(dummy, 0, 32); - if(len > 31) - len = 31; - for(int i = 0; i < len; i++) - dummy[i] = '*'; - DebugPort.printf(" (%s)\r\n", dummy); + if(len == 0) + DebugPort.println(", NOT REQUIRED!"); + else { + char dummy[32]; + memset(dummy, 0, 32); + if(len > 31) + len = 31; + for(int i = 0; i < len; i++) + dummy[i] = '*'; + DebugPort.printf(" (%s)\r\n", dummy); + } } @@ -312,7 +304,7 @@ CSecuritySetup::_handle(char rxVal) break; case 2: DebugPort.print("Enter current AP password"); - _initPassword(0); + _initPassword(0, "inbuilt Access Point"); break; case 3: DebugPort.printf("Enter new Web page access username (currently '%s', CTRL-X to erase)", _credsSetup.webUsername); @@ -320,7 +312,7 @@ CSecuritySetup::_handle(char rxVal) break; case 4: DebugPort.print("Enter current web page access password"); - _initPassword(1); + _initPassword(1, "web page access"); break; case 5: DebugPort.printf("Enter new /update web page access username (currently '%s', CTRL-X to erase)", _credsSetup.webUpdateUsername); @@ -328,7 +320,7 @@ CSecuritySetup::_handle(char rxVal) break; case 6: DebugPort.print("Enter current /update web page access password"); - _initPassword(2); + _initPassword(2, "/update web page access"); break; } DebugPort.print("... "); @@ -355,12 +347,16 @@ CSecuritySetup::_handle(char rxVal) } void -CSecuritySetup::_initPassword(int idx) +CSecuritySetup::_initPassword(int idx, const char* prompt) { _lineInput.reset(); _lineInput.maskEntry(); _password.Idx = idx; _password.State = 1; + if(strlen(_getCurrentPassword()) == 0) { + DebugPort.printf("\rEnter password for %s (CTRL-X for no password) - ", prompt); + _password.State = 2; + } } bool @@ -373,7 +369,7 @@ bool CSecuritySetup::_handlePassword(char rxVal) { switch(_password.State) { - case 1: + case 1: // collect, then test existing password if(_lineInput.handle(rxVal)) { _password.str1 = _lineInput.getString(); _password.str2 = _getCurrentPassword(); @@ -383,7 +379,7 @@ CSecuritySetup::_handlePassword(char rxVal) } else { _password.State = 2; - DebugPort.print("\r\nPlease enter new password - "); + DebugPort.print("\r\nPlease enter new password (CTRL-X for no password) - "); DebugPort.enable(false); // block other debug msgs whilst we get the password } _lineInput.reset(); @@ -391,6 +387,12 @@ CSecuritySetup::_handlePassword(char rxVal) } return true; case 2: + if(rxVal == ('x' & 0x1f)) { // special handling for CTRL-X - erase password + _password.State = 4; + DebugPort.print("\r\nConfirm no password required? (y/n) - "); + _password.str2 = ""; + return true; + } if(_lineInput.handle(rxVal)) { _password.str1 = _lineInput.getString(); if(_lineInput.getLen() < 8) { diff --git a/src/Utility/MQTTsetup.h b/src/Utility/MQTTsetup.h index e78c8a7..b1f7add 100644 --- a/src/Utility/MQTTsetup.h +++ b/src/Utility/MQTTsetup.h @@ -48,7 +48,7 @@ class CSecuritySetup { sCredentials _credsSetup; bool _handle(char rxVal); void _showMenu(bool init = false); - void _initPassword(int idx); + void _initPassword(int idx, const char*prompt); bool _getPassword(); bool _handlePassword(char rxVal); const char* _getCurrentPassword();