diff --git a/build.sh b/build.sh index 341aa5c..7fd126e 100755 --- a/build.sh +++ b/build.sh @@ -3,4 +3,10 @@ # first device connected idf.py -DROLE=SENDER build +ret_val=$? +if (($ret_val != 0)); then + exit +fi + +cp build/wellhub.bin /var/www/esp_ota diff --git a/main/App.cpp b/main/App.cpp index 32934c5..ecde379 100644 --- a/main/App.cpp +++ b/main/App.cpp @@ -7,9 +7,11 @@ #include "errors.h" #include "ProvisionSoftAP.h" +#include "OTA.h" #define LED_PIN 26 + App::App() { @@ -20,21 +22,28 @@ void App::init() m_led = new Led(LED_PIN); m_wifi = new Wifi(); + bool needs_provision = true; + if(SETTINGS.wifi.num > 0) { // try connecting m_led->setPulse(0, 255, 0); - uint32_t status = m_wifi->connect(); + uint32_t status = m_wifi->start(); if(status == WH_OK) { m_led->setColor(0, 0, 0); - return; + needs_provision = false; } } - m_led->setPulse(0, 0, 255); - ProvisionSoftAP p(80); - p.init("wellhub", "12345678"); + if(needs_provision) + { + m_led->setPulse(0, 0, 255); + ProvisionSoftAP provision(80); + provision.start(); + } + OTA ota; + ota.start(); } void App::start() diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 2beacbb..faafaa0 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register(SRCS main.cpp App.cpp Settings.cpp Led.cpp TaskFactory.cpp Wifi.cpp utilities.cpp - ProvisionSoftAP.cpp ReaderWriter.cpp + ProvisionSoftAP.cpp ReaderWriter.cpp OTA.cpp INCLUDE_DIRS "." EMBED_TXTFILES ../html/logo.png ../html/provision.html ) diff --git a/main/Led.cpp b/main/Led.cpp index 079177f..abdd236 100644 --- a/main/Led.cpp +++ b/main/Led.cpp @@ -58,7 +58,6 @@ void Led::run() while(true) { -// vTaskSuspend(NULL); xQueueReceive(m_queue, &msg, portMAX_DELAY); if(msg.program == PROGRAM::STEADY) diff --git a/main/Led.h b/main/Led.h index 6d49429..f43518f 100644 --- a/main/Led.h +++ b/main/Led.h @@ -12,12 +12,9 @@ class Led struct LED_QUEUE { uint8_t program; - struct - { - uint8_t r; - uint8_t g; - uint8_t b; - }; + uint8_t r; + uint8_t g; + uint8_t b; }; public: diff --git a/main/OTA.cpp b/main/OTA.cpp new file mode 100644 index 0000000..95c82f6 --- /dev/null +++ b/main/OTA.cpp @@ -0,0 +1,225 @@ +/* + * OTA.cpp + * + * Created on: Apr 23, 2019 + * Author: miro + */ + +#include "esp_system.h" +#include "esp_log.h" +#include "esp_ota_ops.h" + +#include "esp_flash_partitions.h" +#include "esp_partition.h" +#include "string.h" + +#include "OTA.h" + +#define EXAMPLE_SERVER_URL "http://192.168.1.131/wellhub.bin" + +static const char *TAG = "OTA"; + +#define _FORCE_UPDATE + +OTA::OTA() +{ + +} + +void OTA::httpCleanup(esp_http_client_handle_t client) +{ + esp_http_client_close(client); + esp_http_client_cleanup(client); +} + +void OTA::taskFatalError() +{ + ESP_LOGE(TAG, "Exiting task due to fatal error..."); + (void) vTaskDelete(NULL); + + while (1) + { + ; + } +} + +void OTA::start() +{ + esp_err_t err; + /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ + esp_ota_handle_t update_handle = 0; + const esp_partition_t *update_partition = NULL; + + ESP_LOGI(TAG, "Starting OTA check..."); + + const esp_partition_t *configured = esp_ota_get_boot_partition(); + const esp_partition_t *running = esp_ota_get_running_partition(); + + if (configured != running) + { + ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", + configured->address, running->address); + ESP_LOGW(TAG, + "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)"); + } + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", running->type, running->subtype, + running->address); + + esp_http_client_config_t config = { }; + config.url = EXAMPLE_SERVER_URL; + + esp_http_client_handle_t client = esp_http_client_init(&config); + if (client == NULL) + { + ESP_LOGE(TAG, "Failed to initialize HTTP connection"); + } + err = esp_http_client_open(client, 0); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + esp_http_client_cleanup(client); + return; + } + esp_http_client_fetch_headers(client); + + update_partition = esp_ota_get_next_update_partition(NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, + update_partition->address); + assert(update_partition != NULL); + + int binary_file_length = 0; + /*deal with all receive packet*/ + bool image_header_was_checked = false; + while (1) + { + int data_read = esp_http_client_read(client, otaWriteData, BUFFSIZE); + if (data_read < 0) + { + ESP_LOGE(TAG, "Error: SSL data read error"); + esp_http_client_close(client); + esp_http_client_cleanup(client); + taskFatalError(); + } + else if (data_read > 0) + { + printf("."); + if (image_header_was_checked == false) + { + // this is the first 1k of received image + esp_app_desc_t new_app_info; + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) + { + // check current version with downloading + memcpy(&new_app_info, + &otaWriteData[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], + sizeof(esp_app_desc_t)); + ESP_LOGI(TAG, "New firmware"); + ESP_LOGI(TAG, "version: %s", new_app_info.version); + ESP_LOGI(TAG, " name: %s", new_app_info.project_name); + ESP_LOGI(TAG, " c time: %s", new_app_info.time); + ESP_LOGI(TAG, " c date: %s", new_app_info.date); + ESP_LOGI(TAG, "idf ver: %s", new_app_info.idf_ver); +// ESP_LOGI(TAG, " sha: %s", new_app_info.app_elf_sha256); + + esp_app_desc_t running_app_info; + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) + { + ESP_LOGI(TAG, ""); + ESP_LOGI(TAG, "Running firmware"); + ESP_LOGI(TAG, "version: %s", running_app_info.version); + ESP_LOGI(TAG, " name: %s", running_app_info.project_name); + ESP_LOGI(TAG, " c time: %s", running_app_info.time); + ESP_LOGI(TAG, " c date: %s", running_app_info.date); + ESP_LOGI(TAG, "idf ver: %s", running_app_info.idf_ver); +// ESP_LOGI(TAG, " sha: %s", running_app_info.app_elf_sha256); + } + + const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); + esp_app_desc_t invalid_app_info; + if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) + { + ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); + } + + // check current version with last invalid partition + if (last_invalid_app != NULL) + { + if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version) + sizeof(new_app_info.project_name) + sizeof(new_app_info.time)) == 0) + { + ESP_LOGW(TAG, "New version is the same as invalid version."); + ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", + invalid_app_info.version); + ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); + httpCleanup(client); + return; + } + } +#ifndef FORCE_UPDATE + if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version) + sizeof(new_app_info.project_name) + sizeof(new_app_info.time) + sizeof(new_app_info.date)) == 0) + { + ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update."); + httpCleanup(client); + return; + } +#endif + image_header_was_checked = true; + + err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); + httpCleanup(client); + taskFatalError(); + } + ESP_LOGI(TAG, "esp_ota_begin succeeded"); + } + else + { + ESP_LOGE(TAG, "received package is not fit len"); + httpCleanup(client); + taskFatalError(); + } + } + + err = esp_ota_write(update_handle, (const void *) otaWriteData, data_read); + if (err != ESP_OK) + { + httpCleanup(client); + taskFatalError(); + } + binary_file_length += data_read; + ESP_LOGD(TAG, "Written image length %d", binary_file_length); + } + else if (data_read == 0) + { + ESP_LOGI(TAG, "Connection closed,all data received"); + break; + } + } + + printf("\n"); + + ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length); + + if (esp_ota_end(update_handle) != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_end failed!"); + httpCleanup(client); + taskFatalError(); + } + + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)); + httpCleanup(client); + taskFatalError(); + } + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); + return; +} + +OTA::~OTA() +{ +} \ No newline at end of file diff --git a/main/OTA.h b/main/OTA.h new file mode 100644 index 0000000..0b62bf4 --- /dev/null +++ b/main/OTA.h @@ -0,0 +1,34 @@ +/* + * OTA.h + * + * Created on: Apr 23, 2019 + * Author: miro + */ + +#ifndef __OTA_H__ +#define __OTA_H__ + + +#define BUFFSIZE 1024 +#define HASH_LEN 32 /* SHA-256 digest length */ + +#include "esp_http_client.h" + +class OTA +{ +public: + void start(); + +private: + char otaWriteData[BUFFSIZE + 1] = { 0 }; + void httpCleanup(esp_http_client_handle_t client); + +private: + void taskFatalError(); + +public: + OTA(); + virtual ~OTA(); +}; + +#endif /* __OTA_H__ */ \ No newline at end of file diff --git a/main/ProvisionSoftAP.cpp b/main/ProvisionSoftAP.cpp index f0d48a0..0decefa 100755 --- a/main/ProvisionSoftAP.cpp +++ b/main/ProvisionSoftAP.cpp @@ -1,10 +1,10 @@ -/* - * ProvisionSoftAP.cpp - * - * Created on: Apr 17, 2019 - * Modified: 2024 - * Author: miro - */ +/// +/// © MiroZ 2024 +/// +/// Created on: Apr 17, 2019 +/// Modified: 2024 +/// + #include #include "WiFi.h" #include "Arduino.h" @@ -100,7 +100,7 @@ void ProvisionSoftAP::handleLogo(AsyncWebServerRequest* request) * ssid[in] ssid of the AP * password[in] password for the AP */ -void ProvisionSoftAP::init(const char * ssid, const char * password) +void ProvisionSoftAP::start(const char * ssid, const char * password) { // start AP @@ -125,43 +125,14 @@ void ProvisionSoftAP::init(const char * ssid, const char * password) m_webSocket->onEvent(std::bind(&ProvisionSoftAP::websocketEvent, this, _1, _2, _3, _4, _5, _6)); m_webServer->addHandler(m_webSocket); m_webServer->on("/", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); - m_webServer->on("/provision.html", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); - m_webServer->on("/generate_204", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Android captive portal. Maybe not needed. Might be handled by notFound handler. - m_webServer->on("/fwlink", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. + // m_webServer->on("/provision.html", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); + // m_webServer->on("/generate_204", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Android captive portal. Maybe not needed. Might be handled by notFound handler. + // m_webServer->on("/fwlink", std::bind(&ProvisionSoftAP::handlePortal, this, _1)); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. m_webServer->on("/logo.png", std::bind(&ProvisionSoftAP::handleLogo, this, _1)); m_webServer->onNotFound(std::bind(&ProvisionSoftAP::handlePortal, this, _1)); m_webServer->begin(); - // char buf[64], buf1[32]; - // m_rwriter->copyString("command:verify_wifi|ssid:gumball|pwd:12345688"); - // ESP_LOGI(TAG, "%d", m_rwriter->elementIs(0, "command:verify_wifi")); - - - // uint32_t loc = m_rwriter->getNthDelimiter(0); - // if(m_rwriter->hasMore(loc)) - // ESP_LOGI(TAG, "%d", loc); - // while(m_rwriter->hasMore(loc)) - // { - // loc = m_rwriter->getNextDelimiter(loc); - // ESP_LOGI(TAG, "%d", loc); - // } - - // if(m_rwriter->getElementAt(buf, 64, 3)) - // ESP_LOGI(TAG, "'%s'", buf); - - // bool ret_val = m_rwriter->getAt(buf, 64, 3); - // ESP_LOGI(TAG, "(%d) '%s'", ret_val, buf); - - // Parser p(buf, ":"); - - // while(m_rwriter->getNext(buf, 64)) - // { - // p.reset(); - // while(p.getNext(buf1, 32)) - // ESP_LOGI(TAG, "'%s'", buf1); - // } - while (true) { m_dnsServer.processNextRequest(); @@ -202,6 +173,16 @@ void ProvisionSoftAP::init(const char * ssid, const char * password) } } +void ProvisionSoftAP::start() +{ + uint8_t mac[6]; + WiFi.macAddress(mac); + char ssid[33]; + + sprintf(ssid, "Wellhub-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + start(ssid, "12345678"); +} + const char verify_wifi[] = "command:verify_wifi"; void ProvisionSoftAP::parseWebsocketCommand(char* data, size_t len) @@ -282,7 +263,7 @@ void ProvisionSoftAP::tryConnect() SETTINGS.wifi.selected = 0; SETTINGS.wifi.num = 1; - Settings::getInstance().saveData(); + SETTINGS_SAVE; vTaskDelay(3000 / portTICK_PERIOD_MS); waitBufferEmpty(); diff --git a/main/ProvisionSoftAP.h b/main/ProvisionSoftAP.h index fcd233d..20d7313 100755 --- a/main/ProvisionSoftAP.h +++ b/main/ProvisionSoftAP.h @@ -1,12 +1,12 @@ -/* - * ProvisionSoftAP.h - * - * Created on: Apr 17, 2019 - * Author: miro - */ +/// +/// © MiroZ 2024 +/// +/// Created on: Apr 17, 2019 +/// Modified: 2024 +/// -#ifndef MAIN_PROVISIONSOFTAP_H_ -#define MAIN_PROVISIONSOFTAP_H_ +#ifndef __PROVISIONSOFTAP_H__ +#define __PROVISIONSOFTAP_H__ #include #include @@ -43,10 +43,11 @@ private: void waitBufferEmpty(); public: - void init(const char * ssid, const char * password); + void start(const char * ssid, const char * password); + void start(); ProvisionSoftAP(int port = 80); virtual ~ProvisionSoftAP(); }; -#endif /* MAIN_PROVISIONSOFTAP_H_ */ +#endif /* __PROVISIONSOFTAP_H__ */ diff --git a/main/Settings.h b/main/Settings.h index 87c079f..fff883f 100644 --- a/main/Settings.h +++ b/main/Settings.h @@ -70,7 +70,7 @@ struct SETTINGS_LEGACY uint32_t TEMP_OFFSET; }; -#define SETTINGS_VERSION 3 // cannot be 0 +#define SETTINGS_VERSION 2 // cannot be 0 #define SETTINGS_NUM_WIFI_ENTRIES 8 // wifi definitions @@ -126,5 +126,6 @@ public: }; #define SETTINGS Settings::getInstance().data() +#define SETTINGS_SAVE Settings::getInstance().saveData() #endif /* __SETTINGS_H__ */ \ No newline at end of file diff --git a/main/TaskFactory.cpp b/main/TaskFactory.cpp index 0af6e03..429bedd 100644 --- a/main/TaskFactory.cpp +++ b/main/TaskFactory.cpp @@ -4,27 +4,34 @@ TaskFactory::TaskFactory() { +#ifdef USE_MUTEX m_xMutex = xSemaphoreCreateMutex(); assert(m_xMutex); +#endif } TaskFactory::~TaskFactory() { +#ifdef USE_MUTEX if(m_xMutex != NULL) vSemaphoreDelete(m_xMutex); +#endif } TaskHandle_t TaskFactory::createTask(TASK_FUNCTION task_function, const char *name, uint32_t stack_size, UBaseType_t priority, BaseType_t core) { TaskHandle_t task_handle = nullptr; - + +#ifdef USE_MUTEX if (xSemaphoreTake(m_xMutex, portMAX_DELAY)) { +#endif m_task_function = task_function; xTaskCreatePinnedToCore(taskStarter, name, stack_size, this, priority, &task_handle, core); +#ifdef USE_MUTEX xSemaphoreGive(m_xMutex); } - +#endif return task_handle; } diff --git a/main/TaskFactory.h b/main/TaskFactory.h index b0e8dcf..a1fd247 100644 --- a/main/TaskFactory.h +++ b/main/TaskFactory.h @@ -12,26 +12,24 @@ typedef std::function TASK_FUNCTION; +// no need for mutex here +#define _USE_MUTEX + class TaskFactory { protected: +#ifdef USE_MUTEX SemaphoreHandle_t m_xMutex = NULL; +#endif TASK_FUNCTION m_task_function = nullptr; protected: - static void taskStarter(void * ptr) { - (static_cast(ptr)->run()); - } - void run(){ - m_task_function(); - } + static void taskStarter(void * ptr) { (static_cast(ptr)->run()); } + void run(){ m_task_function(); } public: TaskHandle_t createTask(TASK_FUNCTION task_function, const char *name, uint32_t stack_size, UBaseType_t priority, BaseType_t core); - - static uint32_t getStackHighWaterMark(TaskHandle_t task){ - return (uint32_t)uxTaskGetStackHighWaterMark(task); - } + static uint32_t getStackHighWaterMark(TaskHandle_t task){ return (uint32_t)uxTaskGetStackHighWaterMark(task); } TaskFactory(); ~TaskFactory(); diff --git a/main/Wifi.cpp b/main/Wifi.cpp index 0e6f5f7..339f91b 100644 --- a/main/Wifi.cpp +++ b/main/Wifi.cpp @@ -84,7 +84,7 @@ uint32_t Wifi::connectTo(int index) /// @brief Connects to wifi. Returns immediately if already connected. /// @return WH_OK | WH_ERR_WIFI_NOT_PROVISIONED -uint32_t Wifi::connect() +uint32_t Wifi::start() { if(SETTINGS.wifi.num == 0) { diff --git a/main/Wifi.h b/main/Wifi.h index cf64ccb..556cb79 100644 --- a/main/Wifi.h +++ b/main/Wifi.h @@ -27,7 +27,7 @@ protected: void wifi_event(arduino_event_id_t event, arduino_event_info_t info); public: - uint32_t connect(); + uint32_t start(); }; #endif /* __WIFI_H__ */ \ No newline at end of file diff --git a/make.sh b/make.sh index b74c7dc..3dd24cf 100755 --- a/make.sh +++ b/make.sh @@ -10,6 +10,8 @@ if (($ret_val != 0)); then exit fi +cp build/wellhub.bin /var/www/esp_ota + if [$1 == '']; then idf.py -p /dev/ttyUSB1 -b 2000000 flash && idf.py -p /dev/ttyUSB1 monitor -B 450000 else diff --git a/sdkconfig b/sdkconfig index 1df83e1..8c15246 100644 --- a/sdkconfig +++ b/sdkconfig @@ -45,7 +45,7 @@ CONFIG_ENABLE_ARDUINO_DEPENDS=y CONFIG_ARDUINO_RUN_CORE1=y # CONFIG_ARDUINO_RUN_NO_AFFINITY is not set CONFIG_ARDUINO_RUNNING_CORE=1 -CONFIG_ARDUINO_LOOP_STACK_SIZE=8192 +CONFIG_ARDUINO_LOOP_STACK_SIZE=1024 # CONFIG_ARDUINO_EVENT_RUN_CORE0 is not set CONFIG_ARDUINO_EVENT_RUN_CORE1=y # CONFIG_ARDUINO_EVENT_RUN_NO_AFFINITY is not set diff --git a/sdkconfig.old b/sdkconfig.old index 07bd820..98b54fc 100644 --- a/sdkconfig.old +++ b/sdkconfig.old @@ -190,6 +190,16 @@ CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table +# +# AsyncTCP Configuration +# +# CONFIG_ASYNC_TCP_RUN_CORE0 is not set +CONFIG_ASYNC_TCP_RUN_CORE1=y +# CONFIG_ASYNC_TCP_RUN_NO_AFFINITY is not set +CONFIG_ASYNC_TCP_RUNNING_CORE=1 +CONFIG_ASYNC_TCP_USE_WDT=y +# end of AsyncTCP Configuration + # # Compiler options #