Source project: https://github.com/adafruit/tinyuf2
-
export your ESP-IDF path (
release/v4.4
) -
cd to project
cd ports/espressif
-
build and flash with
idf.py -DBOARD=espressif_saola_1_wrover build flash
Please change
espressif_saola_1_wrover
to your board
You can also merge your bin using:
esptool.py --chip esp32s2 merge_bin --output uf2_bootloder.bin 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0xe000 build/ota_data_initial.bin 0x2d0000 build/tinyuf2.bin
Then write from
0x0
:
esptool.py --chip esp32s2 write_flash --flash_mode dio 0x0 uf2_bootloder.bin
ESP32S2 have different address with ESP32S3, please check the address infomation after build
To create your own UF2 file, simply use the Python conversion script on a .bin file, specifying the family id as ESP32S2
, ``ESP32S3` or their magic number as follows. Note you must specify application address of 0x00 with the -b switch, the bootloader will use it as offset to write to ota partition.
-
add convert script to path
export PATH=$PATH:./utils
-
convert as follow
uf2conv.py firmware.bin -c -b 0x00 -f ESP32S2 uf2conv.py firmware.bin -c -b 0x00 -f 0xbfdd4eee uf2conv.py firmware.bin -c -b 0x00 -f ESP32S3 uf2conv.py firmware.bin -c -b 0x00 -f 0xc47e5767
There are a few ways to enter UF2 mode:
- There is no
ota application
and/orota_data
partition is corrupted PIN_BUTTON_UF2
is gnd when 2nd stage bootloader indicator is on e.g RGB led = Purple. Note: since most ESP32S2 board implementGPIO0
as button for 1st stage ROM bootloader, it can be used for dual-purpose button here as well. The difference is the pressing order:- Holding
GPIO0
then reset -> ROM bootloader - Press reset, see indicator on (purple RGB) then press
GPIO0
-> UF2 bootloader
- Holding
PIN_DOUBLE_RESET_RC
GPIO is attached to an 100K resistor and 1uF Capacitor to serve as 1-bit memory, which hold the pin value long enough for double reset detection. Simply press double reset to enter UF2- Request by application using system reset reason with hint of
0x11F2
. Reset reason hint is different than hardware reset source, it is written to RTC's store6 register and hold value through a software reset. Since Espressif only uses an dozen of value inesp_reset_reason_t
, it is safe to hijack and use 0x11F2 as reset reason to enter UF2 using following snippet.#include "esp_private/system_internal.h" void reboot_to_uf2(void) { // Check out esp_reset_reason_t for other Espressif pre-defined values enum { APP_REQUEST_UF2_RESET_HINT = 0x11F2 }; // call esp_reset_reason() is required for idf.py to properly links esp_reset_reason_set_hint() (void) esp_reset_reason(); esp_reset_reason_set_hint(APP_REQUEST_UF2_RESET_HINT); esp_restart(); }
After 1st stage ROM bootloader runs, which mostly checks GPIO0 to determine whether it should go into ROM DFU, 2nd stage bootloader is loaded. It is responsible for determining and loading either UF2 or user application (OTA0, OTA1). This is the place where we added detection code for entering UF2 mode mentioned by above methods.
Unfortunately ESP32S2 doesn't have a dedicated reset pin, but rather using power pin (CHIP_PU) as way to reset. This makes it impossible to use any RAM (internal and PSRAM) to store the temporary double reset magic. However, using an resistor and capacitor attached to a GPIO, we can implement a 1-bit memory to hold pin value long enough for double reset detection.
TODO guide and schematic as well as note for resistor + capacitor.
UF2 is actually a factory application which is pre-flashed on the board along with 2nd bootloader and partition table. When there is no user application or 2nd bootloader "double reset alternative" decide to load uf2. Therefore it is technically 3rd stage bootloader.
It will show up as mass storage device and accept uf2 file to write to user application partition. UF2 bootloader will always write/update firmware to ota_0 partition, since the actual address is dictated by partitions.csv, uf2 file base address MUST be 0x00, the uf2 will parse the partition table and start writing from address of ota_0. It also makes sure there is no out of partition writing.
After complete writing, uf2 will set the ota0 as bootable and reset, and the application should be running in the next boot.
NOTE: uf2 bootloader, customized 2nd bootloader and partition table can be overwritten by ROM DFU and/or UART uploading. Especially the idf.py flash
which will upload everything from the user application project. It is advisable to upload only user application only with idf.py app-flash
and leave other intact provided the user partition table matched this uf2 partition.
Following is typical partition for 4MB flash, check out the partition-xMB.csv
for details.
# Name, Type, SubType, Offset, Size, Flags
# bootloader.bin,, 0x1000, 32K
# partition table, 0x8000, 4K
nvs, data, nvs, 0x9000, 20K,
otadata, data, ota, 0xe000, 8K,
ota_0, 0, ota_0, 0x10000, 1408K,
ota_1, 0, ota_1, 0x170000, 1408K,
uf2, app, factory,0x2d0000, 256K,
ffat, data, fat, 0x310000, 960K,