diff --git a/fds.md b/fds.md new file mode 100644 index 0000000..aac4af7 --- /dev/null +++ b/fds.md @@ -0,0 +1,228 @@ +Alright, let’s do a **minimal, no-magic FDS example** that actually works for +**nRF52840 + SoftDevice + “user might pull power instantly”**. + +I’ll keep this focused on *one tiny config record*. + +--- + +## What this example assumes + +* You already have **SoftDevice enabled** +* You can handle **SoftDevice events** somewhere in your app +* You want to store ~5–6 bytes reliably + +This is **pure SoftDevice FDS**, no Embassy storage abstractions. + +--- + +## 1️⃣ Define your config format + +Keep it boring and explicit: + +```rust +#[repr(C)] +#[derive(Clone, Copy)] +struct Config { + magic: u8, + version: u8, + data: [u8; 4], +} + +const CONFIG_MAGIC: u8 = 0x42; +const CONFIG_VERSION: u8 = 1; +``` + +Total: **6 bytes** 👍 + +--- + +## 2️⃣ FDS constants + +These must be **non-zero** and stable forever: + +```rust +const FDS_FILE_ID: u16 = 0x1111; +const FDS_RECORD_KEY: u16 = 0x2222; +``` + +--- + +## 3️⃣ Initialize FDS + +Call this **after SoftDevice is enabled**: + +```rust +use nrf_softdevice::raw; + +pub fn fds_init() { + unsafe { + let ret = raw::fds_init(); + assert_eq!(ret, raw::NRF_SUCCESS); + } +} +``` + +⚠️ FDS is async — init finishes when you receive `FDS_EVT_INIT`. + +--- + +## 4️⃣ Handling FDS events (important!) + +You **must** forward SoftDevice events to FDS: + +```rust +pub fn on_sd_evt(evt: *const raw::ble_evt_t) { + unsafe { + raw::fds_sys_evt_handler(evt as u32); + } +} +``` + +And somewhere in your SD event handler: + +```rust +match evt_id { + raw::FDS_EVT_INIT => { + // FDS ready + } + raw::FDS_EVT_WRITE => { + // Write completed safely + } + raw::FDS_EVT_UPDATE => { + // Update completed + } + _ => {} +} +``` + +You don’t need to block — just observe completion. + +--- + +## 5️⃣ Read config from flash + +```rust +pub fn load_config() -> Option { + unsafe { + let mut desc = core::mem::zeroed::(); + let mut token = core::mem::zeroed::(); + + let ret = raw::fds_record_find( + FDS_FILE_ID, + FDS_RECORD_KEY, + &mut desc, + &mut token, + ); + + if ret != raw::NRF_SUCCESS { + return None; + } + + let mut record = core::mem::zeroed::(); + raw::fds_record_open(&desc, &mut record); + + let cfg = *(record.p_data as *const Config); + + raw::fds_record_close(&desc); + + if cfg.magic == CONFIG_MAGIC && cfg.version == CONFIG_VERSION { + Some(cfg) + } else { + None + } + } +} +``` + +On boot: + +* If `None` → use defaults +* If `Some(cfg)` → you’re golden + +--- + +## 6️⃣ Write / update config (power-loss safe) + +This is the *core value* of FDS. + +```rust +pub fn save_config(cfg: &Config) { + unsafe { + let record = raw::fds_record_t { + file_id: FDS_FILE_ID, + key: FDS_RECORD_KEY, + data: raw::fds_record_chunk_t { + p_data: cfg as *const _ as *const core::ffi::c_void, + length_words: (core::mem::size_of::() + 3) / 4, + }, + }; + + let mut desc = core::mem::zeroed::(); + let mut token = core::mem::zeroed::(); + + let ret = raw::fds_record_find( + FDS_FILE_ID, + FDS_RECORD_KEY, + &mut desc, + &mut token, + ); + + if ret == raw::NRF_SUCCESS { + raw::fds_record_update(&mut desc, &record); + } else { + raw::fds_record_write(&mut desc, &record); + } + } +} +``` + +### Why this is safe 🔒 + +* Old record stays valid until new one is fully written +* Power loss = either old config or new config +* Never corrupted data + +--- + +## 7️⃣ BLE write flow (typical) + +```text +BLE write received +→ validate data +→ build Config +→ save_config() +→ wait for FDS_EVT_UPDATE +→ ACK to user +``` + +If power dies *anywhere* → still safe. + +--- + +## 8️⃣ Flash usage (don’t forget this!) + +In your SoftDevice config: + +* Enable **FDS** +* Reserve **at least 2 pages** (3 is safer) + +FDS will garbage-collect automatically. + +--- + +## TL;DR (real talk) + +For your exact requirements: + +* **Yes, this is the right solution** +* **Yes, it’s more code than 6 bytes deserve** +* **But it’s bulletproof** + +If you want next: + +* 🧵 Wrap this in an **Embassy async interface** +* 🧪 Add CRC / version migration +* 🔄 Reduce boilerplate further +* 🧠 Explain FDS GC & page sizing + +Just say the word.