Skip to content

Commit 44f803b

Browse files
committed
Merge branch 'feature/esp_https_ota' into 'master'
Feature/esp https ota See merge request sdk/ESP8266_RTOS_SDK!776
2 parents d5f14db + 43d230a commit 44f803b

22 files changed

+529
-1
lines changed
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(COMPONENT_ADD_INCLUDEDIRS include)
2+
set(COMPONENT_SRCS "src/esp_https_ota.c")
3+
4+
set(COMPONENT_REQUIRES esp_http_client)
5+
set(COMPONENT_PRIV_REQUIRES log app_update)
6+
7+
register_component()

components/esp_https_ota/component.mk

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
COMPONENT_SRCDIRS :=
2+
COMPONENT_ADD_INCLUDEDIRS :=
3+
4+
ifdef CONFIG_SSL_USING_MBEDTLS
5+
COMPONENT_SRCDIRS := src
6+
COMPONENT_ADD_INCLUDEDIRS := include
7+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <esp_http_client.h>
18+
19+
#ifdef __cplusplus
20+
extern "C" {
21+
#endif
22+
23+
/**
24+
* @brief HTTPS OTA Firmware upgrade.
25+
*
26+
* This function performs HTTPS OTA Firmware upgrade
27+
*
28+
* @param[in] config pointer to esp_http_client_config_t structure.
29+
*
30+
* @note For secure HTTPS updates, the `cert_pem` member of `config`
31+
* structure must be set to the server certificate.
32+
*
33+
* @return
34+
* - ESP_OK: OTA data updated, next reboot will use specified partition.
35+
* - ESP_FAIL: For generic failure.
36+
* - ESP_ERR_OTA_VALIDATE_FAILED: Invalid app image
37+
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
38+
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
39+
* - For other return codes, refer OTA documentation in esp-idf's app_update component.
40+
*/
41+
esp_err_t esp_https_ota(const esp_http_client_config_t *config);
42+
43+
#ifdef __cplusplus
44+
}
45+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <stdio.h>
16+
#include <stdlib.h>
17+
#include <string.h>
18+
#include <esp_https_ota.h>
19+
#include <esp_ota_ops.h>
20+
#include <esp_log.h>
21+
22+
#define OTA_BUF_SIZE 256
23+
static const char *TAG = "esp_https_ota";
24+
25+
static void http_cleanup(esp_http_client_handle_t client)
26+
{
27+
esp_http_client_close(client);
28+
esp_http_client_cleanup(client);
29+
}
30+
31+
esp_err_t esp_https_ota(const esp_http_client_config_t *config)
32+
{
33+
if (!config) {
34+
ESP_LOGE(TAG, "esp_http_client config not found");
35+
return ESP_ERR_INVALID_ARG;
36+
}
37+
38+
if (!config->cert_pem) {
39+
ESP_LOGE(TAG, "Server certificate not found in esp_http_client config");
40+
return ESP_FAIL;
41+
}
42+
43+
esp_http_client_handle_t client = esp_http_client_init(config);
44+
if (client == NULL) {
45+
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
46+
return ESP_FAIL;
47+
}
48+
49+
if (esp_http_client_get_transport_type(client) != HTTP_TRANSPORT_OVER_SSL) {
50+
ESP_LOGE(TAG, "Transport is not over HTTPS");
51+
return ESP_FAIL;
52+
}
53+
54+
esp_err_t err = esp_http_client_open(client, 0);
55+
if (err != ESP_OK) {
56+
esp_http_client_cleanup(client);
57+
ESP_LOGE(TAG, "Failed to open HTTP connection: %d", err);
58+
return err;
59+
}
60+
esp_http_client_fetch_headers(client);
61+
62+
esp_ota_handle_t update_handle = 0;
63+
const esp_partition_t *update_partition = NULL;
64+
ESP_LOGI(TAG, "Starting OTA...");
65+
update_partition = esp_ota_get_next_update_partition(NULL);
66+
if (update_partition == NULL) {
67+
ESP_LOGE(TAG, "Passive OTA partition not found");
68+
http_cleanup(client);
69+
return ESP_FAIL;
70+
}
71+
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
72+
update_partition->subtype, update_partition->address);
73+
74+
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
75+
if (err != ESP_OK) {
76+
ESP_LOGE(TAG, "esp_ota_begin failed, error=%d", err);
77+
http_cleanup(client);
78+
return err;
79+
}
80+
ESP_LOGI(TAG, "esp_ota_begin succeeded");
81+
ESP_LOGI(TAG, "Please Wait. This may take time");
82+
83+
esp_err_t ota_write_err = ESP_OK;
84+
char *upgrade_data_buf = (char *)malloc(OTA_BUF_SIZE);
85+
if (!upgrade_data_buf) {
86+
ESP_LOGE(TAG, "Couldn't allocate memory to upgrade data buffer");
87+
return ESP_ERR_NO_MEM;
88+
}
89+
int binary_file_len = 0;
90+
while (1) {
91+
int data_read = esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE);
92+
if (data_read == 0) {
93+
ESP_LOGI(TAG, "Connection closed,all data received");
94+
break;
95+
}
96+
if (data_read < 0) {
97+
ESP_LOGE(TAG, "Error: SSL data read error");
98+
break;
99+
}
100+
if (data_read > 0) {
101+
ota_write_err = esp_ota_write( update_handle, (const void *)upgrade_data_buf, data_read);
102+
if (ota_write_err != ESP_OK) {
103+
break;
104+
}
105+
binary_file_len += data_read;
106+
ESP_LOGD(TAG, "Written image length %d", binary_file_len);
107+
}
108+
}
109+
free(upgrade_data_buf);
110+
http_cleanup(client);
111+
ESP_LOGD(TAG, "Total binary data length writen: %d", binary_file_len);
112+
113+
esp_err_t ota_end_err = esp_ota_end(update_handle);
114+
if (ota_write_err != ESP_OK) {
115+
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%d", err);
116+
return ota_write_err;
117+
} else if (ota_end_err != ESP_OK) {
118+
ESP_LOGE(TAG, "Error: esp_ota_end failed! err=0x%d. Image is invalid", ota_end_err);
119+
return ota_end_err;
120+
}
121+
122+
err = esp_ota_set_boot_partition(update_partition);
123+
if (err != ESP_OK) {
124+
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%d", err);
125+
return err;
126+
}
127+
ESP_LOGI(TAG, "esp_ota_set_boot_partition succeeded");
128+
129+
return ESP_OK;
130+
}

examples/system/ota/README.md renamed to examples/system/ota/native_ota/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Note: The firmware location information must be same as partition table file. **
8484

8585
The OTA_workflow.png diagram demonstrates the overall workflow:
8686

87-
![OTA Workflow diagram](OTA_workflow.png)
87+
![OTA Workflow diagram](../OTA_workflow.png)
8888

8989
## Step 1: Connect to AP
9090

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following lines of boilerplate have to be in your project's CMakeLists
2+
# in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.5)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(simple_ota)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
3+
# project subdirectory.
4+
#
5+
6+
PROJECT_NAME := simple_ota
7+
8+
include $(IDF_PATH)/make/project.mk
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# OTA Demo
2+
3+
## Introduction
4+
5+
Over The Air (OTA) updates can be performed in esp8266 in two ways:
6+
7+
- Using native APIs which are part of OTA component.
8+
- Using simplified APIs which are part of `esp_https_ota`. It is an abstraction layer over OTA APIs to perform updates using HTTPS.
9+
10+
Both these methods are demonstrated in OTA Demo under `native_ota_example` and `simple_ota_example` respectively.
11+
12+
---
13+
14+
## Aim
15+
16+
An app running on ESP8266 can upgrade itself by downloading a new app "image" binary file, and storing it in flash.
17+
18+
In this example, the ESP8266 has 3 images in flash: factory, OTA_0, OTA_1. Each of these is a self-contained partition. The number of OTA image partition is determined by the partition table layout.
19+
20+
Flashing the example over serial with "make flash" updates the factory app image. On first boot, the bootloader loads this factory app image which then performs an OTA update (triggered in the example code). The update downloads a new image from a HTTPS server and saves it into the OTA_0 partition. At this point the example code updates the ota_data partition to indicate the new app partition, and resets. The bootloader reads ota_data, determines the new OTA image has been selected, and runs it.
21+
22+
23+
## Workflow
24+
25+
The OTA_workflow.png diagram demonstrates the overall workflow:
26+
27+
![OTA Workflow diagram](../OTA_workflow.png)
28+
29+
### Step 1: Connect to AP
30+
31+
Connect your host PC to the same AP that you will use for the ESP8266.
32+
33+
### Step 2: Generate the OTA Binary
34+
For our upgrade example OTA file, we're going to use the `get-started/project_template` example.
35+
36+
Build the example:
37+
38+
```
39+
cd $IDF_PATH/examples/get-started/project_template
40+
make
41+
cd build
42+
```
43+
44+
Note: You've probably noticed there is nothing special about the "project_template" example when used for OTA updates. This is because any .bin app file which is built by ESP8266_RTOS_SDK can be used as an app image for OTA. The only difference is whether it is written to a factory partition or an OTA partition.
45+
46+
### Step 3: Run HTTPS Server
47+
48+
Open a new terminal and run these commands to start the HTTPS server:
49+
50+
Generate self-signed certificate and key:
51+
52+
*NOTE: `Common Name` of server certificate should be host-name of your server.*
53+
54+
```
55+
openssl req -x509 -newkey rsa:2048 -keyout ca_key.pem -out ca_cert.pem -days 365
56+
57+
```
58+
59+
Copy the certificate to `server_certs` directory inside OTA example directory:
60+
61+
```
62+
cp ca_cert.pem /path/to/ota/example/server_certs/
63+
```
64+
65+
66+
Start the HTTPS server:
67+
68+
```
69+
openssl s_server -WWW -key ca_key.pem -cert ca_cert.pem -port 8070
70+
```
71+
72+
Copy the generated binary(project_template.bin) into the folder in which the HTTPS server is running.
73+
If you have any firewall software running that will block incoming access to port 8070, configure it to allow access while running the example.
74+
75+
### Step 4: Build OTA Example
76+
77+
Change back to the OTA example directory, and type `make menuconfig` to configure the OTA example. Under the "Example Configuration" submenu, fill in the following details:
78+
79+
* WiFi SSID & Password
80+
* Firmware Upgrade URL. The URL will be look like this:
81+
82+
```
83+
https://<host-ip-address>:<host-port>/<firmware-image-filename>
84+
85+
for e.g,
86+
https://192.168.0.3:8070/project_template.bin
87+
```
88+
89+
Save your changes, and type `make` to build the example.
90+
91+
### Step 5: Flash OTA Example
92+
93+
When flashing, use the `make flash` to flash the factory image. This command will find if partition table has ota_data partition (as in our case) then ota_data will erase to initial.
94+
It allows to run the newly loaded app from a factory partition.
95+
96+
```
97+
make flash
98+
```
99+
100+
### Step 6: Run the OTA Example
101+
102+
When the example starts up, it will print "Starting OTA example..." then:
103+
104+
1. Connect to the AP with configured SSID and password.
105+
2. Connect to the HTTP server and download the new image.
106+
3. Write the image to flash, and configure the next boot from this image.
107+
4. Reboot
108+
109+
## Troubleshooting
110+
111+
### General connectivity issues
112+
113+
* Check your PC can ping the ESP8266 at its IP, and that the IP, AP and other configuration settings are correct in menuconfig.
114+
* Check if any firewall software is preventing incoming connections on the PC.
115+
* Check whether you can see the configured file (default project_template.bin), by checking the output of following command:
116+
117+
```
118+
curl -v https://<host-ip-address>:<host-port>/<firmware-image-filename>
119+
```
120+
121+
* If you have another PC or a phone, try viewing the file listing from the separate host.
122+
123+
### Error "ota_begin error err=0x104"
124+
125+
If you see this error then check that the configured (and actual) flash size is large enough for the partitions in the partition table. The default "two OTA slots" partition table only works with 4MB flash size. To use OTA with smaller flash sizes, create a custom partition table CSV (look in components/partition_table) and configure it in menuconfig.
126+
127+
If changing partition layout, it is usually wise to run "make erase_flash" between steps.
128+
129+
### Error "mbedtls error: 0x7200"
130+
131+
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN = 16384 is set in sdkconfig.defaults in this example and this value(16384) is required to comply fully with TLS standards.
132+
133+
You can set a lower value to save RAM if the other end of the connection supports Maximum Fragment Length Negotiation Extension (max_fragment_length, see RFC6066) or you know for certain that it will never send a message longer than a certain number of bytes.
134+
If the value is set too low, symptoms are a failed TLS handshake or a return value of MBEDTLS_ERR_SSL_INVALID_RECORD (-0x7200).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
set(COMPONENT_SRCS "simple_ota_example.c")
2+
set(COMPONENT_ADD_INCLUDEDIRS ".")
3+
4+
5+
# Embed the server root certificate into the final binary
6+
set(COMPONENT_EMBED_TXTFILES ${PROJECT_PATH}/server_certs/ca_cert.pem)
7+
8+
register_component()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
menu "Example Configuration"
2+
3+
config WIFI_SSID
4+
string "WiFi SSID"
5+
default "myssid"
6+
help
7+
SSID (network name) for the example to connect to.
8+
9+
config WIFI_PASSWORD
10+
string "WiFi Password"
11+
default "mypassword"
12+
help
13+
WiFi password (WPA or WPA2) for the example to use.
14+
15+
config FIRMWARE_UPGRADE_URL
16+
string "firmware upgrade url endpoint"
17+
default "https://192.168.0.3:8070/hello-world.bin"
18+
help
19+
URL of server which hosts the firmware
20+
image.
21+
endmenu
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#
2+
# "main" pseudo-component makefile.
3+
#
4+
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
5+
6+
COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/ca_cert.pem

0 commit comments

Comments
 (0)