Now Reading
Exploring a Customized ESP32 Bootloader · Daniel Mangum

Exploring a Customized ESP32 Bootloader · Daniel Mangum

2023-04-09 10:50:03

I just lately acquired an
module, and, as I are inclined to do, jumped proper into studying about how the system
boots and the way the (fairly good!) tooling Espressif
provides works. Now we have sometimes used QEMU within the
RISC-V Bytes sequence, however
getting our fingers on bodily {hardware} begins to make issues really feel a bit extra
actual. On this first publish on the ESP32, we’ll do some fundamental setup and take a look at a
easy customized bootloader.


The system boots when energy is equipped. Due to this, to watch the logs,
you’ll doubtless have to concern a reset after opening the serial port with a software
equivalent to minicom. With the system related to your machine, try to be ready
to search out the serial port underneath /dev/ttyUSB*. On my machine it was
/dev/ttyUSB0. The baud charge is 115200, which is the default for minicom.

$ minicom -D /dev/ttyUSB0

If we then press the RST button, we must always see output. Earlier than overwriting, a
demo was pre-programmed on my module, and it printed some ASCII artwork over the
serial port.

Espressif offers tooling that makes constructing tasks for any ESP32 module
a lot easier. Although definitely not strictly required, Espressif extremely
recommends using the esp-idf (ESP IOT Development
. Within the root of the
repository, you’ll discover directions to get began with one of many set up
scripts. As traditional, I’m on a Linux machine, so I used the

$ ./set
Detecting the Python interpreter
Checking "python3" ...
Python 3.8.10
"python3" has been detected
Checking Python compatibility
Putting in ESP-IDF instruments
Present system platform: linux-amd64
Chosen targets are: esp32c2, esp32c6, esp32s2, esp32c3, esp32, esp32s3, esp32h4, esp32h2
Putting in instruments: xtensa-esp-elf-gdb, riscv32-esp-elf-gdb, xtensa-esp32-elf, xtensa-esp32s2-elf, xtensa-esp32s3-elf, riscv32-esp-elf, esp32ulp-elf, openocd-esp32, esp-rom-elfs
Skipping xtensa-esp-elf-gdb@12.1_20221002 (already put in)
Skipping riscv32-esp-elf-gdb@12.1_20221002 (already put in)
Skipping xtensa-esp32-elf@esp-12.2.0_20230208 (already put in)
Skipping xtensa-esp32s2-elf@esp-12.2.0_20230208 (already put in)
Skipping xtensa-esp32s3-elf@esp-12.2.0_20230208 (already put in)
Skipping riscv32-esp-elf@esp-12.2.0_20230208 (already put in)
Skipping esp32ulp-elf@2.35_20220830 (already put in)
Skipping openocd-esp32@v0.11.0-esp32-20221026 (already put in)
Skipping esp-rom-elfs@20230113 (already put in)
Putting in Python setting and packages
Putting in collected packages: esp-idf-monitor
  Making an attempt uninstall: esp-idf-monitor
    Discovered current set up: esp-idf-monitor 1.0.1
    Uninstalling esp-idf-monitor-1.0.1:
      Efficiently uninstalled esp-idf-monitor-1.0.1
Efficiently put in esp-idf-monitor-1.0.0
All accomplished! Now you can run:

  . ./

By default, it will set up the toolchains for all targets (or skip in the event that they
are already current as proven above). It is going to additionally set up supporting instruments, such
as and esptool. You may check out all the pieces put in in

$ ls ~/.espressif/instruments/
esp32ulp-elf/        esp-rom-elfs/        openocd-esp32/       riscv32-esp-elf/     riscv32-esp-elf-gdb/ xtensa-esp32-elf/    xtensa-esp32s2-elf/  xtensa-esp32s3-elf/  xtensa-esp-elf-gdb/

$ ls ~/.espressif/python_env/idf5.1_py3.8_env/bin/
activate               allmodconfig           doesitcache               menuconfig             pip                    pyserial-miniterm      savedefconfig
activate.csh           allnoconfig            esp-coredump           futurize               normalizer             pip3                   pyserial-ports         setconfig          allyesconfig             genconfig              oldconfig              pip3.8                 python                 tqdm
Activate.ps1           compote        guiconfig              olddefconfig           __pycache__/           python3                west
alldefconfig           defconfig               listnewconfig          pasteurize             pykwalify        

So as to add the required instruments to your $PATH, the corresponding export script can
be sourced.

$ . ./ 
Detecting the Python interpreter
Checking "python3" ...
Python 3.8.10
"python3" has been detected
Checking Python compatibility
Checking different ESP-IDF model.
Utilizing a supported model of software cmake present in PATH: 3.16.3.
Nevertheless the advisable model is 3.24.0.
Including ESP-IDF instruments to PATH...
Utilizing a supported model of software cmake present in PATH: 3.16.3.
Nevertheless the advisable model is 3.24.0.
Checking if Python packages are updated...
Constraint file: /house/dan/.espressif/espidf.constraints.v5.1.txt
Requirement information:
 - /house/dan/code/
Python being checked: /house/dan/.espressif/python_env/idf5.1_py3.8_env/bin/python
Python necessities are happy.
Added the next directories to PATH:
Finished! Now you can compile ESP-IDF tasks.
Go to the venture listing and run: construct

As indicated within the output, we are actually prepared to begin constructing!

The boot course of is described intimately within the ESP32-C3 API
The principle takeaway is that booting is a two stage course of, the place the primary stage
bootloader, which is saved in ROM and can’t be modified, masses the second
stage one. The second stage bootloader lives in flash reminiscence at offset 0x0,
however is loaded into RAM by the primary stage bootloader.


Earlier than diving deeper, it’s helpful to know the varied parts of the
ESP32-C3-DevKitC-02. The ESP32-C3 is the system-on-chip (SoC), however lives inside
of the ESP32­-C3-­WROOM-­02 module. The module surrounds the SoC with
peripherals, equivalent to SPI flash and a PCB Antenna, enabling the mixed unit to
make the most of WiFi, Bluetooth LE, and extra. The module itself is surrounded by different
peripherals on the event equipment PCB, such because the micro-USB port and
USB-to-UART bridge, making it a lot simpler to work together with from our host


With a view to overwrite the second stage bootloader in flash, we’ll have to
talk with the ESP32-C3, which can then discuss over the SPI bus to the
flash that lives beside it within the WROOM module.

One of many instruments put in throughout setup was Although a lot of the
documentation makes use of, lots of the instructions it provides are simply wrapping and passing mandatory flags. The ESP32-C3 will be configured to
boot in “serial
which implements a serial

with help for a wide range of instructions that enable for operations equivalent to studying
and writing to flash. Curiously, by default will load a stub

that implements the identical protocol, however has some optimizations and extra
options. You may select to skip loading the stub bootloader by passing the
--no-stub argument to any command.

The serial protocol relies on the Serial Line Internet
. The
offers the complete specification for packet format, however the normal construction for
instructions and responses are reproduced under.

Command Packets

Byte Identify Remark
0 Course All the time 0x00 for requests
1 Command Command identifier (see Instructions).
2-3 Dimension Size of Knowledge discipline, in bytes.
4-7 Checksum Easy checksum of a part of the info discipline (solely used for some instructions, see Checksum).
8..n Knowledge Variable size knowledge payload (0-65535 bytes, as indicated by Dimension parameter). Utilization is dependent upon particular command.

Response Packets

Byte Identify Remark
0 Course All the time 0x01 for responses
1 Command Identical worth as Command identifier within the request packet that triggered the response
2-3 Dimension Dimension of knowledge discipline. A minimum of the size of the Standing Bytes (2 or 4 bytes, see under).
4-7 Worth Response worth utilized by READ_REG command (see under). Zero in any other case.
8..n Knowledge Variable size knowledge payload. Size indicated by “Dimension” discipline.

Command sequences used for writing

comply with an analogous sample of a single start command, adopted by some variety of
knowledge instructions, then a single finish command. This sample is used to each load
the stub bootloader and, subsequently, load the second stage bootloader. The
former is written to RAM, whereas the latter is written to flash. The instructions for
writing to RAM are:

  • MEM_BEGIN (0x05)
  • MEM_DATA (0x07)
  • MEM_END (0x06)

The MEM_END command helps supplying an execute flag and an deal with in RAM
(every 32-bit phrases) that the chip will start executing directions at if
execute flag is ready. The instructions for writing to flash are:

  • FLASH_BEGIN (0x02)
  • FLASH_DATA (0x03)
  • FLASH_END (0x04)

The FLASH_END command can provide a single 32-bit phrase and if the worth is 0
the chip will reboot.

Overriding the Second Stage Bootloader