From 9da2d0760e8a106aaa8f55b45b9150c9b2c6b677 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Sat, 7 Dec 2024 11:32:45 +0100 Subject: [PATCH] More adjustments for auto update --- .../include/AmsFirmwareUpdater.h | 34 +-- .../src/AmsFirmwareUpdater.cpp | 248 +++++++++++++----- lib/HwTools/src/HwTools.cpp | 2 +- lib/SvelteUi/include/AmsWebServer.h | 1 - platformio.ini | 8 +- src/AmsToMqttBridge.cpp | 3 +- 6 files changed, 217 insertions(+), 79 deletions(-) diff --git a/lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h b/lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h index c00b2a0b..2e08fbe2 100644 --- a/lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h +++ b/lib/AmsFirmwareUpdater/include/AmsFirmwareUpdater.h @@ -6,18 +6,21 @@ #include "AmsConfiguration.h" #if defined(ESP32) -#include "esp_flash_partitions.h" -#include "LittleFS.h" -#include "WiFi.h" -#include "HTTPClient.h" -#if defined(AMS_REMOTE_DEBUG) -#include "RemoteDebug.h" + #include "esp_flash_partitions.h" + #include "LittleFS.h" + #include "WiFi.h" + #include "HTTPClient.h" + + #define AMS_PARTITION_TABLE_OFFSET 0x8000 + #define AMS_PARTITION_APP0_OFFSET 0x10000 + #define AMS_PARTITION_APP_SIZE 0x1D0000 + #define AMS_PARTITION_MIN_SPIFFS_SIZE 0x20000 +#elif defined(ESP8266) + #include #endif -#define AMS_PARTITION_TABLE_OFFSET 0x8000 -#define AMS_PARTITION_APP0_OFFSET 0x10000 -#define AMS_PARTITION_APP_SIZE 0x1D0000 -#define AMS_PARTITION_MIN_SPIFFS_SIZE 0x20000 +#if defined(AMS_REMOTE_DEBUG) +#include "RemoteDebug.h" #endif #define AMS_UPDATE_ERR_OK 0 @@ -78,7 +81,7 @@ private: #if defined(AMS_REMOTE_DEBUG) RemoteDebug* debugger; #else - Stream* debugger; + Print* debugger; #endif HwTools* hw; AmsData* meterState; @@ -101,12 +104,12 @@ private: bool verifyChecksum(); bool activateNewFirmware(); bool writeUpdateStatus(); - uint32_t sketchSize(sketchSize_t response); + bool isFlashReadyForNextUpdateVersion(uint32_t size); - #if defined(ESP32) - char* buf = NULL; + uint8_t* buf = NULL; uint16_t bufPos = 0; + #if defined(ESP32) bool readPartition(uint8_t num, const esp_partition_info_t* info); bool writePartition(uint8_t num, const esp_partition_info_t* info); bool copyData(const esp_partition_info_t* src, esp_partition_info_t* dst, bool eraseFirst=true); @@ -131,6 +134,7 @@ private: bool moveLittleFsFromOldToApp1(); bool moveLittleFsFromApp1ToNew(); - + #elif defined(ESP8266) + uintptr_t getFirmwareUpdateStart(); #endif }; diff --git a/lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp b/lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp index c5a9fa7a..d79f3708 100644 --- a/lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp +++ b/lib/AmsFirmwareUpdater/src/AmsFirmwareUpdater.cpp @@ -9,7 +9,8 @@ #include "esp_flash_spi_init.h" #include "MD5Builder.h" #elif defined(ESP8266) -#include "" +#include "flash_hal.h" +#include "eboot_command.h" #endif #if defined(AMS_REMOTE_DEBUG) @@ -103,7 +104,7 @@ void AmsFirmwareUpdater::loop() { unsigned long start = 0, end = 0; - if(buf == NULL) buf = (char*) malloc(UPDATE_BUF_SIZE); + if(buf == NULL) buf = (uint8_t*) malloc(UPDATE_BUF_SIZE); if(updateStatus.size == 0) { start = millis(); if(!fetchVersionDetails()) { @@ -210,7 +211,13 @@ bool AmsFirmwareUpdater::fetchNextVersion() { char url[128]; snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/next"), chipType, firmwareVariant); + #if defined(ESP8266) + WiFiClient client; + client.setTimeout(5000); + if(http.begin(client, url)) { + #elif defined(ESP32) if(http.begin(url)) { + #endif http.useHTTP10(true); http.setTimeout(30000); http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); @@ -244,7 +251,13 @@ bool AmsFirmwareUpdater::fetchVersionDetails() { char url[128]; snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/%s/details"), chipType, firmwareVariant, updateStatus.toVersion); + #if defined(ESP8266) + WiFiClient client; + client.setTimeout(5000); + if(http.begin(client, url)) { + #elif defined(ESP32) if(http.begin(url)) { + #endif http.useHTTP10(true); http.setTimeout(30000); http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); @@ -294,7 +307,13 @@ bool AmsFirmwareUpdater::fetchFirmwareChunk(HTTPClient& http) { char url[128]; snprintf_P(url, 128, PSTR("http://hub.amsleser.no/hub/firmware/%s/%s/%s/chunk"), chipType, firmwareVariant, updateStatus.toVersion); + #if defined(ESP8266) + WiFiClient client; + client.setTimeout(5000); + if(http.begin(client, url)) { + #elif defined(ESP32) if(http.begin(url)) { + #endif http.useHTTP10(true); http.setTimeout(30000); http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); @@ -323,7 +342,92 @@ bool AmsFirmwareUpdater::writeUpdateStatus() { return false; } +bool AmsFirmwareUpdater::startFirmwareUpload(uint32_t size, const char* version) { + if(!isFlashReadyForNextUpdateVersion(size)) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("No eligable partition was found for upgrade\n")); + return false; + } + + if(!setTargetVersion(version)) { + return false; + } + updateStatus.size = size; + md5 = F("unknown"); + return true; +} + +bool AmsFirmwareUpdater::addFirmwareUploadChunk(uint8_t* buf, size_t length) { + for(size_t i = 0; i < length; i++) { + this->buf[bufPos++] = buf[i]; + if(bufPos == UPDATE_BUF_SIZE) { + if(!writeBufferToFlash()) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("Unable to write to flash\n")); + return false; + } + bufPos = 0; + memset(this->buf, 0, UPDATE_BUF_SIZE); + } + } + return true; +} + +bool AmsFirmwareUpdater::completeFirmwareUpload() { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::INFO)) + #endif + debugger->printf_P(PSTR("Firmware write complete\n")); + + if(bufPos > 0) { + writeBufferToFlash(); + bufPos = 0; + } + if(md5.equals(F("unknown"))) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::INFO)) + #endif + debugger->printf_P(PSTR("No MD5, skipping verification\n")); + } else if(verifyChecksum()) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::INFO)) + #endif + debugger->printf_P(PSTR("MD5 verified!\n")); + } else { + updateStatus.errorCode = AMS_UPDATE_ERR_MD5; + updateStatusChanged = true; + return false; + } + if(!activateNewFirmware()) { + updateStatus.errorCode = AMS_UPDATE_ERR_ACTIVATE; + updateStatusChanged = true; + return false; + } + updateStatus.errorCode = AMS_UPDATE_ERR_SUCCESS_CONFIRMED; + updateStatusChanged = true; + return true; +} + #if defined(ESP32) +bool AmsFirmwareUpdater::isFlashReadyForNextUpdateVersion(uint32_t size) { + const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); + if(partition == NULL) return false; + esp_partition_info_t p_info; + if(!findPartition(partition->label, &p_info)) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("Unable to find partition info for next update partition\n")); + return false; + } + if(p_info.pos.size < size) return false; + return true; +} + bool AmsFirmwareUpdater::writeBufferToFlash() { uint32_t offset = updateStatus.block_position * UPDATE_BUF_SIZE; const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); @@ -381,7 +485,7 @@ bool AmsFirmwareUpdater::relocateOrRepartitionIfNecessary() { #endif debugger->printf_P(PSTR("Small partition, repartitioning\n")); - if(buf == NULL) buf = (char*) malloc(UPDATE_BUF_SIZE); + if(buf == NULL) buf = (uint8_t*) malloc(UPDATE_BUF_SIZE); if(hasTwoSpiffs()) { if(spiffsOnCorrectLocation()) { @@ -507,7 +611,7 @@ bool AmsFirmwareUpdater::copyFile(fs::LittleFSFS* srcFs, fs::LittleFSFS* dstFs, File dst = dstFs->open(filename, "w"); size_t size; - while((size = src.readBytes(buf, UPDATE_BUF_SIZE)) > 0) { + while((size = src.readBytes((char*) buf, UPDATE_BUF_SIZE)) > 0) { dst.write((uint8_t*) buf, size); } dst.flush(); @@ -725,7 +829,7 @@ bool AmsFirmwareUpdater::writeNewPartitionChecksum(uint8_t num) { } md5.calculate(); - md5.getChars(buf); + md5.getChars((char*) buf); #if defined(AMS_REMOTE_DEBUG) if (debugger->isActive(RemoteDebug::DEBUG)) #endif @@ -1011,74 +1115,100 @@ bool AmsFirmwareUpdater::moveLittleFsFromApp1ToNew() { copyFile(&tmpFs, &newFs, FILE_PRICE_CONF); return true; } -bool AmsFirmwareUpdater::startFirmwareUpload(uint32_t size, const char* version) { - const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); - if(partition == NULL) { +#elif defined(ESP8266) +uintptr_t AmsFirmwareUpdater::getFirmwareUpdateStart() { + return FS_start - 0x40200000; +} + +bool AmsFirmwareUpdater::isFlashReadyForNextUpdateVersion(uint32_t size) { + if(!ESP.checkFlashConfig(false)) { + return false; + } + + //size of current sketch rounded to a sector + size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + + //size of the update rounded to a sector + size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + + //address of the end of the space available for sketch and update + uintptr_t updateEndAddress = FS_start - 0x40200000; + + uintptr_t updateStartAddress = (updateEndAddress > roundedSize) ? (updateEndAddress - roundedSize) : 0; + + //make sure that the size of both sketches is less than the total space (updateEndAddress) + if(updateStartAddress < currentSketchSize) { + return false; + } + return true; +} + +bool AmsFirmwareUpdater::writeBufferToFlash() { + uint32_t offset = updateStatus.block_position * UPDATE_BUF_SIZE; + uintptr_t currentAddress = getFirmwareUpdateStart() + offset; + uint32_t sector = currentAddress/FLASH_SECTOR_SIZE; + if(!ESP.flashEraseSector(sector)) { #if defined(AMS_REMOTE_DEBUG) if (debugger->isActive(RemoteDebug::ERROR)) #endif - debugger->printf_P(PSTR("No eligable partition was found for upgrade\n")); + debugger->printf_P(PSTR("flashEraseSector(%lu) failed\n"), sector); + updateStatus.errorCode = AMS_UPDATE_ERR_ERASE; return false; } - - if(!setTargetVersion(version)) { + if(!ESP.flashWrite(currentAddress, buf, UPDATE_BUF_SIZE)) { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("flashWrite(%lu, buf, %lu) failed\n"), currentAddress, UPDATE_BUF_SIZE); + updateStatus.errorCode = AMS_UPDATE_ERR_WRITE; return false; } - updateStatus.size = size; - md5 = F("unknown"); + updateStatus.block_position++; return true; } -bool AmsFirmwareUpdater::addFirmwareUploadChunk(uint8_t* buf, size_t length) { - for(size_t i = 0; i < length; i++) { - this->buf[bufPos++] = buf[i]; - if(bufPos == UPDATE_BUF_SIZE) { - if(!writeBufferToFlash()) { - #if defined(AMS_REMOTE_DEBUG) - if (debugger->isActive(RemoteDebug::ERROR)) - #endif - debugger->printf_P(PSTR("Unable to write to flash\n")); - return false; - } - bufPos = 0; - memset(this->buf, 0, UPDATE_BUF_SIZE); +bool AmsFirmwareUpdater::verifyChecksum() { + MD5Builder md5; + md5.begin(); + uint32_t offset = 0; + uint32_t lengthLeft = updateStatus.size; + while( lengthLeft > 0) { + size_t bytes = (lengthLeft < UPDATE_BUF_SIZE) ? lengthLeft : UPDATE_BUF_SIZE; + uintptr_t currentAddress = getFirmwareUpdateStart() + offset; + if(!ESP.flashRead(currentAddress, buf, bytes)) { + updateStatus.errorCode = AMS_UPDATE_ERR_READ; + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("Unable to read for MD5, offset %lu, bytes %lu\n"), offset, bytes); + return false; } + md5.add((uint8_t*) buf, bytes); + lengthLeft -= bytes; + offset += bytes; + + delay(1); + } + md5.calculate(); + + if(md5.toString().equals(this->md5)) { + return true; + } else { + #if defined(AMS_REMOTE_DEBUG) + if (debugger->isActive(RemoteDebug::ERROR)) + #endif + debugger->printf_P(PSTR("MD5 %s does not match expected %s\n"), md5.toString().c_str(), this->md5.c_str()); + return false; } - return true; } -bool AmsFirmwareUpdater::completeFirmwareUpload() { - #if defined(AMS_REMOTE_DEBUG) - if (debugger->isActive(RemoteDebug::INFO)) - #endif - debugger->printf_P(PSTR("Firmware write complete\n")); - - if(bufPos > 0) { - writeBufferToFlash(); - bufPos = 0; - } - if(md5.equals(F("unknown"))) { - #if defined(AMS_REMOTE_DEBUG) - if (debugger->isActive(RemoteDebug::INFO)) - #endif - debugger->printf_P(PSTR("No MD5, skipping verification\n")); - } else if(verifyChecksum()) { - #if defined(AMS_REMOTE_DEBUG) - if (debugger->isActive(RemoteDebug::INFO)) - #endif - debugger->printf_P(PSTR("MD5 verified!\n")); - } else { - updateStatus.errorCode = AMS_UPDATE_ERR_MD5; - updateStatusChanged = true; - return false; - } - if(!activateNewFirmware()) { - updateStatus.errorCode = AMS_UPDATE_ERR_ACTIVATE; - updateStatusChanged = true; - return false; - } - updateStatus.errorCode = AMS_UPDATE_ERR_SUCCESS_CONFIRMED; - updateStatusChanged = true; +bool AmsFirmwareUpdater::activateNewFirmware() { + eboot_command ebcmd; + ebcmd.action = ACTION_COPY_RAW; + ebcmd.args[0] = getFirmwareUpdateStart(); + ebcmd.args[1] = 0x00000; + ebcmd.args[2] = updateStatus.size; + eboot_command_write(&ebcmd); return true; } #endif diff --git a/lib/HwTools/src/HwTools.cpp b/lib/HwTools/src/HwTools.cpp index 30626ba8..bfb94102 100644 --- a/lib/HwTools/src/HwTools.cpp +++ b/lib/HwTools/src/HwTools.cpp @@ -412,7 +412,7 @@ float HwTools::getVcc() { #else uint32_t x = 0; for (int i = 0; i < 10; i++) { - x += analogRead(config->vccPin); + x += analogRead(vccPin); } volts = (x * 3.3) / 10.0 / analogRange; #endif diff --git a/lib/SvelteUi/include/AmsWebServer.h b/lib/SvelteUi/include/AmsWebServer.h index bb4419fc..0e642e86 100644 --- a/lib/SvelteUi/include/AmsWebServer.h +++ b/lib/SvelteUi/include/AmsWebServer.h @@ -34,7 +34,6 @@ #include #include #include - #include #include #if defined(CONFIG_IDF_TARGET_ESP32C3) #warning "Cloud disabled" diff --git a/platformio.ini b/platformio.ini index 2e3ebf4a..5bd03415 100755 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ build_flags = -fexceptions [esp32] -lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, Update, HTTPUpdate, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, CloudConnector, SvelteUi +lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, CloudConnector, SvelteUi [env:esp8266] platform = espressif8266@4.2.0 @@ -29,7 +29,7 @@ board_build.ldscript = eagle.flash.4m2m.ld build_flags = ${common.build_flags} -D AMS_REMOTE_DEBUG=1 lib_ldf_mode = off lib_compat_mode = off -lib_deps = ESP8266WiFi, ESP8266mDNS, ESP8266WebServer, ESP8266HTTPClient, ESP8266httpUpdate, ESP8266SSDP, EspSoftwareSerial@6.14.1, ${common.lib_deps}, SvelteUi +lib_deps = ESP8266WiFi, ESP8266mDNS, ESP8266WebServer, ESP8266HTTPClient, ESP8266SSDP, EspSoftwareSerial@6.14.1, ${common.lib_deps}, SvelteUi lib_ignore = ${common.lib_ignore} extra_scripts = ${common.extra_scripts} @@ -38,6 +38,7 @@ platform = https://github.com/tasmota/platform-espressif32/releases/download/202 framework = arduino board = esp32dev board_build.f_cpu = 160000000L +board_build.partitions = custom_partition.csv build_flags = ${common.build_flags} lib_ldf_mode = off lib_compat_mode = off @@ -57,6 +58,7 @@ board_build.variant = esp32s2 board_build.flash_mode = qio board_build.f_cpu = 160000000L board_build.f_flash = 40000000L +board_build.partitions = custom_partition.csv build_flags = ${common.build_flags} -D AMS_REMOTE_DEBUG=1 @@ -74,6 +76,7 @@ platform = https://github.com/tasmota/platform-espressif32/releases/download/202 framework = arduino board = esp32-solo1 board_build.f_cpu = 160000000L +board_build.partitions = custom_partition.csv build_flags = ${common.build_flags} -DFRAMEWORK_ARDUINO_SOLO1 @@ -92,6 +95,7 @@ platform = https://github.com/tasmota/platform-espressif32/releases/download/202 framework = arduino board = esp32-c3-devkitm-1 board_build.mcu = esp32c3 +board_build.partitions = custom_partition.csv build_flags = ${common.build_flags} lib_ldf_mode = off lib_compat_mode = off diff --git a/src/AmsToMqttBridge.cpp b/src/AmsToMqttBridge.cpp index fcdfa0bb..2b80543b 100644 --- a/src/AmsToMqttBridge.cpp +++ b/src/AmsToMqttBridge.cpp @@ -20,7 +20,6 @@ ADC_MODE(ADC_VCC); #include #include #include -#include "Update.h" #include #include #if defined(CONFIG_IDF_TARGET_ESP32C3) @@ -427,10 +426,12 @@ void setup() { if(!hw.ledOn(LED_GREEN)) { hw.ledOn(LED_INTERNAL); } + #if defined(ESP32) if(updater.relocateOrRepartitionIfNecessary()) { ESP.restart(); return; } + #endif hw.ledOff(LED_GREEN); hw.ledOff(LED_INTERNAL);