dankamongmen / dankdryer

firmware for the world's best filament dryer
Apache License 2.0
6 stars 0 forks source link

support OTA #17

Open dankamongmen opened 2 months ago

dankamongmen commented 2 months ago

we'll need be able to load new versions of firmware onto the ESP32-S3. once we've got wifi working (see #16), add OTA capabilities.

dankamongmen commented 2 months ago

this looks pretty complicated, with lots of partitioning and such. ideally we'll find a trustworthy library to handle this.

dankamongmen commented 2 months ago

are we going to have the device pull OTA from the internet, or do we expect it to be sideloaded, or need we support both? pulling it over the internet will require PKI--at the very least, TLS and a cert store on the device. i think we have to support sideload given our "no cloud" guarantee. so let's handle that first. ideally we would have a random password per device, tied to the serial number, but that requires infrastructure as well...

dankamongmen commented 2 months ago

yeah i think sideloading is the way to go, at least at first. we can optionally check to see if there's new firmware, and if we find it, let the user know. even that, though, would require PKI.

dankamongmen commented 2 months ago

https://github.com/arduino-libraries/Arduino_ESP32_OTA explicitly claims esp32-s3 support.. but appears to want to pull it over the internet

dankamongmen commented 2 months ago

https://github.com/gb88/BLEOTA looks nice for using BLE ala nordic's DTU, but there's only one radio on esp32, so you can't use wifi and bluetooth at the same time. we'd probably want to switch to BLE update mode, and if we timeout, switch wifi back on. sounds pretty complex. let's keep looking...

dankamongmen commented 1 week ago

we're now using CONFIG_PARTITION_TABLE_TWO_OTA via menuconfig, which ought generate the necessary partitions to support simple OTA.

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/partition-tables.html

dankamongmen commented 1 week ago

we needed step up to flavor country aka CONFIG_PARTITION_TABLE_TWO_OTA_LARGE=y. i verify that we have two partitions of equal size, one (the boot default) called ota_0, and the other ota_1:

[schwarzgerat](0) $ for i in ota_0 ota_1 ; do /home/dank/src/esp-idf/components/partition_table/parttool.py --quiet --port /dev/ttyACM1 get_partition_info   --part_list --partition-name $i | tail -n 1 ; done 
0x20000 0x1a9000
0x1d0000 0x1a9000
[schwarzgerat](0) $ 

size is 0x1a9000, and offsets are 0x20000 and 0x1d0000.

dankamongmen commented 1 week ago

both of these partitions are type 0 aka "app". subtypes are 16 and 17:

[schwarzgerat](0) $ for i in ota_0 ota_1 ; do /home/dank/src/esp-idf/components/partition_table/parttool.py --quiet --port /dev/ttyACM1 get_partition_info  --info subtype --partition-name $i | tail -n 1 ; done 
16
17
[schwarzgerat](0) $ 

which raises a question: will we use one shared NVS area? if so, i think we need write that as its own partition, and configure NVS to use it somehow.

dankamongmen commented 1 week ago

hrmmm according to the csv we ought have five partitions:

[schwarzgerat](0) $ cat /home/dank/src/esp-idf/components/partition_table/partitions_two_ota_large.csv
# Name,   Type, SubType, Offset,   Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     ,        0x6000,
otadata,  data, ota,     ,        0x2000,
phy_init, data, phy,     ,        0x1000,
ota_0,    app,  ota_0,   ,        1700K,
ota_1,    app,  ota_1,   ,        1700K,
[schwarzgerat](0) $ 
dankamongmen commented 1 week ago

hrrmmm, and indeed we do:

[schwarzgerat](0) $ for i in nvs otadata phy_init ota_0 ota_1 ; do /home/dank/src/esp-idf/components/partition_table/parttool.py --quiet --port /dev/ttyACM1 get_partition_info   --part_list --partition-name $i | tail -n1 ; done
0x9000 0x6000
0xf000 0x2000
0x11000 0x1000
0x20000 0x1a9000
0x1d0000 0x1a9000
[schwarzgerat](0) $ 
dankamongmen commented 1 week ago

so it looks like we just need call esp_ota_begin() with the next OTA partition, call esp_ota_write() (possibly many times), call esp_ota_end() if that succeeds, then call esp_ota_set_boot_partition() and reboot.

this, however, only writes one partition. do we want to write both, so that one can fail and we still run the newer code?