From c03133ee92b54049367b1909c7434047e7935fba Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 12 Apr 2023 14:55:54 +0200 Subject: [PATCH 1/3] Async BLE HCI --- esp-wifi/src/ble/btdm.rs | 19 ++++-- esp-wifi/src/ble/controller/mod.rs | 92 ++++++++++++++++++++++++++ esp-wifi/src/ble/npl.rs | 15 ++++- esp-wifi/src/ble/os_adapter_esp32c3.rs | 13 ++-- esp-wifi/src/lib.rs | 2 + 5 files changed, 126 insertions(+), 15 deletions(-) diff --git a/esp-wifi/src/ble/btdm.rs b/esp-wifi/src/ble/btdm.rs index 7e538bb2..3aca6832 100644 --- a/esp-wifi/src/ble/btdm.rs +++ b/esp-wifi/src/ble/btdm.rs @@ -93,6 +93,9 @@ extern "C" fn notify_host_recv(data: *mut u8, len: u16) -> i32 { let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); queue.enqueue(packet).unwrap(); }); + + #[cfg(feature = "async")] + crate::ble::controller::asynch::hci_read_data_available(); } 0 @@ -522,6 +525,14 @@ static mut BLE_HCI_READ_DATA: [u8; 256] = [0u8; 256]; static mut BLE_HCI_READ_DATA_INDEX: usize = 0; static mut BLE_HCI_READ_DATA_LEN: usize = 0; +#[cfg(feature = "async")] +pub fn have_hci_read_data() -> bool { + critical_section::with(|cs| { + let queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + !queue.is_empty() || unsafe { BLE_HCI_READ_DATA_LEN > 0 && (BLE_HCI_READ_DATA_LEN >= BLE_HCI_READ_DATA_INDEX) } + }) +} + pub fn read_hci(data: &mut [u8]) -> usize { unsafe { if BLE_HCI_READ_DATA_LEN == 0 { @@ -529,11 +540,9 @@ pub fn read_hci(data: &mut [u8]) -> usize { let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); if let Some(packet) = queue.dequeue() { - for i in 0..(packet.len as usize + 0/*1*/) { - BLE_HCI_READ_DATA[i] = packet.data[i]; - } - - BLE_HCI_READ_DATA_LEN = packet.len as usize + 0 /*1*/; + BLE_HCI_READ_DATA[..packet.len as usize] + .copy_from_slice(&packet.data[..packet.len as usize]); + BLE_HCI_READ_DATA_LEN = packet.len as usize; BLE_HCI_READ_DATA_INDEX = 0; } }); diff --git a/esp-wifi/src/ble/controller/mod.rs b/esp-wifi/src/ble/controller/mod.rs index b2361f38..8fa03989 100644 --- a/esp-wifi/src/ble/controller/mod.rs +++ b/esp-wifi/src/ble/controller/mod.rs @@ -67,3 +67,95 @@ impl Write for BleConnector<'_> { Ok(()) } } + +#[cfg(feature = "async")] +pub mod asynch { + use core::task::Poll; + + use crate::ble::ble::have_hci_read_data; + + use super::BleConnectorError; + use super::{read_hci, send_hci}; + use embassy_sync::waitqueue::AtomicWaker; + use embedded_io::asynch; + use embedded_io::Io; + use esp_hal_common::peripheral::{Peripheral, PeripheralRef}; + + static HCI_WAKER: AtomicWaker = AtomicWaker::new(); + + pub(crate) fn hci_read_data_available() { + HCI_WAKER.wake(); + } + + pub struct BleConnector<'d> { + _device: PeripheralRef<'d, esp_hal_common::radio::Bluetooth>, + } + + impl<'d> BleConnector<'d> { + pub fn new( + device: impl Peripheral

+ 'd, + ) -> BleConnector<'d> { + Self { + _device: device.into_ref(), + } + } + } + + impl Io for BleConnector<'_> { + type Error = BleConnectorError; + } + + impl asynch::Read for BleConnector<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + if !have_hci_read_data() { + HciReadyEventFuture.await; + } + + let mut total = 0; + for b in buf { + let mut buffer = [0u8]; + let len = read_hci(&mut buffer); + + if len == 1 { + *b = buffer[0]; + total += 1; + } else { + return Ok(total); + } + } + + Ok(total) + } + } + + impl asynch::Write for BleConnector<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + send_hci(buf); + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), BleConnectorError> { + // nothing to do + Ok(()) + } + } + + pub(crate) struct HciReadyEventFuture; + + impl core::future::Future for HciReadyEventFuture { + type Output = (); + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + HCI_WAKER.register(cx.waker()); + + if have_hci_read_data() { + Poll::Ready(()) + } else { + Poll::Pending + } + } + } +} diff --git a/esp-wifi/src/ble/npl.rs b/esp-wifi/src/ble/npl.rs index 2510db18..dbce8d2c 100644 --- a/esp-wifi/src/ble/npl.rs +++ b/esp-wifi/src/ble/npl.rs @@ -1232,6 +1232,9 @@ unsafe extern "C" fn ble_hs_hci_rx_evt(cmd: *const u8, arg: *const c_void) { }) .unwrap(); }); + + #[cfg(feature = "async")] + crate::ble::controller::asynch::hci_read_data_available(); } unsafe extern "C" fn ble_hs_rx_data(om: *const OsMbuf, arg: *const c_void) { @@ -1255,12 +1258,23 @@ unsafe extern "C" fn ble_hs_rx_data(om: *const OsMbuf, arg: *const c_void) { }) .unwrap(); }); + + #[cfg(feature = "async")] + crate::ble::controller::asynch::hci_read_data_available(); } static mut BLE_HCI_READ_DATA: [u8; 256] = [0u8; 256]; static mut BLE_HCI_READ_DATA_INDEX: usize = 0; static mut BLE_HCI_READ_DATA_LEN: usize = 0; +#[cfg(feature = "async")] +pub fn have_hci_read_data() -> bool { + critical_section::with(|cs| { + let queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + !queue.is_empty() || unsafe { BLE_HCI_READ_DATA_LEN > 0 && (BLE_HCI_READ_DATA_LEN >= BLE_HCI_READ_DATA_INDEX) } + }) +} + pub fn read_hci(data: &mut [u8]) -> usize { unsafe { if BLE_HCI_READ_DATA_LEN == 0 { @@ -1274,7 +1288,6 @@ pub fn read_hci(data: &mut [u8]) -> usize { BLE_HCI_READ_DATA_INDEX = 0; } }); - return 0; } if BLE_HCI_READ_DATA_LEN > 0 { diff --git a/esp-wifi/src/ble/os_adapter_esp32c3.rs b/esp-wifi/src/ble/os_adapter_esp32c3.rs index 79ff3c67..767009cc 100644 --- a/esp-wifi/src/ble/os_adapter_esp32c3.rs +++ b/esp-wifi/src/ble/os_adapter_esp32c3.rs @@ -160,7 +160,7 @@ pub(crate) fn create_ble_config() -> esp_bt_controller_config_t { hci_tl_funcs: core::ptr::null_mut(), txant_dft: 0, rxant_dft: 0, - txpwr_dft: 7, + txpwr_dft: 9, cfg_mask: 1, scan_duplicate_mode: 0, scan_duplicate_type: 0, @@ -179,9 +179,7 @@ pub(crate) fn create_ble_config() -> esp_bt_controller_config_t { pub(crate) unsafe extern "C" fn interrupt_on(intr_num: i32) { trace!("interrupt_on {}", intr_num); - (*esp32c3::INTERRUPT_CORE0::PTR) - .cpu_int_enable - .modify(|r, w| w.bits(r.bits() | 1 << intr_num)); + // NO-OP } pub(crate) unsafe extern "C" fn interrupt_off(_intr_num: i32) { @@ -218,13 +216,10 @@ pub(crate) unsafe extern "C" fn interrupt_set( interrupt_prio ); - ((0x600c2000 + 0x114 + interrupt_no * 4) as *mut u32).write_volatile(interrupt_prio as u32); - /* Set the interrupt type (Edge or Level). */ - // ---- - /* Map the CPU interrupt ID to the peripheral. */ - ((0x600c2000 + intr_source * 4) as *mut u32).write_volatile(interrupt_no as u32); + + // NO-OP } pub(crate) unsafe extern "C" fn interrupt_clear(_interrupt_source: i32, _interrupt_no: i32) { diff --git a/esp-wifi/src/lib.rs b/esp-wifi/src/lib.rs index 8f6e06e0..28efaec8 100644 --- a/esp-wifi/src/lib.rs +++ b/esp-wifi/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))] #![feature(c_variadic)] +#![cfg_attr(feature = "async", feature(async_fn_in_trait))] +#![cfg_attr(feature = "async", allow(incomplete_features))] use core::cell::RefCell; use core::mem::MaybeUninit; From afc7def7c0329d6cf6ff93344fa92f9a07cce042 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 13 Apr 2023 12:56:56 +0200 Subject: [PATCH 2/3] Add async-ble example --- Cargo.toml | 3 +- examples-esp32/Cargo.toml | 3 +- examples-esp32/examples/async_ble.rs | 155 +++++++++++++++++++++++++ examples-esp32c2/Cargo.toml | 3 +- examples-esp32c2/examples/async_ble.rs | 155 +++++++++++++++++++++++++ examples-esp32c3/Cargo.toml | 3 +- examples-esp32c3/examples/async_ble.rs | 155 +++++++++++++++++++++++++ examples-esp32s3/Cargo.toml | 3 +- examples-esp32s3/examples/async_ble.rs | 155 +++++++++++++++++++++++++ examples-util/src/lib.rs | 5 + smoketest.bat | 4 + 11 files changed, 639 insertions(+), 5 deletions(-) create mode 100644 examples-esp32/examples/async_ble.rs create mode 100644 examples-esp32c2/examples/async_ble.rs create mode 100644 examples-esp32c3/examples/async_ble.rs create mode 100644 examples-esp32s3/examples/async_ble.rs diff --git a/Cargo.toml b/Cargo.toml index fac76df1..05e7503e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,10 +52,11 @@ embassy-futures = { version = "0.1.0" } embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "26474ce6eb759e5add1c137f3417845e0797df3a", features = ["nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "26474ce6eb759e5add1c137f3417845e0797df3a" } -bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "33fde67257bfbc6c0aebf7649fd302c82ed94c64" } +bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "daa341f4e5f40db8348886ab245f51902fccaa17" } bleps-macros = { git = "https://github.com/bjoernQ/bleps", package = "bleps-macros", rev = "33fde67257bfbc6c0aebf7649fd302c82ed94c64" } embassy-executor = { package = "embassy-executor", git = "https://github.com/embassy-rs/embassy/", rev = "cd9a65b", features = ["nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["nightly"] } futures-util = { version = "0.3.17", default-features = false } esp-println = { version = "0.4.0", features = ["log"] } esp-backtrace = { version = "0.6.0", features = ["panic-handler", "exception-handler", "print-uart"] } +embedded-hal-async = { version = "0.2.0-alpha.0" } diff --git a/examples-esp32/Cargo.toml b/examples-esp32/Cargo.toml index 8a50adf1..960461d5 100644 --- a/examples-esp32/Cargo.toml +++ b/examples-esp32/Cargo.toml @@ -24,11 +24,12 @@ examples-util = { path = "../examples-util", features = ["esp32"] } esp-println = { workspace = true, features = [ "esp32" ] } esp-backtrace = { workspace = true, features = ["esp32"] } esp-wifi = { path = "../esp-wifi", features = ["esp32"] } +embedded-hal-async = { workspace = true, optional = true } [features] default = ["esp32"] esp32 = [] -async = ["esp-wifi/esp32-async"] +async = ["esp-wifi/esp32-async", "bleps/async", "dep:embedded-hal-async", "esp32-hal/async"] embassy-net = ["esp-wifi/embassy-net","dep:embassy-net"] wifi-logs = ["esp-wifi/wifi-logs"] dump-packets = ["esp-wifi/dump-packets"] diff --git a/examples-esp32/examples/async_ble.rs b/examples-esp32/examples/async_ble.rs new file mode 100644 index 00000000..01e7c687 --- /dev/null +++ b/examples-esp32/examples/async_ble.rs @@ -0,0 +1,155 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_closure)] + +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, +}; +use bleps_macros::gatt; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embedded_hal_async::digital::Wait; +use esp_backtrace as _; +use esp_println::{logger::init_logger, println}; +use esp_wifi::{ble::controller::asynch::BleConnector, initialize}; +use examples_util::hal; +use examples_util::BootButton; +use hal::{ + clock::{ClockControl, CpuClock}, + embassy, + peripherals::*, + prelude::*, + radio::Bluetooth, + timer::TimerGroup, + Rng, Rtc, IO, +}; + +#[embassy_executor::task] +async fn run(mut bluetooth: Bluetooth, pin: BootButton) { + let connector = BleConnector::new(&mut bluetooth); + let mut ble = Ble::new(connector, esp_wifi::current_millis); + println!("Connector created"); + + let pin_ref = RefCell::new(pin); + + 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(examples_util::SOC_NAME), + ])) + .await + ); + println!("{:?}", ble.cmd_set_le_advertise_enable(true).await); + + println!("started advertising"); + + let mut rf = || &b"Hello Bare-Metal BLE"[..]; + let mut wf = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut wf2 = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut rf3 = || &b"Hola!"[..]; + let mut wf3 = |offset: u16, data: &[u8]| { + println!("RECEIVED: Offset {}, data {:x?}", 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 srv = AttributeServer::new(&mut ble, &mut gatt_attributes); + + let counter = RefCell::new(0u8); + let mut notifier = async || { + // TODO how to check if notifications are enabled for the characteristic? + // maybe pass something into the closure which just can query the characterisic value + // probably passing in the attribute server won't work? + pin_ref.borrow_mut().wait_for_rising_edge().await.unwrap(); + 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(); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + init_logger(log::LevelFilter::Info); + + let peripherals = Peripherals::take(); + + let system = examples_util::system!(peripherals); + let clocks = examples_util::clocks!(system); + examples_util::rtc!(peripherals); + + let timer = examples_util::timer!(peripherals, clocks); + initialize( + timer, + Rng::new(peripherals.RNG), + system.radio_clock_control, + &clocks, + ) + .unwrap(); + + let button = examples_util::boot_button!(peripherals); + + // Async requires the GPIO interrupt to wake futures + hal::interrupt::enable( + hal::peripherals::Interrupt::GPIO, + hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let (_, bluetooth) = peripherals.RADIO.split(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run(bluetooth, button)).ok(); + }); +} diff --git a/examples-esp32c2/Cargo.toml b/examples-esp32c2/Cargo.toml index 8f304450..362af35e 100644 --- a/examples-esp32c2/Cargo.toml +++ b/examples-esp32c2/Cargo.toml @@ -24,11 +24,12 @@ examples-util = { path = "../examples-util", features = ["esp32c2"] } esp-println = { workspace = true, features = [ "esp32c2" ] } esp-backtrace = { workspace = true, features = ["esp32c2"] } esp-wifi = { path = "../esp-wifi", features = ["esp32c2"] } +embedded-hal-async = { workspace = true, optional = true } [features] default = ["esp32c2"] esp32c2 = [] -async = ["esp-wifi/esp32c2-async"] +async = ["esp-wifi/esp32c2-async", "bleps/async", "dep:embedded-hal-async", "esp32c2-hal/async"] embassy-net = ["esp-wifi/embassy-net","dep:embassy-net"] wifi-logs = ["esp-wifi/wifi-logs"] dump-packets = ["esp-wifi/dump-packets"] diff --git a/examples-esp32c2/examples/async_ble.rs b/examples-esp32c2/examples/async_ble.rs new file mode 100644 index 00000000..01e7c687 --- /dev/null +++ b/examples-esp32c2/examples/async_ble.rs @@ -0,0 +1,155 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_closure)] + +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, +}; +use bleps_macros::gatt; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embedded_hal_async::digital::Wait; +use esp_backtrace as _; +use esp_println::{logger::init_logger, println}; +use esp_wifi::{ble::controller::asynch::BleConnector, initialize}; +use examples_util::hal; +use examples_util::BootButton; +use hal::{ + clock::{ClockControl, CpuClock}, + embassy, + peripherals::*, + prelude::*, + radio::Bluetooth, + timer::TimerGroup, + Rng, Rtc, IO, +}; + +#[embassy_executor::task] +async fn run(mut bluetooth: Bluetooth, pin: BootButton) { + let connector = BleConnector::new(&mut bluetooth); + let mut ble = Ble::new(connector, esp_wifi::current_millis); + println!("Connector created"); + + let pin_ref = RefCell::new(pin); + + 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(examples_util::SOC_NAME), + ])) + .await + ); + println!("{:?}", ble.cmd_set_le_advertise_enable(true).await); + + println!("started advertising"); + + let mut rf = || &b"Hello Bare-Metal BLE"[..]; + let mut wf = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut wf2 = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut rf3 = || &b"Hola!"[..]; + let mut wf3 = |offset: u16, data: &[u8]| { + println!("RECEIVED: Offset {}, data {:x?}", 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 srv = AttributeServer::new(&mut ble, &mut gatt_attributes); + + let counter = RefCell::new(0u8); + let mut notifier = async || { + // TODO how to check if notifications are enabled for the characteristic? + // maybe pass something into the closure which just can query the characterisic value + // probably passing in the attribute server won't work? + pin_ref.borrow_mut().wait_for_rising_edge().await.unwrap(); + 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(); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + init_logger(log::LevelFilter::Info); + + let peripherals = Peripherals::take(); + + let system = examples_util::system!(peripherals); + let clocks = examples_util::clocks!(system); + examples_util::rtc!(peripherals); + + let timer = examples_util::timer!(peripherals, clocks); + initialize( + timer, + Rng::new(peripherals.RNG), + system.radio_clock_control, + &clocks, + ) + .unwrap(); + + let button = examples_util::boot_button!(peripherals); + + // Async requires the GPIO interrupt to wake futures + hal::interrupt::enable( + hal::peripherals::Interrupt::GPIO, + hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let (_, bluetooth) = peripherals.RADIO.split(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run(bluetooth, button)).ok(); + }); +} diff --git a/examples-esp32c3/Cargo.toml b/examples-esp32c3/Cargo.toml index 62c35a59..dae90242 100644 --- a/examples-esp32c3/Cargo.toml +++ b/examples-esp32c3/Cargo.toml @@ -24,11 +24,12 @@ examples-util = { path = "../examples-util", features = ["esp32c3"] } esp-println = { workspace = true, features = [ "esp32c3" ] } esp-backtrace = { workspace = true, features = ["esp32c3"] } esp-wifi = { path = "../esp-wifi", features = ["esp32c3"] } +embedded-hal-async = {workspace = true, optional = true } [features] default = ["esp32c3"] esp32c3 = [] -async = ["esp-wifi/esp32c3-async"] +async = ["esp-wifi/esp32c3-async", "bleps/async", "dep:embedded-hal-async", "esp32c3-hal/async"] embassy-net = ["esp-wifi/embassy-net","dep:embassy-net"] wifi-logs = ["esp-wifi/wifi-logs"] dump-packets = ["esp-wifi/dump-packets"] diff --git a/examples-esp32c3/examples/async_ble.rs b/examples-esp32c3/examples/async_ble.rs new file mode 100644 index 00000000..01e7c687 --- /dev/null +++ b/examples-esp32c3/examples/async_ble.rs @@ -0,0 +1,155 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_closure)] + +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, +}; +use bleps_macros::gatt; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embedded_hal_async::digital::Wait; +use esp_backtrace as _; +use esp_println::{logger::init_logger, println}; +use esp_wifi::{ble::controller::asynch::BleConnector, initialize}; +use examples_util::hal; +use examples_util::BootButton; +use hal::{ + clock::{ClockControl, CpuClock}, + embassy, + peripherals::*, + prelude::*, + radio::Bluetooth, + timer::TimerGroup, + Rng, Rtc, IO, +}; + +#[embassy_executor::task] +async fn run(mut bluetooth: Bluetooth, pin: BootButton) { + let connector = BleConnector::new(&mut bluetooth); + let mut ble = Ble::new(connector, esp_wifi::current_millis); + println!("Connector created"); + + let pin_ref = RefCell::new(pin); + + 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(examples_util::SOC_NAME), + ])) + .await + ); + println!("{:?}", ble.cmd_set_le_advertise_enable(true).await); + + println!("started advertising"); + + let mut rf = || &b"Hello Bare-Metal BLE"[..]; + let mut wf = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut wf2 = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut rf3 = || &b"Hola!"[..]; + let mut wf3 = |offset: u16, data: &[u8]| { + println!("RECEIVED: Offset {}, data {:x?}", 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 srv = AttributeServer::new(&mut ble, &mut gatt_attributes); + + let counter = RefCell::new(0u8); + let mut notifier = async || { + // TODO how to check if notifications are enabled for the characteristic? + // maybe pass something into the closure which just can query the characterisic value + // probably passing in the attribute server won't work? + pin_ref.borrow_mut().wait_for_rising_edge().await.unwrap(); + 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(); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + init_logger(log::LevelFilter::Info); + + let peripherals = Peripherals::take(); + + let system = examples_util::system!(peripherals); + let clocks = examples_util::clocks!(system); + examples_util::rtc!(peripherals); + + let timer = examples_util::timer!(peripherals, clocks); + initialize( + timer, + Rng::new(peripherals.RNG), + system.radio_clock_control, + &clocks, + ) + .unwrap(); + + let button = examples_util::boot_button!(peripherals); + + // Async requires the GPIO interrupt to wake futures + hal::interrupt::enable( + hal::peripherals::Interrupt::GPIO, + hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let (_, bluetooth) = peripherals.RADIO.split(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run(bluetooth, button)).ok(); + }); +} diff --git a/examples-esp32s3/Cargo.toml b/examples-esp32s3/Cargo.toml index 844422c8..1d8c2bd2 100644 --- a/examples-esp32s3/Cargo.toml +++ b/examples-esp32s3/Cargo.toml @@ -24,11 +24,12 @@ examples-util = { path = "../examples-util", features = ["esp32s3"] } esp-println = { workspace = true, features = [ "esp32s3" ] } esp-backtrace = { workspace = true, features = ["esp32s3"] } esp-wifi = { path = "../esp-wifi", features = ["esp32s3"] } +embedded-hal-async = { workspace = true, optional = true } [features] default = ["esp32s3"] esp32s3 = [] -async = ["esp-wifi/esp32s3-async"] +async = ["esp-wifi/esp32s3-async", "bleps/async", "dep:embedded-hal-async", "esp32s3-hal/async"] embassy-net = ["esp-wifi/embassy-net","dep:embassy-net"] wifi-logs = ["esp-wifi/wifi-logs"] dump-packets = ["esp-wifi/dump-packets"] diff --git a/examples-esp32s3/examples/async_ble.rs b/examples-esp32s3/examples/async_ble.rs new file mode 100644 index 00000000..01e7c687 --- /dev/null +++ b/examples-esp32s3/examples/async_ble.rs @@ -0,0 +1,155 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_closure)] + +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, +}; +use bleps_macros::gatt; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embedded_hal_async::digital::Wait; +use esp_backtrace as _; +use esp_println::{logger::init_logger, println}; +use esp_wifi::{ble::controller::asynch::BleConnector, initialize}; +use examples_util::hal; +use examples_util::BootButton; +use hal::{ + clock::{ClockControl, CpuClock}, + embassy, + peripherals::*, + prelude::*, + radio::Bluetooth, + timer::TimerGroup, + Rng, Rtc, IO, +}; + +#[embassy_executor::task] +async fn run(mut bluetooth: Bluetooth, pin: BootButton) { + let connector = BleConnector::new(&mut bluetooth); + let mut ble = Ble::new(connector, esp_wifi::current_millis); + println!("Connector created"); + + let pin_ref = RefCell::new(pin); + + 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(examples_util::SOC_NAME), + ])) + .await + ); + println!("{:?}", ble.cmd_set_le_advertise_enable(true).await); + + println!("started advertising"); + + let mut rf = || &b"Hello Bare-Metal BLE"[..]; + let mut wf = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut wf2 = |offset: u16, data: &[u8]| { + println!("RECEIVED: {} {:x?}", offset, data); + }; + + let mut rf3 = || &b"Hola!"[..]; + let mut wf3 = |offset: u16, data: &[u8]| { + println!("RECEIVED: Offset {}, data {:x?}", 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 srv = AttributeServer::new(&mut ble, &mut gatt_attributes); + + let counter = RefCell::new(0u8); + let mut notifier = async || { + // TODO how to check if notifications are enabled for the characteristic? + // maybe pass something into the closure which just can query the characterisic value + // probably passing in the attribute server won't work? + pin_ref.borrow_mut().wait_for_rising_edge().await.unwrap(); + 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(); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + init_logger(log::LevelFilter::Info); + + let peripherals = Peripherals::take(); + + let system = examples_util::system!(peripherals); + let clocks = examples_util::clocks!(system); + examples_util::rtc!(peripherals); + + let timer = examples_util::timer!(peripherals, clocks); + initialize( + timer, + Rng::new(peripherals.RNG), + system.radio_clock_control, + &clocks, + ) + .unwrap(); + + let button = examples_util::boot_button!(peripherals); + + // Async requires the GPIO interrupt to wake futures + hal::interrupt::enable( + hal::peripherals::Interrupt::GPIO, + hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let (_, bluetooth) = peripherals.RADIO.split(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + embassy::init(&clocks, timer_group0.timer0); + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run(bluetooth, button)).ok(); + }); +} diff --git a/examples-util/src/lib.rs b/examples-util/src/lib.rs index b2881719..b5f13ed9 100644 --- a/examples-util/src/lib.rs +++ b/examples-util/src/lib.rs @@ -90,6 +90,11 @@ macro_rules! boot_button { }}; } +#[cfg(any(feature = "esp32c2", feature = "esp32c3"))] +pub type BootButton = crate::hal::soc::gpio::Gpio9>; +#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] +pub type BootButton = crate::hal::soc::gpio::Gpio0>; + #[cfg(feature = "esp32c3")] pub const SOC_NAME: &str = "ESP32-C3"; #[cfg(feature = "esp32c2")] diff --git a/smoketest.bat b/smoketest.bat index 034d08e2..6c1b1d44 100644 --- a/smoketest.bat +++ b/smoketest.bat @@ -12,6 +12,7 @@ echo. echo Connect ESP32-C3 pause cargo run --example ble --release --features "ble" +cargo run --example async_ble --release --features "async,ble" cargo run --example dhcp --release --features "embedded-svc,wifi" cargo run --example static_ip --release --features "embedded-svc,wifi" cargo run --example embassy_dhcp --release --features "async,embedded-svc,wifi,embassy-net" @@ -28,6 +29,7 @@ echo. echo Connect ESP32 pause cargo run --example ble --release --features "ble" +cargo run --example async_ble --release --features "async,ble" cargo run --example dhcp --release --features "embedded-svc,wifi" cargo run --example static_ip --release --features "embedded-svc,wifi" cargo run --example embassy_dhcp --release --features "async,embedded-svc,wifi,embassy-net" @@ -43,6 +45,7 @@ echo. echo Connect ESP32-S3 pause cargo run --example ble --release --features "ble" +cargo run --example async_ble --release --features "async,ble" cargo run --example dhcp --release --features "embedded-svc,wifi" cargo run --example static_ip --release --features "embedded-svc,wifi" cargo run --example embassy_dhcp --release --features "async,embedded-svc,wifi,embassy-net" @@ -73,6 +76,7 @@ echo. echo Connect ESP32-C2 pause cargo run --example ble --release --features "ble" +cargo run --example async_ble --release --features "async,ble" set CARGO_PROFILE_RELEASE_OPT_LEVEL=3 cargo run --example dhcp --release --features "embedded-svc,wifi" cargo run --example static_ip --release --features "embedded-svc,wifi" From e6c4479355f0ebb6a77a80bc3024dbe47d4f0718 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 13 Apr 2023 14:51:25 +0200 Subject: [PATCH 3/3] Use nightly-2023-03-09 for CI (for now) --- .github/workflows/ci.yml | 59 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2275d48..d8386105 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,8 @@ jobs: - name: build (ble) run: cd examples-esp32 && cargo build --release --example=ble --features=ble + - name: build (async-ble) + run: cd examples-esp32 && cargo build --release --example=async_ble --features=async,ble - name: build (dhcp) run: cd examples-esp32 && cargo build --release --example=dhcp --features=embedded-svc,wifi - name: build (static_ip) @@ -53,70 +55,77 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@v1 with: - targets: riscv32imc-unknown-none-elf + target: riscv32imc-unknown-none-elf + toolchain: nightly-2023-03-09 components: rust-src - uses: Swatinem/rust-cache@v1 - name: build (ble) - run: cd examples-esp32c2 && cargo build --release --example=ble --features=ble + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=ble --features=ble + - name: build (async-ble) + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=async_ble --features=async,ble - name: build (dhcp) - run: cd examples-esp32c2 && cargo build --release --example=dhcp --features=embedded-svc,wifi + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=dhcp --features=embedded-svc,wifi - name: build (static_ip) - run: cd examples-esp32c2 && cargo build --release --example=static_ip --features=embedded-svc,wifi + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=static_ip --features=embedded-svc,wifi - name: build (esp_now) - run: cd examples-esp32c2 && cargo build --release --example=esp_now --features=esp-now + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=esp_now --features=esp-now - name: build (embassy_esp_now) - run: cd examples-esp32c2 && cargo build --release --example=embassy_esp_now --features=async,esp-now + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=embassy_esp_now --features=async,esp-now - name: build (embassy_dhcp) - run: cd examples-esp32c2 && cargo build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net + run: cd examples-esp32c2 && cargo +nightly-2023-03-09 build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net esp32c3: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@v1 with: - targets: riscv32imc-unknown-none-elf + target: riscv32imc-unknown-none-elf + toolchain: nightly-2023-03-09 components: rust-src - uses: Swatinem/rust-cache@v1 - name: build (ble) - run: cd examples-esp32c3 && cargo build --release --example=ble --features=ble + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=ble --features=ble + - name: build (async-ble) + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=async_ble --features=async,ble - name: build (coex) - run: cd examples-esp32c3 && cargo build --release --example=coex --features=embedded-svc,wifi,ble + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=coex --features=embedded-svc,wifi,ble - name: build (dhcp) - run: cd examples-esp32c3 && cargo build --release --example=dhcp --features=embedded-svc,wifi + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=dhcp --features=embedded-svc,wifi - name: build (static_ip) - run: cd examples-esp32c3 && cargo build --release --example=static_ip --features=embedded-svc,wifi + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=static_ip --features=embedded-svc,wifi - name: build (esp_now) - run: cd examples-esp32c3 && cargo build --release --example=esp_now --features=esp-now + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=esp_now --features=esp-now - name: build (embassy_esp_now) - run: cd examples-esp32c3 && cargo build --release --example=embassy_esp_now --features=async,esp-now + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=embassy_esp_now --features=async,esp-now - name: build (embassy_dhcp) - run: cd examples-esp32c3 && cargo build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net + run: cd examples-esp32c3 && cargo +nightly-2023-03-09 build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net esp32c6: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@v1 with: - targets: riscv32imac-unknown-none-elf + target: riscv32imac-unknown-none-elf + toolchain: nightly-2023-03-09 components: rust-src - uses: Swatinem/rust-cache@v1 - name: build (dhcp) - run: cd examples-esp32c6 && cargo build --release --example=dhcp --features=embedded-svc,wifi + run: cd examples-esp32c6 && cargo +nightly-2023-03-09 build --release --example=dhcp --features=embedded-svc,wifi - name: build (static_ip) - run: cd examples-esp32c6 && cargo build --release --example=static_ip --features=embedded-svc,wifi + run: cd examples-esp32c6 && cargo +nightly-2023-03-09 build --release --example=static_ip --features=embedded-svc,wifi - name: build (esp_now) - run: cd examples-esp32c6 && cargo build --release --example=esp_now --features=esp-now + run: cd examples-esp32c6 && cargo +nightly-2023-03-09 build --release --example=esp_now --features=esp-now - name: build (embassy_esp_now) - run: cd examples-esp32c6 && cargo build --release --example=embassy_esp_now --features=async,esp-now + run: cd examples-esp32c6 && cargo +nightly-2023-03-09 build --release --example=embassy_esp_now --features=async,esp-now - name: build (embassy_dhcp) - run: cd examples-esp32c6 && cargo build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net + run: cd examples-esp32c6 && cargo +nightly-2023-03-09 build --release --example=embassy_dhcp --features=async,embedded-svc,wifi,embassy-net esp32s2: runs-on: ubuntu-latest @@ -153,6 +162,8 @@ jobs: - name: build (ble) run: cd examples-esp32s3 && cargo build --release --example=ble --features=ble + - name: build (async-ble) + run: cd examples-esp32s3 && cargo build --release --example=async_ble --features=async,ble - name: build (coex) run: cd examples-esp32s3 && cargo build --release --example=coex --features=embedded-svc,wifi,ble - name: build (dhcp)