feature/esp_https_ota: Added the esp_https_ota component from idf.

This commit is contained in:
Supreet Deshpande
2019-02-14 16:34:03 +05:30
parent d5f14db6ca
commit 6a6e9cd54b
14 changed files with 393 additions and 0 deletions

BIN
components/esp_https_ota/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCS "src/esp_https_ota.c")
set(COMPONENT_REQUIRES esp_http_client)
set(COMPONENT_PRIV_REQUIRES log app_update)
register_component()

View File

@ -0,0 +1,3 @@
COMPONENT_SRCDIRS := src
COMPONENT_ADD_INCLUDEDIRS := include

View File

@ -0,0 +1,45 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <esp_http_client.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief HTTPS OTA Firmware upgrade.
*
* This function performs HTTPS OTA Firmware upgrade
*
* @param[in] config pointer to esp_http_client_config_t structure.
*
* @note For secure HTTPS updates, the `cert_pem` member of `config`
* structure must be set to the server certificate.
*
* @return
* - ESP_OK: OTA data updated, next reboot will use specified partition.
* - ESP_FAIL: For generic failure.
* - ESP_ERR_OTA_VALIDATE_FAILED: Invalid app image
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
* - For other return codes, refer OTA documentation in esp-idf's app_update component.
*/
esp_err_t esp_https_ota(const esp_http_client_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,130 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <esp_https_ota.h>
#include <esp_ota_ops.h>
#include <esp_log.h>
#define OTA_BUF_SIZE 256
static const char *TAG = "esp_https_ota";
static void http_cleanup(esp_http_client_handle_t client)
{
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
esp_err_t esp_https_ota(const esp_http_client_config_t *config)
{
if (!config) {
ESP_LOGE(TAG, "esp_http_client config not found");
return ESP_ERR_INVALID_ARG;
}
if (!config->cert_pem) {
ESP_LOGE(TAG, "Server certificate not found in esp_http_client config");
return ESP_FAIL;
}
esp_http_client_handle_t client = esp_http_client_init(config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
return ESP_FAIL;
}
if (esp_http_client_get_transport_type(client) != HTTP_TRANSPORT_OVER_SSL) {
ESP_LOGE(TAG, "Transport is not over HTTPS");
return ESP_FAIL;
}
esp_err_t err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
esp_http_client_cleanup(client);
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
return err;
}
esp_http_client_fetch_headers(client);
esp_ota_handle_t update_handle = 0;
const esp_partition_t *update_partition = NULL;
ESP_LOGI(TAG, "Starting OTA...");
update_partition = esp_ota_get_next_update_partition(NULL);
if (update_partition == NULL) {
ESP_LOGE(TAG, "Passive OTA partition not found");
http_cleanup(client);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
update_partition->subtype, update_partition->address);
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed, error=%d", err);
http_cleanup(client);
return err;
}
ESP_LOGI(TAG, "esp_ota_begin succeeded");
ESP_LOGI(TAG, "Please Wait. This may take time");
esp_err_t ota_write_err = ESP_OK;
char *upgrade_data_buf = (char *)malloc(OTA_BUF_SIZE);
if (!upgrade_data_buf) {
ESP_LOGE(TAG, "Couldn't allocate memory to upgrade data buffer");
return ESP_ERR_NO_MEM;
}
int binary_file_len = 0;
while (1) {
int data_read = esp_http_client_read(client, upgrade_data_buf, OTA_BUF_SIZE);
if (data_read == 0) {
ESP_LOGI(TAG, "Connection closed,all data received");
break;
}
if (data_read < 0) {
ESP_LOGE(TAG, "Error: SSL data read error");
break;
}
if (data_read > 0) {
ota_write_err = esp_ota_write( update_handle, (const void *)upgrade_data_buf, data_read);
if (ota_write_err != ESP_OK) {
break;
}
binary_file_len += data_read;
ESP_LOGD(TAG, "Written image length %d", binary_file_len);
}
}
free(upgrade_data_buf);
http_cleanup(client);
ESP_LOGD(TAG, "Total binary data length writen: %d", binary_file_len);
esp_err_t ota_end_err = esp_ota_end(update_handle);
if (ota_write_err != ESP_OK) {
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%d", err);
return ota_write_err;
} else if (ota_end_err != ESP_OK) {
ESP_LOGE(TAG, "Error: esp_ota_end failed! err=0x%d. Image is invalid", ota_end_err);
return ota_end_err;
}
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%d", err);
return err;
}
ESP_LOGI(TAG, "esp_ota_set_boot_partition succeeded");
return ESP_OK;
}

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,7 @@
# Simple OTA example
This example is based on `esp_https_ota` component's APIs.
## Configuration
Refer README.md in the parent directory for setup details

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,4 @@
# 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