Skip to content

Commit c04b8fd

Browse files
authored
Add SSU LZSS HTTP OTA example (#546)
1 parent 6bff0b2 commit c04b8fd

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
Small example sketch demonstrating how to perform OTA via HTTP/S
3+
utilizing a MKRGSM 1400 and the storage on the integrated
4+
SARA U-201 GSM module.
5+
6+
Please, be careful because no verification is done on the
7+
received OTA file, apart size verification of the transmitted
8+
bytes using the HTTP Content-Length header.
9+
10+
For production-grade OTA procedure you might want to implement
11+
a content verification procedure using a CRC calculation
12+
or an hash (eg. MD5 or SHA256) comparison.
13+
14+
Circuit:
15+
* MKR GSM 1400 board
16+
* Antenna
17+
* SIM card with a data plan
18+
19+
Steps to update a sketch:
20+
21+
1) Create a new sketch or update an existing one to be updated over-the-air.
22+
The sketch needs to contain also the code below for future OTAs.
23+
The sketch must include the SSU library via
24+
#include <SSU.h>
25+
26+
2) In the IDE select: Sketch -> Export compiled Binary.
27+
28+
3) Open the location of the sketch (Sketch -> Show Sketch Folder) and compress it
29+
with a lzss tool
30+
(eg. https://github.com/arduino-libraries/ArduinoIoTCloud/blob/master/extras/tools/lzss.py).
31+
32+
4) Upload the .lzss file to your HTTP/S server.
33+
34+
5) Upload this sketch after configuring the server, port and filename variables.
35+
36+
The sketch will download the OTA file, store it into the U-201 storage, and
37+
will reset the board to trigger the SSU update procedure.
38+
39+
40+
created 25 June 2020
41+
by Giampaolo Mancini
42+
*/
43+
44+
#include <MKRGSM.h>
45+
46+
// This includes triggers the firmware update procedure
47+
// in the bootloader after reset.
48+
#include <SSU.h>
49+
50+
// Do not change! SSU will look for these files!
51+
constexpr char UPDATE_FILE_NAME[] = "UPDATE.BIN.LZSS";
52+
static constexpr char CHECK_FILE_NAME[] = "UPDATE.OK";
53+
54+
#include "arduino_secrets.h"
55+
const char PINNUMBER[] = SECRET_PINNUMBER;
56+
// APN data
57+
const char GPRS_APN[] = SECRET_GPRS_APN;
58+
const char GPRS_LOGIN[] = SECRET_GPRS_LOGIN;
59+
const char GPRS_PASSWORD[] = SECRET_GPRS_PASSWORD;
60+
61+
// Change to GSMClient for non-SSL/TLS connection.
62+
// Not recommended.
63+
GSMSSLClient client;
64+
GPRS gprs;
65+
GSM gsmAccess;
66+
67+
GSMFileUtils fileUtils;
68+
69+
bool isHeaderComplete = false;
70+
String httpHeader;
71+
72+
bool isDownloadComplete = false;
73+
unsigned int fileSize = 0;
74+
unsigned int totalWritten = 0;
75+
76+
constexpr char server[] = "example.org";
77+
constexpr int port = 443;
78+
79+
// Name of the new firmware file to be updated.
80+
constexpr char filename[] = "update.lzss";
81+
82+
83+
void setup()
84+
{
85+
unsigned long timeout = millis();
86+
87+
Serial.begin(9600);
88+
while (!Serial && millis() - timeout < 5000)
89+
;
90+
91+
Serial.println("Starting OTA Update via HTTP and Arduino SSU.");
92+
Serial.println();
93+
94+
bool connected = false;
95+
96+
Serial.print("Connecting to cellular network... ");
97+
while (!connected) {
98+
if ((gsmAccess.begin(PINNUMBER) == GSM_READY) && (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)) {
99+
connected = true;
100+
} else {
101+
Serial.println("Not connected");
102+
delay(1000);
103+
}
104+
}
105+
106+
Serial.println("Connected.");
107+
Serial.println();
108+
109+
// Modem has already been initialized in the sketch:
110+
// begin FileUtils without MODEM initialization.
111+
fileUtils.begin(false);
112+
113+
Serial.print("Connecting to ");
114+
Serial.print(server);
115+
Serial.print(":");
116+
Serial.print(port);
117+
Serial.print("... ");
118+
if (client.connect(server, port)) {
119+
Serial.println("Connected.");
120+
Serial.print("Downloading ");
121+
Serial.println(filename);
122+
Serial.print("... ");
123+
// Make the HTTP request:
124+
client.print("GET /");
125+
client.print(filename);
126+
client.println(" HTTP/1.1");
127+
client.print("Host: ");
128+
client.println(server);
129+
client.println("Connection: close");
130+
client.println();
131+
} else {
132+
Serial.println("Connection failed");
133+
}
134+
}
135+
136+
void loop()
137+
{
138+
while (client.available()) {
139+
// Skip the HTTP header
140+
if (!isHeaderComplete) {
141+
const char c = client.read();
142+
httpHeader += c;
143+
if (httpHeader.endsWith("\r\n\r\n")) {
144+
isHeaderComplete = true;
145+
146+
// Get the size of the OTA file from the
147+
// HTTP Content-Length header.
148+
fileSize = getContentLength();
149+
150+
Serial.println();
151+
Serial.print("HTTP header complete. ");
152+
Serial.print("OTA file size is ");
153+
Serial.print(fileSize);
154+
Serial.println(" bytes.");
155+
if (fileSize == 0) {
156+
Serial.println("Unable to get OTA file size.");
157+
while (true)
158+
;
159+
}
160+
}
161+
} else {
162+
// Read the OTA file in len-bytes blocks to preserve RAM.
163+
constexpr size_t len { 512 };
164+
char buf[len] { 0 };
165+
166+
// Read len bytes from HTTP client...
167+
uint32_t read = client.readBytes(buf, len);
168+
// and append them to the update file.
169+
uint32_t written = fileUtils.appendFile(UPDATE_FILE_NAME, buf, read);
170+
171+
if (written != read) {
172+
Serial.println("Error while saving data.");
173+
while (true)
174+
;
175+
}
176+
177+
// Update the received byte counter
178+
totalWritten += written;
179+
180+
// Check for full file received and stored
181+
isDownloadComplete = totalWritten == fileSize;
182+
183+
Serial.print("Received: ");
184+
Serial.print(totalWritten);
185+
Serial.print("/");
186+
Serial.println(fileSize);
187+
}
188+
}
189+
if (isDownloadComplete) {
190+
Serial.println();
191+
Serial.println("Download complete.");
192+
Serial.println("Enabling checkpoint.");
193+
Serial.println();
194+
195+
// Create the checkpoint file: will be removed by SSU
196+
// after successful update.
197+
auto status = fileUtils.downloadFile(CHECK_FILE_NAME, { 0 }, 1);
198+
if (status != 1) {
199+
Serial.println("Unable to create checkpoint file.");
200+
while (true)
201+
;
202+
}
203+
204+
Serial.println("Resetting MCU in order to trigger SSU...");
205+
Serial.println();
206+
delay(500);
207+
NVIC_SystemReset();
208+
}
209+
}
210+
211+
int getContentLength()
212+
{
213+
const String contentLengthHeader = "Content-Length:";
214+
const auto contentLengthHeaderLen = contentLengthHeader.length();
215+
216+
auto indexContentLengthStart = httpHeader.indexOf(contentLengthHeader);
217+
if (indexContentLengthStart < 0) {
218+
Serial.println("Unable to find Content-Length header (Start)");
219+
return 0;
220+
}
221+
auto indexContentLengthStop = httpHeader.indexOf("\r\n", indexContentLengthStart);
222+
if (indexContentLengthStart < 0) {
223+
Serial.println("Unable to find Content-Length header (Stop)");
224+
return 0;
225+
}
226+
auto contentLength = httpHeader.substring(indexContentLengthStart + contentLengthHeaderLen + 1, indexContentLengthStop);
227+
228+
contentLength.trim();
229+
return contentLength.toInt();
230+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#define SECRET_PINNUMBER ""
2+
#define SECRET_GPRS_APN "GPRS_APN" // replace your GPRS APN
3+
#define SECRET_GPRS_LOGIN "login" // replace with your GPRS login
4+
#define SECRET_GPRS_PASSWORD "password" // replace with your GPRS password

0 commit comments

Comments
 (0)