Skip to content

Commit 8eda2ad

Browse files
committed
SFU: Add download function
1 parent 9667d7e commit 8eda2ad

File tree

2 files changed

+210
-2
lines changed

2 files changed

+210
-2
lines changed

libraries/SFU/src/SFU.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,197 @@
1818
*/
1919

2020
#include "SFU.h"
21+
#include <BlockDevice.h>
22+
#include <MBRBlockDevice.h>
23+
#include "FATFileSystem.h"
24+
#include <Arduino_DebugUtils.h>
25+
#include <WiFiC3.h>
26+
#include <WiFiSSLClient.h>
27+
28+
#define AIOT_CONFIG_PORTENTA_C33_OTA_HTTP_HEADER_RECEIVE_TIMEOUT_ms (5*1000UL);
29+
#define AIOT_CONFIG_PORTENTA_C33_OTA_HTTP_DATA_RECEIVE_TIMEOUT_ms (5*60*1000UL);
2130

2231
const unsigned char SFU[0x20000] __attribute__ ((section(".second_stage_ota"), used)) = {
2332
#include "c33.h"
2433
};
34+
35+
BlockDevice* block_device = BlockDevice::get_default_instance();
36+
MBRBlockDevice mbr(block_device, 1);
37+
FATFileSystem fs("ota");
38+
39+
/* Original code: http://stackoverflow.com/questions/2616011/easy-way-to-parse-a-url-in-c-cross-platform */
40+
#include <string>
41+
#include <algorithm>
42+
#include <cctype>
43+
#include <functional>
44+
#include <iostream>
45+
46+
struct URI {
47+
public:
48+
URI(const std::string& url_s) {
49+
this->parse(url_s);
50+
}
51+
std::string protocol_, host_, path_, query_;
52+
private:
53+
void parse(const std::string& url_s);
54+
};
55+
56+
using namespace std;
57+
58+
// ctors, copy, equality, ...
59+
// TODO: change me into something embedded friendly (this function adds ~100KB to flash)
60+
void URI::parse(const string& url_s)
61+
{
62+
const string prot_end("://");
63+
string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
64+
prot_end.begin(), prot_end.end());
65+
protocol_.reserve(distance(url_s.begin(), prot_i));
66+
transform(url_s.begin(), prot_i,
67+
back_inserter(protocol_),
68+
ptr_fun<int,int>(tolower)); // protocol is icase
69+
if( prot_i == url_s.end() )
70+
return;
71+
advance(prot_i, prot_end.length());
72+
string::const_iterator path_i = find(prot_i, url_s.end(), '/');
73+
host_.reserve(distance(prot_i, path_i));
74+
transform(prot_i, path_i,
75+
back_inserter(host_),
76+
ptr_fun<int,int>(tolower)); // host is icase
77+
string::const_iterator query_i = find(path_i, url_s.end(), '?');
78+
path_.assign(path_i, query_i);
79+
if( query_i != url_s.end() )
80+
++query_i;
81+
query_.assign(query_i, url_s.end());
82+
}
83+
84+
int SFU::download(const char* ota_url) {
85+
int err = -1;
86+
87+
if ((err = fs.reformat(&mbr)) != 0)
88+
{
89+
DEBUG_ERROR("%s: fs.reformat() failed with %d", __FUNCTION__, err);
90+
return static_cast<int>(OTAError::PORTENTA_C33_ErrorReformat);
91+
}
92+
93+
FILE * file = fopen("/ota/UPDATE.BIN.OTA", "wb");
94+
if (!file)
95+
{
96+
DEBUG_ERROR("%s: fopen() failed", __FUNCTION__);
97+
fclose(file);
98+
return static_cast<int>(OTAError::PORTENTA_C33_ErrorOpenUpdateFile);
99+
}
100+
101+
URI url(ota_url);
102+
Client * client = nullptr;
103+
int port = 0;
104+
105+
if (url.protocol_ == "http") {
106+
client = new WiFiClient();
107+
port = 80;
108+
} else if (url.protocol_ == "https") {
109+
client = new WiFiSSLClient();
110+
port = 443;
111+
} else {
112+
DEBUG_ERROR("%s: Failed to parse OTA URL %s", __FUNCTION__, url.host_.c_str());
113+
fclose(file);
114+
return static_cast<int>(OTAError::PORTENTA_C33_UrlParseError);
115+
}
116+
117+
if (!client->connect(url.host_.c_str(), port))
118+
{
119+
DEBUG_ERROR("%s: Connection failure with OTA storage server %s", __FUNCTION__, url.host_.c_str());
120+
fclose(file);
121+
return static_cast<int>(OTAError::PORTENTA_C33_ServerConnectError);
122+
}
123+
124+
client->println(String("GET ") + url.path_.c_str() + " HTTP/1.1");
125+
client->println(String("Host: ") + url.host_.c_str());
126+
client->println("Connection: close");
127+
client->println();
128+
129+
/* Receive HTTP header. */
130+
String http_header;
131+
bool is_header_complete = false,
132+
is_http_header_timeout = false;
133+
for (unsigned long const start = millis(); !is_header_complete;)
134+
{
135+
is_http_header_timeout = (millis() - start) > AIOT_CONFIG_PORTENTA_C33_OTA_HTTP_HEADER_RECEIVE_TIMEOUT_ms;
136+
if (is_http_header_timeout) break;
137+
138+
if (client->available())
139+
{
140+
char const c = client->read();
141+
142+
http_header += c;
143+
if (http_header.endsWith("\r\n\r\n"))
144+
is_header_complete = true;
145+
}
146+
}
147+
148+
if (!is_header_complete)
149+
{
150+
DEBUG_ERROR("%s: Error receiving HTTP header %s", __FUNCTION__, is_http_header_timeout ? "(timeout)":"");
151+
fclose(file);
152+
return static_cast<int>(OTAError::PORTENTA_C33_HttpHeaderError);
153+
}
154+
155+
/* Extract concent length from HTTP header. A typical entry looks like
156+
* "Content-Length: 123456"
157+
*/
158+
char const * content_length_ptr = strstr(http_header.c_str(), "Content-Length");
159+
if (!content_length_ptr)
160+
{
161+
DEBUG_ERROR("%s: Failure to extract content length from http header", __FUNCTION__);
162+
fclose(file);
163+
return static_cast<int>(OTAError::PORTENTA_C33_ErrorParseHttpHeader);
164+
}
165+
/* Find start of numerical value. */
166+
char * ptr = const_cast<char *>(content_length_ptr);
167+
for (; (*ptr != '\0') && !isDigit(*ptr); ptr++) { }
168+
/* Extract numerical value. */
169+
String content_length_str;
170+
for (; isDigit(*ptr); ptr++) content_length_str += *ptr;
171+
int const content_length_val = atoi(content_length_str.c_str());
172+
DEBUG_VERBOSE("%s: Length of OTA binary according to HTTP header = %d bytes", __FUNCTION__, content_length_val);
173+
174+
/* Receive as many bytes as are indicated by the HTTP header - or die trying. */
175+
int bytes_received = 0;
176+
bool is_http_data_timeout = false;
177+
for(unsigned long const start = millis(); bytes_received < content_length_val;)
178+
{
179+
is_http_data_timeout = (millis() - start) > AIOT_CONFIG_PORTENTA_C33_OTA_HTTP_DATA_RECEIVE_TIMEOUT_ms;
180+
if (is_http_data_timeout) break;
181+
182+
if (client->available())
183+
{
184+
char const c = client->read();
185+
186+
if (fwrite(&c, 1, sizeof(c), file) != sizeof(c))
187+
{
188+
DEBUG_ERROR("%s: Writing of firmware image to flash failed", __FUNCTION__);
189+
fclose(file);
190+
return static_cast<int>(OTAError::PORTENTA_C33_ErrorWriteUpdateFile);
191+
}
192+
193+
bytes_received++;
194+
}
195+
}
196+
197+
if (bytes_received != content_length_val) {
198+
DEBUG_ERROR("%s: Error receiving HTTP data %s (%d bytes received, %d expected)", __FUNCTION__, is_http_data_timeout ? "(timeout)":"", bytes_received, content_length_val);
199+
fclose(file);
200+
return static_cast<int>(OTAError::PORTENTA_C33_HttpDataError);
201+
}
202+
203+
DEBUG_INFO("%s: %d bytes received", __FUNCTION__, ftell(file));
204+
fclose(file);
205+
206+
/* Unmount the filesystem. */
207+
if ((err = fs.unmount()) != 0)
208+
{
209+
DEBUG_ERROR("%s: fs.unmount() failed with %d", __FUNCTION__, err);
210+
return static_cast<int>(OTAError::PORTENTA_C33_ErrorUnmount);
211+
}
212+
213+
return static_cast<int>(OTAError::None);
214+
}

libraries/SFU/src/SFU.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,29 @@
2222

2323
#include "Arduino.h"
2424

25+
#define PORTENTA_C33_OTA_ERROR_BASE (-300)
26+
27+
enum class OTAError : int
28+
{
29+
None = 0,
30+
DownloadFailed = 1,
31+
PORTENTA_C33_UrlParseError = PORTENTA_C33_OTA_ERROR_BASE - 0,
32+
PORTENTA_C33_ServerConnectError = PORTENTA_C33_OTA_ERROR_BASE - 1,
33+
PORTENTA_C33_HttpHeaderError = PORTENTA_C33_OTA_ERROR_BASE - 2,
34+
PORTENTA_C33_HttpDataError = PORTENTA_C33_OTA_ERROR_BASE - 3,
35+
PORTENTA_C33_ErrorOpenUpdateFile = PORTENTA_C33_OTA_ERROR_BASE - 4,
36+
PORTENTA_C33_ErrorWriteUpdateFile = PORTENTA_C33_OTA_ERROR_BASE - 5,
37+
PORTENTA_C33_ErrorParseHttpHeader = PORTENTA_C33_OTA_ERROR_BASE - 6,
38+
PORTENTA_C33_ErrorFlashInit = PORTENTA_C33_OTA_ERROR_BASE - 7,
39+
PORTENTA_C33_ErrorReformat = PORTENTA_C33_OTA_ERROR_BASE - 8,
40+
PORTENTA_C33_ErrorUnmount = PORTENTA_C33_OTA_ERROR_BASE - 9,
41+
};
42+
2543
class SFU {
2644
public:
27-
static int begin();
45+
static int begin() {};
2846
static int download(const char* url);
29-
static int apply();
47+
static int apply() { NVIC_SystemReset(); };
3048
};
3149

3250
#endif // _SFU_H_

0 commit comments

Comments
 (0)