diff --git a/arduino/beken-72xx/cores/arduino/LibreTuyaAPI.cpp b/arduino/beken-72xx/cores/arduino/LibreTuyaAPI.cpp index d3927a0..47faf56 100644 --- a/arduino/beken-72xx/cores/arduino/LibreTuyaAPI.cpp +++ b/arduino/beken-72xx/cores/arduino/LibreTuyaAPI.cpp @@ -1,6 +1,7 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-06-19. */ #include +#include // can't include as it collides with on Windows -_- #define REG_FLASH_BASE 0x00803000 @@ -109,12 +110,14 @@ uint32_t LibreTuya::getMaxAllocHeap() { /* OTA-related */ +static int8_t otaImage2Valid = -1; + uint8_t LibreTuya::otaGetStoredIndex() { - return 1; + return otaHasImage2() ? 2 : 1; } bool LibreTuya::otaSupportsDual() { - return false; + return true; } bool LibreTuya::otaHasImage1() { @@ -122,11 +125,28 @@ bool LibreTuya::otaHasImage1() { } bool LibreTuya::otaHasImage2() { - return false; + if (otaImage2Valid != -1) + return otaImage2Valid; + // check download RBL + // TODO: maybe check header CRC or even binary hashes + uint32_t magic; + Flash.readBlock(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4); + otaImage2Valid = magic == 0x004C4252; // "RBL\0", little-endian + return otaImage2Valid; } bool LibreTuya::otaSwitch(bool force) { - return true; + // no need to check otaGetStoredIndex() as it does the same as otaHasImage2() + + // force checking validity again + otaImage2Valid = -1; + + if (otaHasImage2() && force) { + // "rollback" - abort bootloader upgrade operation by wiping first sector + return Flash.eraseSector(FLASH_DOWNLOAD_OFFSET); + } + + return otaHasImage2(); // false if second image is not valid } /* Global instance */ diff --git a/arduino/libretuya/core/ChipType.h b/arduino/libretuya/core/ChipType.h index 205bd01..7d27df2 100644 --- a/arduino/libretuya/core/ChipType.h +++ b/arduino/libretuya/core/ChipType.h @@ -1,5 +1,7 @@ /* Copyright (c) Kuba SzczodrzyƄski 2022-05-28. */ +#pragma once + #define CHIP_TYPE(family, chip_id) (((family >> 24) << 8) | chip_id) #define CHIP_TYPE_ENUM(family, chip_id) (ChipType) CHIP_TYPE(family, chip_id) diff --git a/arduino/libretuya/core/LibreTuyaClass.cpp b/arduino/libretuya/core/LibreTuyaClass.cpp index b8c446f..fb739da 100644 --- a/arduino/libretuya/core/LibreTuyaClass.cpp +++ b/arduino/libretuya/core/LibreTuyaClass.cpp @@ -98,7 +98,8 @@ uint8_t LibreTuya::otaGetTarget() { } /** - * @brief Perform OTA rollback. + * @brief Perform OTA rollback: switch to the previous image, or abort current + * switched OTA update, if not rebooted yet. * * @return false if no second image to run, writing failed or dual-OTA not supported */ diff --git a/arduino/libretuya/core/LibreTuyaClass.h b/arduino/libretuya/core/LibreTuyaClass.h index 944e9ab..82a2d33 100644 --- a/arduino/libretuya/core/LibreTuyaClass.h +++ b/arduino/libretuya/core/LibreTuyaClass.h @@ -114,7 +114,9 @@ class LibreTuya { */ uint8_t otaGetStoredIndex(); /** - * @brief Check if the chip supports dual-OTA. + * @brief Check if the chip supports dual-OTA (i.e. OTA is flashed to a different partition). + * + * TODO: make this work for actual dual-OTA chips; remove checking this in otaGetTarget() etc. */ bool otaSupportsDual(); /** diff --git a/arduino/libretuya/core/LibreTuyaConfig.h b/arduino/libretuya/core/LibreTuyaConfig.h index a445c4b..3c097a5 100644 --- a/arduino/libretuya/core/LibreTuyaConfig.h +++ b/arduino/libretuya/core/LibreTuyaConfig.h @@ -95,3 +95,7 @@ #ifndef LT_DEBUG_SSL #define LT_DEBUG_SSL 0 #endif + +#ifndef LT_DEBUG_OTA +#define LT_DEBUG_OTA 0 +#endif diff --git a/arduino/libretuya/core/lt_logger.h b/arduino/libretuya/core/lt_logger.h index 84ac288..d4c4f6b 100644 --- a/arduino/libretuya/core/lt_logger.h +++ b/arduino/libretuya/core/lt_logger.h @@ -185,3 +185,8 @@ void lt_log_disable(); #define LT_T_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__) #define LT_V_SSL(...) LT_T_MOD(LT_DEBUG_SSL, __VA_ARGS__) #define LT_D_SSL(...) LT_D_MOD(LT_DEBUG_SSL, __VA_ARGS__) + +// Update.cpp +#define LT_T_OTA(...) LT_T_MOD(LT_DEBUG_OTA, __VA_ARGS__) +#define LT_V_OTA(...) LT_T_MOD(LT_DEBUG_OTA, __VA_ARGS__) +#define LT_D_OTA(...) LT_D_MOD(LT_DEBUG_OTA, __VA_ARGS__) diff --git a/arduino/libretuya/libraries/Update/Update.cpp b/arduino/libretuya/libraries/Update/Update.cpp index 983f469..da74810 100644 --- a/arduino/libretuya/libraries/Update/Update.cpp +++ b/arduino/libretuya/libraries/Update/Update.cpp @@ -18,6 +18,8 @@ bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3, return false; cleanup(); + LT_D_OTA("begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget()); + ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY); info = uf2_info_init(); @@ -70,6 +72,8 @@ size_t UpdateClass::write(uint8_t *data, size_t len) { // 0 if not running return 0; + LT_D_OTA("write(%u) / buf %u/512", len, bufSize()); + /* while (buf == bufPos && len >= UF2_BLOCK_SIZE) { // buffer empty and entire block is in data if (!tryWriteData(data, UF2_BLOCK_SIZE)) { @@ -82,7 +86,7 @@ size_t UpdateClass::write(uint8_t *data, size_t len) { } */ // write until buffer space is available - uint16_t toWrite; + uint16_t toWrite; // 1..512 while (len && (toWrite = min(len, bufLeft()))) { tryWriteData(data, toWrite); if (hasError()) @@ -141,6 +145,8 @@ size_t UpdateClass::writeStream(Stream &data) { size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) { uf2_block_t *block = NULL; + LT_V_OTA("Writing %u to buffer (%u/512)", len, bufSize()); + if (len == UF2_BLOCK_SIZE) { // data has a complete block block = (uf2_block_t *)data; @@ -172,11 +178,14 @@ size_t UpdateClass::tryWriteData(uint8_t *data, size_t len) { // header is invalid return 0; + LT_I("OTA: %s v%s - LT v%s @ %s", info->fw_name, info->fw_version, info->lt_version, info->board); + if (bytesTotal == UPDATE_SIZE_UNKNOWN) { // set total update size from block count info bytesTotal = block->block_count * UF2_BLOCK_SIZE; } else if (bytesTotal != block->block_count * UF2_BLOCK_SIZE) { // given update size does not match the block count + LT_D_OTA("Image size wrong; got %u, calculated %u", bytesTotal, block->block_count * UF2_BLOCK_SIZE); return errorArd(UPDATE_ERROR_SIZE); } } else { diff --git a/arduino/libretuya/libraries/Update/Update.h b/arduino/libretuya/libraries/Update/Update.h index 6c5e4ca..26211d2 100644 --- a/arduino/libretuya/libraries/Update/Update.h +++ b/arduino/libretuya/libraries/Update/Update.h @@ -118,7 +118,7 @@ class UpdateClass { } void clearError() { - errorUf2(UF2_ERR_OK); + errorArd(UPDATE_ERROR_OK); } bool hasError() { diff --git a/arduino/libretuya/libraries/Update/UpdateUtil.cpp b/arduino/libretuya/libraries/Update/UpdateUtil.cpp index e5f1c0e..d9d8b76 100644 --- a/arduino/libretuya/libraries/Update/UpdateUtil.cpp +++ b/arduino/libretuya/libraries/Update/UpdateUtil.cpp @@ -52,6 +52,8 @@ void UpdateClass::cleanup() { * @return true if err is not OK, false otherwise */ bool UpdateClass::errorUf2(uf2_err_t err) { + if (err) + LT_D_OTA("[%4d] errorUf2(%d)", ctx ? ctx->seq : 0, err); if (err <= UF2_ERR_IGNORE) return false; cleanup(); @@ -67,6 +69,8 @@ bool UpdateClass::errorUf2(uf2_err_t err) { * @return false - always */ bool UpdateClass::errorArd(uint8_t err) { + if (err) + LT_D_OTA("[%4d] errorArd(%d)", ctx ? ctx->seq : 0, err); cleanup(); errUf2 = UF2_ERR_OK; errArd = err; @@ -77,6 +81,7 @@ bool UpdateClass::errorArd(uint8_t err) { * @brief Abort the update with UPDATE_ERROR_ABORT reason. */ void UpdateClass::abort() { + LT_D_OTA("Aborting update"); errorArd(UPDATE_ERROR_ABORT); } @@ -105,7 +110,7 @@ void UpdateClass::printError(Print &out) { * "ard=..,uf2=..". Returns "" if no error. */ const char *UpdateClass::errorString() { - if (!errArd) + if (!errArd && !errUf2) return ""; sprintf(errorStr, "ard=%u,uf2=%u", errArd, errUf2); return errorStr;