embassy-rs/embassy · error
PLATFORM not initialized
Error message
PLATFORM not initialized
What it means
embassy-stm32-wpan's linklayer platform exposes PLATFORM, an Option<&'static Platform> that is populated during stack initialization (init/bring-up). get_platform() unwraps it with expect, so any platform callback invoked before the WPAN stack has been initialized panics with 'PLATFORM not initialized'.
Solutions
- Initialize the WPAN stack (the call that sets PLATFORM) before using any BLEPLAT/LINKLAYER crypto or RNG functions.
- Ensure task ordering: the init task must complete (PLATFORM stored) before radio/crypto tasks run.
- If a callback can fire early, defer it until after initialization or return an error instead of panicking.
Example fix
// before let key = BLEPLAT_PkaReadP256Key(...); // stack not yet initialized // after let mut ble = ble::init(...).await; // stores PLATFORM first let key = BLEPLAT_PkaReadP256Key(...);
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure stack init completed before using BLEPLAT helpers
// embassy API: hold a handle from init which proves initialization
let ble = embassy_stm32_wpan::init(config).expect("wpan init");
// only after `ble` exists, call RNG/PKA helpers Try / catch
// wrap early radio work so a pre-init call cannot panic the app
if stack_ready.load(Ordering::Acquire) {
let key = pka_read_key(...);
} else {
defmt::warn!("WPAN not initialized; skipping crypto call");
} Prevention
- Call WPAN/RNG/PKA helpers only after embassy_stm32_wpan::init has returned.
- Use the initialized stack handle (or a OnceFlag) to gate early interrupts.
- Sequence tasks so init completes before the radio/crypto tasks start.
When it happens
Trigger: Calling get_platform()-dependent functions (LINKLAYER_PLAT_GetRNG, BLEPLAT_RngGet, BLEPLAT_PkaStartP256Key, BLEPLAT_PkaReadP256Key, BLEPLAT_PkaStartDhKey, aes_ecb_encrypt) before the Wireless/BLE stack init has stored the Platform into the static — e.g. calling crypto/RNG plats very early at boot or in a static initializer.
Common situations: Using the WPAN AES/RNG helpers before stm32wb/wba BLE init completed; calling BLEPLAT callbacks from an early interrupt that fires before stack bring-up; a task ordering issue where the radio task runs before init.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Can only take the executor once
- CORE1 not responding
- Could not find any timer
- Could not find DEBUGSS peripheral
- Could not find NRST pin to cfg gate
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/55ca721368204973.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32-wpan/src/wba/linklayer_plat.rs:161
static RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO: AtomicBool = AtomicBool::new(false);
static AHB5_SWITCHED_OFF: AtomicBool = AtomicBool::new(false);
static RADIO_SLEEP_TIMER_VAL: AtomicU32 = AtomicU32::new(0);
// Critical-section restore token for IRQ enable/disable pairing.
// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.
static mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;
// Optional hardware RNG instance for true random number generation.
// The RNG peripheral pointer is stored here to be used by LINKLAYER_PLAT_GetRNG.
// This must be set by the application using `set_rng_instance` before the link layer requests random numbers.
pub(crate) static mut PLATFORM: Option<&'static Platform> = None;
pub(crate) static mut EVENT_CHANNEL: Option<zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket>> =
None;
const fn get_platform() -> &'static Platform {
unsafe { PLATFORM.as_ref().expect("PLATFORM not initialized") }
}
const fn get_channel() -> &'static mut zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket> {
unsafe { EVENT_CHANNEL.as_mut().expect("EVENT_CHANNEL not initialized") }
}
// ============================================================================
// AES-128 ECB Hardware Acceleration (Embassy driver)
// ============================================================================
/// Perform AES-128 ECB encryption using the Embassy AES driver.
fn aes_ecb_encrypt(key: &[u8; 16], input: &[u8; 16], output: &mut [u8; 16]) {
get_platform().borrow_aes(|aes| {
let cipher = AesEcb::new(key);
let mut ctx = aes.start(&cipher, Direction::Encrypt);
aes.payload_blocking(&mut ctx, input, output, true).unwrap();
aes.finish_blocking(ctx).unwrap();
});View on GitHub (pinned to 463a07b963)