Talking to Espressif’s Bootloader

In my article about Espressif’s Automatic Reset, I briefly showed UART output from the bootloader, but did not go in more details. In this article, I want to go just a bit further, by showing some two-way interactions.

As usual, GND to GND, VCC to 5V, TX to RX and RX to TX

We’ll use the initial basic “real” UART setup. Note that I did not connect DTR/RTS to RST/IO0. This is intentional, because Espressif’s software will attempt to reset the board using these, expecting the transistor circuit mentioned before. If we try to connect them directly, the automatic reset will not work either. For the sake of clarity, we’ll just reset the board manually.

You can manually start the board in bootloader mode with the following steps:

  1. Press and hold the “BOOT” button (on the ESP32-S2-Saola-1RI board)
  2. Press and release the “RST” button
  3. Release the “BOOT” button

You can check that this works by running tio while doing it:

Note that you will want to exit tio before running the esptool commands below, or it will interfere.

Flash ID

I picked an arbitrary command for this tutorial, flash_id, which shows some information about the on-board flash memory. We’ll use Espressif’s official tool esptool.

Note: You can use espflash board-info with the same principles.

In this article, I will highlight in yellow the important parts.

No Stub

Let’s look at three lines in particular:

When the board starts in bootloader mode, it merely runs a different firmware than the user-provided one. That bootloader firmware is stored in read-only memory (ROM), so that it is always available, even if the user makes a mistake while uploading a new firmware. Without this, it would be easy to “brick” the chip. That is, put it in an unusable and unrecoverable state.

However, that also mean that this bootloader cannot be updated. If Espressif wants to develop new features in esptool, they might need to run some specific code on the chip. Thus, the ROM bootloader firmware has one very important feature: it lets the host computer send a new firmware to run immediately. This is the “stub”.

Once the stub has been uploaded, it will keep running until you reset the board:

By default, esptool will upload the stub at every run, but we can actually run flash_id without that stub. To test this, we add --no-stub to the command.

Note: The previous command without –no-stub already uploaded a stub. To properly test –no-stub, you’ll need to reset the board. But you won’t see any difference in the output.

The three lines that mentioned a stub are gone, but the rest of the output is unchanged.

No reset

Now, I said that we did not connect DTR and RTS to avoid complications. But esptool is still trying to do it to reset the board after the command has been run:

We can prevent this with --after=no_reset:

In fact, esptool also tries to reset the board before running the command, to put it in bootloader automatically mode. This is mostly the whole point of Espressif’s Automatic Reset. There is no line in the output telling us that this is happening. However, if we add --before=no_reset, we can see a warning:

Note that, even with --before=no_reset --no-stub --after=no_reset, we cannot connect DTR/RTS to RST/IO0, because Linux always toggles DTR & RTS when opening the serial device, thus asserting RST (setting it electrically low) and preventing it to run.

No sync

There is actually one remaining option which is a bit less obvious.

For maximum reliability, the chip will try to detect the host’s baud rate. That is, you do not actually have to use 115200 as the baud rate:

You can see this happening if you connect another UART adapter instead of the ESP32-S2-Saola-1RI board (GND to GND, TX to RX, RX to TX, but do not connect VCC). esptool will of course fail to talk to the chip:

However, some weird stuff will show up in the second UART device:

You can display as hexadecimal ASCII codes:

This is the same SLIP command repeated several times:

I am not sure about 07 07 12 20, but the point of the repeated 55 byte is to provide a very easily identified pattern for the chip to recognize without knowing the baud rate. Note that 0x55 is 01010101 in binary.

Since the chip might already be “synced”, this still need to be a valid SLIP message, not just a bare series of 55.

In any case, we can use no_reset_no_sync to skip all of this. The command will only succeed if the sync command was run previously:

Otherwise, it will fail to talk to the stub:

Higher baud rates

For more trivia, that always uses at most a value of 115200 baud. If you use a higher value, it will sync at 115200 baud, and then send a separate command to raise the baud rate:

Leave a Reply

Your email address will not be published. Required fields are marked *