Skip to content

Commit 8a01191

Browse files
committed
Add Portenta OTA via USB example
1 parent d7610cf commit 8a01191

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

examples/PortentaOTA/PortentaOTA.ino

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#include <FATFileSystem.h>
2+
#include <USBHostMbed5.h>
3+
4+
#include <Arduino_Portenta_OTA.h>
5+
6+
// The sketch expects the update file to be in the root of the USB key.
7+
//
8+
// The file can be both a plain .bin generated from the IDEs or the CLI (-e option)
9+
// or an armored OTA file generated by the `ota-builder` or
10+
// the `lzss.py` + `bin2ota.py` tools. The sketch will take care of both the cases.
11+
//
12+
// Feel free to change the name of the file accordingly.
13+
static const char OTA_FILE[] { "update.ota" }; // Armored OTA file
14+
// static const char OTA_FILE[] { "update.bin" }; // Plain BIN file
15+
16+
// !!! DO NOT TOUCH !!!
17+
// These are the names for the OTA files on the QSPIF
18+
// which the Arduino_Portenta_OTA library looks for.
19+
static const char UPDATE_FILE_NAME[] { "/fs/UPDATE.BIN" };
20+
static const char UPDATE_FILE_NAME_LZSS[] { "/fs/UPDATE.BIN.LZSS" };
21+
22+
// The internal mountpoint for the USB filesystem
23+
static const char USB_MOUNT_POINT[] { "usb" };
24+
mbed::FATFileSystem usb(USB_MOUNT_POINT);
25+
USBHostMSD msd;
26+
27+
// Reuse QSPIF Block Device from WiFi Driver
28+
extern QSPIFBlockDevice* qspi_bd;
29+
30+
constexpr auto PMC_USBA_VBUS_ENABLE { PB_14 };
31+
32+
void setup()
33+
{
34+
// If you are using the Portenta Machine Control
35+
// enable power on the USB connector.
36+
pinMode(PMC_USBA_VBUS_ENABLE, OUTPUT);
37+
digitalWrite(PMC_USBA_VBUS_ENABLE, LOW);
38+
39+
Serial.begin(115200);
40+
while (!Serial) { }
41+
42+
delay(2500);
43+
Serial.println("Starting OTA via USB example...");
44+
45+
Arduino_Portenta_OTA_QSPI ota(QSPI_FLASH_FATFS_MBR, 2);
46+
Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None;
47+
48+
if (!ota.isOtaCapable()) {
49+
Serial.println("Higher version bootloader required to perform OTA.");
50+
Serial.println("Please update the bootloader.");
51+
Serial.println("File -> Examples -> Portenta_System -> PortentaH7_updateBootloader");
52+
return;
53+
}
54+
55+
// Initialize the QSPIF Block Device only if the WiFi Driver has not initialized it already
56+
if (qspi_bd == nullptr) {
57+
Serial.print("Initializing the QSPIF device...");
58+
qspi_bd = new QSPIFBlockDevice(PD_11, PD_12, PF_7, PD_13, PF_10, PG_6, QSPIF_POLARITY_MODE_1, 40000000);
59+
if (qspi_bd->init() != QSPIF_BD_ERROR_OK) {
60+
Serial.println(" Error.");
61+
return;
62+
}
63+
Serial.println(" Done.");
64+
}
65+
66+
if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None) {
67+
Serial.print("Arduino_Portenta_OTA::begin() failed with error code ");
68+
Serial.println((int)ota_err);
69+
return;
70+
}
71+
Serial.println("Initializing OTA storage. Done.");
72+
73+
Serial.print("Please, insert the USB Key...");
74+
while (!msd.connect()) {
75+
Serial.print(".");
76+
delay(1000);
77+
}
78+
Serial.println();
79+
80+
Serial.print("Mounting USB device...");
81+
int err = usb.mount(&msd);
82+
if (err) {
83+
Serial.print(" Error: ");
84+
Serial.println(err);
85+
return;
86+
}
87+
Serial.println(" Done.");
88+
89+
String otaFileLocation;
90+
otaFileLocation += "/";
91+
otaFileLocation += USB_MOUNT_POINT;
92+
otaFileLocation += "/";
93+
otaFileLocation += OTA_FILE;
94+
95+
Serial.print("Opening source file \"");
96+
Serial.print(otaFileLocation);
97+
Serial.print("\"");
98+
FILE* src = fopen(otaFileLocation.c_str(), "rb+");
99+
if (src == nullptr) {
100+
Serial.print(" Error opening file.");
101+
return;
102+
}
103+
104+
// Get file length
105+
fseek(src, 0, SEEK_END);
106+
auto fileLen = ftell(src);
107+
fseek(src, 0, SEEK_SET);
108+
109+
Serial.print(" [");
110+
Serial.print(fileLen);
111+
Serial.println(" bytes].");
112+
113+
// Check for plain .BIN or armored .OTA and select destination file accordingly.
114+
String updateFileName;
115+
if (otaFileLocation.endsWith(".ota") || otaFileLocation.endsWith(".lzss"))
116+
updateFileName = UPDATE_FILE_NAME_LZSS;
117+
else
118+
updateFileName = UPDATE_FILE_NAME;
119+
120+
Serial.print("Opening destination file \"");
121+
Serial.print(updateFileName);
122+
Serial.print("\"...");
123+
FILE* dst = fopen(updateFileName.c_str(), "wb");
124+
if (dst == nullptr) {
125+
Serial.print("Error opening file ");
126+
Serial.println(updateFileName);
127+
return;
128+
}
129+
Serial.println(" Done.");
130+
131+
Serial.print("Copying OTA file from USB key to QSPI...");
132+
constexpr size_t bufLen { 1024u };
133+
char buf[bufLen] {};
134+
int ota_download {};
135+
size_t rb {};
136+
while ((rb = fread(buf, 1, bufLen, src)) > 0)
137+
ota_download += fwrite(buf, 1, rb, dst);
138+
139+
if (ota_download != fileLen) {
140+
Serial.print("Download from USB Key failed with error code ");
141+
Serial.println(ota_download);
142+
return;
143+
}
144+
Serial.print(" [");
145+
Serial.print(ota_download);
146+
Serial.println(" bytes].");
147+
148+
Serial.print("Closing source file...");
149+
err = fclose(src);
150+
if (err < 0) {
151+
Serial.print("fclose error:");
152+
Serial.print(strerror(errno));
153+
Serial.print(" (");
154+
Serial.print(-errno);
155+
Serial.print(")");
156+
return;
157+
}
158+
Serial.println(" Done.");
159+
160+
Serial.print("Closing destination file...");
161+
err = fclose(dst);
162+
if (err < 0) {
163+
Serial.print("fclose error:");
164+
Serial.print(strerror(errno));
165+
Serial.print(" (");
166+
Serial.print(-errno);
167+
Serial.print(")");
168+
return;
169+
}
170+
Serial.println(" Done.");
171+
172+
// Decompress file in case of armored .OTA
173+
if (otaFileLocation.endsWith(".ota") || otaFileLocation.endsWith(".lzss")) {
174+
Serial.print("Decompressing LZSS compressed file... ");
175+
const auto ota_decompress = ota.decompress();
176+
if (ota_decompress < 0) {
177+
Serial.print("Arduino_Portenta_OTA_QSPI::decompress() failed with error code");
178+
Serial.println(ota_decompress);
179+
return;
180+
}
181+
Serial.print(ota_decompress);
182+
Serial.println(" bytes decompressed.");
183+
}
184+
185+
Serial.print("Storing parameters for firmware update in bootloader accessible non-volatile memory...");
186+
if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None) {
187+
Serial.print(" ota.update() failed with error code ");
188+
Serial.println((int)ota_err);
189+
return;
190+
}
191+
Serial.println(" Done.");
192+
193+
Serial.println("Performing a reset after which the bootloader will update the firmware.");
194+
Serial.flush();
195+
delay(1000); /* Make sure the serial message gets out before the reset. */
196+
ota.reset();
197+
}
198+
199+
void loop()
200+
{
201+
}

extras/ExampleOTA/ExampleOTA.ino

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
void setup()
3+
{
4+
Serial.begin(115200);
5+
for(const auto timeout = 2500u; !Serial && millis() < timeout; yield());
6+
7+
delay(2500);
8+
Serial.println();
9+
Serial.println();
10+
Serial.println("***** ***** ***** ***** *****");
11+
Serial.println("After USB OTA");
12+
Serial.print("Compilation Datetime: ");
13+
Serial.print(__DATE__);
14+
Serial.print(" ");
15+
Serial.println(__TIME__);
16+
Serial.println("***** ***** ***** ***** *****");
17+
}
18+
19+
void loop()
20+
{
21+
Serial.print("[");
22+
Serial.print(millis());
23+
Serial.println("] Hello, World!");
24+
delay(1000);
25+
}

0 commit comments

Comments
 (0)