This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
esp32_ota [2024/03/14 21:02] sausage [Switching firmware, and the otadata partition] |
esp32_ota [2024/03/20 01:50] (current) sausage [ESP32 OTA and Rollbacks - What happens under the hood?] |
||
---|---|---|---|
Line 5: | Line 5: | ||
Over The Air (OTA) provides a way to send firmware to your ESP32 board in the field without the need for a cable and physical access to the device. | Over The Air (OTA) provides a way to send firmware to your ESP32 board in the field without the need for a cable and physical access to the device. | ||
- | The purpose of this article is manually step through the various stages of the OTA process and illustrate the state of the ESP32 at each stage. | + | The purpose of this article is to manually step through the various stages of the OTA process and illustrate the state of the ESP32 at each stage. |
Together we will set up a number of firmwares suitable for testing on different partitions in flash memory. | Together we will set up a number of firmwares suitable for testing on different partitions in flash memory. | ||
Line 22: | Line 22: | ||
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html#rollback-process | https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html#rollback-process | ||
- | This article is based on esp-idf-v5.1.1-2. | + | This article is based on esp-idf-v5.2. |
===== The stock firmware ===== | ===== The stock firmware ===== | ||
Line 35: | Line 35: | ||
gen_esp32part.py .\partitions.log > .\partitions.csv | gen_esp32part.py .\partitions.log > .\partitions.csv | ||
+ | | ||
+ | <WRAP center round important 90%> | ||
+ | If you don't have gen_esp32part.py, please check the troubleshooter below. | ||
+ | </WRAP> | ||
Open the CSV, and it will probably look something like this (if anything at all): | Open the CSV, and it will probably look something like this (if anything at all): | ||
Line 46: | Line 50: | ||
</code> | </code> | ||
- | Ignoring the ''nvs'' and ''phy_init'' partitions, we have the default ''factory'' partition that starts at 0x10000. Ironically, the list of partitions doesn't list the bootloader partition at 0x1000 or the partition that the partition table itself is stored on (at 0x8000). | + | Ignoring the ''nvs'' and ''phy_init'' partitions, we have the default ''factory'' partition that starts at 0x10000. Ironically, the list of partitions doesn't list the bootloader partition at 0x1000 or the partition that the partition table itself is stored (at 0x8000). |
===== OTA partitions ===== | ===== OTA partitions ===== | ||
Line 212: | Line 216: | ||
- 4blinks.bin | - 4blinks.bin | ||
- | Now the ''idf.py flash'' command is only able to flash the default project firmware to the ''factory'' partition. So we'll need to switch to a different command to flash a named firmware to a particular partition. | + | Note that the ''idf.py flash'' command is only able to flash the default project firmware to the ''factory'' partition. So we'll need to switch to a different command to flash a named firmware to a particular partition. |
Take another look at our partitions table. | Take another look at our partitions table. | ||
Line 280: | Line 284: | ||
This reads out the ''otadata'' partition, which starts at 0xd000 and is 0x2000 in length out to a file. | This reads out the ''otadata'' partition, which starts at 0xd000 and is 0x2000 in length out to a file. | ||
- | If you open the ''otadata-partition.bin'' file in a hex reader (I recommend HxD - https://mh-nexus.de/en/hxd/), you'll see that the entire file is filled with 0xFF bytes. | + | If you open the ''otadata-partition.bin'' file in a hex reader (I recommend [[https://mh-nexus.de/en/hxd/|HxD]]), you'll see that the entire file is filled with 0xFF bytes. |
We can use a tool to set ''ota_0''s firmware as the one we want to boot from: | We can use a tool to set ''ota_0''s firmware as the one we want to boot from: | ||
Line 355: | Line 359: | ||
In our case, the second sector has a new entry, the ''02'' sequence. This is for the firmware on ''ota_1''. ''02'' is greater than ''01'' so this is the firmware selected for boot by the bootloader. | In our case, the second sector has a new entry, the ''02'' sequence. This is for the firmware on ''ota_1''. ''02'' is greater than ''01'' so this is the firmware selected for boot by the bootloader. | ||
- | How an OTA parition is determined from sequence numbers the otadata partition is not explained in the documentation, but is explained in the code. You can see it here: https://github.com/pycom/esp-idf-2.0/blob/master/components/app_update/esp_ota_ops.c#L297 | + | How an OTA partition is determined from sequence numbers in the otadata partition is not explained in the documentation, but is explained in the code. You can see it here: https://github.com/pycom/esp-idf-2.0/blob/master/components/app_update/esp_ota_ops.c#L297 |
Essentially, for two partitions all odd sequence numbers are ''ota_0'' and even numbers are ''ota_1''. The formula is: | Essentially, for two partitions all odd sequence numbers are ''ota_0'' and even numbers are ''ota_1''. The formula is: | ||
Line 466: | Line 470: | ||
All our firmwares failed to run the ''esp_ota_mark_app_valid_cancel_rollback()'' to mark the ''otadata'' as valid. So in each case, they have become aborted and rolled back to the previously known good firmware. | All our firmwares failed to run the ''esp_ota_mark_app_valid_cancel_rollback()'' to mark the ''otadata'' as valid. So in each case, they have become aborted and rolled back to the previously known good firmware. | ||
- | The ''factory'' firmware isn't controlled by the ''otadata'', the rollback process or OTA at all. | + | The ''factory'' firmware isn't affected by ''otadata'', or the rollback process, or OTA at all. |
To finish up, let's take a last look at the raw otadata: | To finish up, let's take a last look at the raw otadata: | ||
Line 492: | Line 496: | ||
Have fun! | Have fun! | ||
+ | |||
+ | ===== Troubleshooting ===== | ||
+ | //The gen_esp32part.py command isn't found.// | ||
+ | |||
+ | For some reason, gen_esp32part.py is not added to your path by default when starting your ESP-IDF console. For Powershell add the following to line 59 of your ''frameworks\esp-idf-v5.2\export.ps1'' file: | ||
+ | |||
+ | function gen_esp32part.py { &python "$IDF_PATH\components\partition_table\gen_esp32part.py" $args } | ||
+ | |||
+ | For Windows Command Prompt, in ''frameworks\esp-idf-v5.2\export.bat'' add to line 66: | ||
+ | |||
+ | DOSKEY gen_esp32part.py=python.exe "%IDF_PATH%\components\partition_table\gen_esp32part.py" $* | ||
+ | |||
+ | For shell and fish terminals, it's the same deal. | ||
===== More reading ===== | ===== More reading ===== | ||
- https://blog.espressif.com/ota-updates-framework-ab5438e30c12 | - https://blog.espressif.com/ota-updates-framework-ab5438e30c12 | ||
- https://esp32.com/viewtopic.php?t=14939 | - https://esp32.com/viewtopic.php?t=14939 | ||
+ | |||
+ | ===== Thank yous ===== | ||
+ | Many thanks to boarchuz and craig from the [[https://discord.gg/wwK29JJV|Espressif MCUs]] discord server for their assistance with this article. | ||