OTA and Partitions - mriksman/esp-idf-homekit GitHub Wiki

OTA and Partitions

Flash Layout

Pre v3.0 ESP8266 RTOS SDK and ESP8266 NONOS SDK (Arduino)

The flash layout for ESP8266 is defined in the ld files \ld\eagle.flash.1m*.ld. In the Arduino IDE, it can be selected via a menu item.

  Flash Split for 1M chips                  
  ----------------------------------------- -----------------------------------------
  256KB                                     64KB
  sketch @0x40200000 (~743KB) (761840B)     sketch @0x40200000 (~935KB) (958448B)
  empty  @0x402B9FF0 (~4KB)   (4112B)       empty  @0x402E9FF0 (~4KB)   (4112B)
  spiffs @0x402BB000 (~256KB) (262144B)     spiffs @0x402EB000 (~64KB)  (65536B)
  eeprom @0x402FB000 (4KB)                  eeprom @0x402FB000 (4KB)
  rfcal  @0x402FC000 (4KB)                  rfcal  @0x402FC000 (4KB)
  wifi   @0x402FD000 (12KB)                 wifi   @0x402FD000 (12KB)

ESP-IDF and ESP8266 RTOS v3.x

ESP-IDF and ESP8266 RTOS SDK v3.x and higher now uses the same partition/build strategy. You create a csv file describing the flash partitions and configure the project to use it in menuconfig.

HomeKit

HomeKit stores its data in flash, so I created a custom partition labelled homekit. The Type should be set between 0x40-0xFE which is for custom partition types. The minimum size for any partition is 4096 bytes. The NVS is recommended to be between 12 and 24KB. Note the app partitions need to start on a 0x10000 offset.

  Name          Type       SubType       Offset       Size
  ------------- ---------- ------------- ------------ ----------
  nvs           data       nvs           0x9000       16K
  homekit       0x40       0             0xd000       4K
  otadata       data       ota           0xe000       8K
  app0          app        ota_0         0x10000      384K
  app1          app        ota_1         0x70000      576K

Using idf.py menuconfig | Component Configuration you can set the 'SPI Flash Address for Storing HomeKit Data'

Changing NVS Partition Size -- Erase and Initialise

A lot of example code for ESP-IDF has the following NVS initialisation code at the top of app_main()

esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
   ESP_ERROR_CHECK(nvs_flash_erase());
   err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);

However, when I changed the NVS partition size and compiled under ESP8266 RTOS SDK, the program would crash before even reaching app_main(). In components/esp8266/source/startup.c, it calls assert(nvs_flash_init() == 0) which fails. I had to comment out this code, perform a nvs_flash_erase() and then remove the comment. Thus, there is no need to call nvs_flash_init() in ESP8266 RTOS SDK as it has already been called.

Building OTA Binary Apps

For ESP-IDF, you can build a binary application bound for ota_1 as per normal and upload the projectname.bin to any ota partition.

  • Create a partition with ota_0 and ota_1
  • Load (via serial) an application into ota_0
  • Create the hello_world example, and simply idf build to create the bin file
  • Use the application in ota_0 to load the hello_world.bin into ota_1

But for ESP8266 RTOS SDK, it doesn't work. eps_ota_end() complains;

D (48942) esp_image: reading image header @ 0x70000
D (48949) esp_image: image header: 0xe9 0x05 0x02 0x01 40210a9c
D (49040) esp_ota_ops: OTA binary start entry 0x10a8c, partition start from 0x70000 to 0x100000
E (49050) esp_ota_ops: **Important**: The OTA binary link data is error, please refer to document <<ESP8266_RTOS_SDK/examples/system/ota/README.md>> for how to generate OTA binaries
E (49076) httpd: Image validation failed, image is corrupted

Looking at the function esp_ota_verify_binary() in esp_ota_ops.c, it checks to make sure the entry address is between pos->offset and pos->offset + pos->size (start and finish). However, the entry address was set to 0x10a8c -- assuming that it would be flashed to app0.

More peculiar is that it only checks this if flash chip is 1MB or less

if (pos‑\>offset + pos‑\>size \<= 0x100000)

When I set the partition to be larger and expand past 1MB (but leaving offset at 0x7000), the error wasn't generated but the subsequent boot into ota_1 would crash. The only way it would work is if I made ota_1 start at address 0x110000; exactly 1MB after ota_0. This is vaguely mentioned in their documentation.

The examples online suggest you can run make ota and it will configure two binary files; one bound for ota_0 and one bound for ota_1. Looking at components/esp8266/Makefile.projbuild it has the functions to create the two OTA bin files with the correct app_offset, but the CMakeLists.txt does not have this ability.

For CMakeLists.txt I edited lines 96 and 97 from

partition_table_get_partition_info(app_offset "--partition-boot-default" "offset")
partition_table_get_partition_info(app_size "--partition-boot-default" "size")

to

partition_table_get_partition_info(app_offset "-- partition-name app1" "offset")
partition_table_get_partition_info(app_size "-- partition-name app1" "size")

I built the program and uploaded it via the HTTP OTA Server I had running in ota_0.

D (2778245) esp_ota_ops: OTA binary start entry 0x70a8c, partition start from 0x70000 to 0x100000
D (2778255) esp_image: reading image header @ 0x70000

As I suspected. It works, and the entry is where I thought it would be. To manage this for different projects, I have changed the lines to be;

if (DEFINED PARTITION_NAME)
   partition_table_get_partition_info(app_offset "--partition-name ${PARTITION_NAME}" "offset")
   partition_table_get_partition_info(app_size "--partition-name ${PARTITION_NAME}" "size")
else()
   partition_table_get_partition_info(app_offset "--partition-boot-default" "offset")
   partition_table_get_partition_info(app_size "--partition-boot-default" "size")
endif()

message(STATUS "Using partition located at offset " ${app_offset} " with size " ${app_size})

And in the main project CmakeLists.txt;

# used in components/esp8266/CMakeLists.txt to select which partition this
#  .bin file will be loaded; sets entry address accordingly
set(PARTITION_NAME app1)