feature/esp_https_ota: Changes to make esp_https_ota compatible with ESP8266.

Changes:
Changed component.mk to enable only mbedtls based compilation.
Removed esp_err_to_name occurence. Updated README.md.
Shifted OTA examples to examples/system/ota.
Changed the sdkconfig.defaults for the example compilation.
This commit is contained in:
Supreet Deshpande
2019-02-14 18:09:14 +05:30
parent 6a6e9cd54b
commit 43d230a0f1
22 changed files with 145 additions and 10 deletions

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(simple_ota)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := simple_ota
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,134 @@
# OTA Demo
## Introduction
Over The Air (OTA) updates can be performed in esp8266 in two ways:
- Using native APIs which are part of OTA component.
- Using simplified APIs which are part of `esp_https_ota`. It is an abstraction layer over OTA APIs to perform updates using HTTPS.
Both these methods are demonstrated in OTA Demo under `native_ota_example` and `simple_ota_example` respectively.
---
## Aim
An app running on ESP8266 can upgrade itself by downloading a new app "image" binary file, and storing it in flash.
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.
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.
## Workflow
The OTA_workflow.png diagram demonstrates the overall workflow:
![OTA Workflow diagram](../OTA_workflow.png)
### Step 1: Connect to AP
Connect your host PC to the same AP that you will use for the ESP8266.
### Step 2: Generate the OTA Binary
For our upgrade example OTA file, we're going to use the `get-started/project_template` example.
Build the example:
```
cd $IDF_PATH/examples/get-started/project_template
make
cd build
```
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.
### Step 3: Run HTTPS Server
Open a new terminal and run these commands to start the HTTPS server:
Generate self-signed certificate and key:
*NOTE: `Common Name` of server certificate should be host-name of your server.*
```
openssl req -x509 -newkey rsa:2048 -keyout ca_key.pem -out ca_cert.pem -days 365
```
Copy the certificate to `server_certs` directory inside OTA example directory:
```
cp ca_cert.pem /path/to/ota/example/server_certs/
```
Start the HTTPS server:
```
openssl s_server -WWW -key ca_key.pem -cert ca_cert.pem -port 8070
```
Copy the generated binary(project_template.bin) into the folder in which the HTTPS server is running.
If you have any firewall software running that will block incoming access to port 8070, configure it to allow access while running the example.
### Step 4: Build OTA Example
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:
* WiFi SSID & Password
* Firmware Upgrade URL. The URL will be look like this:
```
https://<host-ip-address>:<host-port>/<firmware-image-filename>
for e.g,
https://192.168.0.3:8070/project_template.bin
```
Save your changes, and type `make` to build the example.
### Step 5: Flash OTA Example
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.
It allows to run the newly loaded app from a factory partition.
```
make flash
```
### Step 6: Run the OTA Example
When the example starts up, it will print "Starting OTA example..." then:
1. Connect to the AP with configured SSID and password.
2. Connect to the HTTP server and download the new image.
3. Write the image to flash, and configure the next boot from this image.
4. Reboot
## Troubleshooting
### General connectivity issues
* Check your PC can ping the ESP8266 at its IP, and that the IP, AP and other configuration settings are correct in menuconfig.
* Check if any firewall software is preventing incoming connections on the PC.
* Check whether you can see the configured file (default project_template.bin), by checking the output of following command:
```
curl -v https://<host-ip-address>:<host-port>/<firmware-image-filename>
```
* If you have another PC or a phone, try viewing the file listing from the separate host.
### Error "ota_begin error err=0x104"
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.
If changing partition layout, it is usually wise to run "make erase_flash" between steps.
### Error "mbedtls error: 0x7200"
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.
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.
If the value is set too low, symptoms are a failed TLS handshake or a return value of MBEDTLS_ERR_SSL_INVALID_RECORD (-0x7200).

View File

@ -0,0 +1,8 @@
set(COMPONENT_SRCS "simple_ota_example.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
# Embed the server root certificate into the final binary
set(COMPONENT_EMBED_TXTFILES ${PROJECT_PATH}/server_certs/ca_cert.pem)
register_component()

View File

@ -0,0 +1,21 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config FIRMWARE_UPGRADE_URL
string "firmware upgrade url endpoint"
default "https://192.168.0.3:8070/hello-world.bin"
help
URL of server which hosts the firmware
image.
endmenu

View File

@ -0,0 +1,6 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/ca_cert.pem

View File

@ -0,0 +1,147 @@
/* OTA example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "nvs.h"
#include "nvs_flash.h"
static const char *TAG = "simple_ota_example";
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_WIFI_SSID,
.password = CONFIG_WIFI_PASSWORD,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
void simple_ota_example_task(void * pvParameter)
{
ESP_LOGI(TAG, "Starting OTA example...");
/* Wait for the callback to set the CONNECTED_BIT in the
event group.
*/
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");
esp_http_client_config_t config = {
.url = CONFIG_FIRMWARE_UPGRADE_URL,
.cert_pem = (char *)server_cert_pem_start,
.event_handler = _http_event_handler,
};
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK) {
esp_restart();
} else {
ESP_LOGE(TAG, "Firmware Upgrades Failed");
}
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main()
{
// Initialize NVS.
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
// OTA app partition table has a smaller NVS partition size than the non-OTA
// partition table. This size mismatch may cause NVS initialization to fail.
// If this happens, we erase NVS partition and initialize NVS again.
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
initialise_wifi();
xTaskCreate(&simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
}

View File

@ -0,0 +1,8 @@
# Default sdkconfig parameters to use the OTA
# partition table layout, with a 4MB flash size
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_TWO_OTA=y
CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y
CONFIG_SSL_USING_MBEDTLS=y
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=1024
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384