mirror of
https://github.com/espressif/esp-rainmaker.git
synced 2026-01-15 06:18:36 +00:00
esp_rmaker_ota: Added OTA Updates over MQTT
The regular OTA upgrades with ESP RainMaker required an additional https connection to fetch the OTA image, which has a significant impact on heap usage. With MQTT OTA, the same MQTT channel used for rest of the RainMaker communication is used to fetch the OTA image, thereby saving on RAM. This could be slightly slower and may odd some cost. but the overall impact would be low enough when compared against the advantages. Signed-off-by: sanket.wadekar <sanket.wadekar@espressif.com> Co-authored-by: Piyush Shah <piyush.shah@espressif.com>
This commit is contained in:
committed by
Piyush Shah
parent
e6a3741ee0
commit
cf49ba4219
102
.github/workflows/auto-tag.yml
vendored
Normal file
102
.github/workflows/auto-tag.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
name: Auto Tag from Component Version
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'components/esp_rainmaker/idf_component.yml'
|
||||
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write # Required to create tags
|
||||
|
||||
jobs:
|
||||
auto-tag:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Get full history for tagging
|
||||
|
||||
- name: Extract version from component file
|
||||
id: get_version
|
||||
run: |
|
||||
# Extract version from YAML file using grep and sed
|
||||
VERSION=$(grep '^version:' components/esp_rainmaker/idf_component.yml | sed 's/version: *"\(.*\)"/\1/')
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Error: Could not extract version from idf_component.yml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate version format (basic semver check)
|
||||
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$'; then
|
||||
echo "Error: Invalid version format: $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Extracted version: $VERSION"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag_name=v$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if tag already exists
|
||||
id: check_tag
|
||||
run: |
|
||||
TAG_NAME="v${{ steps.get_version.outputs.version }}"
|
||||
|
||||
# Check if tag exists locally
|
||||
if git tag -l | grep -q "^$TAG_NAME$"; then
|
||||
echo "Tag $TAG_NAME already exists locally"
|
||||
echo "tag_exists=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if tag exists on remote
|
||||
if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME$"; then
|
||||
echo "Tag $TAG_NAME already exists on remote"
|
||||
echo "tag_exists=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Tag $TAG_NAME does not exist"
|
||||
echo "tag_exists=false" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create and push tag
|
||||
if: steps.check_tag.outputs.tag_exists == 'false'
|
||||
run: |
|
||||
TAG_NAME="v${{ steps.get_version.outputs.version }}"
|
||||
|
||||
# Configure git user (use GitHub Actions bot)
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
# Create annotated tag
|
||||
git tag -a "$TAG_NAME" -m "Release $TAG_NAME - Auto-tagged from idf_component.yml"
|
||||
|
||||
# Push tag to origin
|
||||
git push origin "$TAG_NAME"
|
||||
|
||||
echo "✅ Successfully created and pushed tag: $TAG_NAME"
|
||||
|
||||
- name: Tag already exists
|
||||
if: steps.check_tag.outputs.tag_exists == 'true'
|
||||
run: |
|
||||
TAG_NAME="v${{ steps.get_version.outputs.version }}"
|
||||
echo "ℹ️ Tag $TAG_NAME already exists. Skipping tag creation."
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "## Auto-Tag Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Component:** esp_rainmaker" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Version:** ${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Tag:** ${{ steps.get_version.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ steps.check_tag.outputs.tag_exists }}" == "true" ]; then
|
||||
echo "- **Status:** ⚠️ Tag already exists" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- **Status:** ✅ Tag created successfully" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -1,5 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## 1.6.4
|
||||
|
||||
## New feature
|
||||
|
||||
- Support for OTA over MQTT: The regular OTA upgrades with ESP RainMaker required an
|
||||
additional https connection to fetch the OTA image, which has a significant impact on
|
||||
heap usage. With MQTT OTA, the same MQTT channel used for rest of the RainMaker
|
||||
communication is used to fetch the OTA image, thereby saving on RAM.
|
||||
This could be slightly slower and may odd some cost. but the overall
|
||||
impact would be low enough when compared against the advantages. This can be enabled by setting
|
||||
`CONFIG_ESP_RMAKER_OTA_USE_MQTT` to `y` in the menuconfig.
|
||||
(`idf.py menuconfig -> ESP RainMaker Config -> ESP RainMaker OTA Config -> OTA Update Protocol Type -> MQTT`)
|
||||
|
||||
## Bugfix
|
||||
|
||||
- Fix a bug where the OTA fetch was not working when `CONFIG_ESP_RMAKER_OTA_AUTOFETCH` was enabled.
|
||||
|
||||
|
||||
## 1.6.3
|
||||
|
||||
## Bugfix
|
||||
|
||||
- Duplicate otafetch after rebooting into new firmware after an OTA was causing a crash.
|
||||
This was seen when both, CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE and CONFIG_ESP_RMAKER_OTA_AUTOFETCH
|
||||
are enabled.
|
||||
|
||||
## 1.6.2
|
||||
|
||||
## Minor changes
|
||||
|
||||
- AVoid execution of `esp_rmaker_params_mqtt_init()` from esp-idf event handler
|
||||
|
||||
## 1.6.1
|
||||
|
||||
## Minor changes
|
||||
|
||||
- Make cmd response payload publish api (`esp_rmaker_cmd_response_publish()`) public
|
||||
|
||||
## 1.6.0
|
||||
|
||||
### Enhancements
|
||||
@@ -9,4 +47,4 @@
|
||||
- Add retry logic if otafetch fails
|
||||
- Add OTA retry on failure mechanism
|
||||
- Try OTA multiple times (as per `CONFIG_ESP_RMAKER_OTA_MAX_RETRIES`, set to 3 by default) if it fails
|
||||
- Schedule an OTA fetch as per `CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES` if all retries fail
|
||||
- Schedule an OTA fetch as per `CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES` if all retries fail
|
||||
|
||||
@@ -20,6 +20,11 @@ set(priv_req protobuf-c json_parser json_generator
|
||||
nvs_flash esp_http_client app_update esp-tls mbedtls esp_https_ota
|
||||
console esp_local_ctrl esp_https_server mdns esp_schedule efuse driver rmaker_common wifi_provisioning)
|
||||
|
||||
# Add CBOR for MQTT OTA support
|
||||
if (CONFIG_ESP_RMAKER_OTA_USE_MQTT)
|
||||
list(APPEND priv_req cbor)
|
||||
endif()
|
||||
|
||||
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND priv_req esp_app_format)
|
||||
endif()
|
||||
@@ -57,6 +62,12 @@ set(ota_srcs "src/ota/esp_rmaker_ota.c"
|
||||
"src/ota/esp_rmaker_ota_using_params.c"
|
||||
"src/ota/esp_rmaker_ota_using_topics.c")
|
||||
set(ota_priv_includes "src/ota")
|
||||
if (CONFIG_ESP_RMAKER_OTA_USE_MQTT)
|
||||
list(APPEND ota_srcs "src/ota/esp_rmaker_mqtt_ota.c")
|
||||
endif()
|
||||
if (CONFIG_ESP_RMAKER_OTA_USE_HTTPS)
|
||||
list(APPEND ota_srcs "src/ota/esp_rmaker_https_ota.c")
|
||||
endif()
|
||||
|
||||
# Thread BR
|
||||
set(thread_br_srcs )
|
||||
|
||||
@@ -308,15 +308,6 @@ menu "ESP RainMaker Config"
|
||||
help
|
||||
This allows you to skip the project name check.
|
||||
|
||||
config ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
int "OTA HTTP receive buffer size"
|
||||
default 1024
|
||||
range 512 LWIP_TCP_WND_DEFAULT
|
||||
help
|
||||
Increasing this value beyond the default would speed up the OTA download process.
|
||||
However, please ensure that your application has enough memory headroom to allow this,
|
||||
else, the OTA may fail.
|
||||
|
||||
config ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||
int "OTA Rollback Wait Period (Seconds)"
|
||||
default 90
|
||||
@@ -374,6 +365,54 @@ menu "ESP RainMaker Config"
|
||||
help
|
||||
Delay (in minutes) before re-fetching OTA details after all retry attempts fail (for OTA using topics).
|
||||
|
||||
choice ESP_RMAKER_OTA_TYPE
|
||||
prompt "OTA Update Protocol Type"
|
||||
default ESP_RMAKER_OTA_USE_HTTPS
|
||||
help
|
||||
Protocol to be used for OTA Update
|
||||
config ESP_RMAKER_OTA_USE_HTTPS
|
||||
bool "HTTPS"
|
||||
config ESP_RMAKER_OTA_USE_MQTT
|
||||
bool "MQTT"
|
||||
endchoice
|
||||
|
||||
config ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
int "OTA HTTP receive buffer size"
|
||||
default 1024
|
||||
range 512 LWIP_TCP_WND_DEFAULT
|
||||
depends on ESP_RMAKER_OTA_USE_HTTPS
|
||||
help
|
||||
Increasing this value beyond the default would speed up the OTA download process.
|
||||
However, please ensure that your application has enough memory headroom to allow this,
|
||||
else, the OTA may fail.
|
||||
|
||||
config ESP_RMAKER_MQTT_OTA_BLOCK_SIZE
|
||||
int "MQTT OTA Block Length"
|
||||
default 3072
|
||||
depends on ESP_RMAKER_OTA_USE_MQTT
|
||||
range 256 131072
|
||||
help
|
||||
The block size to fetch in a MQTT OTA publish message.
|
||||
Note that number of blocks * block size should not exceed 128KB.
|
||||
|
||||
config ESP_RMAKER_MQTT_OTA_NO_OF_BLOCKS
|
||||
int "MQTT OTA No. of Blocks"
|
||||
default 42
|
||||
depends on ESP_RMAKER_OTA_USE_MQTT
|
||||
range 1 512
|
||||
help
|
||||
The number of blocks of file to fetch in a MQTT OTA publish message.
|
||||
Note that number of blocks * block size should not exceed 128KB.
|
||||
|
||||
config ESP_RMAKER_MQTT_OTA_MAX_RETRIES
|
||||
int "MQTT OTA Maximum Number of Retries"
|
||||
default 3
|
||||
depends on ESP_RMAKER_OTA_USE_MQTT
|
||||
range 1 255
|
||||
help
|
||||
The number of times we should resend request for fetching file blocks, in case we do not get any response.
|
||||
|
||||
|
||||
endmenu
|
||||
|
||||
menu "ESP RainMaker Scheduling"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
version: "1.6.3"
|
||||
version: "1.6.4"
|
||||
description: ESP RainMaker firmware agent
|
||||
url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_rainmaker
|
||||
repository: https://github.com/espressif/esp-rainmaker.git
|
||||
@@ -32,3 +32,5 @@ dependencies:
|
||||
version: "^1.2.0"
|
||||
rules:
|
||||
- if: "idf_version >= 5.1"
|
||||
espressif/cbor:
|
||||
version: "~0.6"
|
||||
|
||||
@@ -70,6 +70,8 @@ typedef void *esp_rmaker_ota_handle_t;
|
||||
typedef struct {
|
||||
/** The OTA URL received from ESP RainMaker Cloud */
|
||||
char *url;
|
||||
/** The Stream ID received from ESP RainMaker Cloud. This will be used only in MQTT based OTA*/
|
||||
char *stream_id;
|
||||
/** Size of the OTA File. Can be 0 if the file size isn't received from
|
||||
* the ESP RainMaker Cloud */
|
||||
int filesize;
|
||||
@@ -177,6 +179,32 @@ typedef struct {
|
||||
void *priv;
|
||||
} esp_rmaker_ota_config_t;
|
||||
|
||||
/** HTTPS OTA Callback
|
||||
*
|
||||
* This callback forces the use of HTTPS protocol for OTA updates.
|
||||
* Use this in your ota_config.ota_cb to always use HTTPS.
|
||||
*
|
||||
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||
* @param[in] ota_data The data to be used for the OTA
|
||||
*
|
||||
* @return ESP_OK if the OTA was successful
|
||||
* @return ESP_FAIL if the OTA failed
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_https_cb(esp_rmaker_ota_handle_t handle, esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
/** MQTT OTA Callback
|
||||
*
|
||||
* This callback forces the use of MQTT protocol for OTA updates.
|
||||
* Use this in your ota_config.ota_cb to always use MQTT.
|
||||
*
|
||||
* @param[in] handle An OTA handle assigned by the ESP RainMaker Core
|
||||
* @param[in] ota_data The data to be used for the OTA
|
||||
*
|
||||
* @return ESP_OK if the OTA was successful
|
||||
* @return ESP_FAIL if the OTA failed
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_mqtt_cb(esp_rmaker_ota_handle_t handle, esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
/** Enable OTA
|
||||
*
|
||||
* Calling this API enables OTA as per the ESP RainMaker specification.
|
||||
|
||||
232
components/esp_rainmaker/src/ota/esp_rmaker_https_ota.c
Normal file
232
components/esp_rainmaker/src/ota/esp_rmaker_https_ota.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_wifi_types.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <errno.h>
|
||||
#include <esp_https_ota.h>
|
||||
|
||||
#if CONFIG_BT_ENABLED
|
||||
#include <esp_bt.h>
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||
#include <esp_crt_bundle.h>
|
||||
#endif
|
||||
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <esp_rmaker_common_events.h>
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_ota_internal.h"
|
||||
#include "esp_rmaker_https_ota.h"
|
||||
|
||||
static const char *TAG = "esp_rmaker_https_ota";
|
||||
|
||||
/* HTTPS-specific certificate definition */
|
||||
extern const char esp_rmaker_ota_def_cert[] asm("_binary_rmaker_ota_server_crt_start");
|
||||
const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT = esp_rmaker_ota_def_cert;
|
||||
|
||||
#define DEF_HTTP_TX_BUFFER_SIZE 1024
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
#define DEF_HTTP_RX_BUFFER_SIZE CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
#else
|
||||
#define DEF_HTTP_RX_BUFFER_SIZE 1024
|
||||
#endif
|
||||
|
||||
/* Local macro for OTA max retries, can be overridden for development */
|
||||
#ifndef ESP_RMAKER_OTA_MAX_RETRIES
|
||||
#define ESP_RMAKER_OTA_MAX_RETRIES CONFIG_ESP_RMAKER_OTA_MAX_RETRIES
|
||||
#endif
|
||||
|
||||
/* Local macro for OTA retry delay in seconds, can be overridden for development */
|
||||
#ifndef ESP_RMAKER_OTA_RETRY_DELAY_SECONDS
|
||||
#define ESP_RMAKER_OTA_RETRY_DELAY_SECONDS (CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES * 60)
|
||||
#endif
|
||||
|
||||
#define ESP_RMAKER_HTTPS_OTA_TIMEOUT_MS 5000
|
||||
|
||||
static esp_err_t esp_rmaker_ota_use_https(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data, char *err_desc, size_t err_desc_size)
|
||||
{
|
||||
int buffer_size_tx = DEF_HTTP_TX_BUFFER_SIZE;
|
||||
/* In case received url is longer, we will increase the tx buffer size
|
||||
* to accomodate the longer url and other headers.
|
||||
*/
|
||||
if (strlen(ota_data->url) > buffer_size_tx) {
|
||||
buffer_size_tx = strlen(ota_data->url) + 128;
|
||||
}
|
||||
|
||||
if (ota_data->filesize) {
|
||||
ESP_LOGD(TAG, "Received file size: %d", ota_data->filesize);
|
||||
}
|
||||
|
||||
esp_err_t ota_finish_err = ESP_OK;
|
||||
esp_http_client_config_t config = {
|
||||
.timeout_ms = ESP_RMAKER_HTTPS_OTA_TIMEOUT_MS,
|
||||
.url = ota_data->url,
|
||||
#ifdef CONFIG_ESP_RMAKER_USE_CERT_BUNDLE
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.cert_pem = ota_data->server_cert ? ota_data->server_cert : ESP_RMAKER_OTA_DEFAULT_SERVER_CERT,
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||
.skip_cert_common_name_check = true,
|
||||
#endif
|
||||
.buffer_size = DEF_HTTP_RX_BUFFER_SIZE,
|
||||
.buffer_size_tx = buffer_size_tx,
|
||||
.keep_alive_enable = true
|
||||
};
|
||||
|
||||
esp_https_ota_config_t ota_config = {
|
||||
.http_config = &config,
|
||||
};
|
||||
/* Using a warning just to highlight the message */
|
||||
ESP_LOGW(TAG, "Starting OTA. This may take time.");
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA Begin failed: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Get the current Wi-Fi power save type. In case OTA fails and we need this
|
||||
* to restore power saving.
|
||||
*/
|
||||
wifi_ps_type_t ps_type;
|
||||
esp_wifi_get_ps(&ps_type);
|
||||
/* Disable Wi-Fi power save to speed up OTA, iff BT is controller is idle/disabled.
|
||||
* Co-ex requirement, device panics otherwise.*/
|
||||
#if CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
esp_app_desc_t app_desc;
|
||||
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "Failed to read image description: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
/* OTA failed, may retry later */
|
||||
goto ota_end;
|
||||
}
|
||||
err = validate_image_header(ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
snprintf(err_desc, err_desc_size, "Image header verification failed");
|
||||
/* OTA should be rejected, returning ESP_ERR_INVALID_STATE */
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
/* Report status: Downloading Firmware Image */
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Downloading Firmware Image");
|
||||
|
||||
int count = 0;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT
|
||||
int last_ota_progress = 0;
|
||||
#endif
|
||||
while (1) {
|
||||
err = esp_https_ota_perform(https_ota_handle);
|
||||
if (err == ESP_ERR_INVALID_VERSION) {
|
||||
snprintf(err_desc, err_desc_size, "Chip revision mismatch");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, err_desc);
|
||||
/* OTA should be rejected, returning ESP_ERR_INVALID_STATE */
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto ota_end;
|
||||
}
|
||||
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
/* esp_https_ota_perform returns after every read operation which gives user the ability to
|
||||
* monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
|
||||
* data read so far.
|
||||
* We are using a counter just to reduce the number of prints
|
||||
*/
|
||||
count++;
|
||||
if (count == 50) {
|
||||
ESP_LOGI(TAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
|
||||
count = 0;
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT
|
||||
int image_size = esp_https_ota_get_image_size(https_ota_handle);
|
||||
int read_size = esp_https_ota_get_image_len_read(https_ota_handle);
|
||||
int ota_progress = 100 * read_size / image_size; // The unit is %
|
||||
/* When ota_progress is 0 or 100, we will not report the progress, beacasue the 0 and 100 is reported by additional_info `Downloading Firmware Image` and
|
||||
* `Firmware Image download complete`. And every progress will only report once and the progress is increasing.
|
||||
*/
|
||||
if (((ota_progress != 0) && (ota_progress != 100)) && (ota_progress % CONFIG_ESP_RMAKER_OTA_PROGRESS_INTERVAL == 0) && (last_ota_progress < ota_progress)) {
|
||||
last_ota_progress = ota_progress;
|
||||
char description[40] = {0};
|
||||
snprintf(description, sizeof(description), "Downloaded %d%% Firmware Image", ota_progress);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, description);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA failed: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
/* OTA failed, may retry later */
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||
snprintf(err_desc, err_desc_size, "Complete data was not received");
|
||||
/* OTA failed, may retry later */
|
||||
err = ESP_FAIL;
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
/* Report completion before finishing */
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Firmware Image download complete");
|
||||
|
||||
ota_end:
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(ps_type);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(ps_type);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
if (err == ESP_OK) {
|
||||
/* Success path: finish the OTA */
|
||||
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||
if (ota_finish_err == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
snprintf(err_desc, err_desc_size, "Image validation failed");
|
||||
} else {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA finish failed: %s (errno=%d: %s)", esp_err_to_name(ota_finish_err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
}
|
||||
/* Handle already closed by esp_https_ota_finish(), don't call abort */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Error path: abort the OTA */
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
return (err == ESP_ERR_INVALID_STATE) ? ESP_ERR_INVALID_STATE : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_https_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
if (!ota_data->url) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Use the common OTA workflow with HTTPS-specific function */
|
||||
return esp_rmaker_ota_start_workflow(ota_handle, ota_data, esp_rmaker_ota_use_https, "HTTPS");
|
||||
}
|
||||
35
components/esp_rainmaker/src/ota/esp_rmaker_https_ota.h
Normal file
35
components/esp_rainmaker/src/ota/esp_rmaker_https_ota.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_HTTPS
|
||||
|
||||
/**
|
||||
* @brief HTTPS OTA callback function
|
||||
*
|
||||
* This function handles HTTPS OTA update with retry logic and status reporting.
|
||||
*
|
||||
* @param[in] ota_handle The OTA handle
|
||||
* @param[in] ota_data The OTA data containing URL and other information
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_FAIL on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_https_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_USE_HTTPS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1102
components/esp_rainmaker/src/ota/esp_rmaker_mqtt_ota.c
Normal file
1102
components/esp_rainmaker/src/ota/esp_rmaker_mqtt_ota.c
Normal file
File diff suppressed because it is too large
Load Diff
154
components/esp_rainmaker/src/ota/esp_rmaker_mqtt_ota.h
Normal file
154
components/esp_rainmaker/src/ota/esp_rmaker_mqtt_ota.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
typedef enum {
|
||||
FILE_BLOCK_FETCHED = 1,
|
||||
FILE_BLOCK_DUPLICATE = 2,
|
||||
FILE_FETCH_COMPLETE = 4,
|
||||
FILE_BLOCK_FETCH_ERR = 8,
|
||||
} esp_rmaker_mqtt_events;
|
||||
|
||||
typedef enum {
|
||||
ESP_MQTT_OTA_INVALID_STATE = -1,
|
||||
ESP_MQTT_OTA_INIT,
|
||||
ESP_MQTT_OTA_BEGIN,
|
||||
ESP_MQTT_OTA_IN_PROGRESS,
|
||||
ESP_MQTT_OTA_SUCCESS,
|
||||
ESP_MQTT_OTA_FAILED,
|
||||
} esp_rmaker_mqtt_ota_state;
|
||||
|
||||
typedef struct {
|
||||
char *stream_id;
|
||||
uint32_t filesize;
|
||||
uint32_t block_length;
|
||||
uint16_t blocks_per_request;
|
||||
uint8_t max_retry_count;
|
||||
} esp_rmaker_mqtt_ota_config_t;
|
||||
|
||||
typedef void *esp_rmaker_mqtt_ota_handle_t;
|
||||
|
||||
/** Begin MQTT OTA Update
|
||||
*
|
||||
* This API allocates memory to the buffer and initializes the initial OTA state.
|
||||
*
|
||||
* @param[in] config MQTT OTA Configuration
|
||||
* @param[out] handle MQTT OTA handle required by other MQTT OTA APIs
|
||||
*
|
||||
* @return ESP_OK if the MQTT OTA handle is created.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_begin(esp_rmaker_mqtt_ota_config_t *config, esp_rmaker_mqtt_ota_handle_t *handle);
|
||||
|
||||
/** Set progress callback for MQTT OTA Update
|
||||
*
|
||||
* This API sets a callback function that will be called during OTA progress
|
||||
* to report the number of bytes downloaded and total file size.
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
* @param[in] progress_cb Progress callback function
|
||||
* @param[in] priv Private data passed to callback function
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_set_progress_cb(esp_rmaker_mqtt_ota_handle_t mqtt_ota_handle, void (*progress_cb)(int bytes_read, int total_bytes, void *priv), void *priv);
|
||||
|
||||
/** Perform MQTT OTA Update file fetch operations.
|
||||
*
|
||||
* This API will fetch blocks of the OTA image as specified in the configuration.
|
||||
* This API should be called repeatedly until all blocks are fetched or a failure occurs.
|
||||
*
|
||||
* @param[in] mqtt_handle MQTT OTA handle
|
||||
*
|
||||
* @return ESP_OK if the file fetch request was successful.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_perform(esp_rmaker_mqtt_ota_handle_t mqtt_handle);
|
||||
|
||||
/** Free the memory resources allocated for MQTT OTA and update the device boot partition.
|
||||
*
|
||||
* This API should be called after the OTA image was fetched successfully.
|
||||
*
|
||||
* @param[in] mqtt_handle MQTT OTA handle
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_finish(esp_rmaker_mqtt_ota_handle_t mqtt_handle);
|
||||
|
||||
/** Free the memory resources allocated for MQTT OTA.
|
||||
*
|
||||
* This API should be called to cancel the OTA update.
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_abort(esp_rmaker_mqtt_ota_handle_t mqtt_handle);
|
||||
|
||||
/** Get current OTA state
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
*
|
||||
* @return state of MQTT OTA
|
||||
* @return ESP_MQTT_OTA_INVALID_STATE if mqtt_ota_handle is NULL
|
||||
*/
|
||||
esp_rmaker_mqtt_ota_state esp_mqtt_ota_get_state(esp_rmaker_mqtt_ota_handle_t mqtt_ota_handle);
|
||||
|
||||
/** Get downloaded length of OTA image
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
*
|
||||
* @return length of downloaded image
|
||||
*/
|
||||
int esp_mqtt_ota_get_image_len_read(esp_rmaker_mqtt_ota_handle_t mqtt_ota_handle);
|
||||
|
||||
/** Find whether OTA image download is complete.
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
*
|
||||
* @return true if complete OTA image is downloaded; false otherwise.
|
||||
*/
|
||||
bool esp_mqtt_ota_is_complete_data_received(esp_rmaker_mqtt_ota_handle_t mqtt_ota_handle);
|
||||
|
||||
/** Read the OTA image header and retrieve its app description.
|
||||
*
|
||||
* @param[in] mqtt_ota_handle MQTT OTA handle
|
||||
* @param[out] new_app_info Image description
|
||||
*
|
||||
* @return ESP_OK if the image description was fetched successfully.
|
||||
* @return error on failure
|
||||
*/
|
||||
esp_err_t esp_mqtt_ota_get_img_desc(esp_rmaker_mqtt_ota_handle_t mqtt_ota_handle, esp_app_desc_t *new_app_info);
|
||||
|
||||
/**
|
||||
* @brief MQTT OTA callback function
|
||||
*
|
||||
* This function handles MQTT OTA update with retry logic and status reporting.
|
||||
* It orchestrates the entire MQTT OTA process from start to finish.
|
||||
*
|
||||
* @param[in] ota_handle The OTA handle
|
||||
* @param[in] ota_data The OTA data containing stream_id, filesize and other information
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return ESP_FAIL on failure
|
||||
*/
|
||||
esp_err_t esp_rmaker_ota_mqtt_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,16 +1,8 @@
|
||||
// Copyright 2020 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
@@ -21,11 +13,16 @@
|
||||
#include <esp_log.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_https_ota.h>
|
||||
#include <esp_wifi_types.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <nvs.h>
|
||||
#include <json_parser.h>
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_HTTPS
|
||||
#include "esp_rmaker_https_ota.h"
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
#include "esp_rmaker_mqtt_ota.h"
|
||||
#endif
|
||||
#if CONFIG_BT_ENABLED
|
||||
#include <esp_bt.h>
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
@@ -37,6 +34,11 @@
|
||||
#include "esp_rmaker_internal.h"
|
||||
#include "esp_rmaker_ota_internal.h"
|
||||
|
||||
/* Forward declarations for static functions */
|
||||
static esp_err_t esp_rmaker_ota_handle_metadata_common(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data);
|
||||
static esp_err_t esp_rmaker_ota_success_reboot_sequence(esp_rmaker_ota_handle_t ota_handle, const char *protocol_name, int attempt_count);
|
||||
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// Features supported in 4.4+
|
||||
|
||||
@@ -54,24 +56,26 @@
|
||||
#endif /* !IDF4.4 */
|
||||
static const char *TAG = "esp_rmaker_ota";
|
||||
|
||||
/* OTA reboot timer and NVS constants */
|
||||
#define OTA_REBOOT_TIMER_SEC 10
|
||||
#define DEF_HTTP_TX_BUFFER_SIZE 1024
|
||||
#define DEF_HTTP_RX_BUFFER_SIZE CONFIG_ESP_RMAKER_OTA_HTTP_RX_BUFFER_SIZE
|
||||
#define ESP_RMAKER_NVS_PART_NAME "nvs"
|
||||
#define RMAKER_OTA_UPDATE_FLAG_NVS_NAME "ota_update"
|
||||
|
||||
/* OTA retry constants */
|
||||
#ifndef ESP_RMAKER_OTA_MAX_RETRIES
|
||||
#define ESP_RMAKER_OTA_MAX_RETRIES CONFIG_ESP_RMAKER_OTA_MAX_RETRIES
|
||||
#endif
|
||||
#ifndef ESP_RMAKER_OTA_RETRY_DELAY_SECONDS
|
||||
#define ESP_RMAKER_OTA_RETRY_DELAY_SECONDS (CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES * 60)
|
||||
#endif
|
||||
|
||||
/* Core OTA rollback functionality */
|
||||
#define RMAKER_OTA_ROLLBACK_WAIT_PERIOD CONFIG_ESP_RMAKER_OTA_ROLLBACK_WAIT_PERIOD
|
||||
extern const char esp_rmaker_ota_def_cert[] asm("_binary_rmaker_ota_server_crt_start");
|
||||
const char *ESP_RMAKER_OTA_DEFAULT_SERVER_CERT = esp_rmaker_ota_def_cert;
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(RMAKER_OTA_EVENT);
|
||||
|
||||
typedef enum {
|
||||
OTA_OK = 0,
|
||||
OTA_ERR,
|
||||
OTA_DELAYED
|
||||
} esp_rmaker_ota_action_t;
|
||||
|
||||
static esp_rmaker_ota_t *g_ota_priv;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT
|
||||
static int last_ota_progress = 0;
|
||||
#endif
|
||||
|
||||
|
||||
char *esp_rmaker_ota_status_to_string(ota_status_t status)
|
||||
{
|
||||
@@ -112,7 +116,7 @@ esp_rmaker_ota_event_t esp_rmaker_ota_status_to_event(ota_status_t status)
|
||||
return RMAKER_OTA_EVENT_INVALID;
|
||||
}
|
||||
|
||||
static inline esp_err_t esp_rmaker_ota_post_event(esp_rmaker_event_t event_id, void* data, size_t data_size)
|
||||
esp_err_t esp_rmaker_ota_post_event(esp_rmaker_event_t event_id, void *data, size_t data_size)
|
||||
{
|
||||
return esp_event_post(RMAKER_OTA_EVENT, event_id, data, data_size, portMAX_DELAY);
|
||||
}
|
||||
@@ -150,6 +154,9 @@ void esp_rmaker_ota_common_cb(void *priv)
|
||||
}
|
||||
esp_rmaker_ota_data_t ota_data = {
|
||||
.url = ota->url,
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
.stream_id = ota->stream_id,
|
||||
#endif
|
||||
.filesize = ota->filesize,
|
||||
.fw_version = ota->fw_version,
|
||||
.ota_job_id = (char *)ota->transient_priv,
|
||||
@@ -166,7 +173,7 @@ ota_finish:
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle,
|
||||
esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle,
|
||||
esp_app_desc_t *new_app_info)
|
||||
{
|
||||
if (new_app_info == NULL) {
|
||||
@@ -207,6 +214,74 @@ static esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle,
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Common retry loop with configurable protocol-specific logic */
|
||||
|
||||
static esp_err_t esp_rmaker_ota_retry_loop(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data,
|
||||
ota_protocol_func_t protocol_func, const char *protocol_name, int *attempt_count)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
char err_desc[128] = {0};
|
||||
int attempt;
|
||||
|
||||
for (attempt = 0; attempt < ESP_RMAKER_OTA_MAX_RETRIES; ++attempt) {
|
||||
/* Simplified status reporting - just say "OTA" for cleaner messages */
|
||||
char info[64];
|
||||
snprintf(info, sizeof(info), "Starting OTA Upgrade (%s attempt %d/%d)", protocol_name, attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, info);
|
||||
ESP_LOGW(TAG, "Starting %s OTA attempt %d/%d. This may take time.", protocol_name, attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES);
|
||||
|
||||
err = protocol_func(ota_handle, ota_data, err_desc, sizeof(err_desc));
|
||||
if (err == ESP_OK) {
|
||||
break;
|
||||
} else if (err == ESP_ERR_INVALID_STATE) {
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s OTA attempt %d failed: %s", protocol_name, attempt + 1, err_desc);
|
||||
/* Simplified failure reporting */
|
||||
char fail_info[192];
|
||||
snprintf(fail_info, sizeof(fail_info), "OTA Attempt %d/%d failed: %s", attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES, err_desc);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, fail_info);
|
||||
}
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
/* Handle retry delay for topic-based OTA */
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
if (ota->type == OTA_USING_TOPICS) {
|
||||
esp_rmaker_ota_fetch_with_delay(ESP_RMAKER_OTA_RETRY_DELAY_SECONDS);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*attempt_count = attempt + 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Complete OTA workflow orchestration function */
|
||||
|
||||
esp_err_t esp_rmaker_ota_start_workflow(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data,
|
||||
ota_protocol_func_t protocol_func, const char *protocol_name)
|
||||
{
|
||||
/* Step 1: Handle metadata */
|
||||
esp_err_t metadata_result = esp_rmaker_ota_handle_metadata_common(ota_handle, ota_data);
|
||||
if (metadata_result != ESP_OK) {
|
||||
return metadata_result;
|
||||
}
|
||||
|
||||
/* Step 2: Post starting event */
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_STARTING, NULL, 0);
|
||||
|
||||
/* Step 3: Execute retry loop */
|
||||
int attempt_count = 0;
|
||||
esp_err_t retry_result = esp_rmaker_ota_retry_loop(ota_handle, ota_data, protocol_func, protocol_name, &attempt_count);
|
||||
if (retry_result != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Step 4: Handle success and reboot */
|
||||
return esp_rmaker_ota_success_reboot_sequence(ota_handle, protocol_name, attempt_count);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT
|
||||
|
||||
/* Retry delay for cases wherein time info itself is not available */
|
||||
@@ -319,10 +394,10 @@ esp_rmaker_ota_action_t esp_rmaker_ota_handle_time(jparse_ctx_t *jptr, esp_rmake
|
||||
|
||||
#endif /* CONFIG_ESP_RMAKER_OTA_TIME_SUPPORT */
|
||||
|
||||
esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
static esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
if (!ota_data->metadata) {
|
||||
return ESP_OK;
|
||||
return OTA_OK;
|
||||
}
|
||||
esp_rmaker_ota_action_t ota_action = OTA_OK;
|
||||
jparse_ctx_t jctx;
|
||||
@@ -336,6 +411,52 @@ esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t o
|
||||
return ota_action;
|
||||
}
|
||||
|
||||
/* Common OTA callback helper functions */
|
||||
|
||||
static esp_err_t esp_rmaker_ota_handle_metadata_common(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
/* Handle OTA metadata, if any */
|
||||
if (ota_data->metadata) {
|
||||
esp_rmaker_ota_action_t metadata_result = esp_rmaker_ota_handle_metadata(ota_handle, ota_data);
|
||||
if (metadata_result != OTA_OK) {
|
||||
ESP_LOGW(TAG, "Cannot proceed with the OTA as per the metadata received.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_rmaker_ota_success_reboot_sequence(esp_rmaker_ota_handle_t ota_handle, const char *protocol_name, int attempt_count)
|
||||
{
|
||||
/* Success path: rest of the reboot/rollback logic */
|
||||
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
nvs_handle handle;
|
||||
esp_err_t nvs_err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (nvs_err == ESP_OK) {
|
||||
uint8_t ota_update = 1;
|
||||
nvs_set_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, sizeof(ota_update));
|
||||
nvs_close(handle);
|
||||
}
|
||||
char reboot_info[80];
|
||||
snprintf(reboot_info, sizeof(reboot_info), "Rebooting into new firmware (after %d %s attempt%s)",
|
||||
attempt_count, protocol_name, (attempt_count == 1) ? "" : "s");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, reboot_info);
|
||||
#else
|
||||
char success_info[80];
|
||||
snprintf(success_info, sizeof(success_info), "%s OTA Upgrade finished successfully (after %d attempt%s)",
|
||||
protocol_name, attempt_count, (attempt_count == 1) ? "" : "s");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_SUCCESS, success_info);
|
||||
#endif
|
||||
#ifndef CONFIG_ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||
ESP_LOGI(TAG, "%s OTA upgrade successful. Rebooting in %d seconds...", protocol_name, OTA_REBOOT_TIMER_SEC);
|
||||
esp_rmaker_reboot(OTA_REBOOT_TIMER_SEC);
|
||||
#else
|
||||
ESP_LOGI(TAG, "%s OTA upgrade successful. Auto reboot is disabled. Requesting a Reboot via Event handler.", protocol_name);
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_REQ_FOR_REBOOT, NULL, 0);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Local macro for OTA max retries, can be overridden for development */
|
||||
#ifndef ESP_RMAKER_OTA_MAX_RETRIES
|
||||
#define ESP_RMAKER_OTA_MAX_RETRIES CONFIG_ESP_RMAKER_OTA_MAX_RETRIES
|
||||
@@ -346,249 +467,18 @@ esp_rmaker_ota_action_t esp_rmaker_ota_handle_metadata(esp_rmaker_ota_handle_t o
|
||||
#define ESP_RMAKER_OTA_RETRY_DELAY_SECONDS (CONFIG_ESP_RMAKER_OTA_RETRY_DELAY_MINUTES * 60)
|
||||
#endif
|
||||
|
||||
/* Helper function to perform OTA download and validation, returns ESP_OK on success, ESP_FAIL on failure. */
|
||||
static esp_err_t esp_rmaker_ota_perform_with_validation(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data, char *err_desc, size_t err_desc_size)
|
||||
{
|
||||
int buffer_size_tx = DEF_HTTP_TX_BUFFER_SIZE;
|
||||
/* In case received url is longer, we will increase the tx buffer size
|
||||
* to accomodate the longer url and other headers.
|
||||
*/
|
||||
if (strlen(ota_data->url) > buffer_size_tx) {
|
||||
buffer_size_tx = strlen(ota_data->url) + 128;
|
||||
}
|
||||
|
||||
if (ota_data->filesize) {
|
||||
ESP_LOGD(TAG, "Received file size: %d", ota_data->filesize);
|
||||
}
|
||||
|
||||
esp_err_t ota_finish_err = ESP_OK;
|
||||
esp_http_client_config_t config = {
|
||||
.url = ota_data->url,
|
||||
#ifdef ESP_RMAKER_USE_CERT_BUNDLE
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
#else
|
||||
.cert_pem = ota_data->server_cert,
|
||||
#endif
|
||||
.timeout_ms = 5000,
|
||||
.buffer_size = DEF_HTTP_RX_BUFFER_SIZE,
|
||||
.buffer_size_tx = buffer_size_tx,
|
||||
.keep_alive_enable = true
|
||||
};
|
||||
#ifdef CONFIG_ESP_RMAKER_SKIP_COMMON_NAME_CHECK
|
||||
config.skip_cert_common_name_check = true;
|
||||
#endif
|
||||
|
||||
esp_https_ota_config_t ota_config = {
|
||||
.http_config = &config,
|
||||
};
|
||||
/* Using a warning just to highlight the message */
|
||||
ESP_LOGW(TAG, "Starting OTA. This may take time.");
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA Begin failed: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Get the current Wi-Fi power save type. In case OTA fails and we need this
|
||||
* to restore power saving.
|
||||
*/
|
||||
wifi_ps_type_t ps_type;
|
||||
esp_wifi_get_ps(&ps_type);
|
||||
/* Disable Wi-Fi power save to speed up OTA, iff BT is controller is idle/disabled.
|
||||
* Co-ex requirement, device panics otherwise.*/
|
||||
#if CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
esp_app_desc_t app_desc;
|
||||
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "Failed to read image description: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
/* OTA failed, may retry later */
|
||||
goto ota_end;
|
||||
}
|
||||
err = validate_image_header(ota_handle, &app_desc);
|
||||
if (err != ESP_OK) {
|
||||
snprintf(err_desc, err_desc_size, "Image header verification failed");
|
||||
/* OTA should be rejected, returning ESP_ERR_INVALID_STATE */
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
/* Report status: Downloading Firmware Image */
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Downloading Firmware Image");
|
||||
|
||||
int count = 0;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT
|
||||
last_ota_progress = 0;
|
||||
#endif
|
||||
while (1) {
|
||||
err = esp_https_ota_perform(https_ota_handle);
|
||||
if (err == ESP_ERR_INVALID_VERSION) {
|
||||
snprintf(err_desc, err_desc_size, "Chip revision mismatch");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_REJECTED, err_desc);
|
||||
/* OTA should be rejected, returning ESP_ERR_INVALID_STATE */
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto ota_end;
|
||||
}
|
||||
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
/* esp_https_ota_perform returns after every read operation which gives user the ability to
|
||||
* monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
|
||||
* data read so far.
|
||||
* We are using a counter just to reduce the number of prints
|
||||
*/
|
||||
count++;
|
||||
if (count == 50) {
|
||||
ESP_LOGI(TAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
|
||||
count = 0;
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_PROGRESS_SUPPORT
|
||||
int image_size = esp_https_ota_get_image_size(https_ota_handle);
|
||||
int read_size = esp_https_ota_get_image_len_read(https_ota_handle);
|
||||
int ota_progress = 100 * read_size / image_size; // The unit is %
|
||||
/* When ota_progress is 0 or 100, we will not report the progress, beacasue the 0 and 100 is reported by additional_info `Downloading Firmware Image` and
|
||||
* `Firmware Image download complete`. And every progress will only report once and the progress is increasing.
|
||||
*/
|
||||
if (((ota_progress != 0) && (ota_progress != 100)) && (ota_progress % CONFIG_ESP_RMAKER_OTA_PROGRESS_INTERVAL == 0) && (last_ota_progress < ota_progress)) {
|
||||
last_ota_progress = ota_progress;
|
||||
char description[40] = {0};
|
||||
snprintf(description, sizeof(description), "Downloaded %d%% Firmware Image", ota_progress);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, description);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA failed: %s (errno=%d: %s)", esp_err_to_name(err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
/* OTA failed, may retry later */
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
|
||||
snprintf(err_desc, err_desc_size, "Complete data was not received");
|
||||
/* OTA failed, may retry later */
|
||||
err = ESP_FAIL;
|
||||
goto ota_end;
|
||||
}
|
||||
|
||||
/* Report completion before finishing */
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, "Firmware Image download complete");
|
||||
|
||||
ota_end:
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_wifi_set_ps(ps_type);
|
||||
}
|
||||
#else
|
||||
esp_wifi_set_ps(ps_type);
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
if (err == ESP_OK) {
|
||||
/* Success path: finish the OTA */
|
||||
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||
if (ota_finish_err == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
snprintf(err_desc, err_desc_size, "Image validation failed");
|
||||
} else {
|
||||
int err_no = errno;
|
||||
snprintf(err_desc, err_desc_size, "OTA finish failed: %s (errno=%d: %s)", esp_err_to_name(ota_finish_err), err_no, err_no ? strerror(err_no) : "Invalid");
|
||||
}
|
||||
/* Handle already closed by esp_https_ota_finish(), don't call abort */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Error path: abort the OTA */
|
||||
esp_https_ota_abort(https_ota_handle);
|
||||
return (err == ESP_ERR_INVALID_STATE) ? ESP_ERR_INVALID_STATE : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_rmaker_ota_default_cb(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data)
|
||||
{
|
||||
if (!ota_data->url) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Handle OTA metadata, if any */
|
||||
if (ota_data->metadata) {
|
||||
if (esp_rmaker_ota_handle_metadata(ota_handle, ota_data) != OTA_OK) {
|
||||
ESP_LOGW(TAG, "Cannot proceed with the OTA as per the metadata received.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_STARTING, NULL, 0);
|
||||
|
||||
esp_err_t err = ESP_FAIL;
|
||||
char err_desc[128] = {0};
|
||||
int attempt;
|
||||
for (attempt = 0; attempt < ESP_RMAKER_OTA_MAX_RETRIES; ++attempt) {
|
||||
char info[64];
|
||||
snprintf(info, sizeof(info), "Starting OTA Upgrade (attempt %d/%d)", attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, info);
|
||||
ESP_LOGW(TAG, "Starting OTA attempt %d/%d. This may take time.", attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES);
|
||||
err = esp_rmaker_ota_perform_with_validation(ota_handle, ota_data, err_desc, sizeof(err_desc));
|
||||
if (err == ESP_OK) {
|
||||
break;
|
||||
} else if (err == ESP_ERR_INVALID_STATE) {
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OTA attempt %d failed: %s", attempt + 1, err_desc);
|
||||
/* Report failure for this attempt */
|
||||
char fail_info[192];
|
||||
snprintf(fail_info, sizeof(fail_info), "Attempt %d/%d failed: %s", attempt + 1, ESP_RMAKER_OTA_MAX_RETRIES, err_desc);
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, fail_info);
|
||||
}
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
/* Final failure already reported in the loop above */
|
||||
/* If OTA is using topics, schedule a re-fetch after the configured delay */
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)ota_handle;
|
||||
if (ota->type == OTA_USING_TOPICS) {
|
||||
esp_rmaker_ota_fetch_with_delay(ESP_RMAKER_OTA_RETRY_DELAY_SECONDS);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Success path: rest of the reboot/rollback logic as before */
|
||||
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
nvs_handle handle;
|
||||
esp_err_t nvs_err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, RMAKER_OTA_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (nvs_err == ESP_OK) {
|
||||
uint8_t ota_update = 1;
|
||||
nvs_set_blob(handle, RMAKER_OTA_UPDATE_FLAG_NVS_NAME, &ota_update, sizeof(ota_update));
|
||||
nvs_close(handle);
|
||||
}
|
||||
char reboot_info[80];
|
||||
snprintf(reboot_info, sizeof(reboot_info), "Rebooting into new firmware (after %d attempt%s)", attempt + 1, (attempt == 0) ? "" : "s");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_IN_PROGRESS, reboot_info);
|
||||
#else
|
||||
char success_info[80];
|
||||
snprintf(success_info, sizeof(success_info), "OTA Upgrade finished successfully (after %d attempt%s)", attempt + 1, (attempt == 0) ? "" : "s");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_SUCCESS, success_info);
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_HTTPS
|
||||
return esp_rmaker_ota_https_cb(ota_handle, ota_data);
|
||||
#else /* CONFIG_ESP_RMAKER_OTA_USE_MQTT */
|
||||
return esp_rmaker_ota_mqtt_cb(ota_handle, ota_data);
|
||||
#endif
|
||||
#ifndef CONFIG_ESP_RMAKER_OTA_DISABLE_AUTO_REBOOT
|
||||
ESP_LOGI(TAG, "OTA upgrade successful. Rebooting in %d seconds...", OTA_REBOOT_TIMER_SEC);
|
||||
esp_rmaker_reboot(OTA_REBOOT_TIMER_SEC);
|
||||
#else
|
||||
ESP_LOGI(TAG, "OTA upgrade successful. Auto reboot is disabled. Requesting a Reboot via Event handler.");
|
||||
esp_rmaker_ota_post_event(RMAKER_OTA_EVENT_REQ_FOR_REBOOT, NULL, 0);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
esp_rmaker_ota_t *ota = (esp_rmaker_ota_t *)arg;
|
||||
esp_rmaker_ota_diag_status_t diag_status = OTA_DIAG_STATUS_SUCCESS;
|
||||
@@ -752,9 +642,12 @@ static void esp_rmaker_ota_manage_rollback(esp_rmaker_ota_t *ota)
|
||||
}
|
||||
}
|
||||
|
||||
/* Protocol-agnostic default config - no protocol-specific dependencies */
|
||||
static const esp_rmaker_ota_config_t ota_default_config = {
|
||||
.server_cert = esp_rmaker_ota_def_cert,
|
||||
.server_cert = NULL, /* Each protocol handles its own certificate defaults */
|
||||
.priv = NULL,
|
||||
};
|
||||
|
||||
/* Enable the ESP RainMaker specific OTA */
|
||||
esp_err_t esp_rmaker_ota_enable(esp_rmaker_ota_config_t *ota_config, esp_rmaker_ota_type_t type)
|
||||
{
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
// Copyright 2020 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_rmaker_ota.h>
|
||||
#include <esp_app_format.h>
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RMAKER_OTA_NVS_NAMESPACE "rmaker_ota"
|
||||
#define RMAKER_OTA_JOB_ID_NVS_NAME "rmaker_ota_id"
|
||||
@@ -34,6 +33,9 @@ typedef struct {
|
||||
const char *server_cert;
|
||||
char *url;
|
||||
char *fw_version;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
char *stream_id;
|
||||
#endif
|
||||
int filesize;
|
||||
bool ota_in_progress;
|
||||
bool validation_in_progress;
|
||||
@@ -43,7 +45,16 @@ typedef struct {
|
||||
char *metadata;
|
||||
} esp_rmaker_ota_t;
|
||||
|
||||
|
||||
|
||||
char *esp_rmaker_ota_status_to_string(ota_status_t status);
|
||||
esp_err_t esp_rmaker_ota_post_event(esp_rmaker_event_t event_id, void *data, size_t data_size);
|
||||
typedef enum {
|
||||
OTA_OK = 0,
|
||||
OTA_ERR,
|
||||
OTA_DELAYED
|
||||
} esp_rmaker_ota_action_t;
|
||||
|
||||
void esp_rmaker_ota_common_cb(void *priv);
|
||||
void esp_rmaker_ota_finish_using_params(esp_rmaker_ota_t *ota);
|
||||
void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota);
|
||||
@@ -53,3 +64,17 @@ esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_
|
||||
esp_err_t esp_rmaker_ota_enable_using_topics(esp_rmaker_ota_t *ota);
|
||||
esp_err_t esp_rmaker_ota_report_status_using_topics(esp_rmaker_ota_handle_t ota_handle,
|
||||
ota_status_t status, char *additional_info);
|
||||
|
||||
/* Common retry loop infrastructure with function pointer pattern */
|
||||
typedef esp_err_t (*ota_protocol_func_t)(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data, char *err_desc, size_t err_desc_size);
|
||||
|
||||
/* Complete OTA workflow orchestration */
|
||||
esp_err_t esp_rmaker_ota_start_workflow(esp_rmaker_ota_handle_t ota_handle, esp_rmaker_ota_data_t *ota_data,
|
||||
ota_protocol_func_t protocol_func, const char *protocol_name);
|
||||
|
||||
/* OTA image header validation */
|
||||
esp_err_t validate_image_header(esp_rmaker_ota_handle_t ota_handle, esp_app_desc_t *new_app_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
// Copyright 2020 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
@@ -93,7 +85,7 @@ esp_err_t esp_rmaker_ota_report_status_using_params(esp_rmaker_ota_handle_t ota_
|
||||
|
||||
esp_rmaker_param_update_and_report(info_param, esp_rmaker_str(additional_info));
|
||||
esp_rmaker_param_update_and_report(status_param, esp_rmaker_str(esp_rmaker_ota_status_to_string(status)));
|
||||
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
// Copyright 2020 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <json_parser.h>
|
||||
@@ -106,8 +99,8 @@ static void ota_fetch_timeout_timer_cb(TimerHandle_t xTimer)
|
||||
|
||||
|
||||
|
||||
static void ota_fetch_mqtt_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
static void ota_fetch_mqtt_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (!event_data || event_base != RMAKER_COMMON_EVENT) {
|
||||
return;
|
||||
@@ -183,6 +176,12 @@ void esp_rmaker_ota_finish_using_topics(esp_rmaker_ota_t *ota)
|
||||
free(ota->url);
|
||||
ota->url = NULL;
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
if (ota->stream_id) {
|
||||
free(ota->stream_id);
|
||||
ota->stream_id = NULL;
|
||||
}
|
||||
#endif
|
||||
ota->filesize = 0;
|
||||
if (ota->transient_priv) {
|
||||
free(ota->transient_priv);
|
||||
@@ -222,6 +221,9 @@ static void ota_url_handler(const char *topic, void *payload, size_t payload_len
|
||||
*/
|
||||
jparse_ctx_t jctx;
|
||||
char *url = NULL, *ota_job_id = NULL, *fw_version = NULL;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
char *stream_id = NULL;
|
||||
#endif
|
||||
int ret = json_parse_start(&jctx, (char *)payload, (int) payload_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Invalid JSON received: %s", (char *)payload);
|
||||
@@ -269,10 +271,27 @@ static void ota_url_handler(const char *topic, void *payload, size_t payload_len
|
||||
json_obj_get_string(&jctx, "url", url, len);
|
||||
ESP_LOGI(TAG, "URL: %s", url);
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
len = 0;
|
||||
ret = json_obj_get_strlen(&jctx, "stream_id", &len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Aborted. Stream ID not found in JSON");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. Stream ID not found in JSON");
|
||||
goto end;
|
||||
}
|
||||
len++; /* Increment for NULL character */
|
||||
stream_id = calloc(1, len);
|
||||
if (!stream_id) {
|
||||
ESP_LOGE(TAG, "Aborted. Stream ID memory allocation failed");
|
||||
esp_rmaker_ota_report_status(ota_handle, OTA_STATUS_FAILED, "Aborted. Stream ID memory allocation failed");
|
||||
goto end;
|
||||
}
|
||||
json_obj_get_string(&jctx, "stream_id", stream_id, len);
|
||||
ESP_LOGI(TAG, "Stream ID: %s", stream_id);
|
||||
#endif
|
||||
int filesize = 0;
|
||||
json_obj_get_int(&jctx, "file_size", &filesize);
|
||||
ESP_LOGI(TAG, "File Size: %d", filesize);
|
||||
|
||||
len = 0;
|
||||
ret = json_obj_get_strlen(&jctx, "fw_version", &len);
|
||||
if (ret == ESP_OK && len > 0) {
|
||||
@@ -307,6 +326,9 @@ static void ota_url_handler(const char *topic, void *payload, size_t payload_len
|
||||
free(ota->url);
|
||||
}
|
||||
ota->url = url;
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
ota->stream_id = stream_id;
|
||||
#endif
|
||||
ota->fw_version = fw_version;
|
||||
ota->filesize = filesize;
|
||||
ota->ota_in_progress = true;
|
||||
@@ -318,6 +340,11 @@ end:
|
||||
if (url) {
|
||||
free(url);
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_OTA_USE_MQTT
|
||||
if (stream_id) {
|
||||
free(stream_id);
|
||||
}
|
||||
#endif
|
||||
if (fw_version) {
|
||||
free(fw_version);
|
||||
}
|
||||
@@ -543,4 +570,4 @@ esp_err_t esp_rmaker_ota_fetch_with_delay(int time)
|
||||
xTimerStart(timer, 0);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user