diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 22d4a42a4..d491ead03 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -137,6 +137,7 @@ jobs: name: arduino:mbed libraries: | - name: ArduinoECCX08 + - source-url: https://github.com/arduino-libraries/Arduino_Portenta_OTA.git sketch-paths: | - examples/utility/Provisioning # ESP8266 boards diff --git a/extras/tools/bin2ota.py b/extras/tools/bin2ota.py index 99e86c0df..648aa2395 100755 --- a/extras/tools/bin2ota.py +++ b/extras/tools/bin2ota.py @@ -22,6 +22,8 @@ magic_number = 0x23418054.to_bytes(4,byteorder='little') elif board == "NANO_33_IOT": magic_number = 0x23418057.to_bytes(4,byteorder='little') +elif board == "PORTENTA_H7_M7": + magic_number = 0x2341025B.to_bytes(4,byteorder='little') else: print ("Error,", board, "is not a supported board type") sys.exit() diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index 59292db11..bc451ad44 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -100,7 +100,13 @@ #define OTA_STORAGE_SSU (0) #endif -#if (OTA_STORAGE_SFU || OTA_STORAGE_SSU || OTA_STORAGE_SNU) && !defined(ARDUINO_AVR_UNO_WIFI_REV2) +#if defined(ARDUINO_PORTENTA_H7_M7) + #define OTA_STORAGE_PORTENTA_QSPI (1) +#else + #define OTA_STORAGE_PORTENTA_QSPI (0) +#endif + +#if (OTA_STORAGE_SFU || OTA_STORAGE_SSU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI) && !defined(ARDUINO_AVR_UNO_WIFI_REV2) #define OTA_ENABLED (1) #else #define OTA_ENABLED (0) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 4a68ac7ae..4bd44e056 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -123,7 +123,12 @@ int ArduinoIoTCloudTCP::begin(String brokerAddress, uint16_t brokerPort) * The bootloader is excluded from the calculation and occupies flash address * range 0 to 0x2000, total flash size of 0x40000 bytes (256 kByte). */ +#if defined(ARDUINO_PORTENTA_H7_M7) + // TODO: check if we need to checksum the whole flash or just the first megabyte + _ota_img_sha256 = FlashSHA256::calc(0x8040000, 0x200000 - 0x40000); +#else _ota_img_sha256 = FlashSHA256::calc(0x2000, 0x40000 - 0x2000); +#endif #endif /* OTA_ENABLED */ #ifdef BOARD_HAS_OFFLOADED_ECCX08 @@ -186,6 +191,21 @@ int ArduinoIoTCloudTCP::begin(String brokerAddress, uint16_t brokerPort) addPropertyReal(_ota_req, "OTA_REQ", Permission::ReadWrite).onSync(DEVICE_WINS); #endif /* OTA_ENABLED */ +#if OTA_STORAGE_PORTENTA_QSPI + #define BOOTLOADER_ADDR (0x8000000) + uint32_t bootloader_data_offset = 0x1F000; + uint8_t* bootloader_data = (uint8_t*)(BOOTLOADER_ADDR + bootloader_data_offset); + uint8_t currentBootloaderVersion = bootloader_data[1]; + if (currentBootloaderVersion < 22) { + _ota_cap = false; + DEBUG_WARNING("ArduinoIoTCloudTCP::%s In order to be ready for cloud OTA, update the bootloader", __FUNCTION__); + DEBUG_WARNING("ArduinoIoTCloudTCP::%s File -> Examples -> Portenta_System -> PortentaH7_updateBootloader", __FUNCTION__); + } + else { + _ota_cap = true; + } +#endif + #if OTA_STORAGE_SNU && OTA_ENABLED String const nina_fw_version = WiFi.firmwareVersion(); if (nina_fw_version < "1.4.1") { @@ -443,11 +463,6 @@ void ArduinoIoTCloudTCP::onOTARequest() { DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s _ota_url = %s", __FUNCTION__, _ota_url.c_str()); - /* Status flag to prevent the reset from being executed - * when HTTPS download is not supported. - */ - bool ota_download_success = false; - #if OTA_STORAGE_SNU /* Just to be safe delete any remains from previous updates. */ WiFiStorage.remove("/fs/UPDATE.BIN.LZSS"); @@ -462,15 +477,47 @@ void ArduinoIoTCloudTCP::onOTARequest() return; } - /* The download was a success. */ - ota_download_success = true; + /* Perform the reset to reboot to SxU. */ + NVIC_SystemReset(); #endif /* OTA_STORAGE_SNU */ -#ifndef __AVR__ - /* Perform the reset to reboot to SxU. */ - if (ota_download_success) - NVIC_SystemReset(); -#endif +#if OTA_STORAGE_PORTENTA_QSPI + Arduino_Portenta_OTA::Error ota_portenta_err = Arduino_Portenta_OTA::Error::None; + /* Use 2nd partition of QSPI (1st partition contains WiFi firmware) */ + Arduino_Portenta_OTA_QSPI ota_portenta_qspi(QSPI_FLASH_FATFS_MBR, 2); + + /* Initialize the QSPI memory for OTA handling. */ + if((ota_portenta_err = ota_portenta_qspi.begin()) != Arduino_Portenta_OTA::Error::None) { + DEBUG_ERROR("Arduino_Portenta_OTA_QSPI::begin() failed with %d", static_cast(ota_portenta_err)); + return; + } + + /* Just to be safe delete any remains from previous updates. */ + remove("/fs/UPDATE.BIN"); + remove("/fs/UPDATE.BIN.LZSS"); + + /* Download the OTA file from the web storage location. */ + int const ota_portenta_qspi_download_ret_code = ota_portenta_qspi.download((char*)(_ota_url.c_str()), true /* is_https */); + DEBUG_VERBOSE("Arduino_Portenta_OTA_QSPI::download(%s) returns %d", _ota_url.c_str(), ota_portenta_qspi_download_ret_code); + + /* Decompress the LZSS compressed OTA file. */ + int const ota_portenta_qspi_decompress_ret_code = ota_portenta_qspi.decompress(); + DEBUG_VERBOSE("Arduino_Portenta_OTA_QSPI::decompress() returns %d", ota_portenta_qspi_decompress_ret_code); + if (ota_portenta_qspi_decompress_ret_code < 0) + { + DEBUG_ERROR("Arduino_Portenta_OTA_QSPI::decompress() failed with %d", ota_portenta_qspi_decompress_ret_code); + return; + } + + /* Schedule the firmware update. */ + if((ota_portenta_err = ota_portenta_qspi.update()) != Arduino_Portenta_OTA::Error::None) { + DEBUG_ERROR("Arduino_Portenta_OTA_QSPI::update() failed with %d", static_cast(ota_portenta_err)); + return; + } + + /* Perform the reset to reboot - then the bootloader performs the actual application update. */ + NVIC_SystemReset(); +#endif /* OTA_STORAGE_PORTENTA_QSPI */ } #endif diff --git a/src/utility/ota/OTA.h b/src/utility/ota/OTA.h index bd1476fba..670b5ac07 100644 --- a/src/utility/ota/OTA.h +++ b/src/utility/ota/OTA.h @@ -37,6 +37,10 @@ #include #endif /* OTA_STORAGE_SFU */ +#if OTA_STORAGE_PORTENTA_QSPI + #include +#endif /* OTA_STORAGE_PORTENTA_QSPI */ + /****************************************************************************** * TYPEDEF ******************************************************************************/