194 lines
8.0 KiB
Markdown
194 lines
8.0 KiB
Markdown
This is a [USB DFU](https://www.usb.org/document-library/device-firmware-upgrade-11-new-version-31-aug-2004) (DFU mode) implementation for [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) based on [tinyUSB](https://docs.tinyusb.org/en/latest/index.html).
|
|
It allows flashing the firmware using [dfu-util](http://dfu-util.sourceforge.net/).
|
|
|
|
background
|
|
==========
|
|
|
|
ESP-ROM
|
|
-------
|
|
|
|
The ESP-S2 comes with a ROM bootloader that already allows you to flash over USB using the serial CDC ACM profile.
|
|
But this method does not let you restart into the main firmware.
|
|
The ROM bootloader USB stack even offers DFU capability, and you can flash using dfu-util.
|
|
But this one also does not let you restart into the main firmware.
|
|
|
|
To flash using USB serial (can't restart the device):
|
|
|
|
~~~
|
|
idf.py -p /dev/ttyACM0 flash
|
|
...
|
|
WARNING: ESP32-S2 chip was placed into download mode using GPIO0.
|
|
esptool.py can not exit the download mode over USB. To run the app, reset the chip manually.
|
|
To suppress this note, set --after option to 'no_reset'.
|
|
~~~
|
|
|
|
And to [flash using DFU](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/dfu.html):
|
|
|
|
~~~
|
|
# generate the DFU binary (not the same as the usual flash binary)
|
|
idf.py dfu
|
|
dfu-util --device 303a:0002 --download build/dfu.bin
|
|
# or
|
|
idf.py dfu-flash
|
|
~~~
|
|
|
|
Note: after detaching, the bootloader claims for be in runtime mode, but this is still the bootloader, and not the flashed firmware.
|
|
|
|
mode switch
|
|
-----------
|
|
|
|
To switch from the ESP32-S2 ROM bootloader back to the firmware flashed over USB, you need to press the reset button.
|
|
This is cumbersome when developing firmware, and sometimes impossible if the board is encased or installed remotely.
|
|
|
|
This USB DFU implementation allows to switch back to runtime mode without pressing a button.
|
|
The runtime firmware can also reboot into the DFU mode without pressing a button.
|
|
|
|
ESP USB CDC
|
|
-----------
|
|
|
|
You can change the console output using `idf.py menuconfig` under `Component config → ESP System Settings → Channel for console output` from the default UART0 to USB CDC.
|
|
After re-powering and re-plugging USB, the device appears as USB CDC ACM device.
|
|
Not only it the console over this serial port, but you can even use it to flash the device without pressing any button.
|
|
|
|
~~~
|
|
idf.py --port=/dev/ttyACM0 flash
|
|
~~~
|
|
|
|
The USB device also comes with DFU capabilities, and can be flashed using DFU, also without pressing on buttons:
|
|
|
|
Using the IDF:
|
|
|
|
~~~
|
|
idf.py dfu dfu-flash
|
|
~~~
|
|
|
|
Or using `dfu-util` directly:
|
|
|
|
~~~
|
|
idf.py dfu
|
|
dfu-util --device 303a:0002 --download build/dfu.bin
|
|
~~~
|
|
|
|
This can only be used with the USB CDC ACM profile, Espressif USB VID/PID.
|
|
It makes it simpler and is sufficient for most projects.
|
|
Our DFU implementation can be use with and USB configuration, allows custom USB VID/PID, and is faster.
|
|
It makes it a bit more complex, and needs more space, but allows using custom USB configuration.
|
|
|
|
install
|
|
=======
|
|
|
|
compile
|
|
-------
|
|
|
|
To compile the firmware, you need the [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html#installation).
|
|
Here I compile it for the [WEMOS S2 mini](https://www.wemos.cc/en/latest/s2/s2_mini.html) board (other board definitions are available in `hw/bsp/esp32s2/boards/`).
|
|
|
|
~~~
|
|
cd examples/device/dfu_freertos/
|
|
make BOARD=wemos_s2_mini all
|
|
~~~
|
|
|
|
This is equivalent to:
|
|
|
|
~~~
|
|
idf.py -B_build/wemos_s2_mini -DFAMILY=esp32s2 -DBOARD=wemos_s2_mini -DIDF_TARGET=esp32s2 build
|
|
~~~
|
|
|
|
flash
|
|
-----
|
|
|
|
To flash the compiled firmware:
|
|
|
|
~~~
|
|
idf.py -B_build/wemos_s2_mini -DFAMILY=esp32s2 -DBOARD=wemos_s2_mini -DIDF_TARGET=esp32s2 -p /dev/ttyACM0 flash
|
|
~~~
|
|
|
|
or to use esptool directly over the ROM USB DFU bootloader, without ESP-IDF:
|
|
|
|
~~~
|
|
esptool.py -p /dev/ttyACM0 --before no_reset --after no_reset --chip esp32s2 write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x1000 _build/wemos_s2_mini/bootloader/bootloader.bin 0x8000 _build/wemos_s2_mini/partition_table/partition-table.bin 0xd000 _build/wemos_s2_mini/ota_data_initial.bin 0x10000 _build/wemos_s2_mini/dfu_freertos.bin
|
|
~~~
|
|
|
|
This will flash the DFU firmware to the `factory` partition.
|
|
The DFU firmware will in turn flash the downloaded image onto the [OTA0](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/ota.html) partition.
|
|
For that, a custom [partition table](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/partition-tables.html) is used.
|
|
|
|
config
|
|
------
|
|
|
|
To further configure the build:
|
|
|
|
~~~
|
|
idf.py -B_build/wemos_s2_mini -DFAMILY=esp32s2 -DBOARD=wemos_s2_mini -DIDF_TARGET=esp32s2 menuconfig
|
|
~~~
|
|
|
|
runtime
|
|
-------
|
|
|
|
This firmware is just for the DFU mode.
|
|
The main application needs to implement the runtime mode separately.
|
|
|
|
To switch from runtime to DFU mode during detach, set the boot partition in `otadata` to `factory`, and restart the ESP.
|
|
|
|
force DFU
|
|
---------
|
|
|
|
The USB DFU firmware does not act as bootloader.
|
|
Meaning, the ESP bootloader does not start the USB DFU firmware, which in turn starts the main firmware.
|
|
The main firmware in the OTA0 partition is directly loaded by the ESP bootloader.
|
|
Thus is it up to the main firmware to start the DFU mode, as described in `runtime`.
|
|
|
|
In case the main firmware is defective, and does not allow to switch back to DFU mode, you can still force booting the USB DFU firmware.
|
|
For that, boot the ESP ROM bootloader (i.e. download mode), and invalidate the OTA data partition:
|
|
|
|
~~~
|
|
otatool.py --esptool-args after=no_reset_stub --port /dev/ttyACM0 erase_otadata
|
|
~~~
|
|
|
|
When restating the ESP by pressing the reset button, the ESP bootloader will start the firmware which is in the factory partition, which should be the DFU firmware previously flashed.
|
|
|
|
design choice
|
|
=============
|
|
|
|
ESP-IDF
|
|
-------
|
|
|
|
The [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) uses [tinyUSB](https://github.com/espressif/tinyusb), but only offers [few profiles](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_device.html) (e.g. CDC, MSC).
|
|
They are defined in 'esp-idf/components/tinyusb/additions', and re-use the tinyUSB device implementations.
|
|
But there is no DFU implementation (runtime or DFU mode).
|
|
|
|
TinyUSB
|
|
-------
|
|
|
|
[TinyUSB](https://github.com/hathach/tinyusb) is a generic USB stack for micro-controllers.
|
|
It [supports](https://docs.tinyusb.org/en/latest/reference/supported.html) ESP32-S2.
|
|
DFU is [implemented](https://github.com/hathach/tinyusb/tree/master/src/class/dfu) and there is an [example](https://github.com/hathach/tinyusb/tree/master/examples/device/dfu).
|
|
This example can't be used for ESP32-S2 though.
|
|
I've added freeRTOS to it so it can be used on the ESP32-S2.
|
|
|
|
bootloader
|
|
----------
|
|
|
|
The ESP-IDF allows you to have a [custom](https://github.com/espressif/esp-idf/tree/master/examples/custom_bootloader) [bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/bootloader.html) (2nd stage), but I was not able to add freeRTOS to it, required by the tinyUSB implementation.
|
|
|
|
factory
|
|
-------
|
|
|
|
This USB DFU implementation is flashed as a factory application image.
|
|
It will then flash the main firmware image into the [OTA0 partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/ota.html).
|
|
Thus it requires a custom [partition table](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/partition-tables.html), with a "small" factory partition, just one OTA application partition, and one OTA data partition.
|
|
The factory partition should be 200 KB.
|
|
This is super large for just a "bootloader" to flash firmware images, but since ESP32 often use large external flash memory, this is not too much of an issue.
|
|
Feel free to reuse the 'partitions.csv' file as example.
|
|
|
|
alternatives
|
|
============
|
|
|
|
TinyUF2
|
|
-------
|
|
|
|
[TinyUF2](https://github.com/adafruit/tinyuf2) is a UF2 bootloader by Adafruit.
|
|
It is based on TinyUSB, and supports ESP32-S2.
|
|
It uses the DFU name (Device Firmware Upgrade), but it's not the DFU specified by USB.
|
|
Instead it provides a MSC interface where you can copy the firmware binary file to.
|