Skip to content

esp32s3 BLE can not advertise after waking up from light sleepΒ #3873

@vult0306

Description

@vult0306

Bug description

esp32s3 BLE sometime can not advertise after ESP wakeup from light sleep

To Reproduce

Repeat below step multiple times. After waking up from light sleep, the phone sometime can scan the ESP, sometime can not, and sometime the phone has to wait for very long time to can see the ESP (couple minutes)

  1. Initialize peripherals and ble
  2. Use phone to scan and connect to ESP
  3. Disconnect
  4. Stop advertising
  5. Enter light sleep
  6. wakeup from light sleep

This is the test code (base on wifi_embassy_ble example)

//! Embassy BLE Example
//!
//! - starts Bluetooth advertising
//! - offers one service with three characteristics (one is read/write, one is write only, one is
//!   read/write/notify)
//! - pressing the boot-button on a dev-board will send a notification if it is subscribed

//% FEATURES: embassy esp-wifi esp-wifi/ble esp-hal/unstable
//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2

// Embassy offers another compatible BLE crate [trouble](https://github.com/embassy-rs/trouble/tree/main/examples/esp32) with esp32 examples.

#![no_std]
#![no_main]

use core::cell::RefCell;

use bleps::{
    ad_structure::{
        AdStructure,
        BR_EDR_NOT_SUPPORTED,
        LE_GENERAL_DISCOVERABLE,
        create_advertising_data,
    },
    async_attribute_server::AttributeServer,
    asynch::Ble,
    attribute_server::NotificationData,
    gatt,
};
use embassy_executor::Spawner;
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{
    clock::CpuClock,
    gpio::{Input, InputConfig, Pull},
    time,
    timer::timg::TimerGroup,
};
use esp_println::println;
use esp_wifi::{EspWifiController, ble::controller::BleConnector, init};

esp_bootloader_esp_idf::esp_app_desc!();

// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html
macro_rules! mk_static {
    ($t:ty,$val:expr) => {{
        static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
        #[deny(unused_attributes)]
        let x = STATIC_CELL.uninit().write(($val));
        x
    }};
}

#[esp_hal_embassy::main]
async fn main(spawner: Spawner) -> ! {
    esp_println::logger::init_logger_from_env();
    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);
    let rtc = esp_hal::rtc_cntl::Rtc::new(peripherals.LPWR);
    let timer = esp_hal::rtc_cntl::sleep::TimerWakeupSource::new(core::time::Duration::from_secs(5));

    esp_alloc::heap_allocator!(size: 72 * 1024);

    let timg0 = TimerGroup::new(peripherals.TIMG0);

    let esp_wifi_ctrl = &*mk_static!(EspWifiController<'static>, init(timg0.timer0).unwrap());

    let config = InputConfig::default().with_pull(Pull::Down);
    cfg_if::cfg_if! {
        if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] {
            let button = Input::new(peripherals.GPIO0, config);
        } else {
            let button = Input::new(peripherals.GPIO9, config);
        }
    }

    cfg_if::cfg_if! {
        if #[cfg(feature = "esp32")] {
            let timg1 = TimerGroup::new(peripherals.TIMG1);
            esp_hal_embassy::init(timg1.timer0);
        } else {
            use esp_hal::timer::systimer::SystemTimer;
            let systimer = SystemTimer::new(peripherals.SYSTIMER);
            esp_hal_embassy::init(systimer.alarm0);
        }
    }

    let connector = BleConnector::new(&esp_wifi_ctrl, peripherals.BT);

    let now = || time::Instant::now().duration_since_epoch().as_millis();
    let ble = Ble::new(connector, now);
    println!("Connector created");
    spawner.spawn(ble_task(
        ble,
        rtc,
        button,
        timer
    )).ok();


    loop {
        embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await;
        println!("Waiting for button press...");
    }
}

#[embassy_executor::task]
async fn ble_task(
    mut ble: Ble<BleConnector<'static>>,
    mut rtc: esp_hal::rtc_cntl::Rtc<'static>,
    button: Input<'static>,
    timer: esp_hal::rtc_cntl::sleep::TimerWakeupSource<>,
) {

    let mut sleep_config = esp_hal::rtc_cntl::sleep::RtcSleepConfig::default();
    sleep_config.set_modem_pd_en(false);
    sleep_config.set_int_8m_pd_en(false);
    sleep_config.set_rtc_peri_pd_en(false);
    sleep_config.set_dig_peri_pd_en(false);
    sleep_config.set_rtc_fastmem_pd_en(false);
    sleep_config.set_rtc_slowmem_pd_en(false);
    sleep_config.set_rtc_regulator_fpu(true);
    sleep_config.set_xtal_fpu(true);
    sleep_config.set_rtc_mem_inf_follow_cpu(true);
    sleep_config.set_cpu_pd_en(false);
    sleep_config.set_deep_slp(false);
    sleep_config.set_deep_slp_reject(false);
    sleep_config.set_light_slp_reject(false);

    let pin_ref = RefCell::new(button);
    let pin_ref = &pin_ref;

    loop {
        println!("{:?}", ble.init().await);
        println!("{:?}", ble.cmd_set_le_advertising_parameters().await);
        println!(
            "{:?}",
            ble.cmd_set_le_advertising_data(
                create_advertising_data(&[
                    AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
                    AdStructure::ServiceUuids16(&[Uuid::Uuid16(0x1809)]),
                    AdStructure::CompleteLocalName(esp_hal::chip!()),
                ])
                .unwrap()
            )
            .await
        );
        println!("{:?}", ble.cmd_set_le_advertise_enable(true).await);

        println!("started advertising");

        let mut rf = |_offset: usize, data: &mut [u8]| {
            data[..20].copy_from_slice(&b"Hello Bare-Metal BLE"[..]);
            17
        };
        let mut wf = |offset: usize, data: &[u8]| {
            println!("RECEIVED: {} {:?}", offset, data);
        };

        let mut wf2 = |offset: usize, data: &[u8]| {
            println!("RECEIVED: {} {:?}", offset, data);
        };

        let mut rf3 = |_offset: usize, data: &mut [u8]| {
            data[..5].copy_from_slice(&b"Hola!"[..]);
            5
        };
        let mut wf3 = |offset: usize, data: &[u8]| {
            println!("RECEIVED: Offset {}, data {:?}", offset, data);
        };

        gatt!([service {
            uuid: "937312e0-2354-11eb-9f10-fbc30a62cf38",
            characteristics: [
                characteristic {
                    uuid: "937312e0-2354-11eb-9f10-fbc30a62cf38",
                    read: rf,
                    write: wf,
                },
                characteristic {
                    uuid: "957312e0-2354-11eb-9f10-fbc30a62cf38",
                    write: wf2,
                },
                characteristic {
                    name: "my_characteristic",
                    uuid: "987312e0-2354-11eb-9f10-fbc30a62cf38",
                    notify: true,
                    read: rf3,
                    write: wf3,
                },
            ],
        },]);

        let mut rng = bleps::no_rng::NoRng;
        let mut srv = AttributeServer::new(&mut ble, &mut gatt_attributes, &mut rng);

        let counter = RefCell::new(0u8);
        let counter = &counter;

        let mut notifier = || {
            // TODO how to check if notifications are enabled for the characteristic?
            // maybe pass something into the closure which just can query the characteristic
            // value probably passing in the attribute server won't work?

            async {
                pin_ref.borrow_mut().wait_for_rising_edge().await;
                let mut data = [0u8; 13];
                data.copy_from_slice(b"Notification0");
                {
                    let mut counter = counter.borrow_mut();
                    data[data.len() - 1] += *counter;
                    *counter = (*counter + 1) % 10;
                }
                NotificationData::new(my_characteristic_handle, &data)
            }
        };

        srv.run(&mut notifier).await.unwrap();

        println!("stop advertising");
        println!("{:?}",ble.cmd_set_le_advertise_enable(false).await);
        println!("Enter sleep mode");
        embassy_time::Timer::after(embassy_time::Duration::from_millis(1000)).await;
        rtc.sleep(&sleep_config, &[&timer]);
        embassy_time::Timer::after(embassy_time::Duration::from_millis(1000)).await;
        println!("wakeup!");
    }
}

This is the output log
After waking up, it says it starts to advertise. But the phone can not see it

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fce2810,len:0x15a0
load:0x403c8700,len:0x4
load:0x403c8704,len:0xd24
load:0x403cb700,len:0x2f04
entry 0x403c8928
I (30) boot: ESP-IDF v5.4.1-426-g3ad36321ea 2nd stage bootloader
I (30) boot: compile time Apr 24 2025 15:55:11
I (30) boot: Multicore bootloader
I (32) boot: chip revision: v0.1
I (34) boot: efuse block revision: v1.2
I (38) boot.esp32s3: Boot SPI Speed : 40MHz
I (42) boot.esp32s3: SPI Mode       : DIO
I (46) boot.esp32s3: SPI Flash Size : 8MB
I (49) boot: Enabling RNG early entropy source...
I (54) boot: Partition Table:
I (56) boot: ## Label            Usage          Type ST Offset   Length
I (63) boot:  0 nvs              WiFi data        01 02 00009000 00004000
I (69) boot:  1 otadata          OTA data         01 00 0000d000 00002000
I (76) boot:  2 phy_init         RF data          01 01 0000f000 00001000
I (82) boot:  3 factory          factory app      00 00 00010000 00100000
I (89) boot:  4 ota_0            OTA app          00 10 00110000 00100000
I (95) boot:  5 ota_1            OTA app          00 11 00210000 00100000
I (102) boot: End of partition table
I (105) boot: Defaulting to factory image
I (109) esp_image: segment 0: paddr=00010020 vaddr=3c000020 size=0b250h ( 45648) map
I (128) esp_image: segment 1: paddr=0001b278 vaddr=3fc8f418 size=0105ch (  4188) load
I (129) esp_image: segment 2: paddr=0001c2dc vaddr=40378000 size=03d3ch ( 15676) load
I (136) esp_image: segment 3: paddr=00020020 vaddr=42010020 size=31638h (202296) map
I (189) esp_image: segment 4: paddr=00051660 vaddr=4037bd3c size=036d8h ( 14040) load
I (196) boot: Loaded app from partition at offset 0x10000
I (196) boot: Disabling RNG early entropy source...
INFO - esp-wifi configuration EspWifiConfig { rx_queue_size: 5, tx_queue_size: 3, static_rx_buf_num: 10, dynamic_rx_buf_num: 32, static_tx_buf_num: 0, dynamic_tx_buf_num: 32, ampdu_rx_enable: true, ampdu_tx_enable: true, amsdu_tx_enable: false, rx_ba_win: 6, max_burst_size: 1, country_code: "CN", country_code_operating_class: 0, mtu: 1492, tick_rate_hz: 100, listen_interval: 3, beacon_timeout: 6, ap_beacon_timeout: 300, failure_retry_cnt: 1, scan_method: 0 }
Connector created
Ok(CommandComplete { num_packets: 5, opcode: 3075, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8198, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8200, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8202, data: [0] })
started advertising
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
WARN - Ignoring unknown le-meta event 03 data = [00, 01, 00, 06, 00, 00, 00, f4, 01]
WARN - Ignoring unknown le-meta event 03 data = [00, 01, 00, 27, 00, 00, 00, f4, 01]
Waiting for button press...
Waiting for button press...
Waiting for button press...
stop advertising
Ok(CommandComplete { num_packets: 5, opcode: 8202, data: [0] })
Enter sleep mode
Waiting for button press...
Waiting for button press...
wakeup!
Ok(CommandComplete { num_packets: 5, opcode: 3075, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8198, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8200, data: [0] })
Ok(CommandComplete { num_packets: 5, opcode: 8202, data: [0] })
started advertising
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...
Waiting for button press...

Expected behavior

After wakeup, the BLE should be able to advertise normally

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingchip:esp32s3Issue related to ESP32-S3 chippackage:esp-radioIssues related to the esp-wifi package

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions