diff --git a/README.md b/README.md index cdf271e0..91b514fc 100644 --- a/README.md +++ b/README.md @@ -437,6 +437,7 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat 2 = reset device to factory settings 3 = flush send queues 4 = restart device (warmstart) + 8 = reboot device to maintenance mode (local web server) 9 = reboot device to OTA update via Wifi mode 0x0A set LoRaWAN payload send cycle diff --git a/include/globals.h b/include/globals.h index adddd3ab..e236998f 100644 --- a/include/globals.h +++ b/include/globals.h @@ -58,7 +58,8 @@ enum runmode_t { RUNMODE_NORMAL, RUNMODE_WAKEUP, RUNMODE_UPDATE, - RUNMODE_SLEEP + RUNMODE_SLEEP, + RUNMODE_MAINTENANCE }; // Struct holding devices's runtime configuration @@ -83,7 +84,6 @@ typedef struct __attribute__((packed)) { uint8_t macfilter; // 0=disabled, 1=enabled uint8_t rgblum; // RGB Led luminosity (0..100%) uint8_t monitormode; // 0=disabled, 1=enabled - uint8_t runmode; // 0=normal, 1=update uint8_t payloadmask; // bitswitches for payload data uint8_t enscount; // 0=disabled 1= enabled @@ -149,5 +149,6 @@ extern TaskHandle_t irqHandlerTask, ClockTask, macProcessTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; extern RTC_DATA_ATTR runmode_t RTC_runmode; +extern char clientId[20]; // generated device name #endif \ No newline at end of file diff --git a/include/ota.h b/include/ota.h index 9e53cae1..5018d3ea 100644 --- a/include/ota.h +++ b/include/ota.h @@ -13,8 +13,13 @@ #include #include +#include +#include +#include + int do_ota_update(); void start_ota_update(); +void start_maintenance(); void ota_display(const uint8_t row, const std::string status, const std::string msg); void show_progress(unsigned long current, unsigned long size); diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 8ccc3086..3876ea0c 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -13,15 +13,13 @@ Ticker cyclicTimer; extern boolean isSDS011Active; #endif -void setCyclicIRQ() { - xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); -} +void setCyclicIRQ() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); } // do all housekeeping void doHousekeeping() { - // check if update mode trigger switch was set by rcommand - if (RTC_runmode == RUNMODE_UPDATE) + // check if update or maintenance mode trigger switch was set by rcommand + if ((RTC_runmode == RUNMODE_UPDATE) || (RTC_runmode == RUNMODE_MAINTENANCE)) do_reset(true); // heap and task storage debugging diff --git a/src/main.cpp b/src/main.cpp index 3fb5c60b..e3b2b59f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,6 +93,7 @@ uint8_t batt_level = 0; // display value uint8_t volatile channel = WIFI_CHANNEL_MIN; // channel rotation counter uint8_t volatile rf_load = 0; // RF traffic indicator uint16_t volatile macs_wifi = 0, macs_ble = 0; // globals for display +char clientId[20]; // generated device name hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL; @@ -142,6 +143,12 @@ void setup() { do_after_reset(); + // generate unique clientId from device's MAC + uint8_t mac[6]; + esp_eth_get_mac(mac); + const uint32_t hashedmac = hash((const char *)mac, 6); + snprintf(clientId, 20, "paxcounter_%08x", hashedmac); + // print chip information on startup if in verbose mode after coldstart #if (VERBOSE) @@ -290,6 +297,10 @@ void setup() { start_ota_update(); #endif + // start local webserver if maintenance trigger switch is set + if (RTC_runmode == RUNMODE_MAINTENANCE) + start_maintenance(); + // start mac processing task ESP_LOGI(TAG, "Starting MAC processor..."); macQueueInit(); diff --git a/src/mqttclient.cpp b/src/mqttclient.cpp index 89d54fde..4948409b 100644 --- a/src/mqttclient.cpp +++ b/src/mqttclient.cpp @@ -43,13 +43,6 @@ esp_err_t mqtt_init(void) { int mqtt_connect(const char *my_host, const uint16_t my_port) { IPAddress mqtt_server_ip; - uint8_t mac[6]; - char clientId[20]; - - // hash 6 byte MAC to 4 byte hash - esp_eth_get_mac(mac); - const uint32_t hashedmac = hash((const char *)mac, 6); - snprintf(clientId, 20, "paxcounter_%08x", hashedmac); ESP_LOGI(TAG, "MQTT name is %s", MQTT_CLIENTNAME); diff --git a/src/ota.cpp b/src/ota.cpp index bf4a41e0..ae46d42f 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -330,4 +330,117 @@ void show_progress(unsigned long current, unsigned long size) { #endif } +// start local web user with user interface for maintenance mode +// currently used only for manually uploading a firmware file via wifi + +void start_maintenance(void) { + + // code snippets taken from + // https://github.com/espressif/arduino-esp32/blob/master/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino + + const char *host = MQTT_CLIENTNAME; + const char *ssid = WIFI_SSID; + const char *password = WIFI_PASS; + + WebServer server(80); + + const char *serverIndex = + "" + "
" + "" + "" + "
" + "
progress: 0%
" + ""; + + // Connect to WiFi network + WiFi.begin(ssid, password); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + ESP_LOGI(TAG, "Connected to %s", ssid); + + // use mdns for host name resolution + if (!MDNS.begin(host)) { + ESP_LOGI(TAG, "Error setting up mDNS responder!"); + delay(3000); + do_reset(false); + } + server.on("/", HTTP_GET, [&server, &serverIndex]() { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", serverIndex); + }); + // handling uploading firmware file + server.on( + "/update", HTTP_POST, + [&server]() { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + do_reset(false); + }, + [&server]() { + HTTPUpload &upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + ESP_LOGI(TAG, "Update: %s\n", upload.filename.c_str()); + if (!Update.begin( + UPDATE_SIZE_UNKNOWN)) { // start with max available size + ESP_LOGE(TAG, "Error: %s", Update.errorString()); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + // flashing firmware to ESP + if (Update.write(upload.buf, upload.currentSize) != + upload.currentSize) { + ESP_LOGE(TAG, "Error: %s", Update.errorString()); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end( + true)) { // true to set the size to the current progress + ESP_LOGI(TAG, "Update Success: %u\nRebooting...\n", + upload.totalSize); + } else { + ESP_LOGE(TAG, "Update failed"); + } + } + }); + server.begin(); + + while (1) { + server.handleClient(); + delay(1); + } +} + #endif // USE_OTA \ No newline at end of file diff --git a/src/rcommand.cpp b/src/rcommand.cpp index c902a9d5..23ff0d98 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -31,7 +31,11 @@ void set_reset(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: restart device warm"); do_reset(true); break; - case 9: // reset and ask for software update via Wifi OTA + case 8: // reset and start local web server for manual software update + ESP_LOGI(TAG, "Remote command: software update via user interface"); + RTC_runmode = RUNMODE_MAINTENANCE; + break; + case 9: // reset and ask OTA server via Wifi for automated software update ESP_LOGI(TAG, "Remote command: software update via Wifi"); #if (USE_OTA) // check power status before scheduling ota update diff --git a/src/reset.cpp b/src/reset.cpp index 7c0b0d48..77323e40 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -14,7 +14,7 @@ RTC_DATA_ATTR struct timeval RTC_sleep_start_time; RTC_DATA_ATTR unsigned long long RTC_millis = 0; timeval sleep_stop_time; -const char *runmode[5] = {"powercycle", "normal", "wakeup", "update", "sleep"}; +const char *runmode[6] = {"powercycle", "normal", "wakeup", "update", "sleep", "maintenance"}; void do_reset(bool warmstart) { if (warmstart) {