esp-hal/examples/src/bin/wifi_embassy_ble.rs
Dániel Buga 571760884b
Update our time driver for upcoming embassy changes (#2701)
* Add a timer-driven task

* Spawn another timer

* Log

* foo

* Do not access current time on each schedule

* Update generic queue

* Minimize alarm priorities

* Point to github with patches

* Fix build without any queue impl selected

* Remove explicit generic-queue features

* Define cfgs, fix calling something uninitialized

* Clean up RefCell+generic queue

* Fix arg order

* Feature

* Fix single integrated-timer queue

* Fix next expiration when arming

* Add note

* Adjust impl to latest changes

* Local patch

* Refactor the refactor refactor

* Track the timer item's owner

* Clear owner on dequeue

* Clean up

* Point at the right branch

* Fix panic message

* Hide private function

* Remove integrated-timer references

* Point at upstream embassy

* Configure via esp-config

* Document, clean up, fix

* Hack

* Remove patches

* Update config separator, test the complex variant

* Undo esp-config hack

* Remove trouble example, update edge-net

* Update test deps

* Document

* Update bt-hci.

* Fix generic queue

* Fix panic message

* Fix UB

* Fix rebase

* Resolve UB

* Avoid mutable reference in interrupt executor

---------

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2025-01-14 16:17:58 +00:00

188 lines
5.9 KiB
Rust

//! 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
#![no_std]
#![no_main]
use core::cell::RefCell;
use bleps::{
ad_structure::{
create_advertising_data,
AdStructure,
BR_EDR_NOT_SUPPORTED,
LE_GENERAL_DISCOVERABLE,
},
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, Pull},
rng::Rng,
time,
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_wifi::{ble::controller::BleConnector, init, EspWifiController};
// 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);
esp_alloc::heap_allocator!(72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0);
let init = &*mk_static!(
EspWifiController<'static>,
init(
timg0.timer0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
)
.unwrap()
);
cfg_if::cfg_if! {
if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] {
let button = Input::new(peripherals.GPIO0, Pull::Down);
} else {
let button = Input::new(peripherals.GPIO9, Pull::Down);
}
}
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 mut bluetooth = peripherals.BT;
let connector = BleConnector::new(&init, &mut bluetooth);
let now = || time::now().duration_since_epoch().to_millis();
let mut ble = Ble::new(connector, now);
println!("Connector created");
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();
}
}