embassy-rs/embassy · error

OTP not present

Error message

OTP not present

What it means

Generated into the flash driver: when no OTP region is present in the chip's memory map, `otp` compiles to `panic!("OTP not present")`. It fires at runtime when user code reads the OTP (one-time-programmable) flash area on a chip where embassy-stm32 has no OTP base address.

Solutions

  1. Gate OTP usage behind a chip check — only read OTP on parts known to expose it.
  2. Verify the target chip actually has an OTP region and that embassy-stm32's metadata includes it; update the chip data if it's missing.
  3. Replace OTP reads with another storage (e.g. a flash sector) on chips without OTP.

Example fix

// before
let mut otp = flash.in_otp().unwrap();
otp.read(0, &mut buf);
// after
#[cfg(feature = "chip-with-otp")]
{
    let mut otp = flash.in_otp().unwrap();
    otp.read(0, &mut buf);
}
Defensive patterns

Strategy: validation

Validate before calling

// feature-gate OTP use
#[cfg(feature = "has-otp")]
fn read_otp(flash: &mut Flash) { let mut otp = flash.in_otp().unwrap(); otp.read(0, &mut buf); }

Prevention

When it happens

Trigger: Calling `flash.in_otp()` / OTP region reads on a chip whose memory-map data has no region named "OTP", so `otp_base` stays None in build.rs.

Common situations: Porting code that reads factory OTP (e.g. MAC addresses) from one STM32 family to a chip that lacks a modeled OTP region; missing metadata for newly supported chips.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/5099d998b292cdd1. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/build.rs:547

        let mut otp_base = None;
        for region in flash_memory_regions.iter() {
            if region.name == "BANK_1" || region.name == "BANK_1_REGION_1" {
                bank_1_base = Some(region.address);
            } else if region.name == "BANK_2" || region.name == "BANK_2_REGION_1" {
                bank_2_base = Some(region.address);
            } else if region.name == "OTP" {
                otp_base = Some(region.address);
            }
        }
        let bank_1 = bank_1_base
            .map(|a| quote!(#a))
            .unwrap_or_else(|| quote!(panic!("Bank 1 not present")));
        let bank_2 = bank_2_base
            .map(|a| quote!(#a))
            .unwrap_or_else(|| quote!(panic!("Bank 2 not present")));
        let otp = otp_base
            .map(|a| quote!(#a))
            .unwrap_or_else(|| quote!(panic!("OTP not present")));

        let (swap_check, bank1, bank2) = if check_fb_mode {
            (
                quote! { let is_swapped = crate::pac::SYSCFG.memrmp().read().fb_mode(); },
                quote! { if is_swapped { #bank_2 } else { #bank_1 } },
                quote! { if is_swapped { #bank_1 } else { #bank_2 } },
            )
        } else {
            (quote! {}, quote! { #bank_1 }, quote! { #bank_2 })
        };

        flash_regions.extend(quote! {
            impl crate::flash::FlashBank {
                /// Absolute base address.
                pub fn base(&self) -> u32 {
                    #swap_check
                    match self {
                        crate::flash::FlashBank::Bank1 => #bank1,

View on GitHub (pinned to 463a07b963)