// Copyright (c) 2023 Cesanta Software Limited // All rights reserved #pragma once #include "arch.h" #include "net.h" #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_STM32H5 1 // STM32 H5 #define MG_OTA_STM32H7 2 // STM32 H7 #define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core #define MG_OTA_STM32F 4 // STM32 F7/F4/F2 #define MG_OTA_CH32V307 100 // WCH CH32V307 #define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6 #define MG_OTA_RT1020 300 // IMXRT1020 #define MG_OTA_RT1050 301 // IMXRT1050 #define MG_OTA_RT1060 302 // IMXRT1060 #define MG_OTA_RT1064 303 // IMXRT1064 #define MG_OTA_RT1170 304 // IMXRT1170 #define MG_OTA_MCXN 310 // MCXN947 #define MG_OTA_RW612 320 // FRDM-RW612 #define MG_OTA_FLASH 900 // OTA via an internal flash #define MG_OTA_ESP32 910 // ESP32 OTA implementation #define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash #define MG_OTA_CUSTOM 1000 // Custom implementation #ifndef MG_OTA #define MG_OTA MG_OTA_NONE #else #ifndef MG_IRAM #if defined(__GNUC__) #define MG_IRAM __attribute__((noinline, section(".iram"))) #else #define MG_IRAM #endif // compiler #endif // IRAM #endif // OTA // Firmware update API bool mg_ota_begin(size_t new_firmware_size); // Start writing bool mg_ota_write(const void *buf, size_t len); // Write chunk, aligned to 1k bool mg_ota_end(void); // Stop writing // MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA the // default calls swap_fn() (no-op on single-bank) then resets. Override in // mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.). #ifndef MG_OTA_ROLLBACK #define MG_OTA_ROLLBACK() \ do { \ mg_flash->swap_fn(); \ NVIC_SystemReset(); \ } while (0) #endif // MG_OTA_ROLLBACK_TIMER_START(seconds): arm a hardware watchdog as the rollback // deadline. If it fires before MG_OTA_STATE_SET(MG_OTA_CONFIRMED), the device // resets and MG_OTA_STATE_GET() == MG_OTA_FAILED triggers MG_OTA_ROLLBACK(). #ifndef MG_OTA_ROLLBACK_TIMER_START #define MG_OTA_ROLLBACK_TIMER_START(seconds) (void) (seconds) #endif // OTA state values for MG_OTA_STATE_GET / MG_OTA_STATE_SET. // MG_OTA_CONFIRMED: firmware is committed, normal operation. // MG_OTA_TESTING: new firmware booted for the first time; IWDG is armed. // If the device reboots while in this state, it transitions // to MG_OTA_FAILED and rolls back on the next boot. // MG_OTA_FAILED: previous boot did not commit in time; rollback on next // boot. enum { MG_OTA_CONFIRMED = 0, MG_OTA_TESTING = 1, MG_OTA_FAILED = 2 }; // Persistent OTA state storage. Define in mongoose_config.h to use a // location that survives resets #ifndef MG_OTA_STATE_GET #define MG_OTA_STATE_GET() 0 #endif #ifndef MG_OTA_STATE_SET #define MG_OTA_STATE_SET(val) (void) (val) #endif // Call once at boot. Arms the rollback watchdog for (seconds) seconds when // the new firmware is in TESTING state. Rolls back immediately if FAILED. // boot (TESTING) → set FAILED → arm IWDG → run firmware // ├── commit called → set CONFIRMED → NVIC_SystemReset() → clean boot // └── IWDG fires → hard reset → boot → state is still FAILED → rollback #define MG_OTA_BOOT_CHECK(seconds) \ do { \ if (MG_OTA_STATE_GET() == MG_OTA_FAILED) { \ MG_OTA_STATE_SET(MG_OTA_CONFIRMED); \ MG_INFO(("Commit deadline expired, rolling back")); \ MG_OTA_ROLLBACK(); \ } else if (MG_OTA_STATE_GET() == MG_OTA_TESTING) { \ MG_OTA_STATE_SET(MG_OTA_FAILED); \ MG_INFO(("New firmware: commit within %u sec or rolls back", \ (unsigned) (seconds))); \ MG_OTA_ROLLBACK_TIMER_START(seconds); \ } \ } while (0) // Pull based OTA over HTTP void mg_ota_url_check(struct mg_mgr *mgr, const char *current_version, const char *metadata_url, void (*fn)(const char *status));