Skip to content

Commit 619d5a9

Browse files
Merge pull request #43 from andreagilardoni/lzss-streaming
OTA download and decompress on the fly
2 parents 5b3e2af + f3db4bc commit 619d5a9

File tree

9 files changed

+725
-191
lines changed

9 files changed

+725
-191
lines changed

Diff for: .github/workflows/compile-examples.yml

+8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242
- examples/OTA_Qspi_Flash_Ethernet
4343
- examples/OTA_SD_Portenta
4444
- examples/OTA_Usage_Portenta
45+
- examples/LZSS
46+
- examples/OTA_Qspi_Flash_download_onthefly
4547
- fqbn: arduino:mbed_nicla:nicla_vision
4648
platforms: |
4749
- name: arduino:mbed_nicla
@@ -50,6 +52,8 @@ jobs:
5052
sketch-paths: |
5153
- examples/OTA_Qspi_Flash
5254
- examples/OTA_Usage_Portenta
55+
- examples/LZSS
56+
- examples/OTA_Qspi_Flash_download_onthefly
5357
- fqbn: arduino:mbed_opta:opta
5458
platforms: |
5559
- name: arduino:mbed_opta
@@ -59,6 +63,8 @@ jobs:
5963
- examples/OTA_Qspi_Flash
6064
- examples/OTA_Qspi_Flash_Ethernet
6165
- examples/OTA_Usage_Portenta
66+
- examples/LZSS
67+
- examples/OTA_Qspi_Flash_download_onthefly
6268
- fqbn: arduino:mbed_giga:giga
6369
platforms: |
6470
- name: arduino:mbed_giga
@@ -67,6 +73,8 @@ jobs:
6773
sketch-paths: |
6874
- examples/OTA_Qspi_Flash
6975
- examples/OTA_Usage_Portenta
76+
- examples/LZSS
77+
- examples/OTA_Qspi_Flash_download_onthefly
7078
7179
steps:
7280
- name: Checkout

Diff for: examples/LZSS/LZSS.ino

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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 <Arduino_Portenta_OTA.h>
14+
15+
#include <WiFi.h>
16+
17+
#include "arduino_secrets.h"
18+
#include <decompress/lzss.h>
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<MbedSocketClass*>(&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+

Diff for: examples/LZSS/arduino_secrets.h

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define SECRET_SSID ""
2+
#define SECRET_PASS ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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 <Arduino_Portenta_OTA.h>
23+
24+
#include <WiFi.h>
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define SECRET_SSID ""
2+
#define SECRET_PASS ""

Diff for: src/Arduino_Portenta_OTA.h

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Arduino_Portenta_OTA
8686
OtaHeaterMagicNumber = -7,
8787
CaStorageInit = -8,
8888
CaStorageOpen = -9,
89+
OtaDownload = -12,
8990
};
9091

9192
Arduino_Portenta_OTA(StorageTypePortenta const storage_type, uint32_t const data_offset);
@@ -102,6 +103,8 @@ class Arduino_Portenta_OTA
102103
*/
103104
int download(const char * url, bool const is_https, MbedSocketClass * socket = static_cast<MbedSocketClass*>(&WiFi));
104105
int decompress();
106+
int downloadAndDecompress(const char * url, bool const is_https, MbedSocketClass * socket = static_cast<MbedSocketClass*>(&WiFi));
107+
105108
void setFeedWatchdogFunc(ArduinoPortentaOtaWatchdogResetFuncPointer func);
106109
void feedWatchdog();
107110

0 commit comments

Comments
 (0)