Skip to content

Commit

Permalink
Merge pull request #43 from andreagilardoni/lzss-streaming
Browse files Browse the repository at this point in the history
OTA download and decompress on the fly
  • Loading branch information
andreagilardoni authored Feb 13, 2024
2 parents 5b3e2af + f3db4bc commit 619d5a9
Show file tree
Hide file tree
Showing 9 changed files with 725 additions and 191 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/compile-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:
- examples/OTA_Qspi_Flash_Ethernet
- examples/OTA_SD_Portenta
- examples/OTA_Usage_Portenta
- examples/LZSS
- examples/OTA_Qspi_Flash_download_onthefly
- fqbn: arduino:mbed_nicla:nicla_vision
platforms: |
- name: arduino:mbed_nicla
Expand All @@ -50,6 +52,8 @@ jobs:
sketch-paths: |
- examples/OTA_Qspi_Flash
- examples/OTA_Usage_Portenta
- examples/LZSS
- examples/OTA_Qspi_Flash_download_onthefly
- fqbn: arduino:mbed_opta:opta
platforms: |
- name: arduino:mbed_opta
Expand All @@ -59,6 +63,8 @@ jobs:
- examples/OTA_Qspi_Flash
- examples/OTA_Qspi_Flash_Ethernet
- examples/OTA_Usage_Portenta
- examples/LZSS
- examples/OTA_Qspi_Flash_download_onthefly
- fqbn: arduino:mbed_giga:giga
platforms: |
- name: arduino:mbed_giga
Expand All @@ -67,6 +73,8 @@ jobs:
sketch-paths: |
- examples/OTA_Qspi_Flash
- examples/OTA_Usage_Portenta
- examples/LZSS
- examples/OTA_Qspi_Flash_download_onthefly
steps:
- name: Checkout
Expand Down
187 changes: 187 additions & 0 deletions examples/LZSS/LZSS.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* This example demonstrates how to download a lzss file and decompress it in two ways:
* -1 download the file on the filesystem and then decompress the downloaded file on the filesystem
* -2 download and decompress the file on the fly
* this sketch also provides a comparison in terms of speed and execution time
*
*/

/******************************************************************************
* INCLUDE
******************************************************************************/

#include <Arduino_Portenta_OTA.h>

#include <WiFi.h>

#include "arduino_secrets.h"
#include <decompress/lzss.h>

/******************************************************************************
* CONSTANT
******************************************************************************/

/* Please enter your sensitive data in the Secret tab/arduino_secrets.h */
static char const SSID[] = SECRET_SSID; /* your network SSID (name) */
static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */


#if defined(ARDUINO_NICLA_VISION)
static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.NICLA_VISION.ota";
#elif defined(ARDUINO_PORTENTA_H7_M7)
static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota";
#elif defined(ARDUINO_OPTA)
static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota";
#elif defined(ARDUINO_GIGA)
static char const URL_FILE[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.GIGA.ota";
#else
#error "Board not supported"
#endif

static char const DOWNLOAD_DESTINATION[] = "/fs/UPDATE.BIN.LZSS";
static char const DECOMPRESSED_DESTINATION[] = "/fs/UPDATE.BIN";

LZSSDecoder *decoder = nullptr;
FILE* download_target = nullptr;

/******************************************************************************
* SETUP/LOOP
******************************************************************************/
void decompress_on_the_fly_cbk(const char*, uint32_t);
void putc_file(const uint8_t c);

void setup() {
Serial.begin(115200);
while (!Serial) {}

if (WiFi.status() == WL_NO_SHIELD)
{
Serial.println("Communication with WiFi module failed!");
return;
}

int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED)
{
Serial.print ("Attempting to connect to '");
Serial.print (SSID);
Serial.println("'");
status = WiFi.begin(SSID, PASS);
if(status != WL_CONNECTED) {
delay(10000);
}
}
Serial.print ("You're connected to '");
Serial.print (WiFi.SSID());
Serial.println("'");

// Init fs
mbed::BlockDevice * _bd_raw_qspi = mbed::BlockDevice::get_default_instance();;
auto _bd_qspi = new mbed::MBRBlockDevice(_bd_raw_qspi, 2);
auto _fs_qspi = new mbed::FATFileSystem("fs");
int const err_mount = _fs_qspi->mount(_bd_qspi);
if (err_mount) {
Serial.print("Error while mounting the filesystem. Err = ");
Serial.println(err_mount);
return;
}

MbedSocketClass * socket = static_cast<MbedSocketClass*>(&WiFi);
remove(DOWNLOAD_DESTINATION);
remove(DECOMPRESSED_DESTINATION);

uint32_t start;
int bytes;
float elapsed, speed;
start = millis();
Serial.println("Starting download to QSPI ...");
bytes = socket->download(URL_FILE, DOWNLOAD_DESTINATION, true /* is_https */);
if (bytes <= 0)
{
Serial.print ("MbedSocketClass::download failed with error code ");
Serial.println(bytes);
return;
}
Serial.print (bytes);
Serial.println(" bytes stored.");

elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds
speed = (bytes/elapsed)/1024;

Serial.print("download elapsed ");
Serial.print(elapsed);
Serial.print("s speed: ");
Serial.print(speed);
Serial.println("KBps");

FILE* downloaded_file = fopen(DOWNLOAD_DESTINATION, "rb");
FILE* decompressed_file = fopen(DECOMPRESSED_DESTINATION, "wb");

start = millis();
lzss_init(downloaded_file, decompressed_file, bytes, nullptr);

lzss_decode();
/* Write the data remaining in the write buffer to
* the file.
*/
lzss_flush();

elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds

Serial.print("decompress elapsed ");
Serial.print(elapsed);
Serial.print("s");
Serial.print(" size ");
Serial.println(ftell(decompressed_file));

fclose(downloaded_file);
fclose(decompressed_file);

// On the fly decompression
remove(DOWNLOAD_DESTINATION);
remove(DECOMPRESSED_DESTINATION);

download_target = fopen(DECOMPRESSED_DESTINATION, "wb");
decoder = new LZSSDecoder(putc_file);

Serial.println("Starting download & decompress on the fly");
start = millis();
bytes = socket->download(URL_FILE, true /* is_https */, decompress_on_the_fly_cbk);
if (bytes <= 0)
{
Serial.print ("MbedSocketClass::download failed with error code ");
Serial.println(bytes);
return;
}

Serial.print("downloaded ");
Serial.print(bytes);
Serial.print(" bytes ");

elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds
speed = (bytes/elapsed)/1024;

Serial.print (ftell(download_target));
Serial.println(" bytes stored.");

Serial.print("download elapsed ");
Serial.print(elapsed);
Serial.print("s speed: ");
Serial.print(speed);
Serial.println("KBps");

delete decoder;
fclose(download_target);
}

void loop() {
}

void decompress_on_the_fly_cbk(const char* buffer, uint32_t size) {
decoder->decompress((uint8_t*)buffer, size);
}

void putc_file(const uint8_t c) {
fwrite(&c, 1, 1, download_target);
}

2 changes: 2 additions & 0 deletions examples/LZSS/arduino_secrets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define SECRET_SSID ""
#define SECRET_PASS ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using
* a firmware image stored on the QSPI.
*
* Steps:
* 1) Create a sketch for the Portenta H7 and verify
* that it both compiles and works on a board.
* 2) In the IDE select: Sketch -> Export compiled Binary.
* 3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in
* https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools .
* A) ./lzss.py --encode SKETCH.bin SKETCH.lzss
* B) ./bin2ota.py PORTENTA_H7_M7 SKETCH.lzss SKETCH.ota
* 4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota
* has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota
* 5) Perform an OTA update via steps outlined below.
*/

/******************************************************************************
* INCLUDE
******************************************************************************/

#include <Arduino_Portenta_OTA.h>

#include <WiFi.h>

#include "arduino_secrets.h"

/******************************************************************************
* CONSTANT
******************************************************************************/

/* Please enter your sensitive data in the Secret tab/arduino_secrets.h */
static char const SSID[] = SECRET_SSID; /* your network SSID (name) */
static char const PASS[] = SECRET_PASS; /* your network password (use for WPA, or use as key for WEP) */

#if defined(ARDUINO_NICLA_VISION)
static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.NICLA_VISION.ota";
#elif defined(ARDUINO_PORTENTA_H7_M7)
static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota";
#elif defined(ARDUINO_OPTA)
static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.OPTA.ota";
#elif defined(ARDUINO_GIGA)
static char const OTA_FILE_LOCATION[] = "https://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.GIGA.ota";
#else
#error "Board not supported"
#endif

/******************************************************************************
* SETUP/LOOP
******************************************************************************/

void setup()
{
Serial.begin(115200);
while (!Serial) {}

if (WiFi.status() == WL_NO_SHIELD)
{
Serial.println("Communication with WiFi module failed!");
return;
}

int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED)
{
Serial.print ("Attempting to connect to '");
Serial.print (SSID);
Serial.println("'");
status = WiFi.begin(SSID, PASS);
if(status != WL_CONNECTED) {
delay(10000);
}
}
Serial.print ("You're connected to '");
Serial.print (WiFi.SSID());
Serial.println("'");

Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2);
Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None;

if (!ota.isOtaCapable())
{
Serial.println("Higher version bootloader required to perform OTA.");
Serial.println("Please update the bootloader.");
Serial.println("File -> Examples -> Portenta_System -> PortentaH7_updateBootloader");
return;
}

Serial.println("Initializing OTA storage");
if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None)
{
Serial.print ("Arduino_Portenta_OTA::begin() failed with error code ");
Serial.println((int)ota_err);
return;
}

uint32_t start = millis();
float elapsed, speed;

Serial.println("Starting download to QSPI ...");
int const ota_download = ota.downloadAndDecompress(OTA_FILE_LOCATION, true /* is_https */);
if (ota_download <= 0)
{
Serial.print ("Arduino_Portenta_OTA_QSPI::download failed with error code ");
Serial.println(ota_download);
return;
}
Serial.print (ota_download);
Serial.println(" bytes stored.");

elapsed = (millis()-start)/1000.0; // elapsed expressed in seconds
speed = (ota_download/elapsed)/1024;

Serial.print("download elapsed ");
Serial.print(elapsed);
Serial.print("s speed: ");
Serial.print(speed);
Serial.println("KBps");

Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory ...");
if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None)
{
Serial.print ("ota.update() failed with error code ");
Serial.println((int)ota_err);
return;
}

Serial.println("Performing a reset after which the bootloader will update the firmware.");
Serial.println("Hint: Portenta H7 LED will blink Red-Blue-Green.");
delay(1000); /* Make sure the serial message gets out before the reset. */
ota.reset();
}

void loop()
{

}
2 changes: 2 additions & 0 deletions examples/OTA_Qspi_Flash_download_onthefly/arduino_secrets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define SECRET_SSID ""
#define SECRET_PASS ""
3 changes: 3 additions & 0 deletions src/Arduino_Portenta_OTA.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Arduino_Portenta_OTA
OtaHeaterMagicNumber = -7,
CaStorageInit = -8,
CaStorageOpen = -9,
OtaDownload = -12,
};

Arduino_Portenta_OTA(StorageTypePortenta const storage_type, uint32_t const data_offset);
Expand All @@ -102,6 +103,8 @@ class Arduino_Portenta_OTA
*/
int download(const char * url, bool const is_https, MbedSocketClass * socket = static_cast<MbedSocketClass*>(&WiFi));
int decompress();
int downloadAndDecompress(const char * url, bool const is_https, MbedSocketClass * socket = static_cast<MbedSocketClass*>(&WiFi));

void setFeedWatchdogFunc(ArduinoPortentaOtaWatchdogResetFuncPointer func);
void feedWatchdog();

Expand Down
Loading

0 comments on commit 619d5a9

Please sign in to comment.