STM32 Custom Bootloader v1.0

Minimal bootloader for STM32 — UART & USB

A small, self-contained bootloader for STM32 microcontrollers. It handles flash layout, erase, write, validation, and protection in one portable implementation.
The bootloader uses a simple communication protocol over UART or USB CDC. Firmware updates are performed using the Web Serial API directly from a web browser.
Tested on: STM32F103, STM32F407, STM32G474, STM32H743, STM32L476, STM32U575. Easily extended to other series.

Flashing Bootloader

Integrate into STM32CubeMX project

Add examples.h into includes of main.c:
/* USER CODE BEGIN Includes */

#include "examples.h"

/* USER CODE END Includes */
Insert APP_Boot(), APP_Init() and APP_Loop():
  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  APP_Boot();   /* <-- HERE */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */

  APP_Init();   /* <-- HERE */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    APP_Loop(); /* <-- and HERE */
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
Build and run from STM32CubeIDE with ST-Link connected. Rapid LED blinking confirms bootloader execution.

Compiling Application

Run from second half of flash

Switch examples by editing examples.h.
#define ACTIVE_EXAMPLE_NUM 2

#define FILE_1 "01_firmware_uploader.bin"
#define FILE_2 "02_led_control.bin"  /* SELECTED */
#define FILE_3 "03_device_configurator.bin"
#define FILE_4 "04_temperature_monitor.bin"
#define FILE_5 "05_fft_benchmark.bin"
Update STM32xxxx_FLASH.ld for second-half flash execution:
MEMORY
{
  RAM  (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 32K  /* 0x0000 → 0x8000 */
}
Update system_stm32fxxxx.c for second-half flash execution:
Output binary is located in the Debug folder.
Offset calculation: (64 × 1024 ÷ 2) = 32768 → 0x8000

Preparing Binary

Add CRC32 footer and encrypt using Web UI

The encrypted file contains a CRC32 footer and AES encryption, matching the format expected by the bootloader.
Firmware layout after processing:
┌── base firmware (STM32CubeIDE output)
│
├── ↓↓↓  Footer appended at end
│
├── version string (N bytes)
├── version string length N (1 byte)
├── CRC32 (4 bytes, little-endian)
├── 0x55 0x55 0x55 0x55
└── 0xFF ... (empty space)

Flashing Application

Upload firmware via Web UI

The bootloader receives the encrypted firmware over UART, writes it to flash, and validates the CRC32 checksum before executing the application.

USB Support

Flash over USB using CDC

Revert linker script ORIGIN to 0x800000 (beginning of flash memory):
/* Memories definition (STM32F103C8T6) */
MEMORY
{
  RAM  (xrw) : ORIGIN = 0x20000000, LENGTH = 20K

  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K  /* <-- when compiling as application */
  /* FLASH (rx) : ORIGIN = 0x8008000, LENGTH = 32K */  /* <-- when compiling as bootloader */

}
Build and run from STM32CubeIDE with ST-Link connected. This will flash the USB-based bootloader.
You can convert application to use USB peripheral using the same approach.
With USB enabled, the device appears as a serial port over USB CDC. Connect using a USB cable and use the same Web UI to flash firmware, replacing the UART connection and USB-to-RS232 adapter.
Note: Flashing over USB is not supported on STM32U5 series yet.

Configuring Encryption

Set a secure AES key for production

The default AES-128 key is a simple test value. For production use, replace it with a secure random key in both the firmware and the preparation tool.
/* In flash_helper.c: */
uint8_t key[16] = {
  1,2,3,4,5,6,7,8,
  9,10,11,12,13,14,15,16
};
/* In 00_firmware_prepare.html: */
// AES Encryption key
const key = [
  1, 2, 3, 4, 5, 6, 7, 8,
  9, 10, 11, 12, 13, 14, 15, 16
];
To flash unencrypted binaries, compile bootloader without #define USE_ENCRYPTION in flash_helper.c.
Even when encryption is disabled, binaries must still be processed with 00_firmware_prepare.html to append the CRC32 checksum.
Production notes: call FLASH_SetRDP(RDP_STATE_ENABLED) once to enable readout protection before releasing devices. To prevent the bootloader itself from being overwritten, enable write protection for REGION_BOOTLOADER with FLASH_SetWRP(WRP_STATE_ENABLED).

Communication Protocol

How the bootloader talks to the host

The bootloader communicates with the host using a simple command–response protocol over UART or USB CDC. All multi-byte values are sent little-endian.
Practical implementations of this protocol are included in the project. A working firmware uploader using the Web Serial API can be found in 01_firmware_uploader.html. An example protocol parser running on STM32 hardware is provided in 01_firmware_uploader.c.
0x10 – Get REGION_APPLICATION flash region information
TX:0x10
RX:0x10 + <region_size:4> + <stored_crc:4> + <calc_crc:4> + <version_len:1> + <version_string:version_len>
Returns the size of the application region, the CRC32 stored in flash, a live-calculated CRC32 of the current contents, and the length plus ASCII string of the firmware version.
0x20 – Erase REGION_APPLICATION and reset write pointer
TX:0x20
RX:0x20
Erases the entire REGION_APPLICATION flash space and sets the current write position to the start of the region (offset 0). Subsequent 0x30 writes will begin filling the region from this starting address.
0x30 – Write 32-byte data block and advance write pointer
TX:0x30 + <crc32:4> + <data:32>
RX:0x30 on success / 0xFF on error
Writes exactly 32 bytes of data to the current write position within the selected flash region. After a successful write, the internal write pointer is incremented by 32 bytes. The host must provide a CRC32 checksum calculated over the 32 data bytes. If the checksum is invalid or the flash write operation fails, the device returns 0xFF. On success, it returns 0x30.
0x40 – Jump to REGION_APPLICATION
TX:0x40
RX:0x40
Sends acknowledgment byte 0x40, then jumps to execute the application code located in REGION_APPLICATION.
© HexFlashLab.com