├── examples ├── LZSS │ ├── arduino_secrets.h │ └── LZSS.ino ├── OTA_Qspi_Flash │ ├── arduino_secrets.h │ └── OTA_Qspi_Flash.ino ├── OTA_SD_Portenta │ ├── arduino_secrets.h │ └── OTA_SD_Portenta.ino ├── OTA_Qspi_Flash_download_onthefly │ ├── arduino_secrets.h │ └── OTA_Qspi_Flash_download_onthefly.ino ├── OTA_Usage_Portenta │ ├── OTA_Usage_Portenta.ino.PORTENTA_H7_M7.bin │ ├── OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota │ └── OTA_Usage_Portenta.ino └── OTA_Qspi_Flash_Ethernet │ └── OTA_Qspi_Flash_Ethernet.ino ├── .codespellrc ├── library.properties ├── .github ├── dependabot.yml └── workflows │ ├── arduino-lint.yml │ ├── report-size-deltas.yml │ ├── spell-check.yml │ ├── compile-examples.yml │ └── sync-labels.yml ├── keywords.txt ├── src ├── Arduino_Portenta_OTA_Debug.h ├── Arduino_Portenta_OTA_Config.h ├── Arduino_Portenta_OTA_QSPI.h ├── Arduino_Portenta_OTA_SD.h ├── Arduino_Portenta_OTA_QSPI.cpp ├── Arduino_Portenta_OTA_SD.cpp ├── decompress │ ├── lzss.h │ ├── lzss.cpp │ └── utility.cpp ├── Arduino_Portenta_OTA.h └── Arduino_Portenta_OTA.cpp └── README.md /examples/LZSS/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /examples/OTA_Qspi_Flash/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /examples/OTA_SD_Portenta/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /examples/OTA_Qspi_Flash_download_onthefly/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /examples/OTA_Usage_Portenta/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_Portenta_OTA/HEAD/examples/OTA_Usage_Portenta/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.bin -------------------------------------------------------------------------------- /examples/OTA_Usage_Portenta/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_Portenta_OTA/HEAD/examples/OTA_Usage_Portenta/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 3 | ignore-words-list = , 4 | check-filenames = 5 | check-hidden = 6 | skip = ./.git 7 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_Portenta_OTA 2 | version=1.2.1 3 | author=Arduino 4 | maintainer=Arduino 5 | sentence=Firmware update for the Portenta H7. 6 | paragraph=This library allows performing a firmware update on the Arduino Portenta H7. The firmware can be stored in various different locations such as within the microcontroller's flash, on an external SD card or on the QSPI flash chip. 7 | category=Communication 8 | url=https://github.com/arduino-libraries/Arduino_Portenta_OTA 9 | architectures=mbed,mbed_portenta,mbed_nicla,mbed_opta,mbed_giga 10 | includes=Arduino_Portenta_OTA.h 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/dependabot/README.md 7 | # See: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-actions-up-to-date-with-dependabot 8 | - package-ecosystem: github-actions 9 | directory: / # Check the repository's workflows under /.github/workflows/ 10 | schedule: 11 | interval: daily 12 | labels: 13 | - "topic: infrastructure" 14 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Lint 2 | on: 3 | push: 4 | pull_request: 5 | # Scheduled trigger checks for breakage caused by new rules added to Arduino Lint 6 | schedule: 7 | # run every Saturday at 3 AM UTC 8 | - cron: "0 3 * * 6" 9 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_dispatch 10 | workflow_dispatch: 11 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch 12 | repository_dispatch: 13 | 14 | jobs: 15 | lint: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v6 21 | 22 | - name: Arduino Lint 23 | uses: arduino/arduino-lint-action@v2 24 | with: 25 | official: true 26 | library-manager: update 27 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | on: 4 | schedule: 5 | - cron: '*/5 * * * *' 6 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_dispatch 7 | workflow_dispatch: 8 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch 9 | repository_dispatch: 10 | 11 | jobs: 12 | report: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | # See: https://github.com/arduino/actions/blob/master/libraries/report-size-deltas/README.md 17 | - name: Comment size deltas reports to PRs 18 | uses: arduino/report-size-deltas@main 19 | with: 20 | # Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow 21 | sketches-reports-source: ^sketches-report-.+ 22 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | # Run every Saturday at 3 AM UTC to catch new misspelling detections resulting from dictionary updates. 8 | - cron: "0 3 * * 6" 9 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_dispatch 10 | workflow_dispatch: 11 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch 12 | repository_dispatch: 13 | 14 | jobs: 15 | spellcheck: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v6 21 | 22 | # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md 23 | - name: Spell check 24 | uses: codespell-project/actions-codespell@master 25 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Arduino_Portenta_OTA 3 | ####################################### 4 | 5 | ####################################### 6 | # Class (KEYWORD1) 7 | ####################################### 8 | 9 | Arduino_Portenta_OTA KEYWORD1 10 | Arduino_Portenta_OTA_SD KEYWORD1 11 | Arduino_Portenta_OTA_QSPI KEYWORD1 12 | Error KEYWORD1 13 | StorageTypePortenta KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | isOtaCapable KEYWORD2 20 | begin KEYWORD2 21 | update KEYWORD2 22 | reset KEYWORD2 23 | download KEYWORD2 24 | decompress KEYWORD2 25 | 26 | ####################################### 27 | # Constants (LITERAL1) 28 | ####################################### 29 | 30 | QSPI_FLASH_FATFS LITERAL1 31 | QSPI_FLASH_FATFS_MBR LITERAL1 32 | SD_FATFS LITERAL1 33 | SD_FATFS_MBR LITERAL1 34 | 35 | None LITERAL1 36 | NoCapableBootloader LITERAL1 37 | NoOtaStorage LITERAL1 38 | OtaStorageInit LITERAL1 39 | OtaStorageOpen LITERAL1 40 | OtaHeaderLength LITERAL1 41 | OtaHeaderCrc LITERAL1 42 | OtaHeaterMagicNumber LITERAL1 43 | -------------------------------------------------------------------------------- /examples/OTA_Usage_Portenta/OTA_Usage_Portenta.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch can be used to generate an example binary that can be uploaded to Portenta via OTA. 3 | It needs to be used together with 4 | - 'OTA_Qspi_Flash.ino' if you want to use the QSPI Flash as storage system 5 | OR 6 | - 'OTA_SD_Portenta.ino' if you want to use the SD card as storage system 7 | 8 | Steps to test OTA on Portenta: 9 | 1) Upload this sketch or any other sketch (this one lights up the RGB LED with different colours). 10 | 2) In the IDE select: Sketch -> Export compiled Binary 11 | 3) Upload the exported binary to a server 12 | 4) Choose a storage mechanism (SD or QSPI), open the related OTA_*_Portenta.ino sketch, 13 | eventually update the OTA_FILE_LOCATION 14 | 5) Upload the sketch OTA_*_Portenta.ino to perform OTA via SD or QSPI Flash 15 | */ 16 | 17 | void setLed(int blue, int gree, int red) { 18 | if (blue == 1) { 19 | digitalWrite(LEDB, LOW); 20 | } 21 | else { 22 | digitalWrite(LEDB, HIGH); 23 | } 24 | 25 | if (gree == 1) { 26 | digitalWrite(LEDG, LOW); 27 | } 28 | else { 29 | digitalWrite(LEDG, HIGH); 30 | } 31 | 32 | if (red == 1) { 33 | digitalWrite(LEDR, LOW); 34 | } 35 | else { 36 | digitalWrite(LEDR, HIGH); 37 | } 38 | } 39 | 40 | 41 | void setup() 42 | { 43 | pinMode(LEDB, OUTPUT); 44 | pinMode(LEDG, OUTPUT); 45 | pinMode(LEDR, OUTPUT); 46 | } 47 | 48 | void loop() 49 | { // Blue LED on 50 | setLed(1, 0, 0); 51 | delay(1000); 52 | // Green LED on 53 | setLed(0, 1, 0); 54 | delay(1000); 55 | // Red LED on 56 | setLed(0, 0, 1); 57 | delay(1000); 58 | } 59 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_Debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2024 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_PORTENTA_OTA_DEBUG_H_ 19 | #define ARDUINO_PORTENTA_OTA_DEBUG_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #if defined __has_include 26 | #if __has_include () 27 | #include 28 | #else 29 | #define ARDUINO_PORTENTA_OTA_NO_DEBUG 30 | #endif 31 | #else 32 | #define ARDUINO_PORTENTA_OTA_NO_DEBUG 33 | #endif 34 | 35 | #ifdef ARDUINO_PORTENTA_OTA_NO_DEBUG 36 | #define DEBUG_ERROR(fmt, ...) ((void)0) 37 | #define DEBUG_WARNING(fmt, ...) ((void)0) 38 | #define DEBUG_INFO(fmt, ...) ((void)0) 39 | #define DEBUG_DEBUG(fmt, ...) ((void)0) 40 | #define DEBUG_VERBOSE(fmt, ...) ((void)0) 41 | #endif 42 | #undef ARDUINO_PORTENTA_OTA_NO_DEBUG 43 | 44 | #endif /* ARDUINO_PORTENTA_OTA_DEBUG_H_ */ 45 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2022 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_PORTENTA_OTA_CONFIG_H_ 19 | #define ARDUINO_PORTENTA_OTA_CONFIG_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #include 26 | 27 | #if defined(ARDUINO_PORTENTA_H7_M7) 28 | #define ARDUINO_PORTENTA_OTA_MAGIC 0x2341025b 29 | #define ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT 30 | #define ARDUINO_PORTENTA_OTA_QSPI_SUPPORT 31 | #endif 32 | 33 | #if defined(ARDUINO_NICLA_VISION) 34 | #define ARDUINO_PORTENTA_OTA_MAGIC 0x2341025f 35 | #define ARDUINO_PORTENTA_OTA_QSPI_SUPPORT 36 | #endif 37 | 38 | #if defined(ARDUINO_OPTA) 39 | #define ARDUINO_PORTENTA_OTA_MAGIC 0x23410064 40 | #define ARDUINO_PORTENTA_OTA_QSPI_SUPPORT 41 | #endif 42 | 43 | #if defined(ARDUINO_GIGA) 44 | #define ARDUINO_PORTENTA_OTA_MAGIC 0x23410266 45 | #define ARDUINO_PORTENTA_OTA_QSPI_SUPPORT 46 | #endif 47 | 48 | #endif /* ARDUINO_PORTENTA_OTA_CONFIG_H_ */ 49 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_QSPI.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2020 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_PORTENTA_OTA_QSPI_H_ 19 | #define ARDUINO_PORTENTA_OTA_QSPI_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #include "Arduino_Portenta_OTA_Config.h" 26 | #if defined(ARDUINO_PORTENTA_OTA_QSPI_SUPPORT) 27 | 28 | #include "Arduino_Portenta_OTA.h" 29 | 30 | /****************************************************************************** 31 | * CLASS DECLARATION 32 | ******************************************************************************/ 33 | 34 | class Arduino_Portenta_OTA_QSPI : public Arduino_Portenta_OTA 35 | { 36 | 37 | public: 38 | 39 | Arduino_Portenta_OTA_QSPI(StorageTypePortenta const storage_type, uint32_t const data_offset); 40 | virtual ~Arduino_Portenta_OTA_QSPI() { } 41 | 42 | 43 | protected: 44 | 45 | virtual bool init () override; 46 | virtual bool open () override; 47 | 48 | 49 | private: 50 | 51 | mbed::BlockDevice * _bd_qspi; 52 | mbed::FATFileSystem * _fs_qspi; 53 | }; 54 | 55 | #endif /* ARDUINO_PORTENTA_OTA_QSPI_SUPPORT */ 56 | #endif /* ARDUINO_PORTENTA_OTA_QSPI_H_ */ 57 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_SD.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2020 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_PORTENTA_OTA_SD_H_ 19 | #define ARDUINO_PORTENTA_OTA_SD_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #include "Arduino_Portenta_OTA_Config.h" 26 | #if defined(ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT) 27 | 28 | #include "Arduino_Portenta_OTA.h" 29 | 30 | /****************************************************************************** 31 | * CLASS DECLARATION 32 | ******************************************************************************/ 33 | 34 | class Arduino_Portenta_OTA_SD : public Arduino_Portenta_OTA 35 | { 36 | public: 37 | 38 | Arduino_Portenta_OTA_SD(StorageTypePortenta const storage_type, uint32_t const data_offset); 39 | virtual ~Arduino_Portenta_OTA_SD() { } 40 | 41 | 42 | protected: 43 | 44 | virtual bool init () override; 45 | virtual bool open () override; 46 | 47 | 48 | private: 49 | 50 | mbed::BlockDevice * _bd; 51 | SDMMCBlockDevice _block_device; 52 | mbed::FATFileSystem * _fs_sd; 53 | 54 | }; 55 | 56 | #endif /* ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT */ 57 | #endif /* ARDUINO_PORTENTA_OTA_SD_H_ */ 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arduino_Portenta_OTA 2 | ==================== 3 | 4 | [![Compile Examples](https://github.com/arduino-libraries/Arduino_Portenta_OTA/workflows/Compile%20Examples/badge.svg)](https://github.com/arduino-libraries/Arduino_Portenta_OTA/actions?workflow=Compile+Examples) 5 | [![Arduino Lint](https://github.com/arduino-libraries/Arduino_Portenta_OTA/workflows/Arduino%20Lint/badge.svg)](https://github.com/arduino-libraries/Arduino_Portenta_OTA/actions?workflow=Arduino+Lint) 6 | [![Spell Check](https://github.com/arduino-libraries/Arduino_Portenta_OTA/workflows/Spell%20Check/badge.svg)](https://github.com/arduino-libraries/Arduino_Portenta_OTA/actions?workflow=Spell+Check) 7 | 8 | This library allows OTA (Over-The-Air) firmware updates for the Arduino Portenta H7. OTA binaries are downloaded via WiFi and stored on a SD card or on the Portenta H7's QSPI flash storage. Next, all information relevant to the firmware update is stored in non-volatile memory. After a reset the Portenta H7 bootloader accesses this information and uses it to perform the firmware update. 9 | 10 | ### Example 11 | ```C++ 12 | #include 13 | #include 14 | #include "arduino_secrets.h" 15 | /* ... */ 16 | void setup() 17 | { 18 | if (WiFi.status() == WL_NO_SHIELD) 19 | return; 20 | 21 | int status = WL_IDLE_STATUS; 22 | while (status != WL_CONNECTED) 23 | { 24 | status = WiFi.begin(SSID, PASS); 25 | delay(10000); 26 | } 27 | 28 | Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2); 29 | Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None; 30 | 31 | if (!ota.isOtaCapable()) 32 | return; 33 | 34 | if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) 35 | return; 36 | 37 | int const ota_download = ota.download("http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota", false /* is_https */); 38 | if (ota_download <= 0) 39 | return; 40 | 41 | int const ota_decompress = ota.decompress(); 42 | if (ota_decompress < 0) 43 | return; 44 | 45 | if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) 46 | return; 47 | 48 | ota.reset(); 49 | } 50 | 51 | void loop() 52 | { 53 | 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_QSPI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2020 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | /****************************************************************************** 19 | INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include "Arduino_Portenta_OTA_Config.h" 23 | #if defined(ARDUINO_PORTENTA_OTA_QSPI_SUPPORT) 24 | 25 | #include "Arduino_Portenta_OTA_QSPI.h" 26 | #include "Arduino_Portenta_OTA_Debug.h" 27 | #include 28 | 29 | using namespace arduino; 30 | 31 | /****************************************************************************** 32 | CTOR/DTOR 33 | ******************************************************************************/ 34 | 35 | Arduino_Portenta_OTA_QSPI::Arduino_Portenta_OTA_QSPI(StorageTypePortenta const storage_type, uint32_t const data_offset) 36 | : Arduino_Portenta_OTA(storage_type, data_offset) 37 | , _bd_qspi{NULL} 38 | , _fs_qspi{NULL} 39 | { 40 | assert(_storage_type == QSPI_FLASH_FATFS || _storage_type == QSPI_FLASH_FATFS_MBR); 41 | } 42 | 43 | /****************************************************************************** 44 | PUBLIC MEMBER FUNCTIONS 45 | ******************************************************************************/ 46 | 47 | bool Arduino_Portenta_OTA_QSPI::init() 48 | { 49 | if(_storage_type == QSPI_FLASH_FATFS) 50 | { 51 | _fs_qspi = new mbed::FATFileSystem("fs"); 52 | int const err_mount = _fs_qspi->mount(_bd_raw_qspi); 53 | if (err_mount) 54 | { 55 | DEBUG_ERROR(F("Error while mounting the filesystem. Err = %d"), err_mount); 56 | return false; 57 | } 58 | return true; 59 | } 60 | 61 | if (_storage_type == QSPI_FLASH_FATFS_MBR) 62 | { 63 | _bd_qspi = new mbed::MBRBlockDevice(_bd_raw_qspi, _data_offset); 64 | _fs_qspi = new mbed::FATFileSystem("fs"); 65 | int const err_mount = _fs_qspi->mount(_bd_qspi); 66 | if (err_mount) { 67 | DEBUG_ERROR(F("Error while mounting the filesystem. Err = %d"), err_mount); 68 | return false; 69 | } 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | bool Arduino_Portenta_OTA_QSPI::open() 76 | { 77 | DIR * dir = NULL; 78 | if ((dir = opendir("/fs")) != NULL) 79 | { 80 | if (Arduino_Portenta_OTA::findProgramLength(dir, _program_length)) 81 | { 82 | closedir(dir); 83 | return true; 84 | } 85 | closedir(dir); 86 | } 87 | 88 | return false; 89 | } 90 | 91 | #endif /* ARDUINO_PORTENTA_OTA_QSPI_SUPPORT */ 92 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA_SD.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2020 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | /****************************************************************************** 19 | INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include "Arduino_Portenta_OTA_Config.h" 23 | #if defined(ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT) 24 | 25 | #include "Arduino_Portenta_OTA_SD.h" 26 | #include "Arduino_Portenta_OTA_Debug.h" 27 | #include "BSP.h" 28 | #include "stm32h7xx_hal_sd.h" 29 | 30 | #include 31 | 32 | using namespace arduino; 33 | 34 | /****************************************************************************** 35 | CONSTANTS 36 | ******************************************************************************/ 37 | 38 | static char const SD_UPDATE_FILENAME[] = "UPDATE.BIN"; 39 | 40 | /****************************************************************************** 41 | CTOR/DTOR 42 | ******************************************************************************/ 43 | 44 | Arduino_Portenta_OTA_SD::Arduino_Portenta_OTA_SD(StorageTypePortenta const storage_type, uint32_t const data_offset) 45 | : Arduino_Portenta_OTA(storage_type, data_offset) 46 | , _bd{NULL} 47 | , _block_device() 48 | , _fs_sd{NULL} 49 | { 50 | assert(_storage_type == SD_FATFS || _storage_type == SD_FATFS_MBR); 51 | } 52 | 53 | /****************************************************************************** 54 | PUBLIC MEMBER FUNCTIONS 55 | ******************************************************************************/ 56 | 57 | bool Arduino_Portenta_OTA_SD::init() 58 | { 59 | if(_storage_type == SD_FATFS) 60 | { 61 | _fs_sd = new mbed::FATFileSystem("fs"); 62 | int const err = _fs_sd->mount(&_block_device); 63 | if (err) { 64 | DEBUG_ERROR(F("Error while mounting the filesystem. Err = %d"), err); 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | if (_storage_type == SD_FATFS_MBR) 71 | { 72 | _bd = new mbed::MBRBlockDevice(reinterpret_cast(&_block_device), _data_offset); 73 | _fs_sd = new mbed::FATFileSystem("fs"); 74 | int const err = _fs_sd->mount(_bd); 75 | if (err) 76 | { 77 | DEBUG_ERROR(F("Error while mounting the filesystem. Err = %d"), err); 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | bool Arduino_Portenta_OTA_SD::open() 87 | { 88 | DIR * dir = NULL; 89 | if ((dir = opendir("/fs")) != NULL) 90 | { 91 | if (Arduino_Portenta_OTA::findProgramLength(dir, _program_length)) 92 | { 93 | closedir(dir); 94 | return true; 95 | } 96 | closedir(dir); 97 | } 98 | 99 | return false; 100 | } 101 | 102 | #endif /* ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT */ 103 | -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | name: Compile Examples 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/compile-examples.yml" 7 | - "examples/**" 8 | - "src/**" 9 | push: 10 | paths: 11 | - ".github/workflows/compile-examples.yml" 12 | - "examples/**" 13 | - "src/**" 14 | # Scheduled trigger checks for breakage caused by changes to external resources (libraries, platforms) 15 | schedule: 16 | # run every Saturday at 3 AM UTC 17 | - cron: "0 3 * * 6" 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_dispatch 19 | workflow_dispatch: 20 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch 21 | repository_dispatch: 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | 27 | env: 28 | SKETCHES_REPORTS_PATH: sketches-reports 29 | 30 | strategy: 31 | fail-fast: false 32 | 33 | matrix: 34 | board: 35 | - fqbn: arduino:mbed_portenta:envie_m7 36 | platforms: | 37 | - name: arduino:mbed_portenta 38 | libraries: | 39 | - name: Arduino_DebugUtils 40 | sketch-paths: | 41 | - examples/OTA_Qspi_Flash 42 | - examples/OTA_Qspi_Flash_Ethernet 43 | - examples/OTA_SD_Portenta 44 | - examples/OTA_Usage_Portenta 45 | - examples/LZSS 46 | - examples/OTA_Qspi_Flash_download_onthefly 47 | artifact-name-suffix: arduino-mbed_portenta-envie_m7 48 | - fqbn: arduino:mbed_nicla:nicla_vision 49 | platforms: | 50 | - name: arduino:mbed_nicla 51 | libraries: | 52 | - name: Arduino_DebugUtils 53 | sketch-paths: | 54 | - examples/OTA_Qspi_Flash 55 | - examples/OTA_Usage_Portenta 56 | - examples/LZSS 57 | - examples/OTA_Qspi_Flash_download_onthefly 58 | artifact-name-suffix: arduino-mbed_nicla-nicla_vision 59 | - fqbn: arduino:mbed_opta:opta 60 | platforms: | 61 | - name: arduino:mbed_opta 62 | libraries: | 63 | - name: Arduino_DebugUtils 64 | sketch-paths: | 65 | - examples/OTA_Qspi_Flash 66 | - examples/OTA_Qspi_Flash_Ethernet 67 | - examples/OTA_Usage_Portenta 68 | - examples/LZSS 69 | - examples/OTA_Qspi_Flash_download_onthefly 70 | artifact-name-suffix: arduino-mbed_opta-opta 71 | - fqbn: arduino:mbed_giga:giga 72 | platforms: | 73 | - name: arduino:mbed_giga 74 | libraries: | 75 | - name: Arduino_DebugUtils 76 | sketch-paths: | 77 | - examples/OTA_Qspi_Flash 78 | - examples/OTA_Usage_Portenta 79 | - examples/LZSS 80 | - examples/OTA_Qspi_Flash_download_onthefly 81 | artifact-name-suffix: arduino-mbed_giga-giga 82 | 83 | steps: 84 | - name: Checkout 85 | uses: actions/checkout@v6 86 | 87 | - name: Compile examples 88 | uses: arduino/compile-sketches@main 89 | with: 90 | github-token: ${{ secrets.GITHUB_TOKEN }} 91 | fqbn: ${{ matrix.board.fqbn }} 92 | platforms: ${{ matrix.board.platforms }} 93 | libraries: | 94 | # Install the library from the local path. 95 | - source-path: ./ 96 | ${{ matrix.board.libraries }} 97 | sketch-paths: | 98 | ${{ matrix.board.sketch-paths }} 99 | enable-deltas-report: true 100 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 101 | 102 | - name: Save memory usage change report as artifact 103 | uses: actions/upload-artifact@v6 104 | with: 105 | if-no-files-found: error 106 | path: ${{ env.SKETCHES_REPORTS_PATH }} 107 | name: sketches-report-${{ matrix.board.artifact-name-suffix }} 108 | -------------------------------------------------------------------------------- /src/decompress/lzss.h: -------------------------------------------------------------------------------- 1 | #ifndef SSU_LZSS_H_ 2 | #define SSU_LZSS_H_ 3 | 4 | /************************************************************************************** 5 | INCLUDE 6 | **************************************************************************************/ 7 | 8 | #include 9 | #include 10 | #include "Arduino_Portenta_OTA.h" 11 | 12 | /************************************************************************************** 13 | FUNCTION DEFINITION 14 | **************************************************************************************/ 15 | 16 | void lzss_init(FILE * update_file_ptr, FILE * target_file_ptr, uint32_t const lzss_file_size, ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func_ptr); 17 | void lzss_decode(); 18 | void lzss_flush(); 19 | 20 | /************************************************************************************** 21 | LZSS DECODER CLASS 22 | **************************************************************************************/ 23 | 24 | 25 | class LZSSDecoder { 26 | public: 27 | 28 | /** 29 | * Build an LZSS decoder by providing a callback for storing the decoded bytes 30 | * @param putc_cbk: a callback that takes a char and stores it e.g. a callback to fwrite 31 | */ 32 | LZSSDecoder(std::function putc_cbk); 33 | 34 | /** 35 | * Build an LZSS decoder providing a callback for getting a char and putting a char 36 | * in this way you need to call decompress with no parameters 37 | * @param putc_cbk: a callback that takes a char and stores it e.g. a callback to fwrite 38 | * @param getc_cbk: a callback that returns the next char to consume 39 | * -1 means EOF, -2 means buffer is temporairly finished 40 | */ 41 | LZSSDecoder(std::function getc_cbk, std::function putc_cbk); 42 | 43 | /** 44 | * this enum describes the result of the computation of a single FSM computation 45 | * DONE: the decompression is completed 46 | * IN_PROGRESS: the decompression cycle completed successfully, ready to compute next 47 | * NOT_COMPLETED: the current cycle didn't complete because the available data is not enough 48 | */ 49 | enum status: uint8_t { 50 | DONE, 51 | IN_PROGRESS, 52 | NOT_COMPLETED 53 | }; 54 | 55 | /** 56 | * decode the provided buffer until buffer ends, then pause the process 57 | * @return DONE if the decompression is completed, NOT_COMPLETED if not 58 | */ 59 | status decompress(uint8_t* const buffer=nullptr, uint32_t size=0); 60 | 61 | static const int LZSS_EOF = -1; 62 | static const int LZSS_BUFFER_EMPTY = -2; 63 | private: 64 | // TODO provide a way for the user to set these parameters 65 | static const int EI = 11; /* typically 10..13 */ 66 | static const int EJ = 4; /* typically 4..5 */ 67 | static const int N = (1 << EI); /* buffer size */ 68 | static const int F = ((1 << EJ) + 1); /* lookahead buffer size */ 69 | 70 | // algorithm specific buffer used to store text that could be later referenced and copied 71 | uint8_t buffer[N * 2]; 72 | 73 | // this function gets 1 single char from the input buffer 74 | int getc(); 75 | uint8_t* in_buffer = nullptr; 76 | uint32_t available = 0; 77 | 78 | status handle_state(); 79 | 80 | // get 1 bit from the available input buffer 81 | int getbit(uint8_t n); 82 | // the following 2 are variables used by getbits 83 | uint32_t buf, buf_size=0; 84 | 85 | enum FSM_STATES: uint8_t { 86 | FSM_0 = 0, 87 | FSM_1 = 1, 88 | FSM_2 = 2, 89 | FSM_3 = 3, 90 | FSM_EOF 91 | } state; 92 | 93 | // these variable are used in a decode session and specific to the old C implementation 94 | // there is no documentation about their meaning 95 | int i, r; 96 | 97 | std::function put_char_cbk; 98 | std::function get_char_cbk; 99 | 100 | inline void putc(const uint8_t c) { if(put_char_cbk) { put_char_cbk(c); } } 101 | 102 | // get the number of bits the FSM will require given its state 103 | uint8_t bits_required(FSM_STATES s); 104 | }; 105 | 106 | #endif /* SSU_LZSS_H_ */ 107 | -------------------------------------------------------------------------------- /examples/OTA_Qspi_Flash_Ethernet/OTA_Qspi_Flash_Ethernet.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using 3 | * a firmware image stored on the QSPI. 4 | * 5 | * Steps: 6 | * 1) Create a sketch for the Portenta H7 and verify 7 | * that it both compiles and works on a board. 8 | * 2) In the IDE select: Sketch -> Export compiled Binary. 9 | * 3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in 10 | * https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools . 11 | * A) ./lzss.py --encode SKETCH.bin SKETCH.lzss 12 | * B) ./bin2ota.py PORTENTA_H7_M7 SKETCH.lzss SKETCH.ota 13 | * 4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 14 | * has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 15 | * 5) Perform an OTA update via steps outlined below. 16 | */ 17 | 18 | /****************************************************************************** 19 | * INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include 23 | 24 | #include 25 | 26 | /****************************************************************************** 27 | * CONSTANT 28 | ******************************************************************************/ 29 | #if defined(ARDUINO_OPTA) 30 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota"; 31 | #elif defined(ARDUINO_PORTENTA_H7_M7) 32 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota"; 33 | #else 34 | #error "Board not supported" 35 | #endif 36 | 37 | /****************************************************************************** 38 | * SETUP/LOOP 39 | ******************************************************************************/ 40 | 41 | void setup() 42 | { 43 | Serial.begin(115200); 44 | while (!Serial) {} 45 | 46 | while (Ethernet.linkStatus() == LinkOFF) 47 | { 48 | Serial.println("Attempting to connect to the network ..."); 49 | Ethernet.begin(); 50 | } 51 | Serial.println("Connected"); 52 | 53 | Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2); 54 | Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None; 55 | 56 | if (!ota.isOtaCapable()) 57 | { 58 | Serial.println("Higher version bootloader required to perform OTA."); 59 | Serial.println("Please update the bootloader."); 60 | Serial.println("File -> Examples -> STM32H747_System -> STM32H747_manageBootloader"); 61 | return; 62 | } 63 | 64 | Serial.println("Initializing OTA storage"); 65 | if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) 66 | { 67 | Serial.print ("Arduino_Portenta_OTA::begin() failed with error code "); 68 | Serial.println((int)ota_err); 69 | return; 70 | } 71 | 72 | Serial.println("Starting download to QSPI ..."); 73 | int const ota_download = ota.download(OTA_FILE_LOCATION, true /* is_https */); 74 | if (ota_download <= 0) 75 | { 76 | Serial.print ("Arduino_Portenta_OTA_QSPI::download failed with error code "); 77 | Serial.println(ota_download); 78 | return; 79 | } 80 | Serial.print (ota_download); 81 | Serial.println(" bytes stored."); 82 | 83 | 84 | Serial.println("Decompressing LZSS compressed file ..."); 85 | int const ota_decompress = ota.decompress(); 86 | if (ota_decompress < 0) 87 | { 88 | Serial.print("Arduino_Portenta_OTA_QSPI::decompress() failed with error code"); 89 | Serial.println(ota_decompress); 90 | return; 91 | } 92 | Serial.print(ota_decompress); 93 | Serial.println(" bytes decompressed."); 94 | 95 | 96 | Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory ..."); 97 | if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) 98 | { 99 | Serial.print ("ota.update() failed with error code "); 100 | Serial.println((int)ota_err); 101 | return; 102 | } 103 | 104 | Serial.println("Performing a reset after which the bootloader will update the firmware."); 105 | Serial.println("Hint: Board LED will blink Red-Blue-Green."); 106 | delay(1000); /* Make sure the serial message gets out before the reset. */ 107 | ota.reset(); 108 | } 109 | 110 | void loop() 111 | { 112 | 113 | } 114 | -------------------------------------------------------------------------------- /examples/OTA_SD_Portenta/OTA_SD_Portenta.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using 3 | * a firmware image stored on the SD. 4 | * 5 | * Steps: 6 | * 1) Create a sketch for the Portenta H7 and verify 7 | * that it both compiles and works on a board. 8 | * 2) In the IDE select: Sketch -> Export compiled Binary. 9 | * 3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in 10 | * https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools . 11 | * A) ./lzss.py --encode SKETCH.bin PORTENTA_H7_M7.lzss 12 | * B) ./bin2ota.py PORTENTA_H7_M7.lzss PORTENTA_H7_M7.ota 13 | * 4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 14 | * has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 15 | * 5) Perform an OTA update via steps outlined below. 16 | */ 17 | 18 | /****************************************************************************** 19 | * INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "arduino_secrets.h" 27 | 28 | /****************************************************************************** 29 | * CONSTANT 30 | ******************************************************************************/ 31 | 32 | /* Please enter your sensitive data in the Secret tab/arduino_secrets.h */ 33 | static char const SSID[] = SECRET_SSID; /* your network SSID (name) */ 34 | static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */ 35 | 36 | static char const OTA_FILE_LOCATION[] = "http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota"; 37 | 38 | /****************************************************************************** 39 | * SETUP/LOOP 40 | ******************************************************************************/ 41 | 42 | void setup() 43 | { 44 | Serial.begin(115200); 45 | while (!Serial) {} 46 | 47 | if (WiFi.status() == WL_NO_SHIELD) 48 | { 49 | Serial.println("Communication with WiFi module failed!"); 50 | return; 51 | } 52 | 53 | int status = WL_IDLE_STATUS; 54 | while (status != WL_CONNECTED) 55 | { 56 | Serial.print ("Attempting to connect to '"); 57 | Serial.print (SSID); 58 | Serial.println("'"); 59 | status = WiFi.begin(SSID, PASS); 60 | delay(10000); 61 | } 62 | Serial.print ("You're connected to '"); 63 | Serial.print (WiFi.SSID()); 64 | Serial.println("'"); 65 | 66 | //Arduino_Portenta_OTA_SD ota(SD_FATFS, 0); 67 | Arduino_Portenta_OTA_SD ota(SD_FATFS_MBR, 1); 68 | Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None; 69 | 70 | if (!ota.isOtaCapable()) 71 | { 72 | Serial.println("Higher version bootloader required to perform OTA."); 73 | Serial.println("Please update the bootloader."); 74 | Serial.println("File -> Examples -> Portenta_System -> PortentaH7_updateBootloader"); 75 | return; 76 | } 77 | 78 | Serial.println("Initializing OTA storage"); 79 | if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) 80 | { 81 | Serial.print ("Arduino_Portenta_OTA::begin() failed with error code "); 82 | Serial.println((int)ota_err); 83 | return; 84 | } 85 | 86 | 87 | Serial.println("Starting download to SD ..."); 88 | int const ota_download = ota.download(OTA_FILE_LOCATION, false /* is_https */); 89 | if (ota_download <= 0) 90 | { 91 | Serial.print ("Arduino_Portenta_OTA_SD::download failed with error code "); 92 | Serial.println(ota_download); 93 | return; 94 | } 95 | Serial.print (ota_download); 96 | Serial.println(" bytes stored."); 97 | 98 | 99 | Serial.println("Decompressing LZSS compressed file ..."); 100 | int const ota_decompress = ota.decompress(); 101 | if (ota_decompress < 0) 102 | { 103 | Serial.print("Arduino_Portenta_OTA_SD::decompress() failed with error code"); 104 | Serial.println(ota_decompress); 105 | return; 106 | } 107 | Serial.print(ota_decompress); 108 | Serial.println(" bytes decompressed."); 109 | 110 | 111 | Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory"); 112 | if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) 113 | { 114 | Serial.print ("Arduino_Portenta_OTA::update() failed with error code "); 115 | Serial.println((int)ota_err); 116 | return; 117 | } 118 | 119 | Serial.println("Performing a reset after which the bootloader will update the firmware."); 120 | Serial.println("Hint: Portenta H7 LED will blink Red-Blue-Green."); 121 | delay(1000); 122 | ota.reset(); 123 | } 124 | 125 | void loop() 126 | { 127 | 128 | } -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v6 31 | 32 | - name: Download JSON schema for labels configuration file 33 | id: download-schema 34 | uses: carlosperate/download-file-action@v2 35 | with: 36 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 37 | location: ${{ runner.temp }}/label-configuration-schema 38 | 39 | - name: Install JSON schema validator 40 | run: | 41 | sudo npm install \ 42 | --global \ 43 | ajv-cli \ 44 | ajv-formats 45 | 46 | - name: Validate local labels configuration 47 | run: | 48 | # See: https://github.com/ajv-validator/ajv-cli#readme 49 | ajv validate \ 50 | --all-errors \ 51 | -c ajv-formats \ 52 | -s "${{ steps.download-schema.outputs.file-path }}" \ 53 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 54 | 55 | download: 56 | needs: check 57 | runs-on: ubuntu-latest 58 | 59 | strategy: 60 | matrix: 61 | filename: 62 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 63 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 64 | - universal.yml 65 | 66 | steps: 67 | - name: Download 68 | uses: carlosperate/download-file-action@v2 69 | with: 70 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 71 | 72 | - name: Pass configuration files to next job via workflow artifact 73 | uses: actions/upload-artifact@v6 74 | with: 75 | path: | 76 | *.yaml 77 | *.yml 78 | if-no-files-found: error 79 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 80 | 81 | sync: 82 | needs: download 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Set environment variables 87 | run: | 88 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 89 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 90 | 91 | - name: Determine whether to dry run 92 | id: dry-run 93 | if: > 94 | github.event_name == 'pull_request' || 95 | ( 96 | ( 97 | github.event_name == 'push' || 98 | github.event_name == 'workflow_dispatch' 99 | ) && 100 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 101 | ) 102 | run: | 103 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 104 | # configuration. 105 | echo "::set-output name=flag::--dry-run" 106 | 107 | - name: Checkout repository 108 | uses: actions/checkout@v6 109 | 110 | - name: Download configuration files artifact 111 | uses: actions/download-artifact@v7 112 | with: 113 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 114 | path: ${{ env.CONFIGURATIONS_FOLDER }} 115 | 116 | - name: Remove unneeded artifact 117 | uses: geekyeggo/delete-artifact@v5 118 | with: 119 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 120 | 121 | - name: Merge label configuration files 122 | run: | 123 | # Merge all configuration files 124 | shopt -s extglob 125 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 126 | 127 | - name: Install github-label-sync 128 | run: sudo npm install --global github-label-sync 129 | 130 | - name: Sync labels 131 | env: 132 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 133 | run: | 134 | # See: https://github.com/Financial-Times/github-label-sync 135 | github-label-sync \ 136 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 137 | ${{ steps.dry-run.outputs.flag }} \ 138 | ${{ github.repository }} 139 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2019 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | #ifndef ARDUINO_PORTENTA_OTA_H_ 19 | #define ARDUINO_PORTENTA_OTA_H_ 20 | 21 | /****************************************************************************** 22 | * INCLUDE 23 | ******************************************************************************/ 24 | 25 | #include "Arduino_Portenta_OTA_Config.h" 26 | #if defined(ARDUINO_PORTENTA_OTA_QSPI_SUPPORT) 27 | #include 28 | #endif 29 | 30 | #if defined(ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT) 31 | #include 32 | #endif 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "WiFi.h" /* WiFi from ArduinoCore-mbed */ 40 | #include 41 | 42 | /****************************************************************************** 43 | * DEFINE 44 | ******************************************************************************/ 45 | 46 | #define APOTA_QSPI_FLASH_FLAG (1 << 2) 47 | #define APOTA_SDCARD_FLAG (1 << 3) 48 | #define APOTA_RAW_FLAG (1 << 4) 49 | #define APOTA_FATFS_FLAG (1 << 5) 50 | #define APOTA_LITTLEFS_FLAG (1 << 6) 51 | #define APOTA_MBR_FLAG (1 << 7) 52 | 53 | #define ARDUINO_PORTENTA_OTA_HAS_WATCHDOG_FEED 54 | 55 | /****************************************************************************** 56 | * TYPEDEF 57 | ******************************************************************************/ 58 | 59 | enum StorageTypePortenta { 60 | QSPI_FLASH_FATFS = APOTA_QSPI_FLASH_FLAG | APOTA_FATFS_FLAG, 61 | QSPI_FLASH_FATFS_MBR = APOTA_QSPI_FLASH_FLAG | APOTA_FATFS_FLAG | APOTA_MBR_FLAG, 62 | SD_FATFS = APOTA_SDCARD_FLAG | APOTA_FATFS_FLAG, 63 | SD_FATFS_MBR = APOTA_SDCARD_FLAG | APOTA_FATFS_FLAG | APOTA_MBR_FLAG, 64 | }; 65 | 66 | typedef void(*ArduinoPortentaOtaWatchdogResetFuncPointer)(void); 67 | 68 | /****************************************************************************** 69 | * CLASS DECLARATION 70 | ******************************************************************************/ 71 | 72 | class Arduino_Portenta_OTA 73 | { 74 | public: 75 | 76 | enum class Error : int 77 | { 78 | None = 0, 79 | NoCapableBootloader = -1, 80 | NoOtaStorage = -2, 81 | OtaStorageInit = -3, 82 | OtaStorageOpen = -4, 83 | OtaHeaderLength = -5, 84 | OtaHeaderCrc = -6, 85 | OtaHeaterMagicNumber = -7, 86 | CaStorageInit = -8, 87 | CaStorageOpen = -9, 88 | OtaDownload = -12, 89 | }; 90 | 91 | Arduino_Portenta_OTA(StorageTypePortenta const storage_type, uint32_t const data_offset); 92 | virtual ~Arduino_Portenta_OTA(); 93 | 94 | 95 | static bool isOtaCapable(); 96 | Error begin(); 97 | Error update(); 98 | void reset(); 99 | 100 | /* This functionality is intended for usage with the Arduino IoT Cloud for 101 | * performing OTA firmware updates using the Arduino IoT Cloud servers. 102 | */ 103 | int download(const char * url, bool const is_https, MbedSocketClass * socket = static_cast(&WiFi)); 104 | int decompress(); 105 | int downloadAndDecompress(const char * url, bool const is_https, MbedSocketClass * socket = static_cast(&WiFi)); 106 | 107 | void setFeedWatchdogFunc(ArduinoPortentaOtaWatchdogResetFuncPointer func); 108 | void feedWatchdog(); 109 | 110 | 111 | protected: 112 | 113 | StorageTypePortenta _storage_type; 114 | uint32_t _data_offset; 115 | uint32_t _program_length; 116 | mbed::BlockDevice * _bd_raw_qspi; 117 | 118 | virtual bool init() = 0; 119 | virtual bool open() = 0; 120 | 121 | static bool findProgramLength(DIR * dir, uint32_t & program_length); 122 | 123 | private: 124 | 125 | void write(); 126 | bool caStorageInit(); 127 | bool caStorageOpen(); 128 | ArduinoPortentaOtaWatchdogResetFuncPointer _feed_watchdog_func = 0; 129 | 130 | }; 131 | 132 | /****************************************************************************** 133 | * INCLUDE 134 | ******************************************************************************/ 135 | 136 | #if defined(ARDUINO_PORTENTA_OTA_SDMMC_SUPPORT) 137 | #include "Arduino_Portenta_OTA_SD.h" 138 | #endif 139 | 140 | #if defined(ARDUINO_PORTENTA_OTA_QSPI_SUPPORT) 141 | #include "Arduino_Portenta_OTA_QSPI.h" 142 | #endif 143 | 144 | #endif /* ARDUINO_PORTENTA_OTA_H_ */ -------------------------------------------------------------------------------- /examples/OTA_Qspi_Flash/OTA_Qspi_Flash.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using 3 | * a firmware image stored on the QSPI. 4 | * 5 | * Steps: 6 | * 1) Create a sketch for the Portenta H7 and verify 7 | * that it both compiles and works on a board. 8 | * 2) In the IDE select: Sketch -> Export compiled Binary. 9 | * 3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in 10 | * https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools . 11 | * A) ./lzss.py --encode SKETCH.bin SKETCH.lzss 12 | * B) ./bin2ota.py PORTENTA_H7_M7 SKETCH.lzss SKETCH.ota 13 | * 4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 14 | * has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 15 | * 5) Perform an OTA update via steps outlined below. 16 | */ 17 | 18 | /****************************************************************************** 19 | * INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "arduino_secrets.h" 27 | 28 | /****************************************************************************** 29 | * CONSTANT 30 | ******************************************************************************/ 31 | 32 | /* Please enter your sensitive data in the Secret tab/arduino_secrets.h */ 33 | static char const SSID[] = SECRET_SSID; /* your network SSID (name) */ 34 | static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */ 35 | 36 | #if defined(ARDUINO_NICLA_VISION) 37 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.NICLA_VISION.ota"; 38 | #elif defined(ARDUINO_PORTENTA_H7_M7) 39 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota"; 40 | #elif defined(ARDUINO_OPTA) 41 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota"; 42 | #elif defined(ARDUINO_GIGA) 43 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.GIGA.ota"; 44 | #else 45 | #error "Board not supported" 46 | #endif 47 | 48 | /****************************************************************************** 49 | * SETUP/LOOP 50 | ******************************************************************************/ 51 | 52 | void setup() 53 | { 54 | Serial.begin(115200); 55 | while (!Serial) {} 56 | 57 | if (WiFi.status() == WL_NO_SHIELD) 58 | { 59 | Serial.println("Communication with WiFi module failed!"); 60 | return; 61 | } 62 | 63 | int status = WL_IDLE_STATUS; 64 | while (status != WL_CONNECTED) 65 | { 66 | Serial.print ("Attempting to connect to '"); 67 | Serial.print (SSID); 68 | Serial.println("'"); 69 | status = WiFi.begin(SSID, PASS); 70 | delay(10000); 71 | } 72 | Serial.print ("You're connected to '"); 73 | Serial.print (WiFi.SSID()); 74 | Serial.println("'"); 75 | 76 | Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2); 77 | Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None; 78 | 79 | if (!ota.isOtaCapable()) 80 | { 81 | Serial.println("Higher version bootloader required to perform OTA."); 82 | Serial.println("Please update the bootloader."); 83 | Serial.println("File -> Examples -> Portenta_System -> PortentaH7_updateBootloader"); 84 | return; 85 | } 86 | 87 | Serial.println("Initializing OTA storage"); 88 | if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) 89 | { 90 | Serial.print ("Arduino_Portenta_OTA::begin() failed with error code "); 91 | Serial.println((int)ota_err); 92 | return; 93 | } 94 | 95 | 96 | Serial.println("Starting download to QSPI ..."); 97 | int const ota_download = ota.download(OTA_FILE_LOCATION, true /* is_https */); 98 | if (ota_download <= 0) 99 | { 100 | Serial.print ("Arduino_Portenta_OTA_QSPI::download failed with error code "); 101 | Serial.println(ota_download); 102 | return; 103 | } 104 | Serial.print (ota_download); 105 | Serial.println(" bytes stored."); 106 | 107 | 108 | Serial.println("Decompressing LZSS compressed file ..."); 109 | int const ota_decompress = ota.decompress(); 110 | if (ota_decompress < 0) 111 | { 112 | Serial.print("Arduino_Portenta_OTA_QSPI::decompress() failed with error code"); 113 | Serial.println(ota_decompress); 114 | return; 115 | } 116 | Serial.print(ota_decompress); 117 | Serial.println(" bytes decompressed."); 118 | 119 | 120 | Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory ..."); 121 | if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) 122 | { 123 | Serial.print ("ota.update() failed with error code "); 124 | Serial.println((int)ota_err); 125 | return; 126 | } 127 | 128 | Serial.println("Performing a reset after which the bootloader will update the firmware."); 129 | Serial.println("Hint: Portenta H7 LED will blink Red-Blue-Green."); 130 | delay(1000); /* Make sure the serial message gets out before the reset. */ 131 | ota.reset(); 132 | } 133 | 134 | void loop() 135 | { 136 | 137 | } 138 | -------------------------------------------------------------------------------- /examples/OTA_Qspi_Flash_download_onthefly/OTA_Qspi_Flash_download_onthefly.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using 3 | * a firmware image stored on the QSPI. 4 | * 5 | * Steps: 6 | * 1) Create a sketch for the Portenta H7 and verify 7 | * that it both compiles and works on a board. 8 | * 2) In the IDE select: Sketch -> Export compiled Binary. 9 | * 3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in 10 | * https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools . 11 | * A) ./lzss.py --encode SKETCH.bin SKETCH.lzss 12 | * B) ./bin2ota.py PORTENTA_H7_M7 SKETCH.lzss SKETCH.ota 13 | * 4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 14 | * has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota 15 | * 5) Perform an OTA update via steps outlined below. 16 | */ 17 | 18 | /****************************************************************************** 19 | * INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "arduino_secrets.h" 27 | 28 | /****************************************************************************** 29 | * CONSTANT 30 | ******************************************************************************/ 31 | 32 | /* Please enter your sensitive data in the Secret tab/arduino_secrets.h */ 33 | static char const SSID[] = SECRET_SSID; /* your network SSID (name) */ 34 | static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */ 35 | 36 | #if defined(ARDUINO_NICLA_VISION) 37 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.NICLA_VISION.ota"; 38 | #elif defined(ARDUINO_PORTENTA_H7_M7) 39 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota"; 40 | #elif defined(ARDUINO_OPTA) 41 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota"; 42 | #elif defined(ARDUINO_GIGA) 43 | static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.GIGA.ota"; 44 | #else 45 | #error "Board not supported" 46 | #endif 47 | 48 | /****************************************************************************** 49 | * SETUP/LOOP 50 | ******************************************************************************/ 51 | 52 | void setup() 53 | { 54 | Serial.begin(115200); 55 | while (!Serial) {} 56 | 57 | if (WiFi.status() == WL_NO_SHIELD) 58 | { 59 | Serial.println("Communication with WiFi module failed!"); 60 | return; 61 | } 62 | 63 | int status = WL_IDLE_STATUS; 64 | while (status != WL_CONNECTED) 65 | { 66 | Serial.print ("Attempting to connect to '"); 67 | Serial.print (SSID); 68 | Serial.println("'"); 69 | status = WiFi.begin(SSID, PASS); 70 | if(status != WL_CONNECTED) { 71 | delay(10000); 72 | } 73 | } 74 | Serial.print ("You're connected to '"); 75 | Serial.print (WiFi.SSID()); 76 | Serial.println("'"); 77 | 78 | Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2); 79 | Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None; 80 | 81 | if (!ota.isOtaCapable()) 82 | { 83 | Serial.println("Higher version bootloader required to perform OTA."); 84 | Serial.println("Please update the bootloader."); 85 | Serial.println("File -> Examples -> Portenta_System -> PortentaH7_updateBootloader"); 86 | return; 87 | } 88 | 89 | Serial.println("Initializing OTA storage"); 90 | if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) 91 | { 92 | Serial.print ("Arduino_Portenta_OTA::begin() failed with error code "); 93 | Serial.println((int)ota_err); 94 | return; 95 | } 96 | 97 | uint32_t start = millis(); 98 | float elapsed, speed; 99 | 100 | Serial.println("Starting download to QSPI ..."); 101 | int const ota_download = ota.downloadAndDecompress(OTA_FILE_LOCATION, true /* is_https */); 102 | if (ota_download <= 0) 103 | { 104 | Serial.print ("Arduino_Portenta_OTA_QSPI::download failed with error code "); 105 | Serial.println(ota_download); 106 | return; 107 | } 108 | Serial.print (ota_download); 109 | Serial.println(" bytes stored."); 110 | 111 | elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds 112 | speed = (ota_download/elapsed)/1024; 113 | 114 | Serial.print("download elapsed "); 115 | Serial.print(elapsed); 116 | Serial.print("s speed: "); 117 | Serial.print(speed); 118 | Serial.println("KBps"); 119 | 120 | Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory ..."); 121 | if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) 122 | { 123 | Serial.print ("ota.update() failed with error code "); 124 | Serial.println((int)ota_err); 125 | return; 126 | } 127 | 128 | Serial.println("Performing a reset after which the bootloader will update the firmware."); 129 | Serial.println("Hint: Portenta H7 LED will blink Red-Blue-Green."); 130 | delay(1000); /* Make sure the serial message gets out before the reset. */ 131 | ota.reset(); 132 | } 133 | 134 | void loop() 135 | { 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/Arduino_Portenta_OTA.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Arduino_Portenta_OTA. 3 | 4 | Copyright 2019 ARDUINO SA (http://www.arduino.cc/) 5 | 6 | This software is released under the GNU General Public License version 3, 7 | which covers the main part of arduino-cli. 8 | The terms of this license can be found at: 9 | https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | You can be released from the requirements of the above licenses by purchasing 12 | a commercial license. Buying such a license is mandatory if you want to modify or 13 | otherwise use the software for commercial activities involving the Arduino 14 | software without disclosing the source code of your own applications. To purchase 15 | a commercial license, send an email to license@arduino.cc. 16 | */ 17 | 18 | /****************************************************************************** 19 | * INCLUDE 20 | ******************************************************************************/ 21 | 22 | #include "Arduino_Portenta_OTA.h" 23 | #include "Arduino_Portenta_OTA_Debug.h" 24 | #include 25 | 26 | /****************************************************************************** 27 | * EXTERN 28 | ******************************************************************************/ 29 | 30 | extern RTC_HandleTypeDef RTCHandle; 31 | 32 | /****************************************************************************** 33 | CTOR/DTOR 34 | ******************************************************************************/ 35 | 36 | Arduino_Portenta_OTA::Arduino_Portenta_OTA(StorageTypePortenta const storage_type, uint32_t const data_offset) 37 | : _storage_type{storage_type} 38 | , _data_offset{data_offset} 39 | , _program_length{0} 40 | { 41 | 42 | } 43 | 44 | Arduino_Portenta_OTA::~Arduino_Portenta_OTA() 45 | { 46 | 47 | } 48 | 49 | /****************************************************************************** 50 | * PUBLIC MEMBER FUNCTIONS 51 | ******************************************************************************/ 52 | 53 | bool Arduino_Portenta_OTA::isOtaCapable() 54 | { 55 | #define BOOTLOADER_ADDR (0x8000000) 56 | uint32_t bootloader_data_offset = 0x1F000; 57 | uint8_t* bootloader_data = (uint8_t*)(BOOTLOADER_ADDR + bootloader_data_offset); 58 | uint8_t currentBootloaderVersion = bootloader_data[1]; 59 | if (currentBootloaderVersion < 22) 60 | return false; 61 | else 62 | return true; 63 | } 64 | 65 | Arduino_Portenta_OTA::Error Arduino_Portenta_OTA::begin() 66 | { 67 | if (!isOtaCapable()) 68 | return Error::NoCapableBootloader; 69 | 70 | if (!caStorageInit()) 71 | return Error::CaStorageInit; 72 | 73 | if (!caStorageOpen()) 74 | return Error::CaStorageOpen; 75 | 76 | if (!init()) 77 | return Error::OtaStorageInit; 78 | 79 | return Error::None; 80 | } 81 | 82 | Arduino_Portenta_OTA::Error Arduino_Portenta_OTA::update() 83 | { 84 | if(!open()) 85 | return Error::OtaStorageOpen; 86 | 87 | write(); 88 | 89 | return Error::None; 90 | } 91 | 92 | void Arduino_Portenta_OTA::reset() 93 | { 94 | NVIC_SystemReset(); 95 | } 96 | 97 | void Arduino_Portenta_OTA::setFeedWatchdogFunc(ArduinoPortentaOtaWatchdogResetFuncPointer func) 98 | { 99 | _feed_watchdog_func = func; 100 | } 101 | 102 | void Arduino_Portenta_OTA::feedWatchdog() 103 | { 104 | if (_feed_watchdog_func) 105 | _feed_watchdog_func(); 106 | } 107 | 108 | /****************************************************************************** 109 | * PROTECTED MEMBER FUNCTIONS 110 | ******************************************************************************/ 111 | 112 | bool Arduino_Portenta_OTA::findProgramLength(DIR * dir, uint32_t & program_length) 113 | { 114 | struct dirent * entry = NULL; 115 | while ((entry = readdir(dir)) != NULL) 116 | { 117 | if (String(entry->d_name) == "UPDATE.BIN") 118 | { 119 | struct stat stat_buf; 120 | stat("/fs/UPDATE.BIN", &stat_buf); 121 | program_length = stat_buf.st_size; 122 | return true; 123 | } 124 | } 125 | 126 | return false; 127 | } 128 | 129 | /****************************************************************************** 130 | * PRIVATE MEMBER FUNCTIONS 131 | ******************************************************************************/ 132 | 133 | void Arduino_Portenta_OTA::write() 134 | { 135 | HAL_RTCEx_BKUPWrite(&RTCHandle, RTC_BKP_DR0, 0x07AA); 136 | HAL_RTCEx_BKUPWrite(&RTCHandle, RTC_BKP_DR1, _storage_type); 137 | HAL_RTCEx_BKUPWrite(&RTCHandle, RTC_BKP_DR2, _data_offset); 138 | HAL_RTCEx_BKUPWrite(&RTCHandle, RTC_BKP_DR3, _program_length); 139 | } 140 | 141 | bool Arduino_Portenta_OTA::caStorageInit() 142 | { 143 | _bd_raw_qspi = mbed::BlockDevice::get_default_instance(); 144 | 145 | if (_bd_raw_qspi->init() != QSPIF_BD_ERROR_OK) { 146 | DEBUG_ERROR(F("Error: QSPI init failure.")); 147 | return false; 148 | } 149 | 150 | mbed::MBRBlockDevice * cert_bd_qspi = new mbed::MBRBlockDevice(_bd_raw_qspi, 1); 151 | mbed::FATFileSystem * cert_fs_qspi = new mbed::FATFileSystem("wlan"); 152 | int const err_mount = cert_fs_qspi->mount(cert_bd_qspi); 153 | if (err_mount) { 154 | DEBUG_ERROR(F("Error while mounting the certificate filesystem. Err = %d"), err_mount); 155 | return false; 156 | } 157 | return true; 158 | } 159 | 160 | bool Arduino_Portenta_OTA::caStorageOpen() 161 | { 162 | FILE* fp = fopen("/wlan/cacert.pem", "r"); 163 | if (!fp) { 164 | DEBUG_ERROR(F("Error while opening the certificate file.")); 165 | return false; 166 | } 167 | fclose(fp); 168 | 169 | return true; 170 | } 171 | -------------------------------------------------------------------------------- /examples/LZSS/LZSS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example demonstrates how to download a lzss file and decompress it in two ways: 3 | * -1 download the file on the filesystem and then decompress the downloaded file on the filesystem 4 | * -2 download and decompress the file on the fly 5 | * this sketch also provides a comparison in terms of speed and execution time 6 | * 7 | */ 8 | 9 | /****************************************************************************** 10 | * INCLUDE 11 | ******************************************************************************/ 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include "arduino_secrets.h" 18 | #include 19 | 20 | /****************************************************************************** 21 | * CONSTANT 22 | ******************************************************************************/ 23 | 24 | /* Please enter your sensitive data in the Secret tab/arduino_secrets.h */ 25 | static char const SSID[] = SECRET_SSID; /* your network SSID (name) */ 26 | static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */ 27 | 28 | 29 | #if defined(ARDUINO_NICLA_VISION) 30 | static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.NICLA_VISION.ota"; 31 | #elif defined(ARDUINO_PORTENTA_H7_M7) 32 | static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota"; 33 | #elif defined(ARDUINO_OPTA) 34 | static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota"; 35 | #elif defined(ARDUINO_GIGA) 36 | static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.GIGA.ota"; 37 | #else 38 | #error "Board not supported" 39 | #endif 40 | 41 | static char const DOWNLOAD_DESTINATION[] = "/fs/UPDATE.BIN.LZSS"; 42 | static char const DECOMPRESSED_DESTINATION[] = "/fs/UPDATE.BIN"; 43 | 44 | LZSSDecoder *decoder = nullptr; 45 | FILE* download_target = nullptr; 46 | 47 | /****************************************************************************** 48 | * SETUP/LOOP 49 | ******************************************************************************/ 50 | void decompress_on_the_fly_cbk(const char*, uint32_t); 51 | void putc_file(const uint8_t c); 52 | 53 | void setup() { 54 | Serial.begin(115200); 55 | while (!Serial) {} 56 | 57 | if (WiFi.status() == WL_NO_SHIELD) 58 | { 59 | Serial.println("Communication with WiFi module failed!"); 60 | return; 61 | } 62 | 63 | int status = WL_IDLE_STATUS; 64 | while (status != WL_CONNECTED) 65 | { 66 | Serial.print ("Attempting to connect to '"); 67 | Serial.print (SSID); 68 | Serial.println("'"); 69 | status = WiFi.begin(SSID, PASS); 70 | if(status != WL_CONNECTED) { 71 | delay(10000); 72 | } 73 | } 74 | Serial.print ("You're connected to '"); 75 | Serial.print (WiFi.SSID()); 76 | Serial.println("'"); 77 | 78 | // Init fs 79 | mbed::BlockDevice * _bd_raw_qspi = mbed::BlockDevice::get_default_instance();; 80 | auto _bd_qspi = new mbed::MBRBlockDevice(_bd_raw_qspi, 2); 81 | auto _fs_qspi = new mbed::FATFileSystem("fs"); 82 | int const err_mount = _fs_qspi->mount(_bd_qspi); 83 | if (err_mount) { 84 | Serial.print("Error while mounting the filesystem. Err = "); 85 | Serial.println(err_mount); 86 | return; 87 | } 88 | 89 | MbedSocketClass * socket = static_cast(&WiFi); 90 | remove(DOWNLOAD_DESTINATION); 91 | remove(DECOMPRESSED_DESTINATION); 92 | 93 | uint32_t start; 94 | int bytes; 95 | float elapsed, speed; 96 | start = millis(); 97 | Serial.println("Starting download to QSPI ..."); 98 | bytes = socket->download(URL_FILE, DOWNLOAD_DESTINATION, true /* is_https */); 99 | if (bytes <= 0) 100 | { 101 | Serial.print ("MbedSocketClass::download failed with error code "); 102 | Serial.println(bytes); 103 | return; 104 | } 105 | Serial.print (bytes); 106 | Serial.println(" bytes stored."); 107 | 108 | elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds 109 | speed = (bytes/elapsed)/1024; 110 | 111 | Serial.print("download elapsed "); 112 | Serial.print(elapsed); 113 | Serial.print("s speed: "); 114 | Serial.print(speed); 115 | Serial.println("KBps"); 116 | 117 | FILE* downloaded_file = fopen(DOWNLOAD_DESTINATION, "rb"); 118 | FILE* decompressed_file = fopen(DECOMPRESSED_DESTINATION, "wb"); 119 | 120 | start = millis(); 121 | lzss_init(downloaded_file, decompressed_file, bytes, nullptr); 122 | 123 | lzss_decode(); 124 | /* Write the data remaining in the write buffer to 125 | * the file. 126 | */ 127 | lzss_flush(); 128 | 129 | elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds 130 | 131 | Serial.print("decompress elapsed "); 132 | Serial.print(elapsed); 133 | Serial.print("s"); 134 | Serial.print(" size "); 135 | Serial.println(ftell(decompressed_file)); 136 | 137 | fclose(downloaded_file); 138 | fclose(decompressed_file); 139 | 140 | // On the fly decompression 141 | remove(DOWNLOAD_DESTINATION); 142 | remove(DECOMPRESSED_DESTINATION); 143 | 144 | download_target = fopen(DECOMPRESSED_DESTINATION, "wb"); 145 | decoder = new LZSSDecoder(putc_file); 146 | 147 | Serial.println("Starting download & decompress on the fly"); 148 | start = millis(); 149 | bytes = socket->download(URL_FILE, true /* is_https */, decompress_on_the_fly_cbk); 150 | if (bytes <= 0) 151 | { 152 | Serial.print ("MbedSocketClass::download failed with error code "); 153 | Serial.println(bytes); 154 | return; 155 | } 156 | 157 | Serial.print("downloaded "); 158 | Serial.print(bytes); 159 | Serial.print(" bytes "); 160 | 161 | elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds 162 | speed = (bytes/elapsed)/1024; 163 | 164 | Serial.print (ftell(download_target)); 165 | Serial.println(" bytes stored."); 166 | 167 | Serial.print("download elapsed "); 168 | Serial.print(elapsed); 169 | Serial.print("s speed: "); 170 | Serial.print(speed); 171 | Serial.println("KBps"); 172 | 173 | delete decoder; 174 | fclose(download_target); 175 | } 176 | 177 | void loop() { 178 | } 179 | 180 | void decompress_on_the_fly_cbk(const char* buffer, uint32_t size) { 181 | decoder->decompress((uint8_t*)buffer, size); 182 | } 183 | 184 | void putc_file(const uint8_t c) { 185 | fwrite(&c, 1, 1, download_target); 186 | } 187 | 188 | -------------------------------------------------------------------------------- /src/decompress/lzss.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************************** 2 | INCLUDE 3 | **************************************************************************************/ 4 | 5 | #include "lzss.h" 6 | 7 | #include 8 | 9 | /************************************************************************************** 10 | GLOBAL VARIABLES 11 | **************************************************************************************/ 12 | 13 | static FILE * update_file = nullptr; 14 | static FILE * target_file = nullptr; 15 | 16 | static ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func = nullptr; 17 | static LZSSDecoder* decoder = nullptr; 18 | 19 | /************************************************************************************** 20 | PUBLIC FUNCTIONS 21 | **************************************************************************************/ 22 | 23 | void lzss_init( 24 | FILE * update_file_ptr, 25 | FILE * target_file_ptr, 26 | uint32_t const lzss_file_size, 27 | ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func_ptr) { 28 | update_file = update_file_ptr; 29 | target_file = target_file_ptr; 30 | wdog_feed_func = wdog_feed_func_ptr; 31 | 32 | if(decoder != nullptr) { 33 | delete decoder; 34 | decoder = nullptr; 35 | } 36 | 37 | decoder = new LZSSDecoder( 38 | [target_file](const uint8_t c) { 39 | fwrite(&c, 1, 1, target_file); 40 | } 41 | ); 42 | } 43 | 44 | void lzss_flush() { 45 | fflush(target_file); 46 | } 47 | 48 | void lzss_decode() { 49 | if(decoder == nullptr) { 50 | return; 51 | } 52 | const size_t buf_size = 64; 53 | uint8_t buffer[buf_size]; 54 | size_t res = 0; 55 | 56 | do { 57 | if(wdog_feed_func) { 58 | wdog_feed_func(); 59 | } 60 | res = fread(buffer, sizeof(uint8_t), buf_size, update_file); 61 | decoder->decompress(buffer, res); 62 | } while(res == buf_size); 63 | } 64 | 65 | 66 | /************************************************************************************** 67 | LZSS DECODER CLASS IMPLEMENTATION 68 | **************************************************************************************/ 69 | 70 | // get the number of bits the algorithm will try to get given the state 71 | uint8_t LZSSDecoder::bits_required(LZSSDecoder::FSM_STATES s) { 72 | switch(s) { 73 | case FSM_0: 74 | return 1; 75 | case FSM_1: 76 | return 8; 77 | case FSM_2: 78 | return EI; 79 | case FSM_3: 80 | return EJ; 81 | default: 82 | return 0; 83 | } 84 | } 85 | 86 | LZSSDecoder::LZSSDecoder(std::function getc_cbk, std::function putc_cbk) 87 | : available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(getc_cbk) { 88 | for (int i = 0; i < N - F; i++) buffer[i] = ' '; 89 | r = N - F; 90 | } 91 | 92 | 93 | LZSSDecoder::LZSSDecoder(std::function putc_cbk) 94 | : available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(nullptr) { 95 | for (int i = 0; i < N - F; i++) buffer[i] = ' '; 96 | r = N - F; 97 | } 98 | 99 | LZSSDecoder::status LZSSDecoder::handle_state() { 100 | LZSSDecoder::status res = IN_PROGRESS; 101 | 102 | int c = getbit(bits_required(this->state)); 103 | 104 | if(c == LZSS_BUFFER_EMPTY) { 105 | res = NOT_COMPLETED; 106 | } else if (c == LZSS_EOF) { 107 | res = DONE; 108 | this->state = FSM_EOF; 109 | } else { 110 | switch(this->state) { 111 | case FSM_0: 112 | if(c) { 113 | this->state = FSM_1; 114 | } else { 115 | this->state = FSM_2; 116 | } 117 | break; 118 | case FSM_1: 119 | putc(c); 120 | buffer[r++] = c; 121 | r &= (N - 1); // equivalent to r = r % N when N is a power of 2 122 | 123 | this->state = FSM_0; 124 | break; 125 | case FSM_2: 126 | this->i = c; 127 | this->state = FSM_3; 128 | break; 129 | case FSM_3: { 130 | int j = c; 131 | 132 | // This is where the actual decompression takes place: we look into the local buffer for reuse 133 | // of byte chunks. This can be improved by means of memcpy and by changing the putc function 134 | // into a put_buf function in order to avoid buffering on the other end. 135 | // TODO improve this section of code 136 | for (int k = 0; k <= j + 1; k++) { 137 | c = buffer[(this->i + k) & (N - 1)]; // equivalent to buffer[(i+k) % N] when N is a power of 2 138 | putc(c); 139 | buffer[r++] = c; 140 | r &= (N - 1); // equivalent to r = r % N 141 | } 142 | this->state = FSM_0; 143 | 144 | break; 145 | } 146 | case FSM_EOF: 147 | break; 148 | } 149 | } 150 | 151 | return res; 152 | } 153 | 154 | LZSSDecoder::status LZSSDecoder::decompress(uint8_t* const buffer, uint32_t size) { 155 | if(!get_char_cbk) { 156 | this->in_buffer = buffer; 157 | this->available += size; 158 | } 159 | 160 | status res = IN_PROGRESS; 161 | 162 | while((res = handle_state()) == IN_PROGRESS); 163 | 164 | this->in_buffer = nullptr; 165 | 166 | return res; 167 | } 168 | 169 | int LZSSDecoder::getbit(uint8_t n) { // get n bits from buffer 170 | int x=0, c; 171 | 172 | // if the local bit buffer doesn't have enough bit get them 173 | while(buf_size < n) { 174 | switch(c=getc()) { 175 | case LZSS_EOF: 176 | case LZSS_BUFFER_EMPTY: 177 | return c; 178 | } 179 | buf <<= 8; 180 | 181 | buf |= (uint8_t)c; 182 | buf_size += sizeof(uint8_t)*8; 183 | } 184 | 185 | // the result is the content of the buffer starting from msb to n successive bits 186 | x = buf >> (buf_size-n); 187 | 188 | // remove from the buffer the read bits with a mask 189 | buf &= (1<<(buf_size-n))-1; 190 | 191 | buf_size-=n; 192 | 193 | return x; 194 | } 195 | 196 | int LZSSDecoder::getc() { 197 | int c; 198 | 199 | if(get_char_cbk) { 200 | c = get_char_cbk(); 201 | } else if(in_buffer == nullptr || available == 0) { 202 | c = LZSS_BUFFER_EMPTY; 203 | } else { 204 | c = *in_buffer; 205 | in_buffer++; 206 | available--; 207 | } 208 | return c; 209 | } 210 | -------------------------------------------------------------------------------- /src/decompress/utility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Arduino LLC. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "lzss.h" 24 | 25 | #include "Arduino_Portenta_OTA.h" 26 | 27 | /************************************************************************************** 28 | CONST 29 | **************************************************************************************/ 30 | 31 | const char * UPDATE_FILE_NAME = "/fs/UPDATE.BIN"; 32 | const char * UPDATE_FILE_NAME_LZSS = "/fs/UPDATE.BIN.LZSS"; 33 | 34 | static const uint32_t crc_table[256] = 35 | { 36 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 37 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 38 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 39 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 40 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 41 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 42 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 43 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 44 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 45 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 46 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 47 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 48 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 49 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 50 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 51 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 52 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 53 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 54 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 55 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 56 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 57 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 58 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 59 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 60 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 61 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 62 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 63 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 64 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 65 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 66 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 67 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 68 | }; 69 | 70 | /************************************************************************************** 71 | FUNCTIONS 72 | **************************************************************************************/ 73 | 74 | uint32_t crc_update(uint32_t crc, const void * data, size_t data_len) 75 | { 76 | const unsigned char *d = (const unsigned char *)data; 77 | unsigned int tbl_idx; 78 | 79 | while (data_len--) { 80 | tbl_idx = (crc ^ *d) & 0xff; 81 | crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; 82 | d++; 83 | } 84 | 85 | return crc & 0xffffffff; 86 | } 87 | 88 | /************************************************************************************** 89 | MAIN 90 | **************************************************************************************/ 91 | 92 | union HeaderVersion 93 | { 94 | struct __attribute__((packed)) 95 | { 96 | uint32_t header_version : 6; 97 | uint32_t compression : 1; 98 | uint32_t signature : 1; 99 | uint32_t spare : 4; 100 | uint32_t payload_target : 4; 101 | uint32_t payload_major : 8; 102 | uint32_t payload_minor : 8; 103 | uint32_t payload_patch : 8; 104 | uint32_t payload_build_num : 24; 105 | } field; 106 | uint8_t buf[sizeof(field)]; 107 | static_assert(sizeof(buf) == 8, "Error: sizeof(HEADER.VERSION) != 8"); 108 | }; 109 | 110 | union OTAHeader 111 | { 112 | struct __attribute__((packed)) 113 | { 114 | uint32_t len; 115 | uint32_t crc32; 116 | uint32_t magic_number; 117 | HeaderVersion hdr_version; 118 | } header; 119 | uint8_t buf[sizeof(header)]; 120 | static_assert(sizeof(buf) == 20, "Error: sizeof(HEADER) != 20"); 121 | }; 122 | 123 | int Arduino_Portenta_OTA::download(const char * url, bool const is_https, MbedSocketClass * socket) 124 | { 125 | return socket->download((char *)url, UPDATE_FILE_NAME_LZSS, is_https); 126 | } 127 | 128 | int Arduino_Portenta_OTA::downloadAndDecompress(const char * url, bool const is_https, MbedSocketClass * socket) { 129 | int res=0; 130 | 131 | FILE* decompressed = fopen(UPDATE_FILE_NAME, "wb"); 132 | OTAHeader ota_header; 133 | 134 | LZSSDecoder decoder([&decompressed](const uint8_t c) { 135 | fwrite(&c, 1, 1, decompressed); 136 | }); 137 | 138 | enum OTA_DOWNLOAD_STATE: uint8_t { 139 | OTA_DOWNLOAD_HEADER=0, 140 | OTA_DOWNLOAD_FILE, 141 | OTA_DOWNLOAD_ERR 142 | }; 143 | 144 | // since mbed::Callback requires a function to not exceed a certain size, we group the following parameters in a struct 145 | struct { 146 | uint32_t crc32 = 0xFFFFFFFF; 147 | uint32_t header_copied_bytes = 0; 148 | OTA_DOWNLOAD_STATE state=OTA_DOWNLOAD_HEADER; 149 | Arduino_Portenta_OTA* ref; 150 | } ota_progress; 151 | 152 | ota_progress.ref = this; 153 | 154 | int bytes = socket->download(url, is_https, [ &decoder, &ota_header, &ota_progress](const char* buffer, uint32_t size) { 155 | ota_progress.ref->feedWatchdog(); 156 | for(char* cursor=(char*)buffer; cursor(Error::OtaDownload); 205 | goto exit; 206 | } 207 | 208 | if(ota_header.header.len == (bytes-sizeof(ota_header.buf))) { 209 | res = static_cast(Error::OtaHeaderLength); 210 | goto exit; 211 | } 212 | 213 | // verify magic number: it may be done in the download function and stop the download immediately 214 | if(ota_header.header.magic_number != ARDUINO_PORTENTA_OTA_MAGIC) { 215 | res = static_cast(Error::OtaHeaterMagicNumber); 216 | goto exit; 217 | } 218 | 219 | // finalize CRC and verify it 220 | ota_progress.crc32 ^= 0xFFFFFFFF; 221 | if(ota_header.header.crc32 != ota_progress.crc32) { 222 | res = static_cast(Error::OtaHeaderCrc); 223 | goto exit; 224 | } 225 | 226 | res = ftell(decompressed); 227 | 228 | exit: 229 | fclose(decompressed); 230 | 231 | if(res < 0) { 232 | remove(UPDATE_FILE_NAME); 233 | } 234 | 235 | return res; 236 | } 237 | 238 | 239 | int Arduino_Portenta_OTA::decompress() 240 | { 241 | struct stat stat_buf; 242 | stat(UPDATE_FILE_NAME_LZSS, &stat_buf); 243 | auto update_file_size = stat_buf.st_size; 244 | 245 | /* For UPDATE.BIN.LZSS - LZSS compressed binary files. */ 246 | FILE* update_file = fopen(UPDATE_FILE_NAME_LZSS, "rb"); 247 | 248 | OTAHeader ota_header; 249 | uint32_t crc32, bytes_read; 250 | uint8_t crc_buf[128]; 251 | 252 | feedWatchdog(); 253 | 254 | /* Read the OTA header ... */ 255 | fread(ota_header.buf, 1, sizeof(ota_header.buf), update_file); 256 | 257 | /* ... and check first length ... */ 258 | if (ota_header.header.len != (update_file_size - sizeof(ota_header.header.len) - sizeof(ota_header.header.crc32))) { 259 | fclose(update_file); 260 | remove(UPDATE_FILE_NAME_LZSS); 261 | return static_cast(Error::OtaHeaderLength); 262 | } 263 | 264 | feedWatchdog(); 265 | 266 | /* ... and the CRC second ... rewind to start of CRC verified header ... */ 267 | fseek(update_file, sizeof(ota_header.header.len) + sizeof(ota_header.header.crc32), SEEK_SET); 268 | /* ... initialize CRC ... */ 269 | crc32 = 0xFFFFFFFF; 270 | /* ... and calculate over file ... */ 271 | for(bytes_read = 0; 272 | bytes_read < (ota_header.header.len - sizeof(crc_buf)); 273 | bytes_read += sizeof(crc_buf)) 274 | { 275 | fread(crc_buf, 1, sizeof(crc_buf), update_file); 276 | crc32 = crc_update(crc32, crc_buf, sizeof(crc_buf)); 277 | } 278 | fread(crc_buf, 1, ota_header.header.len - bytes_read, update_file); 279 | crc32 = crc_update(crc32, crc_buf, ota_header.header.len - bytes_read); 280 | 281 | feedWatchdog(); 282 | 283 | /* ... then finalise ... */ 284 | crc32 ^= 0xFFFFFFFF; 285 | /* ... and compare. */ 286 | if (ota_header.header.crc32 != crc32) { 287 | fclose(update_file); 288 | remove(UPDATE_FILE_NAME_LZSS); 289 | return static_cast(Error::OtaHeaderCrc); 290 | } 291 | 292 | feedWatchdog(); 293 | 294 | if (ota_header.header.magic_number != ARDUINO_PORTENTA_OTA_MAGIC) 295 | { 296 | fclose(update_file); 297 | remove(UPDATE_FILE_NAME_LZSS); 298 | return static_cast(Error::OtaHeaterMagicNumber); 299 | } 300 | 301 | /* Rewind to start of LZSS compressed binary. */ 302 | fseek(update_file, sizeof(ota_header.buf), SEEK_SET); 303 | 304 | uint32_t const LZSS_FILE_SIZE = update_file_size - sizeof(ota_header.buf); 305 | 306 | FILE* decompressed = fopen(UPDATE_FILE_NAME, "w"); 307 | 308 | lzss_init(update_file, decompressed, LZSS_FILE_SIZE, _feed_watchdog_func); 309 | 310 | /* During the process of decoding UPDATE.BIN.LZSS 311 | * is decompressed and stored as UPDATE.BIN. 312 | */ 313 | lzss_decode(); 314 | /* Write the data remaining in the write buffer to 315 | * the file. 316 | */ 317 | lzss_flush(); 318 | 319 | /* Determine the size of the decompressed file. */ 320 | int const decompressed_file_size = ftell(decompressed); 321 | 322 | /* Delete UPDATE.BIN.LZSS because this update is complete. */ 323 | fclose(update_file); 324 | fclose(decompressed); 325 | remove(UPDATE_FILE_NAME_LZSS); 326 | 327 | return decompressed_file_size; 328 | } 329 | --------------------------------------------------------------------------------