Key-Value Flash Storage
Relevant source files
The following files were used as context for generating this wiki page:
The Key-Value Flash Storage system provides a persistent abstraction layer for managing device state, configuration, and firmware metadata on OpenAwair devices. It serves as the primary mechanism for the bootloader and application to communicate state across reboots, such as DFU (Device Firmware Update) triggers and sensor calibration data.
In the TypeScript host tooling, this system is represented by the FlashStore interface, which provides a simple read/write API for string-based key-value pairs. This abstraction allows the firmware logic to remain decoupled from the specific physical flash memory implementation, supporting both in-memory testing and hardware-specific flash controllers.
Sources: host/src/firmware/storage/flash.ts:1-15, CLAUDE.md:58-62
Architecture and Interfaces
The storage system is built around a central interface that defines how data is persisted and retrieved.
FlashStore Interface
The FlashStore interface defines the contract for any storage implementation within the project. It requires two primary methods:
read(key: string): Retrieves a value associated with a key, returningundefinedif the key does not exist.write(key: string, value: string): Persists a string value against a specific key.
Sources: host/src/firmware/storage/flash.ts:1-4
Implementation: InMemoryFlashStore
For testing and simulation within the host environment, the InMemoryFlashStore class provides a volatile implementation using a standard JavaScript Map. This is used to validate bootloader logic and DFU flows without requiring physical hardware access.
The diagram above illustrates the relationship between the storage interface and its in-memory implementation used for host-side tooling and tests.
Sources: host/src/firmware/storage/flash.ts:1-15
Bootloader Integration
The storage system is critical for the Bootloader class, which relies on specific flash keys to make decisions about the device's startup sequence.
Boot Decision Logic
During the boot sequence, the bootloader reads a specific DFU flag from storage. If this flag is set to '1', the device enters DFU mode; otherwise, it attempts to boot the main application.
DFU Metadata Management
The storage system tracks firmware integrity and size using specific keys. This metadata is used to initialize a DfuSession and to verify the application image after a successful transfer.
Sources: host/src/firmware/bootloader/bootloader.ts:13-33
| Key (Configurable) | Description | Typical Value |
|---|---|---|
dfuFlagKey | Indicates if the device should enter DFU mode. | '1' (DFU), '0' (App) |
firmwareCrcKey | Stores the CRC32 checksum of the current firmware. | Numeric string (e.g., '1191942644') |
firmwareSizeKey | Stores the total byte size of the firmware image. | Numeric string |
Sources: host/src/firmware/bootloader/bootloader.ts:6-11, host/src/firmware/bootloader/bootloader.ts:35-43
The sequence shows how the Bootloader queries the Flash Storage to determine the next state and prepare for firmware updates.
Sources: host/src/firmware/bootloader/bootloader.ts:22-38
Application Use Cases
Beyond the bootloader, the application firmware utilizes flash storage for long-term data persistence.
Sensor Calibrations
As noted in the project overview, the SGP30 VOC sensor requires approximately 24 hours to auto-calibrate. The resulting baseline data must be persisted to flash every ~12 hours to ensure the sensor remains accurate across power cycles.
Device Configuration
Storage is used to maintain Wi-Fi credentials and MQTT connectivity settings, though the TypeScript implementation currently focuses on the interface for these flows.
Sources: CLAUDE.md:84-85, host/src/loader/wifi/provisioning.ts:1-20
Implementation Details
The storage system handles numeric data (like CRC32 and image size) as strings, requiring parsing when retrieved for logic operations.
// Example of committing firmware metadata to storage
// Sources: host/src/firmware/bootloader/bootloader.ts:40-44
public commitFirmware(image: FirmwareImage): void {
this.store.write(this.config.firmwareCrcKey, image.crc.toString(10));
this.store.write(this.config.firmwareSizeKey, image.bytes.length.toString(10));
this.store.write(this.config.dfuFlagKey, '0');
}
CRC32 Validation
Firmware images stored in flash or received via DFU are validated using a CRC32 checksum. The storage system facilitates this by keeping the "known-good" CRC value available for comparison during the boot process or after a DFU session is completed.
Sources: host/src/firmware/bootloader/dfu.ts:43-49, host/src/shared/crc32.ts:14-21
Conclusion
Key-Value Flash Storage in OpenAwair acts as the persistent backbone for device state management. By abstracting the storage mechanism through the FlashStore interface, the project ensures that critical data—ranging from boot flags and DFU metadata to sensor calibration baselines—can be handled consistently across both simulated host environments and the final STM32F412-based hardware.