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