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