Skip to content

Commit

Permalink
Merge ca86968 into d8f5bf1
Browse files Browse the repository at this point in the history
  • Loading branch information
PSONALl authored Mar 2, 2024
2 parents d8f5bf1 + ca86968 commit 1012655
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 1 deletion.
5 changes: 5 additions & 0 deletions config/esp32/components/chip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,11 @@ if (CONFIG_ENABLE_ENCRYPTED_OTA)
list(APPEND chip_libraries $<TARGET_FILE:${esp_encrypted_img_lib}>)
endif()

if (CONFIG_ENABLE_DELTA_OTA)
idf_component_get_property(esp_delta_ota_lib espressif__esp_delta_ota COMPONENT_LIB)
list(APPEND chip_libraries $<TARGET_FILE:${esp_delta_ota_lib}>)
endif()

idf_component_get_property(main_lib main COMPONENT_LIB)
list(APPEND chip_libraries $<TARGET_FILE:${main_lib}>)

Expand Down
9 changes: 9 additions & 0 deletions config/esp32/components/chip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ menu "CHIP Core"
help
Enable this option to use the pre encrypted OTA image

config ENABLE_DELTA_OTA
bool "Enable delta OTA"
depends on ENABLE_OTA_REQUESTOR
default n
help
Enable this option for delta OTA image updates.
Delta OTA updates allow for smaller, more efficient updates by only
sending the changes between the current and new firmware versions.

config OTA_AUTO_REBOOT_ON_APPLY
bool "Reboot the device after applying the OTA image"
depends on ENABLE_OTA_REQUESTOR
Expand Down
6 changes: 6 additions & 0 deletions config/esp32/components/chip/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,9 @@ dependencies:
rules:
- if: "idf_version >=5.0"
- if: "target != esp32h2"

espressif/esp_delta_ota:
version: "1.1.0"
require: public
rules:
- if: "idf_version >=4.3"
31 changes: 31 additions & 0 deletions docs/guides/esp32/ota.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,34 @@ Please follow the steps below to generate an application image for OTA upgrades:
```

3. Use the `lighting-app-encrypted-ota.bin` file with the OTA Provider app.

## Delta OTA

Delta OTA Updates is a feature that enables Over-the-Air (OTA) firmware update with compressed delta binaries. Patch files have smaller size than the original firmware file, which reduces the time and network usage to download the file from the server. Also, no additional storage partition is required for the "patch".

### Firmware Changes

- Enable configuration options for OTA requestor and Delta OTA:

```
CONFIG_ENABLE_OTA_REQUESTOR=y
CONFIG_ENABLE_DELTA_OTA=y
```

- Delta binary needs to be generated using binary delta encoding in Python 3.6+. You can install detools using the following command.

```
pip install detools>=0.49.0
```

- Generate delta binary and compress it using Heatshrink algorithm.
```
detools create_patch -c heatshrink <base-binary> <new-binary> delta-ota.bin
```

- Append the Matter OTA header:
```
src/app/ota_image_tool.py create --vendor-id 0xFFF1 --product-id 0x8000 --version 2 --version-str "v2.0" -da sha256 delta-ota.bin lighting-app-delta-ota.bin
```

- Use the `lighting-app-delta-ota.bin` file with the OTA Provider app.
142 changes: 141 additions & 1 deletion src/platform/ESP32/OTAImageProcessorImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <platform/ESP32/ESP32Utils.h>

#include "OTAImageProcessorImpl.h"
#include "esp_app_format.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
Expand All @@ -31,7 +32,12 @@
#include <esp_encrypted_img.h>
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

#ifdef CONFIG_ENABLE_DELTA_OTA
#include <esp_delta_ota.h>
#endif // CONFIG_ENABLE_DELTA_OTA

#define TAG "OTAImageProcessor"

using namespace chip::System;
using namespace ::chip::DeviceLayer::Internal;

Expand Down Expand Up @@ -123,6 +129,96 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block)
return CHIP_NO_ERROR;
}

#ifdef CONFIG_ENABLE_DELTA_OTA
esp_err_t OTAImageProcessorImpl::DeltaOTAReadCallback(uint8_t * buf_p, size_t size, int src_offset)
{
if (size <= 0 || buf_p == NULL)
{
return ESP_ERR_INVALID_ARG;
}

const esp_partition_t * current_partition = esp_ota_get_running_partition();
if (current_partition == NULL)
{
return ESP_FAIL;
}

esp_err_t err = esp_partition_read(current_partition, src_offset, buf_p, size);

if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_partition_read failed (%s)!", esp_err_to_name(err));
}

return err;
}

esp_err_t OTAImageProcessorImpl::DeltaOTAWriteHeader(OTAImageProcessorImpl * imageProcessor, const uint8_t * buf_p, size_t size,
int index)
{
if (size <= 0 || buf_p == NULL)
{
return ESP_ERR_INVALID_ARG;
}

static char header_data[IMG_HEADER_LEN];
static bool chip_id_verified = false;
static int header_data_read = 0;

if (!chip_id_verified)
{
if (header_data_read + size <= IMG_HEADER_LEN)
{
memcpy(header_data + header_data_read, buf_p, size);
header_data_read += size;
return ESP_OK;
}
else
{
index = IMG_HEADER_LEN - header_data_read;
memcpy(header_data + header_data_read, buf_p, index);

chip_id_verified = true;

// Write data in header_data buffer.
esp_err_t err = esp_ota_write(imageProcessor->mOTAUpdateHandle, header_data, IMG_HEADER_LEN);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_ota_write failed (%s)!", esp_err_to_name(err));
return err;
}
}
}
return ESP_OK;
}

esp_err_t OTAImageProcessorImpl::DeltaOTAWriteCallback(const uint8_t * buf_p, size_t size, void * arg)
{
static int index = 0;
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(arg);
if (size <= 0 || buf_p == NULL)
{
return ESP_ERR_INVALID_ARG;
}

esp_err_t err = imageProcessor->DeltaOTAWriteHeader(imageProcessor, buf_p, size, index);

if (err != ESP_OK)
{
ESP_LOGE(TAG, "DeltaOTAWriteHeader failed (%s)!", esp_err_to_name(err));
return err;
}

err = esp_ota_write(imageProcessor->mOTAUpdateHandle, buf_p + index, size - index);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_ota_write failed (%s)!", esp_err_to_name(err));
}

return err;
}
#endif // CONFIG_ENABLE_DELTA_OTA

void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
Expand All @@ -142,13 +238,31 @@ void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
ChipLogError(SoftwareUpdate, "OTA partition not found");
return;
}
#ifdef CONFIG_ENABLE_DELTA_OTA
// New image size is unknown for delta OTA, so we use OTA_SIZE_UNKNOWN flag.
esp_err_t err = esp_ota_begin(imageProcessor->mOTAUpdatePartition, OTA_SIZE_UNKNOWN, &(imageProcessor->mOTAUpdateHandle));
#else
esp_err_t err =
esp_ota_begin(imageProcessor->mOTAUpdatePartition, OTA_WITH_SEQUENTIAL_WRITES, &(imageProcessor->mOTAUpdateHandle));
#endif // CONFIG_ENABLE_DELTA_OTA

if (err != ESP_OK)
{
imageProcessor->mDownloader->OnPreparedForDownload(ESP32Utils::MapError(err));
return;
}
#ifdef CONFIG_ENABLE_DELTA_OTA
imageProcessor->delta_ota_cfg.user_data = imageProcessor,
imageProcessor->delta_ota_cfg.read_cb = &(imageProcessor->DeltaOTAReadCallback),
imageProcessor->delta_ota_cfg.write_cb_with_user_data = &(imageProcessor->DeltaOTAWriteCallback),

imageProcessor->mDeltaOTAUpdateHandle = esp_delta_ota_init(&imageProcessor->delta_ota_cfg);
if (imageProcessor->mDeltaOTAUpdateHandle == NULL)
{
ChipLogError(SoftwareUpdate, "esp_delta_ota_init failed");
return;
}
#endif // CONFIG_ENABLE_DELTA_OTA

#if CONFIG_ENABLE_ENCRYPTED_OTA
CHIP_ERROR chipError = imageProcessor->DecryptStart();
Expand Down Expand Up @@ -182,7 +296,29 @@ void OTAImageProcessorImpl::HandleFinalize(intptr_t context)
}
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

#ifdef CONFIG_ENABLE_DELTA_OTA
esp_err_t err = esp_delta_ota_finalize(imageProcessor->mDeltaOTAUpdateHandle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_delta_ota_finalize() failed (%s)!", esp_err_to_name(err));
esp_ota_abort(imageProcessor->mOTAUpdateHandle);
imageProcessor->ReleaseBlock();
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
}

err = esp_delta_ota_deinit(imageProcessor->mDeltaOTAUpdateHandle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_delta_ota_deinit() failed (%s)!", esp_err_to_name(err));
esp_ota_abort(imageProcessor->mOTAUpdateHandle);
imageProcessor->ReleaseBlock();
PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed);
}

err = esp_ota_end(imageProcessor->mOTAUpdateHandle);
#else
esp_err_t err = esp_ota_end(imageProcessor->mOTAUpdateHandle);
#endif // CONFIG_ENABLE_DELTA_OTA
if (err != ESP_OK)
{
if (err == ESP_ERR_OTA_VALIDATE_FAILED)
Expand Down Expand Up @@ -264,7 +400,11 @@ void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context)
}
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size());
#ifdef CONFIG_ENABLE_DELTA_OTA
err = esp_delta_ota_feed_patch(imageProcessor->mDeltaOTAUpdateHandle, blockToWrite.data(), blockToWrite.size());
#else
err = esp_ota_write(imageProcessor->mOTAUpdateHandle, blockToWrite.data(), blockToWrite.size());
#endif // CONFIG_ENABLE_DELTA_OTA

#if CONFIG_ENABLE_ENCRYPTED_OTA
free((void *) (blockToWrite.data()));
Expand Down
15 changes: 15 additions & 0 deletions src/platform/ESP32/OTAImageProcessorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
#include <esp_encrypted_img.h>
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

#ifdef CONFIG_ENABLE_DELTA_OTA
#include "esp_app_format.h"
#include <esp_delta_ota.h>
#define IMG_HEADER_LEN sizeof(esp_image_header_t)
#endif // CONFIG_ENABLE_DELTA_OTA

namespace chip {

class OTAImageProcessorImpl : public OTAImageProcessorInterface
Expand All @@ -48,6 +54,11 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
// @return CHIP_NO_ERROR on success, appropriate error code otherwise
CHIP_ERROR InitEncryptedOTA(const CharSpan & key);
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
#ifdef CONFIG_ENABLE_DELTA_OTA
esp_err_t DeltaOTAWriteHeader(OTAImageProcessorImpl * imageProcessor, const uint8_t * buf_p, size_t size, int index);
static esp_err_t DeltaOTAReadCallback(uint8_t * buf_p, size_t size, int src_offset);
static esp_err_t DeltaOTAWriteCallback(const uint8_t * buf_p, size_t size, void * arg);
#endif // CONFIG_ENABLE_DELTA_OTA

private:
static void HandlePrepareDownload(intptr_t context);
Expand All @@ -64,6 +75,10 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
MutableByteSpan mBlock;
const esp_partition_t * mOTAUpdatePartition = nullptr;
esp_ota_handle_t mOTAUpdateHandle;
#ifdef CONFIG_ENABLE_DELTA_OTA
esp_delta_ota_handle_t mDeltaOTAUpdateHandle;
esp_delta_ota_cfg_t delta_ota_cfg;
#endif // CONFIG_ENABLE_DELTA_OTA
OTAImageHeaderParser mHeaderParser;

#if CONFIG_ENABLE_ENCRYPTED_OTA
Expand Down

0 comments on commit 1012655

Please sign in to comment.