Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idf_build_apps.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ manifest_file = [
"ccomp_timer/.build-test-rules.yml",
"coremark/.build-test-rules.yml",
"esp_daylight/.build-test-rules.yml",
"esp_delta_ota/.build-test-rules.yml",
"esp_cli_commands/.build-test-rules.yml",
"esp_encrypted_img/.build-test-rules.yml",
"esp_flash_dispatcher/.build-test-rules.yml",
Expand Down
9 changes: 9 additions & 0 deletions esp_delta_ota/.build-test-rules.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
esp_delta_ota/examples:
enable:
- if: IDF_TARGET == "esp32"
reason: Delta OTA example currently only tested on ESP32

esp_delta_ota/test_apps:
enable:
- if: IDF_TARGET == "esp32"
reason: Delta OTA test app currently only tested on ESP32
4 changes: 2 additions & 2 deletions esp_delta_ota/examples/https_delta_ota/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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)
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
set(COMPONENTS main esp_eth)
project(https_delta_ota)
20 changes: 16 additions & 4 deletions esp_delta_ota/examples/https_delta_ota/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
EMBED_TXTFILES ca_cert.pem
PRIV_REQUIRES esp_http_client esp_partition nvs_flash app_update esp_timer esp_wifi console)
set(SRCS "main.c")
set(INCLUDE_DIRS ".")
set(EMBED_TXTFILES "tests/certs/servercert.pem")
set(PRIV_REQS "esp_http_client esp_partition nvs_flash app_update esp_timer esp_wifi console esp_eth esp_https_server")

if(CONFIG_EXAMPLE_ENABLE_CI_TEST)
list(APPEND SRCS
"tests/test_local_server_ota.c")
list(APPEND INCLUDE_DIRS "tests")
list(APPEND EMBED_TXTFILES "tests/certs/prvtkey.pem")
endif()

idf_component_register(SRCS ${SRCS}
INCLUDE_DIRS ${INCLUDE_DIRS}
EMBED_TXTFILES ${EMBED_TXTFILES}
PRIV_REQUIRES ${PRIV_REQS})
10 changes: 10 additions & 0 deletions esp_delta_ota/examples/https_delta_ota/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,14 @@ menu "Example Configuration"
help
Maximum time for reception

config EXAMPLE_FIRMWARE_UPG_URL_FROM_STDIN
bool
default y if EXAMPLE_FIRMWARE_UPG_URL = "FROM_STDIN"

config EXAMPLE_ENABLE_CI_TEST
bool "Enable the CI test code"
default n
help
This enables the CI test code i.e. https local server code.

endmenu
39 changes: 37 additions & 2 deletions esp_delta_ota/examples/https_delta_ota/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@
#define BUFFSIZE 1024
#define PATCH_HEADER_SIZE 64
#define DIGEST_SIZE 32
#define OTA_URL_SIZE 256
static uint32_t esp_delta_ota_magic = 0xfccdde10;

static const char *TAG = "https_delta_ota_example";

static char ota_write_data[BUFFSIZE + 1] = { 0 };
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");
extern const uint8_t server_cert_pem_start[] asm("_binary_servercert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_servercert_pem_end");

#ifdef CONFIG_EXAMPLE_ENABLE_CI_TEST
#include "test_local_server_ota.h"
#endif

const esp_partition_t *current_partition, *destination_partition;
static esp_ota_handle_t ota_handle;
Expand Down Expand Up @@ -156,6 +161,29 @@ static void ota_example_task(void *pvParameter)
config.skip_cert_common_name_check = true;
#endif

#ifdef CONFIG_EXAMPLE_FIRMWARE_UPG_URL_FROM_STDIN
if (strcmp(config.url, "FROM_STDIN") == 0) {
ESP_LOGI(TAG, "Reading OTA URL from stdin");
#ifdef CONFIG_EXAMPLE_ENABLE_CI_TEST
delta_ota_test_firmware_data_from_stdin(&config.url);
if (config.url == NULL) {
ESP_LOGE(TAG, "Failed to read URL from stdin");
abort();
}
#else
char url_buf[OTA_URL_SIZE];
example_configure_stdin_stdout();
fgets(url_buf, OTA_URL_SIZE, stdin);
int len = strlen(url_buf);
url_buf[len - 1] = '\0';
config.url = url_buf;
#endif
} else {
ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
abort();
}
#endif

esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
Expand Down Expand Up @@ -273,5 +301,12 @@ void app_main(void)
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());

#ifdef CONFIG_EXAMPLE_ENABLE_CI_TEST
/* Start the local HTTPS server for CI test */
ESP_ERROR_CHECK(delta_ota_test_start_webserver());
ESP_LOGI(TAG, "Local HTTPS server started for CI test");
#endif

xTaskCreate(&ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhxF/y7bygndxP
wiWLSwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQu
c32WukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2m
KRbQS5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO
2fEzYaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnv
L6Oz3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdO
AoaprFTRAgMBAAECggEAE0HCxV/N1Q1h+1OeDDGL5+74yjKSFKyb/vTVcaPCrmaH
fPvp0ddOvMZJ4FDMAsiQS6/n4gQ7EKKEnYmwTqj4eUYW8yxGUn3f0YbPHbZT+Mkj
z5woi3nMKi/MxCGDQZX4Ow3xUQlITUqibsfWcFHis8c4mTqdh4qj7xJzehD2PVYF
gNHZsvVj6MltjBDAVwV1IlGoHjuElm6vuzkfX7phxcA1B4ZqdYY17yCXUnvui46z
Xn2kUTOOUCEgfgvGa9E+l4OtdXi5IxjaSraU+dlg2KsE4TpCuN2MEVkeR5Ms3Y7Q
jgJl8vlNFJDQpbFukLcYwG7rO5N5dQ6WWfVia/5XgQKBgQD74at/bXAPrh9NxPmz
i1oqCHMDoM9sz8xIMZLF9YVu3Jf8ux4xVpRSnNy5RU1gl7ZXbpdgeIQ4v04zy5aw
8T4tu9K3XnR3UXOy25AK0q+cnnxZg3kFQm+PhtOCKEFjPHrgo2MUfnj+EDddod7N
JQr9q5rEFbqHupFPpWlqCa3QmQKBgQDldWUGokNaEpmgHDMnHxiibXV5LQhzf8Rq
gJIQXb7R9EsTSXEvsDyqTBb7PHp2Ko7rZ5YQfyf8OogGGjGElnPoU/a+Jij1gVFv
kZ064uXAAISBkwHdcuobqc5EbG3ceyH46F+FBFhqM8KcbxJxx08objmh58+83InN
P9Qr25Xw+QKBgEGXMHuMWgQbSZeM1aFFhoMvlBO7yogBTKb4Ecpu9wI5e3Kan3Al
pZYltuyf+VhP6XG3IMBEYdoNJyYhu+nzyEdMg8CwXg+8LC7FMis/Ve+o7aS5scgG
1to/N9DK/swCsdTRdzmc/ZDbVC+TuVsebFBGYZTyO5KgqLpezqaIQrTxAoGALFCU
10glO9MVyl9H3clap5v+MQ3qcOv/EhaMnw6L2N6WVT481tnxjW4ujgzrFcE4YuxZ
hgwYu9TOCmeqopGwBvGYWLbj+C4mfSahOAs0FfXDoYazuIIGBpuv03UhbpB1Si4O
rJDfRnuCnVWyOTkl54gKJ2OusinhjztBjcrV1XkCgYEA3qNi4uBsPdyz9BZGb/3G
rOMSw0CaT4pEMTLZqURmDP/0hxvTk1polP7O/FYwxVuJnBb6mzDa0xpLFPTpIAnJ
YXB8xpXU69QVh+EBbemdJWOd+zp5UCfXvb2shAeG3Tn/Dz4cBBMEUutbzP+or0nG
vSXnRLaxQhooWm+IuX9SuBQ=
-----END PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWDCCAkACCQCbF4+gVh/MLjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJJ
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
b20wHhcNMjEwNzEyMTIzNjI3WhcNNDEwNzA3MTIzNjI3WjBuMQswCQYDVQQGEwJJ
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhxF/y7bygndxPwiWL
SwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQuc32W
ukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2mKRbQ
S5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO2fEz
YaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnvL6Oz
3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdOAoap
rFTRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAItw24y565k3C/zENZlxyzto44ud
IYPQXN8Fa2pBlLe1zlSIyuaA/rWQ+i1daS8nPotkCbWZyf5N8DYaTE4B0OfvoUPk
B5uGDmbuk6akvlB5BGiYLfQjWHRsK9/4xjtIqN1H58yf3QNROuKsPAeywWS3Fn32
3//OpbWaClQePx6udRYMqAitKR+QxL7/BKZQsX+UyShuq8hjphvXvk0BW8ONzuw9
RcoORxM0FzySYjeQvm4LhzC/P3ZBhEq0xs55aL2a76SJhq5hJy7T/Xz6NFByvlrN
lFJJey33KFrAf5vnV9qcyWFIo7PYy2VsaaEjFeefr7q3sTFSMlJeadexW2Y=
-----END CERTIFICATE-----
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Delta OTA HTTPS example's test file
*
* 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 "esp_https_server.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_local_server_ota.h"
#include "protocol_examples_common.h"
#include "esp_partition.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define OTA_URL_SIZE 256
#define PARTITION_READ_BUFFER_SIZE 256
#define PARTITION_READ_SIZE PARTITION_READ_BUFFER_SIZE

static const char *TAG = "test_local_server_ota";
static size_t patch_size = 0;

#ifdef CONFIG_EXAMPLE_FIRMWARE_UPG_URL_FROM_STDIN
void delta_ota_test_firmware_data_from_stdin(const char **data)
{
char input_buf[OTA_URL_SIZE];
if (strcmp(*data, "FROM_STDIN") == 0) {
example_configure_stdin_stdout();
fflush(stdin);
char *url = NULL;
char *tokens[OTA_URL_SIZE];
char *saveptr;
int token_count = 0;

if (fgets(input_buf, OTA_URL_SIZE, stdin) == NULL) {
ESP_LOGE(TAG, "Failed to read URL from stdin");
abort();
}
int len = strlen(input_buf);
if (len == 0) {
ESP_LOGE(TAG, "Empty URL read from stdin");
abort();
}
if (input_buf[len - 1] == '\n') {
input_buf[len - 1] = '\0';
len--;
}
char *token = strtok_r(input_buf, " ", &saveptr);
if (token == NULL) {
ESP_LOGE(TAG, "No URL token found in input");
return;
}
// First token is the URL
url = token;
tokens[token_count++] = url;
// Process remaining tokens
while ((token = strtok_r(NULL, " ", &saveptr)) != NULL) {
tokens[token_count++] = token;
}
// Require patch_size to be provided (at least 2 tokens: URL and patch_size)
if (token_count < 2) {
ESP_LOGE(TAG, "Expected URL and patch_size, but only got %d token(s)", token_count);
return;
}
*data = strdup(tokens[0]);
// Assign the URL and additional data after the loop
if (token_count > 1) {
ESP_LOGI(TAG, "patch_size: %s\n", tokens[1]);
patch_size = atoi(tokens[1]); // Assuming the next token is the patch size
}
// Tokens are collected in the tokens array
} else {
ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
abort();
}
}
#endif

/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/octet-stream");

// Find the patch_data partition where pytest writes the patch
const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_ANY, "patch_data");

if (p == NULL) {
ESP_LOGE(TAG, "patch_data partition not found");
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Partition not found");
return ESP_FAIL;
}

if (patch_size == 0) {
ESP_LOGE(TAG, "Patch size is 0");
return ESP_FAIL;
}

int image_len = patch_size;
char buffer[PARTITION_READ_BUFFER_SIZE];
int size = PARTITION_READ_SIZE;
int offset = 0;

do {
/* Read file in chunks into the scratch buffer */
if (offset + size > image_len) {
size = image_len - offset;
}
if (size == 0) {
break;
}
esp_err_t ret = esp_partition_read(p, offset, buffer, size);
if (ret == ESP_OK) {
/* Send the buffer contents as HTTP response chunk */
if (httpd_resp_send_chunk(req, buffer, size) != ESP_OK) {
ESP_LOGE(TAG, "File sending failed!");
/* Abort sending file */
httpd_resp_sendstr_chunk(req, NULL);
/* Respond with 500 Internal Server Error */
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
return ESP_FAIL;
}
} else {
ESP_LOGE(TAG, "Partition read failed: %s", esp_err_to_name(ret));
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read partition");
return ESP_FAIL;
}
offset += size;

/* Keep looping till the whole file is sent */
} while (offset < image_len);

ESP_LOGI(TAG, "Patch file sending complete");

// Set headers
httpd_resp_set_hdr(req, "Accept-Ranges", "bytes");
httpd_resp_set_hdr(req, "Connection", "close");
httpd_resp_send_chunk(req, NULL, 0);

return ESP_OK;
}

static esp_err_t root_head_handler(httpd_req_t *req)
{
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_ANY, "patch_data");

if (partition == NULL) {
ESP_LOGE(TAG, "Partition not found");
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Partition not found");
return ESP_FAIL;
}

if (patch_size == 0) {
return ESP_FAIL;
}

// Get the size of the patch
httpd_resp_set_type(req, "application/octet-stream");
httpd_resp_set_hdr(req, "Accept-Ranges", "bytes");
httpd_resp_set_hdr(req, "Connection", "close");

// Complete HEAD response with no body
return httpd_resp_send(req, NULL, patch_size); // No body for HEAD method
}

static const httpd_uri_t get_root = {
.uri = "/patch.bin",
.method = HTTP_GET,
.handler = root_get_handler
};

static const httpd_uri_t head_root = {
.uri = "/patch.bin",
.method = HTTP_HEAD,
.handler = root_head_handler
};

esp_err_t delta_ota_test_start_webserver(void)
{
httpd_handle_t server = NULL;
// Start the httpd server
ESP_LOGI(TAG, "Starting HTTPS server for CI test");

httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();

extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");
extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end");
conf.servercert = servercert_start;
conf.servercert_len = servercert_end - servercert_start;

extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");
extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end");
conf.prvtkey_pem = prvtkey_pem_start;
conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;

esp_err_t ret = httpd_ssl_start(&server, &conf);
if (ESP_OK != ret) {
ESP_LOGE(TAG, "Error starting server!");
return ret;
}

// Set URI handlers
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &get_root);
httpd_register_uri_handler(server, &head_root);
return ESP_OK;
}
Loading
Loading