Skip to content

Commit 5560b31

Browse files
committed
Merge branch 'master' into sse
2 parents dfc535d + aceea3e commit 5560b31

File tree

7 files changed

+843
-1
lines changed

7 files changed

+843
-1
lines changed

Diff for: libraries/Update/examples/HTTP_Client_AES_OTA_Update/.skip.esp32h2

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
/*
2+
An example of how to use HTTPClient to download an encrypted and plain image files OTA from a web server.
3+
This example uses Wifi & HTTPClient to connect to webserver and two functions for obtaining firmware image from webserver.
4+
One uses the example 'updater.php' code on server to check and/or send relavent download firmware image file,
5+
the other directly downloads the firmware file from web server.
6+
7+
To use:-
8+
Make a folder/directory on your webserver where your firmware images will be uploaded to. ie. /firmware
9+
The 'updater.php' file can also be uploaded to the same folder. Edit and change definitions in 'update.php' to suit your needs.
10+
In sketch:
11+
set HTTPUPDATE_HOST to domain name or IP address if on LAN of your web server
12+
set HTTPUPDATE_UPDATER_URI to path and file to call 'updater.php'
13+
or set HTTPUPDATE_DIRECT_URI to path and firmware file to download
14+
edit other HTTPUPDATE_ as needed
15+
16+
Encrypted image will help protect your app image file from being copied and used on blank devices, encrypt your image file by using espressif IDF.
17+
First install an app on device that has Update setup with the OTA decrypt mode on, same key, address and flash_crypt_conf as used in IDF to encrypt image file or vice versa.
18+
19+
For easier development use the default U_AES_DECRYPT_AUTO decrypt mode. This mode allows both plain and encrypted app images to be uploaded.
20+
21+
Note:- App image can also encrypted on device, by using espressif IDF to configure & enabled FLASH encryption, suggest the use of a different 'OTA_KEY' key for update from the eFuses 'flash_encryption' key used by device.
22+
23+
ie. "Update.setupCrypt(OTA_KEY, OTA_ADDRESS, OTA_CFG);"
24+
25+
defaults:- {if not set ie. "Update.setupCrypt();" }
26+
OTA_KEY = 0 ( 0 = no key, disables decryption )
27+
OTA_ADDRESS = 0 ( suggest dont set address to app0=0x10000 usually or app1=varies )
28+
OTA_CFG = 0xf
29+
OTA_MODE = U_AES_DECRYPT_AUTO
30+
31+
OTA_MODE options:-
32+
U_AES_DECRYPT_NONE decryption disabled, loads OTA image files as sent(plain)
33+
U_AES_DECRYPT_AUTO auto loads both plain & encrypted OTA FLASH image files, and plain OTA SPIFFS image files
34+
U_AES_DECRYPT_ON decrypts OTA image files
35+
36+
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/
37+
38+
Example:
39+
espsecure.py encrypt_flash_data -k ota_key.bin --flash_crypt_conf 0xf -a 0x4320 -o output_filename.bin source_filename.bin
40+
41+
espsecure.py encrypt_flash_data = runs the idf encryption function to make a encrypted output file from a source file
42+
-k text = path/filename to the AES 256bit(32byte) encryption key file
43+
--flash_crypt_conf 0xn = 0x0 to 0xf, the more bits set the higher the security of encryption(address salting, 0x0 would use ota_key with no address salting)
44+
-a 0xnnnnnn00 = 0x00 to 0x00fffff0 address offset(must be a multiple of 16, but better to use multiple of 32), used to offset the salting (has no effect when = --flash_crypt_conf 0x0)
45+
-o text = path/filename to save encrypted output file to
46+
text = path/filename to open source file from
47+
*/
48+
49+
#include <Arduino.h>
50+
#include <WiFi.h>
51+
#include <WiFiClient.h>
52+
#include <HTTPClient.h>
53+
#include <Update.h>
54+
55+
//==========================================================================
56+
//==========================================================================
57+
const char* WIFI_SSID = "wifi-ssid";
58+
const char* WIFI_PASSWORD = "wifi-password";
59+
60+
const uint8_t OTA_KEY[32] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, \
61+
0x38, 0x39, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, \
62+
0x61, 0x20, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, \
63+
0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79 };
64+
65+
/*
66+
const uint8_t OTA_KEY[32] = {'0', '1', '2', '3', '4', '5', '6', '7',
67+
'8', '9', ' ', 't', 'h', 'i', 's', ' ',
68+
'a', ' ', 's', 'i', 'm', 'p', 'l', 'e',
69+
't', 'e', 's', 't', ' ', 'k', 'e', 'y' };
70+
*/
71+
72+
//const uint8_t OTA_KEY[33] = "0123456789 this a simpletest key";
73+
74+
const uint32_t OTA_ADDRESS = 0x4320;
75+
const uint32_t OTA_CFG = 0x0f;
76+
const uint32_t OTA_MODE = U_AES_DECRYPT_AUTO;
77+
78+
const char* HTTPUPDATE_USERAGRENT = "ESP32-Updater";
79+
//const char* HTTPUPDATE_HOST = "www.yourdomain.com";
80+
const char* HTTPUPDATE_HOST = "192.168.1.2";
81+
const uint16_t HTTPUPDATE_PORT = 80;
82+
const char* HTTPUPDATE_UPDATER_URI = "/firmware/updater.php"; //uri to 'updater.php'
83+
const char* HTTPUPDATE_DIRECT_URI = "/firmware/HTTP_Client_AES_OTA_Update-v1.1.xbin"; //uri to image file
84+
85+
const char* HTTPUPDATE_USER = NULL; //use NULL if no authentication needed
86+
//const char* HTTPUPDATE_USER = "user";
87+
const char* HTTPUPDATE_PASSWORD = "password";
88+
89+
const char* HTTPUPDATE_BRAND = "21"; /* Brand ID */
90+
const char* HTTPUPDATE_MODEL = "HTTP_Client_AES_OTA_Update"; /* Project name */
91+
const char* HTTPUPDATE_FIRMWARE = "0.9"; /* Firmware version */
92+
93+
//==========================================================================
94+
//==========================================================================
95+
String urlEncode(const String& url, const char* safeChars="-_.~") {
96+
String encoded = "";
97+
char temp[4];
98+
99+
for (int i = 0; i < url.length(); i++){
100+
temp[0] = url.charAt(i);
101+
if(temp[0] == 32){//space
102+
encoded.concat('+');
103+
}else if( (temp[0] >= 48 && temp[0] <= 57) /*0-9*/
104+
|| (temp[0] >= 65 && temp[0] <= 90) /*A-Z*/
105+
|| (temp[0] >= 97 && temp[0] <= 122) /*a-z*/
106+
|| (strchr(safeChars, temp[0]) != NULL) /* "=&-_.~" */
107+
){
108+
encoded.concat(temp[0]);
109+
}else{ //character needs encoding
110+
snprintf(temp, 4, "%%%02X", temp[0]);
111+
encoded.concat(temp);
112+
}
113+
}
114+
return encoded;
115+
}
116+
117+
//==========================================================================
118+
bool addQuery(String* query, const String name, const String value) {
119+
if( name.length() && value.length() ){
120+
if( query->length() < 3 ){
121+
*query = "?";
122+
}else{
123+
query->concat('&');
124+
}
125+
query->concat( urlEncode(name) );
126+
query->concat('=');
127+
query->concat( urlEncode(value) );
128+
return true;
129+
}
130+
return false;
131+
}
132+
133+
//==========================================================================
134+
//==========================================================================
135+
void printProgress(size_t progress, const size_t& size) {
136+
static int last_progress=-1;
137+
if(size>0){
138+
progress = (progress*100)/size;
139+
progress = (progress>100 ? 100 : progress); //0-100
140+
if( progress != last_progress ){
141+
Serial.printf("Progress: %d%%\n", progress);
142+
last_progress = progress;
143+
}
144+
}
145+
}
146+
147+
//==========================================================================
148+
bool http_downloadUpdate(HTTPClient& http, uint32_t size=0) {
149+
size = (size == 0 ? http.getSize() : size);
150+
if(size == 0){
151+
return false;
152+
}
153+
WiFiClient *client = http.getStreamPtr();
154+
155+
if( !Update.begin(size, U_FLASH) ) {
156+
Serial.printf("Update.begin failed! (%s)\n", Update.errorString() );
157+
return false;
158+
}
159+
160+
if( !Update.setupCrypt(OTA_KEY, OTA_ADDRESS, OTA_CFG, OTA_MODE)){
161+
Serial.println("Update.setupCrypt failed!");
162+
}
163+
164+
if( Update.writeStream(*client) != size ) {
165+
Serial.printf("Update.writeStream failed! (%s)\n", Update.errorString() );
166+
return false;
167+
}
168+
169+
if( !Update.end() ) {
170+
Serial.printf("Update.end failed! (%s)\n", Update.errorString() );
171+
return false;
172+
}
173+
return true;
174+
}
175+
176+
//==========================================================================
177+
int http_sendRequest(HTTPClient& http) {
178+
179+
//set request Headers to be sent to server
180+
http.useHTTP10(true); // use HTTP/1.0 for update since the update handler not support any transfer Encoding
181+
http.setTimeout(8000);
182+
http.addHeader("Cache-Control", "no-cache");
183+
184+
//set own name for HTTPclient user-agent
185+
http.setUserAgent(HTTPUPDATE_USERAGRENT);
186+
187+
int code = http.GET(); //send the GET request to HTTP server
188+
int len = http.getSize();
189+
190+
if(code == HTTP_CODE_OK){
191+
return (len>0 ? len : 0); //return 0 or length of image to download
192+
}else if(code < 0){
193+
Serial.printf("Error: %s\n", http.errorToString(code).c_str());
194+
return code; //error code should be minus between -1 to -11
195+
}else{
196+
Serial.printf("Error: HTTP Server response code %i\n", code);
197+
return -code; //return code should be minus between -100 to -511
198+
}
199+
}
200+
201+
//==========================================================================
202+
/* http_updater sends a GET request to 'update.php' on web server */
203+
bool http_updater(const String& host, const uint16_t& port, String uri, const bool& download, const char* user=NULL, const char* password=NULL) {
204+
//add GET query params to be sent to server (are used by server 'updater.php' code to determine what action to take)
205+
String query = "";
206+
addQuery(&query, "cmd",(download ? "download" :"check") ); //action command
207+
208+
//setup HTTPclient to be ready to connect & send a request to HTTP server
209+
HTTPClient http;
210+
WiFiClient client;
211+
uri.concat(query); //GET query added to end of uri path
212+
if( !http.begin(client, host, port, uri) ){
213+
return false; //httpclient setup error
214+
}
215+
Serial.printf( "Sending HTTP request 'http://%s:%i%s'\n", host.c_str(), port, uri.c_str() );
216+
217+
//set basic authorization, if needed for webpage access
218+
if(user != NULL && password != NULL){
219+
http.setAuthorization(user, password); //set basic Authorization to server, if needed be gain access
220+
}
221+
222+
//add unique Headers to be sent to server used by server 'update.php' code to determine there a suitable firmware update image avaliable
223+
http.addHeader("Brand-Code", HTTPUPDATE_BRAND);
224+
http.addHeader("Model", HTTPUPDATE_MODEL);
225+
http.addHeader("Firmware", HTTPUPDATE_FIRMWARE);
226+
227+
//set headers to look for to get returned values in servers http response to our http request
228+
const char * headerkeys[] = { "update", "version" }; //server returns update 0=no update found, 1=update found, version=version of update found
229+
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
230+
http.collectHeaders(headerkeys, headerkeyssize);
231+
232+
//connect & send HTTP request to server
233+
int size = http_sendRequest(http);
234+
235+
//is there an image to download
236+
if( size > 0 || (!download && size == 0) ){
237+
if( !http.header("update") || http.header("update").toInt() == 0 ){
238+
Serial.println("No Firmware avaliable");
239+
}else if( !http.header("version") || http.header("version").toFloat() <= String(HTTPUPDATE_FIRMWARE).toFloat() ){
240+
Serial.println("Firmware is upto Date");
241+
}else{
242+
//image avaliabe to download & update
243+
if(!download){
244+
Serial.printf( "Found V%s Firmware\n", http.header("version").c_str() );
245+
}else{
246+
Serial.printf( "Downloading & Installing V%s Firmware\n", http.header("version").c_str() );
247+
}
248+
if( !download || http_downloadUpdate(http) ){
249+
http.end(); //end connection
250+
return true;
251+
}
252+
}
253+
}
254+
255+
http.end(); //end connection
256+
return false;
257+
}
258+
259+
//==========================================================================
260+
/* this downloads Firmware image file directly from web server */
261+
bool http_direct(const String& host, const uint16_t& port, const String& uri, const char* user=NULL, const char* password=NULL) {
262+
//setup HTTPclient to be ready to connect & send a request to HTTP server
263+
HTTPClient http;
264+
WiFiClient client;
265+
if( !http.begin(client, host, port, uri) ){
266+
return false; //httpclient setup error
267+
}
268+
Serial.printf( "Sending HTTP request 'http://%s:%i%s'\n", host.c_str(), port, uri.c_str() );
269+
270+
//set basic authorization, if needed for webpage access
271+
if(user != NULL && password != NULL){
272+
http.setAuthorization(user, password); //set basic Authorization to server, if needed be gain access
273+
}
274+
275+
//connect & send HTTP request to server
276+
int size = http_sendRequest(http);
277+
278+
//is there an image to download
279+
if(size > 0){
280+
if( http_downloadUpdate(http) ){
281+
http.end();
282+
return true; //end connection
283+
}
284+
}else{
285+
Serial.println("Image File not found");
286+
}
287+
288+
http.end(); //end connection
289+
return false;
290+
}
291+
292+
//==========================================================================
293+
//==========================================================================
294+
295+
void setup() {
296+
Serial.begin(115200);
297+
Serial.println();
298+
Serial.printf("Booting %s V%s\n", HTTPUPDATE_MODEL, HTTPUPDATE_FIRMWARE);
299+
300+
WiFi.mode(WIFI_AP_STA);
301+
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
302+
if(WiFi.waitForConnectResult() != WL_CONNECTED){
303+
Serial.println("WiFi failed, retrying.");
304+
}
305+
int i = 0;
306+
while (WiFi.waitForConnectResult() != WL_CONNECTED){
307+
Serial.print(".");
308+
if( (++i % 100) == 0){
309+
Serial.println();
310+
}
311+
delay(100);
312+
}
313+
Serial.printf( "Connected to Wifi\nLocal IP: %s\n", WiFi.localIP().toString().c_str() );
314+
315+
Update.onProgress(printProgress);
316+
317+
Serial.println("Checking with Server, if New Firmware avaliable");
318+
if( http_updater(HTTPUPDATE_HOST, HTTPUPDATE_PORT, HTTPUPDATE_UPDATER_URI, 0, HTTPUPDATE_USER, HTTPUPDATE_PASSWORD) ){ //check for new firmware
319+
if( http_updater(HTTPUPDATE_HOST, HTTPUPDATE_PORT, HTTPUPDATE_UPDATER_URI, 1, HTTPUPDATE_USER, HTTPUPDATE_PASSWORD) ){ //update to new firmware
320+
Serial.println("Firmware Update Sucessfull, rebooting");
321+
ESP.restart();
322+
}
323+
}
324+
325+
Serial.println("Checking Server for Firmware Image File to Download & Install");
326+
if( http_direct(HTTPUPDATE_HOST, HTTPUPDATE_PORT, HTTPUPDATE_DIRECT_URI, HTTPUPDATE_USER, HTTPUPDATE_PASSWORD) ){
327+
Serial.println("Firmware Update Sucessfull, rebooting");
328+
ESP.restart();
329+
}
330+
}
331+
332+
void loop() {
333+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/* Updater Server-side Example */
3+
$brand_codes = array("20", "21");
4+
$commands = array("check", "download");
5+
6+
function verify($valid){
7+
if(!$valid){
8+
http_response_code(404);
9+
echo "Sorry, page not found";
10+
die();
11+
}
12+
}
13+
14+
$headers = array();
15+
foreach (getallheaders() as $name => $value) {
16+
$headers += [$name => $value];
17+
}
18+
verify( in_array($headers['Brand-Code'], $brand_codes) );
19+
20+
$GetArgs = filter_input_array(INPUT_GET);
21+
verify( in_array($GetArgs['cmd'], $commands) );
22+
23+
if($GetArgs['cmd'] == "check" || $GetArgs['cmd'] == "download"){
24+
/*********************************************************************************/
25+
/* $firmware version & filename definitions for different Brands, Models & Firmware versions */
26+
if($headers['Brand-Code'] == "21"){
27+
if($headers['Model'] == "HTTP_Client_AES_OTA_Update"){
28+
29+
if($headers['Firmware'] < "0.9"){//ie. update to latest of this major version
30+
$firmware = array('version'=>"0.9", 'filename'=>"HTTP_Client_AES_OTA_Update-v0.9.xbin");
31+
}
32+
elseif($headers['Firmware'] == "0.9"){//ie. update between major versions
33+
$firmware = array('version'=>"1.0", 'filename'=>"HTTP_Client_AES_OTA_Update-v1.0.xbin");
34+
}
35+
elseif($headers['Firmware'] <= "1.4"){//ie. update to latest version
36+
$firmware = array('version'=>"1.4", 'filename'=>"HTTP_Client_AES_OTA_Update-v1.4.xbin");
37+
}
38+
39+
}
40+
}
41+
/* end of $firmware definitions for firmware update images on server */
42+
/*********************************************************************************/
43+
44+
if( !$firmware['filename'] || !file_exists($firmware['filename']) ){
45+
header('update: 0' );//no update avaliable
46+
exit;
47+
}else{
48+
header('update: 1' );//update avaliable
49+
header('version: ' . $firmware['version'] );
50+
if($GetArgs['cmd'] == "download"){
51+
//Get file type and set it as Content Type
52+
$finfo = finfo_open(FILEINFO_MIME_TYPE);
53+
header('Content-Type: ' . finfo_file($finfo, $firmware['filename']));//application/octet-stream for binary file
54+
finfo_close($finfo);
55+
//Define file size
56+
header('Content-Length: ' . filesize($firmware['filename']));
57+
readfile($firmware['filename']); //send file
58+
}
59+
exit;
60+
}
61+
}
62+
63+
verify(false);
64+
?>

Diff for: libraries/Update/examples/HTTP_Server_AES_OTA_Update/.skip.esp32h2

Whitespace-only changes.

0 commit comments

Comments
 (0)