Skip to content

Commit 246809d

Browse files
committed
article: MCUboot Getting Start Guide for ESP32
Article translated and updated from my original from Embarcados portal. Signed-off-by: Almir Okato <[email protected]>
1 parent 1ee8cff commit 246809d

File tree

5 files changed

+390
-0
lines changed

5 files changed

+390
-0
lines changed

.lycheeignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ https://www.theembeddedrustacean.com/subscribe
55
# content/blog/nuttx-adding-porting-an-app/index.md
66
https://github.com/ARMmbed/mbedtls/archive
77

8+
# content/blog/2025/12/mcuboot-getting-started/index.md
9+
https://embarcados.com.br/primeiros-passos-com-esp32-utilizando-mcuboot-como-bootloader/
10+
811
# Domains known to reject validation requests from GitHub Actions
912
https://www.kickstarter.com
1013
https://platform.openai.com
15.5 KB
Loading
37.1 KB
Loading
39.5 KB
Loading
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
---
2+
title: "MCUboot - Getting Started Guide for ESP32"
3+
date: 2025-12-16
4+
showAuthor: false
5+
authors:
6+
- "almir-okato"
7+
tags: ["Bootloader", "MCUboot", "NuttX", "Zephyr"]
8+
summary: "This guide provides a practical getting‑started walkthrough for using MCUboot on ESP32. It covers, step-by-step, environment setup, bootloader building, application configuration, flashing and also gives a overview through the MCUboot's execution flow"
9+
---
10+
11+
## Introduction
12+
13+
In the embedded world, firmware updates are essential and increasingly important. As IoT solutions grow exponentially in numbers and complexity, so does the concern to make devices secure and updatable in the field efficiently.
14+
15+
Some years ago, **MCUboot** emerged as an open source bootloader project for small and low-cost systems, designed to simplify and standardize solutions for these challenges. It started as an Apache Mynewt subproject when developers decided to separate the bootloader from OS development. Later, it was ported to **Zephyr RTOS** and became its default bootloader.
16+
17+
**Espressif Systems** has been expanding support for other **3rd party RTOSes** like **Zephyr** and **NuttX** as attractive options for a developer adopt within its SoCs. Since then, a port for **Espressif** SoCs has been developed in the **MCUboot** project.
18+
19+
This guide shows how to get started with **MCUboot** on your ESP32-based project (note: this is a translated and updated version of my original article in [**Embarcados** website](https://embarcados.com.br/primeiros-passos-com-esp32-utilizando-mcuboot-como-bootloader/)). It covers environment setup, required configuration in the application side, how to build the **MCUboot Espressif Port** bootloader and how to flash it into the device. The [ESP32 DevKitC](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html) board was used to prepare this guide.
20+
21+
{{< alert icon="eye" >}}
22+
The MCUboot-compatible application mentioned in this guide can be either a **NuttX RTOS** application or a **Zephyr RTOS** application built for Espressif SoCs with MCUboot compatibility configuration enable.
23+
24+
This guide assumes that you are familiar with building and configuring the chosen RTOS.
25+
{{< /alert >}}
26+
27+
## What is MCUboot?
28+
29+
As previously mentioned, **MCUboot** is an open source secure bootloader for 32-bit microcontrollers. The project defines a common infrastructure for secure boot and it covers:
30+
31+
- **Fault-tolerant updates**: the flash organization is well defined, using the concept of slots for placing the main bootable image and update image in different flash regions, with a scratch area to help the swapping process during firmware updates. This provides mechanisms for reliable firmware updates and also allows recovering and reverting if any problem during the process.
32+
- **Security**: image validation through hash checking and signature verification using asymmetric key algorithms like RSA 2048/3072, ECDSA and ed25519 ensures integrity and authenticity of firmware images.
33+
34+
The project aims to standardize these aspects comprehensively. **MCUboot** core features are independent from any operating system and hardware, relying on hardware porting layers from the target host OS.
35+
36+
### High-level overview of the boot process
37+
38+
{{< figure
39+
default=true
40+
src="./assets/mcuboot-process-overview1.webp"
41+
caption="MCUboot boot process overview"
42+
>}}
43+
44+
{{< figure
45+
default=true
46+
src="./assets/mcuboot-process-overview2.webp"
47+
caption="MCUboot boot process overview"
48+
>}}
49+
50+
**Important concepts:**
51+
52+
- **Primary slot** is where the main bootable image resides, code always runs from there.
53+
- **Secondary slot** is used for updates; it stores the incoming image and, after the swap, stores the original image to enable recovery if needed.
54+
- **Scratch region** is used to help image swap when updating.
55+
- A header and trailer are added to the image to track general information, swapping, and update states.
56+
57+
{{< alert icon="eye" >}}
58+
The booting process and concepts presented here are related to the **Espressif Port** current support to **MCUboot**. See more at the official [MCUboot documentation](https://docs.mcuboot.com/).
59+
{{< /alert >}}
60+
61+
## Setting the environment
62+
63+
First, prepare the development environment. This guide assumes the use of Linux (Ubuntu 20.04.2 LTS or later).
64+
65+
Ensure you have **Git**, **Python3**, **pip**, **CMake** and **Ninja** installed. If you don't, you can run the following (this step is optional):
66+
67+
```bash
68+
sudo apt update
69+
sudo apt upgrade
70+
sudo apt install git python3 python3-pip cmake ninja-build
71+
```
72+
73+
### **MCUboot Espressif Port** HAL
74+
75+
The **MCUboot Espressif Port** depends on HAL (Hardware Abstraction Layer) sources based on **ESP-IDF**. Then it is required to have either **ESP-IDF** itself installed, which allows building the project in a standalone way, or one of the Espressif's HAL sources used by **Zephyr RTOS** (`zephyrproject-rtos/hal_espressif/`) or **NuttX RTOS**
76+
(`espressif/esp-hal-3rdparty`). For both the later options, it is recommended using them only within their RTOS build system, since their sources revision may differ from what is currently expected.
77+
78+
**Installing ESP-IDF v5.1.6**
79+
80+
```bash
81+
git clone --recursive https://github.com/espressif/esp-idf.git
82+
```
83+
84+
```bash
85+
cd <IDF_PATH>
86+
git checkout v5.1.6
87+
```
88+
89+
```bash
90+
<IDF_PATH>/install.sh
91+
```
92+
93+
```bash
94+
. <IDF_PATH>/export.sh
95+
```
96+
97+
More information about **ESP-IDF** installation can be found [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#manual-installation).
98+
99+
### Clone and set up MCUboot
100+
101+
1. Clone the repository into a directory of your choice (other than **ESP-IDF** installation):
102+
103+
```bash
104+
git clone https://github.com/mcu-tools/mcuboot.git
105+
cd mcuboot
106+
```
107+
108+
2. Install the additional dependencies needed by **MCUboot**:
109+
110+
```bash
111+
pip3 install -r scripts/requirements.txt
112+
```
113+
114+
3. Update the **MbedTLS** submodule required by **MCUboot**:
115+
116+
```bash
117+
git submodule update --init --recursive ext/mbedtls
118+
```
119+
120+
4. For any images you may boot with **MCUboot**, you need to sign them using `imgtool`. This tool adds the **MCUboot** expected headers and trailers to the binary, signs the firmware, and can also generate the signing keys. `imgtool` can be found at `<MCUBOOT_DIR>/scripts/imgtool.py` (where `<MCUBOOT_DIR>` is the path of the cloned repository), or you can install it as follows (optional):
121+
122+
```bash
123+
pip3 install imgtool
124+
```
125+
126+
{{< alert icon="eye" >}}
127+
**MCUboot** repository includes some sample cryptographic keys for signing and testing the secure boot verification. It is crucial that you never use these keys in production since the private key is available in the **MCUboot** repository.
128+
See how to generate and manage cryptographic keys at the [imgtool documentation](https://docs.mcuboot.com/imgtool.html).
129+
{{< /alert >}}
130+
131+
## Building and flashing MCUboot on ESP32
132+
133+
With everything set, let's configure, build and flash the **MCUboot Espressif Port** bootloader.
134+
135+
1. Firstly set the flash layout organization in the `<MCUBOOT_DIR>/boot/espressif/port/esp32/bootloader.conf` file, it must match the same flash organization as the chosen RTOS. For example:
136+
137+
```text
138+
CONFIG_ESP_FLASH_SIZE=4MB
139+
CONFIG_ESP_BOOTLOADER_SIZE=0xF000
140+
CONFIG_ESP_BOOTLOADER_OFFSET=0x1000
141+
CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x20000
142+
CONFIG_ESP_APPLICATION_SIZE=0x150000
143+
CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x170000
144+
CONFIG_ESP_SCRATCH_OFFSET=0x3E0000
145+
CONFIG_ESP_SCRATCH_SIZE=0x1F000
146+
```
147+
148+
2. Compile and generate the ELF:
149+
150+
```bash
151+
cd <MCUBOOT_DIR>/boot/espressif
152+
cmake -DCMAKE_TOOLCHAIN_FILE=tools/toolchain-esp32.cmake -DMCUBOOT_TARGET=esp32 -DESP_HAL_PATH=<IDF_PATH> -DMCUBOOT_FLASH_PORT=/dev/ttyUSB0 -B build -GNinja
153+
```
154+
155+
```bash
156+
ninja -C build/
157+
```
158+
159+
3. Erase the device's flash:
160+
161+
```bash
162+
esptool.py -p <PORT> erase_flash
163+
```
164+
165+
4. Flash **MCUboot Espressif Port** bootloader to your board:
166+
167+
```bash
168+
ninja -C build/ flash
169+
```
170+
171+
Alternatively:
172+
173+
```bash
174+
esptool.py -p /dev/ttyUSB0 -b 921600 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x1000 build/mcuboot_esp32.bin
175+
```
176+
177+
> **Note:** You may adjust the USB port (`-p /dev/ttyUSB0`) and baud rate (`-b 921600`) according to your board connection.
178+
179+
5. Check the serial monitor on the UART port (the same port used to flash) and reset your **ESP32** board. This guide uses `picocom` tool, but any serial monitor tool can be used:
180+
181+
```bash
182+
picocom -b 115200 /dev/ttyUSB0
183+
```
184+
185+
Since no image has been flashed yet, you will see **MCUboot** waiting for an application image in the primary slot.
186+
187+
```text
188+
[esp32] [INF] *** Booting MCUboot build v2.3.0-rc2-3-g234c66e6 ***
189+
[esp32] [INF] [boot] chip revision: v3.0
190+
[esp32] [INF] [boot.esp32] SPI Speed : 40MHz
191+
[esp32] [INF] [boot.esp32] SPI Mode : DIO
192+
[esp32] [INF] [boot.esp32] SPI Flash Size : 4MB
193+
[esp32] [INF] [boot] Enabling RNG early entropy source...
194+
[esp32] [INF] Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
195+
[esp32] [INF] Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
196+
[esp32] [INF] Boot source: primary slot
197+
[esp32] [INF] Image index: 0, Swap type: none
198+
[esp32] [ERR] Unable to find bootable image
199+
```
200+
201+
## Preparing and flashing an application
202+
203+
You can use applications from either **Zephyr RTOS** or **NuttX RTOS**, ensuring **MCUboot** compatibility is enabled in whichever RTOS you use.
204+
205+
This section will quickly mention the required `Kconfigs` for setting a **NuttX RTOS** and a **Zephyr RTOS** application with **MCUboot** compatibility, assuming you are familiar with how to configure and build the chosen RTOS.
206+
207+
### NuttX RTOS
208+
209+
Enable the `CONFIG_ESP32_APP_FORMAT_MCUBOOT` configuration and check if the following config values matches what was set in the `<MCUBOOT_DIR>/boot/espressif/port/esp32/bootloader.conf`. For the example from this guide, `menuconfig` was configured as:
210+
211+
- System Type -> Bootloader and Image Configuration->
212+
- Enable MCUboot-bootable format (`CONFIG_ESP32_APP_FORMAT_MCUBOOT`): y
213+
- System Type -> SPI Flash Configuration ->
214+
- Application image primary slot offset (`CONFIG_ESPRESSIF_OTA_PRIMARY_SLOT_OFFSET`): 0x20000
215+
- Application image secondary slot offset (`CONFIG_ESPRESSIF_OTA_SECONDARY_SLOT_OFFSET`): 0x170000
216+
- Application image slot size (in bytes) (`CONFIG_ESPRESSIF_OTA_SLOT_SIZE`): 0x150000
217+
- Scratch partition offset (`CONFIG_ESPRESSIF_OTA_SCRATCH_OFFSET`): 0x3E0000
218+
- Scratch partition size (`CONFIG_ESPRESSIF_OTA_SCRATCH_SIZE`): 0x1F000
219+
220+
### Zephyr RTOS
221+
222+
Enable the `CONFIG_BOOTLOADER_MCUBOOT` configuration and check if the flash partitions on the `DTS` from your board matches what was set in the `<MCUBOOT_DIR>/boot/espressif/port/esp32/bootloader.conf`. For this example it is possible to set an overlay file with the following content:
223+
224+
```text
225+
&flash0 {
226+
partitions {
227+
boot_partition: partition@0 {
228+
label = "mcuboot";
229+
reg = <0x0 DT_SIZE_K(64)>;
230+
};
231+
232+
slot0_partition: partition@20000 {
233+
label = "image-0";
234+
reg = <0x20000 DT_SIZE_K(1344)>;
235+
};
236+
237+
slot1_partition: partition@170000 {
238+
label = "image-1";
239+
reg = <0x170000 DT_SIZE_K(1344)>;
240+
};
241+
242+
storage_partition: partition@3b0000 {
243+
label = "storage";
244+
reg = <0x3B0000 DT_SIZE_K(192)>;
245+
};
246+
247+
scratch_partition: partition@3e0000 {
248+
label = "image-scratch";
249+
reg = <0x3E0000 DT_SIZE_K(124)>;
250+
};
251+
};
252+
};
253+
```
254+
255+
### Signing and Flashing the Application
256+
257+
Before flashing, sign the application binary using `imgtool`. Replace `<APP_BIN>` with your application binary (e.g., `nuttx.hex` or `zephyr.bin`), and `signed.bin` will be the output signed image:
258+
259+
```bash
260+
python3 scripts/imgtool.py sign --align 4 -v 0 -H 32 --pad-header -S 0x00150000 <APP_BIN> signed.bin
261+
```
262+
263+
Alternatively, if you installed **imgtool** through **pip**:
264+
265+
```bash
266+
imgtool sign --align 4 -v 0 -H 32 --pad-header -S 0x00150000 <APP_BIN> signed.bin
267+
```
268+
269+
> **Note:** a **Zephyr** image with **MCUboot** compatibility doesn't need the `--pad-header` parameter.
270+
271+
Here is a quick look on what these `imgtool sign` parameters do:
272+
273+
- `--align 4`: Specify the flash device alignment as 4 bytes (32-bit word).
274+
- `-v 0`: Specify the image version (in this case, `0`).
275+
- `-H 32`: Specify the **MCUboot** header size to be added to the image binary.
276+
- `--pad-header`: Indicates to `imgtool` to add the **MCUboot** header into the beginning of the image binary, instead of overwritting it (the **Zephyr** build for some platforms already pads the binary beginning with 0s and may not need this parameter).
277+
- `-S 0x00150000`: Indicates the slot size so the tool can add the **MCUboot** trailer properly.
278+
279+
> **Note:** This step just adds the basic required header, however notice that we didn't add any cryptographic key for security verification yet. It will be covered in the next parts of this **MCUboot** guide series. Refer to the [imgtool documentation](https://docs.mcuboot.com/imgtool.html) for more information.
280+
281+
Flash the signed image to the device at the primary slot address:
282+
283+
```bash
284+
esptool.py -p /dev/ttyUSB0 -b 921600 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x20000 signed.bin
285+
```
286+
287+
Checking the serial monitor, **MCUboot** should now successfully load and boot the application from the primary slot.
288+
289+
**NuttX's nsh**:
290+
291+
```text
292+
[esp32] [INF] *** Booting MCUboot build v2.3.0-rc2-3-g234c66e6 ***
293+
[esp32] [INF] [boot] chip revision: v3.0
294+
[esp32] [INF] [boot.esp32] SPI Speed : 40MHz
295+
[esp32] [INF] [boot.esp32] SPI Mode : DIO
296+
[esp32] [INF] [boot.esp32] SPI Flash Size : 4MB
297+
[esp32] [INF] [boot] Enabling RNG early entropy source...
298+
[esp32] [INF] Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
299+
[esp32] [INF] Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
300+
[esp32] [INF] Boot source: primary slot
301+
[esp32] [INF] Image index: 0, Swap type: none
302+
[esp32] [INF] Disabling RNG early entropy source...
303+
[esp32] [INF] br_image_off = 0x20000
304+
[esp32] [INF] ih_hdr_size = 0x20
305+
[esp32] [INF] Loading image 0 - slot 0 from flash, area id: 1
306+
[esp32] [INF] DRAM segment: start=0x250a4, size=0xce4, vaddr=0x3ffb2010
307+
[esp32] [INF] IRAM segment: start=0x20080, size=0x5024, vaddr=0x40080000
308+
[esp32] [INF] start=0x40082048
309+
IROM segment aligned lma 0x00040000 vma 0x400d0000 len 0x012afc (76540)
310+
DROM segment aligned lma 0x00030000 vma 0x3f410000 len 0x003060 (12384)
311+
312+
NuttShell (NSH) NuttX-10.4.0
313+
nsh>
314+
```
315+
316+
**Zephyr's hello world**:
317+
318+
```text
319+
[esp32] [INF] *** Booting MCUboot build v2.3.0-rc2-3-g234c66e6 ***
320+
[esp32] [INF] [boot] chip revision: v3.0
321+
[esp32] [INF] [boot.esp32] SPI Speed : 40MHz
322+
[esp32] [INF] [boot.esp32] SPI Mode : DIO
323+
[esp32] [INF] [boot.esp32] SPI Flash Size : 4MB
324+
[esp32] [INF] [boot] Enabling RNG early entropy source...
325+
[esp32] [INF] Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
326+
[esp32] [INF] Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
327+
[esp32] [INF] Boot source: primary slot
328+
[esp32] [INF] Image index: 0, Swap type: none
329+
[esp32] [INF] Disabling RNG early entropy source...
330+
[esp32] [INF] br_image_off = 0x20000
331+
[esp32] [INF] ih_hdr_size = 0x20
332+
[esp32] [INF] Loading image 0 - slot 0 from flash, area id: 1
333+
[esp32] [INF] DRAM segment: start=0x25f10, size=0x1110, vaddr=0x3ffb0000
334+
[esp32] [INF] IRAM segment: start=0x20080, size=0x5e90, vaddr=0x40080000
335+
[esp32] [INF] start=0x400830e0
336+
I (327) boot: IROM : lma=00040000h vma=400d0000h size=036D8h ( 14040) map
337+
I (327) boot: DROM : lma=00030000h vma=3f400000h size=00E94h ( 3732) map
338+
I (343) boot: libc heap size 182 kB.
339+
I (343) spi_flash: detected chip: generic
340+
I (343) spi_flash: flash io: dio
341+
*** Booting Zephyr OS build v4.3.0-rc2-65-g03ce83a18fd5 ***
342+
Hello World! esp32_devkitc/esp32/procpu
343+
```
344+
345+
## Flash Organization
346+
347+
**MCUboot** defines a flash organization where a flash area can contain multiple executable images depending on its boot and update configuration. Each image area contains two image slots: a primary and a secondary. By default, the bootloader only runs an image from the primary slot. The secondary slot is where an incoming image is staged prior to being installed; then its content will be either swapped to the primary slot or overwrite it when updating.
348+
349+
Therefore, we can identify four types of flash areas in the layout:
350+
351+
| AREA | ID | DESCRIPTION |
352+
|------|----|----|
353+
| Bootloader | 0 | Bootloader region (MCUboot) |
354+
| Primary Slot | 1 | Main bootable image |
355+
| Secondary Slot | 2 | Staging area for firmware updates |
356+
| Scratch | 3 | Temporary area for image swapping |
357+
358+
**MCUboot** also supports multiple images, allowing you to define additional image areas with their own primary and secondary slots.
359+
360+
The layout information is stored in the `bootloader.conf` file from the **Espressif Port**. Addresses and sizes can be modified, but the following rules must be followed:
361+
362+
- Bootloader address must be kept since it's where **ESP32** jumps after reset by default.
363+
- None of the slots must overlap.
364+
- Primary and Secondary slots must be the same size.
365+
- Scratch area must be large enough to store at least the largest flash sector that will be swapped.
366+
- Both **MCUboot** and the application must be aware of this layout for correct operation.
367+
368+
{{< alert icon="eye" >}}
369+
Flash organization is configurable and must match between **MCUboot** and the application being booted to ensure proper operation.
370+
{{< /alert >}}
371+
372+
## Conclusion
373+
374+
**MCUboot** provides a solid structure and defines a standard flow for firmware updates and secure boot. These features are already implemented in the bootloader and can be easily enabled without many modifications when developing firmware.
375+
376+
Furthermore, as an open source project, **MCUboot** benefits from development by an interested and diverse community, resulting in faster issue resolution and active development.
377+
378+
In this guide, we covered how to build **MCUboot** bootloader for ESP32, how to sign an image, and how to organize flash properly. For more detailed information, refer to the [official MCUboot documentation](https://docs.mcuboot.com/).
379+
380+
The next step for this series is to understand how updates work in the **MCUboot** and use this feature appropriately.
381+
382+
## References
383+
384+
- https://embarcados.com.br/primeiros-passos-com-esp32-utilizando-mcuboot-como-bootloader/
385+
- https://docs.mcuboot.com/design.html
386+
- https://docs.mcuboot.com/readme-espressif.html
387+
- https://interrupt.memfault.com/blog/mcuboot-overview

0 commit comments

Comments
 (0)