Bootloader Design & Flash Layout
Relevant source files
The following files were used as context for generating this wiki page:
The Bootloader in the OpenAwair project serves as the primary gateway for firmware management on STM32F412-based Awair devices. Its critical responsibilities include managing Device Firmware Update (DFU) sessions, validating firmware integrity via CRC32 checksums, and determining whether the device should boot into the main application or remain in recovery mode for a firmware update.
The system is designed to prevent "bricking" by ensuring the bootloader remains functional even if application firmware is corrupted. It supports both manual DFU entry (via button hold) and software-triggered entry (via flash flags).
Sources: CLAUDE.md:73-82, host/src/firmware/bootloader/bootloader.ts:18-21
Boot Decision Logic
At system startup, the bootloader evaluates specific flags stored in the device's flash memory to decide the execution path. The decision is primarily driven by a dfuFlagKey. If this flag is set to '1', the system enters DFU mode; otherwise, it attempts to boot the application.
The logic ensures that the device can be recovered if the application becomes unresponsive by forcing a DFU state.
Sources: host/src/firmware/bootloader/bootloader.ts:23-26, CLAUDE.md:73-74
Flash Memory Layout
The flash memory of the STM32F412 is partitioned into distinct regions to isolate the bootloader from the application and provide space for persistent configuration.
| Region | Size (Approx) | Purpose |
|---|---|---|
| Bootloader | 64-128 KB | Handles BLE DFU and recovery; resides at base address. |
| Application | Remaining Flash | Main firmware logic, sensor tasks, and networking. |
| Storage/NVM | Specified Sectors | Stores CRC32, firmware size, DFU flags, and sensor baselines (e.g., SGP30). |
Sources: CLAUDE.md:58-61, CLAUDE.md:63-65, host/src/firmware/bootloader/bootloader.ts:7-11
Persistent Metadata
The bootloader relies on specific keys stored in the FlashStore to manage firmware state:
dfuFlagKey: Determines boot mode.firmwareCrcKey: Stores the expected CRC32 of the current firmware image.firmwareSizeKey: Stores the byte length of the firmware image.
Sources: host/src/firmware/bootloader/bootloader.ts:7-11, host/src/firmware/bootloader/bootloader.ts:40-44
Device Firmware Update (DFU) Protocol
The DFU process follows a structured sequence where a host client (e.g., a mobile app) streams the firmware in chunks over BLE. The protocol is modeled after Nordic's Secure DFU but customized for the OpenAwair architecture.
DFU Session Flow
- Initiation: The host sends metadata (size and expected CRC).
- Streaming: Data is sent in small chunks (default 128-512 bytes).
- Verification: Once all bytes are received, the bootloader calculates the CRC32 of the buffered image.
- Commit: If valid, the bootloader updates the flash metadata and clears the DFU flag.
Chunk Handling and Verification
The DfuSession class manages the state of an active update. It ensures that chunks do not exceed the allocated size and maintains a rolling count of received bytes.
// host/src/firmware/bootloader/dfu.ts:31-38
public acceptChunk(chunk: FirmwareChunk): void {
if (chunk.offset + chunk.data.length > this.size) {
throw new Error('Chunk exceeds firmware size');
}
this.buffer.set(chunk.data, chunk.offset);
this.received += chunk.data.length;
}
Final verification is performed using a CRC32 algorithm to ensure data integrity before the bootloader allows the application to execute.
Sources: host/src/firmware/bootloader/dfu.ts:44-53, host/src/shared/crc32.ts
Summary
The OpenAwair bootloader design prioritizes device safety and remote recoverability. By utilizing a dedicated flash region and a robust DFU protocol, it allows for seamless firmware updates via BLE while protecting the device from corruption through strict CRC32 validation and persistent state management in flash memory.