Add BLE examples using trouBLE, move some Wi-Fi examples to qa-test (#3999)

* Move `embassy_wifi_bench`, `wifi_bench`, and `wifi_csi` examples to `qa-test` package

* Remove blocking BLE example, replace Embassy example with trouble-based scanner

* Add BLE `bas_peripheral` example
This commit is contained in:
Jesse Braham 2025-08-28 02:31:37 -07:00 committed by GitHub
parent 588140a55f
commit 1ecb6f5f76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 508 additions and 731 deletions

View File

@ -1,19 +1,14 @@
[package]
name = "embassy-bench"
name = "bas-peripheral"
version = "0.0.0"
edition = "2024"
publish = false
[dependencies]
cfg-if = "1.0.0"
bt-hci = "0.4.0"
embassy-executor = { version = "0.7.0", features = ["task-arena-size-20480"] }
embassy-futures = "0.1.1"
embassy-net = { version = "0.6.0", features = [
"dhcpv4",
"medium-ethernet",
"tcp",
"udp",
] }
embassy-sync = "0.7.2"
embassy-time = "0.4.0"
esp-alloc = { path = "../../../esp-alloc" }
esp-backtrace = { path = "../../../esp-backtrace", features = [
@ -24,15 +19,20 @@ esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal-embassy = { path = "../../../esp-hal-embassy" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }
esp-preempt = { path = "../../../esp-preempt", features = [
"log-04",
] }
esp-preempt = { path = "../../../esp-preempt", features = ["log-04"] }
esp-radio = { path = "../../../esp-radio", features = [
"ble",
"log-04",
"unstable",
"wifi",
] }
static_cell = "2.1.0"
heapless = "0.9.1"
log = "0.4.27"
trouble-host = { git = "https://github.com/embassy-rs/trouble", rev = "366ee88", features = [
"default-packet-pool-mtu-255",
"derive",
"scan",
] }
static_cell = "2.1.1"
[features]
esp32 = [
@ -67,13 +67,13 @@ esp32c6 = [
"esp-preempt/esp32c6",
"esp-radio/esp32c6",
]
esp32s2 = [
"esp-backtrace/esp32s2",
"esp-bootloader-esp-idf/esp32s2",
"esp-hal-embassy/esp32s2",
"esp-hal/esp32s2",
"esp-preempt/esp32s2",
"esp-radio/esp32s2",
esp32h2 = [
"esp-backtrace/esp32h2",
"esp-bootloader-esp-idf/esp32h2",
"esp-hal-embassy/esp32h2",
"esp-hal/esp32h2",
"esp-preempt/esp32h2",
"esp-radio/esp32h2",
]
esp32s3 = [
"esp-backtrace/esp32s3",

View File

@ -0,0 +1,242 @@
//! A bluetooth battery service example built using Embassy and trouBLE.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_futures::{join::join, select::select};
use embassy_time::Timer;
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{clock::CpuClock, timer::timg::TimerGroup};
use esp_radio::ble::controller::BleConnector;
use log::{info, warn};
use static_cell::StaticCell;
use trouble_host::prelude::*;
esp_bootloader_esp_idf::esp_app_desc!();
#[esp_hal_embassy::main]
async fn main(_s: Spawner) {
esp_println::logger::init_logger_from_env();
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
esp_alloc::heap_allocator!(size: 72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_preempt::init(timg0.timer0);
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
let radio = RADIO.init(esp_radio::init().unwrap());
#[cfg(not(feature = "esp32"))]
{
let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(systimer.alarm0);
}
#[cfg(feature = "esp32")]
{
esp_hal_embassy::init(timg0.timer1);
}
let bluetooth = peripherals.BT;
let connector = BleConnector::new(radio, bluetooth);
let controller: ExternalController<_, 20> = ExternalController::new(connector);
ble_bas_peripheral_run(controller).await;
}
/// Max number of connections
const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att
// GATT Server definition
#[gatt_server]
struct Server {
battery_service: BatteryService,
}
/// Battery service
#[gatt_service(uuid = service::BATTERY)]
struct BatteryService {
/// Battery Level
#[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])]
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level")]
#[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)]
level: u8,
#[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)]
status: bool,
}
/// Run the BLE stack.
pub async fn ble_bas_peripheral_run<C>(controller: C)
where
C: Controller,
{
// Using a fixed "random" address can be useful for testing. In real scenarios, one would
// use e.g. the MAC 6 byte array as the address (how to get that varies by the platform).
let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]);
info!("Our address = {:?}", address);
let mut resources: HostResources<DefaultPacketPool, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> =
HostResources::new();
let stack = trouble_host::new(controller, &mut resources).set_random_address(address);
let Host {
mut peripheral,
runner,
..
} = stack.build();
info!("Starting advertising and GATT service");
let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig {
name: "TrouBLE",
appearance: &appearance::power_device::GENERIC_POWER_DEVICE,
}))
.unwrap();
let _ = join(ble_task(runner), async {
loop {
match advertise("Trouble Example", &mut peripheral, &server).await {
Ok(conn) => {
// set up tasks when the connection is established to a central, so they don't
// run when no one is connected.
let a = gatt_events_task(&server, &conn);
let b = custom_task(&server, &conn, &stack);
// run until any task ends (usually because the connection has been closed),
// then return to advertising state.
select(a, b).await;
}
Err(e) => {
panic!("[adv] error: {:?}", e);
}
}
}
})
.await;
}
/// This is a background task that is required to run forever alongside any other BLE tasks.
///
/// ## Alternative
///
/// If you didn't require this to be generic for your application, you could statically spawn this
/// with i.e.
///
/// ```rust,ignore
///
/// #[embassy_executor::task]
/// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) {
/// runner.run().await;
/// }
///
/// spawner.must_spawn(ble_task(runner));
/// ```
async fn ble_task<C: Controller, P: PacketPool>(mut runner: Runner<'_, C, P>) {
loop {
if let Err(e) = runner.run().await {
panic!("[ble_task] error: {:?}", e);
}
}
}
/// Stream Events until the connection closes.
///
/// This function will handle the GATT events and process them.
/// This is how we interact with read and write requests.
async fn gatt_events_task<P: PacketPool>(
server: &Server<'_>,
conn: &GattConnection<'_, '_, P>,
) -> Result<(), Error> {
let level = server.battery_service.level;
let reason = loop {
match conn.next().await {
GattConnectionEvent::Disconnected { reason } => break reason,
GattConnectionEvent::Gatt { event } => {
match &event {
GattEvent::Read(event) => {
if event.handle() == level.handle {
let value = server.get(&level);
info!("[gatt] Read Event to Level Characteristic: {:?}", value);
}
}
GattEvent::Write(event) => {
if event.handle() == level.handle {
info!(
"[gatt] Write Event to Level Characteristic: {:?}",
event.data()
);
}
}
_ => {}
};
// This step is also performed at drop(), but writing it explicitly is necessary
// in order to ensure reply is sent.
match event.accept() {
Ok(reply) => reply.send().await,
Err(e) => warn!("[gatt] error sending response: {:?}", e),
};
}
_ => {} // ignore other Gatt Connection Events
}
};
info!("[gatt] disconnected: {:?}", reason);
Ok(())
}
/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
async fn advertise<'values, 'server, C: Controller>(
name: &'values str,
peripheral: &mut Peripheral<'values, C, DefaultPacketPool>,
server: &'server Server<'values>,
) -> Result<GattConnection<'values, 'server, DefaultPacketPool>, BleHostError<C::Error>> {
let mut advertiser_data = [0; 31];
let len = AdStructure::encode_slice(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[[0x0f, 0x18]]),
AdStructure::CompleteLocalName(name.as_bytes()),
],
&mut advertiser_data[..],
)?;
let advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..len],
scan_data: &[],
},
)
.await?;
info!("[adv] advertising");
let conn = advertiser.accept().await?.with_attribute_server(server)?;
info!("[adv] connection established");
Ok(conn)
}
/// Example task to use the BLE notifier interface.
/// This task will notify the connected central of a counter value every 2 seconds.
/// It will also read the RSSI value every 2 seconds.
/// and will stop when the connection is closed by the central or an error occurs.
async fn custom_task<C: Controller, P: PacketPool>(
server: &Server<'_>,
conn: &GattConnection<'_, '_, P>,
stack: &Stack<'_, C, P>,
) {
let mut tick: u8 = 0;
let level = server.battery_service.level;
loop {
tick = tick.wrapping_add(1);
info!("[custom_task] notifying connection of tick {}", tick);
if level.notify(conn, &tick).await.is_err() {
info!("[custom_task] error notifying connection");
break;
};
// read RSSI (Received Signal Strength Indicator) of the connection.
if let Ok(rssi) = conn.raw().rssi(stack).await {
info!("[custom_task] RSSI: {:?}", rssi);
} else {
info!("[custom_task] error getting RSSI");
break;
};
Timer::after_secs(2).await;
}
}

View File

@ -1,78 +0,0 @@
[package]
name = "ble"
version = "0.0.0"
edition = "2024"
publish = false
[dependencies]
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8", features = [
"async",
"macros",
] }
cfg-if = "1.0.0"
esp-alloc = { path = "../../../esp-alloc" }
esp-backtrace = { path = "../../../esp-backtrace", features = [
"panic-handler",
"println",
] }
esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }
esp-preempt = { path = "../../../esp-preempt", features = [
"log-04",
] }
esp-radio = { path = "../../../esp-radio", features = [
"ble",
"log-04",
"unstable",
] }
[features]
esp32 = [
"esp-backtrace/esp32",
"esp-bootloader-esp-idf/esp32",
"esp-hal/esp32",
"esp-preempt/esp32",
"esp-radio/esp32",
]
esp32c2 = [
"esp-backtrace/esp32c2",
"esp-bootloader-esp-idf/esp32c2",
"esp-hal/esp32c2",
"esp-preempt/esp32c2",
"esp-radio/esp32c2",
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal/esp32c3",
"esp-preempt/esp32c3",
"esp-radio/esp32c3",
]
esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal/esp32c6",
"esp-preempt/esp32c6",
"esp-radio/esp32c6",
]
esp32h2 = [
"esp-backtrace/esp32h2",
"esp-bootloader-esp-idf/esp32h2",
"esp-hal/esp32h2",
"esp-preempt/esp32h2",
"esp-radio/esp32h2",
]
esp32s3 = [
"esp-backtrace/esp32s3",
"esp-bootloader-esp-idf/esp32s3",
"esp-hal/esp32s3",
"esp-preempt/esp32s3",
"esp-radio/esp32s3",
]
[profile.release]
debug = true
debug-assertions = true
lto = "fat"
codegen-units = 1

View File

@ -1,170 +0,0 @@
//! 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
#![no_std]
#![no_main]
use bleps::{
Ble,
HciConnector,
ad_structure::{
AdStructure,
BR_EDR_NOT_SUPPORTED,
LE_GENERAL_DISCOVERABLE,
create_advertising_data,
},
attribute_server::{AttributeServer, NotificationData, WorkResult},
gatt,
};
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{
clock::CpuClock,
gpio::{Input, InputConfig, Pull},
main,
time,
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::ble::controller::BleConnector;
esp_bootloader_esp_idf::esp_app_desc!();
#[main]
fn main() -> ! {
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!(size: 72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_preempt::init(timg0.timer0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let config = InputConfig::default().with_pull(Pull::Down);
cfg_if::cfg_if! {
if #[cfg(any(feature = "esp32", feature = "esp32s3"))] {
let button = Input::new(peripherals.GPIO0, config);
} else {
let button = Input::new(peripherals.GPIO9, config);
}
}
let mut debounce_cnt = 500;
let mut bluetooth = peripherals.BT;
let now = || time::Instant::now().duration_since_epoch().as_millis();
loop {
let connector = BleConnector::new(&esp_radio_ctrl, bluetooth.reborrow());
let hci = HciConnector::new(connector, now);
let mut ble = Ble::new(&hci);
println!("{:?}", ble.init());
println!("{:?}", ble.cmd_set_le_advertising_parameters());
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(),
),
);
println!("{:?}", ble.cmd_set_le_advertise_enable(true));
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);
loop {
let mut notification = None;
if button.is_low() && debounce_cnt > 0 {
debounce_cnt -= 1;
if debounce_cnt == 0 {
let mut cccd = [0u8; 1];
if let Some(1) = srv.get_characteristic_value(
my_characteristic_notify_enable_handle,
0,
&mut cccd,
) {
// if notifications enabled
if cccd[0] == 1 {
notification = Some(NotificationData::new(
my_characteristic_handle,
&b"Notification"[..],
));
}
}
}
};
if button.is_high() {
debounce_cnt = 500;
}
match srv.do_work_with_notification(notification) {
Ok(res) => {
if let WorkResult::GotDisconnected = res {
break;
}
}
Err(err) => {
println!("{:?}", err);
}
}
}
}
}

View File

@ -1,182 +0,0 @@
//! 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
// 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_radio::{Controller, ble::controller::BleConnector};
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);
esp_alloc::heap_allocator!(size: 72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_preempt::init(timg0.timer0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let config = InputConfig::default().with_pull(Pull::Down);
cfg_if::cfg_if! {
if #[cfg(any(feature = "esp32", 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 mut bluetooth = peripherals.BT;
let connector = BleConnector::new(esp_radio_ctrl, bluetooth.reborrow());
let now = || time::Instant::now().duration_since_epoch().as_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();
}
}

View File

@ -1,16 +1,14 @@
[package]
name = "embassy-ble"
name = "scanner"
version = "0.0.0"
edition = "2024"
publish = false
[dependencies]
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8", features = [
"async",
"macros",
] }
cfg-if = "1.0.0"
bt-hci = "0.4.0"
embassy-executor = { version = "0.7.0", features = ["task-arena-size-20480"] }
embassy-futures = "0.1.1"
embassy-time = "0.4.0"
esp-alloc = { path = "../../../esp-alloc" }
esp-backtrace = { path = "../../../esp-backtrace", features = [
"panic-handler",
@ -20,15 +18,20 @@ esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal-embassy = { path = "../../../esp-hal-embassy" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }
esp-preempt = { path = "../../../esp-preempt", features = [
"log-04",
] }
esp-preempt = { path = "../../../esp-preempt", features = ["log-04"] }
esp-radio = { path = "../../../esp-radio", features = [
"ble",
"log-04",
"unstable",
] }
static_cell = "2.1.0"
heapless = "0.9.1"
log = "0.4.27"
trouble-host = { git = "https://github.com/embassy-rs/trouble", rev = "366ee88", features = [
"default-packet-pool-mtu-255",
"derive",
"scan",
] }
static_cell = "2.1.1"
[features]
esp32 = [

View File

@ -0,0 +1,111 @@
//! A bluetooth scanner built using Embassy and trouBLE.
#![no_std]
#![no_main]
use core::cell::RefCell;
use bt_hci::{cmd::le::LeSetScanParams, controller::ControllerCmdSync};
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_time::{Duration, Timer};
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{clock::CpuClock, timer::timg::TimerGroup};
use esp_radio::ble::controller::BleConnector;
use heapless::Deque;
use log::info;
use static_cell::StaticCell;
use trouble_host::prelude::*;
esp_bootloader_esp_idf::esp_app_desc!();
#[esp_hal_embassy::main]
async fn main(_s: Spawner) {
esp_println::logger::init_logger_from_env();
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
esp_alloc::heap_allocator!(size: 72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_preempt::init(timg0.timer0);
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
let radio = RADIO.init(esp_radio::init().unwrap());
#[cfg(not(feature = "esp32"))]
{
let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(systimer.alarm0);
}
#[cfg(feature = "esp32")]
{
esp_hal_embassy::init(timg0.timer1);
}
let bluetooth = peripherals.BT;
let connector = BleConnector::new(radio, bluetooth);
let controller: ExternalController<_, 20> = ExternalController::new(connector);
ble_scanner_run(controller).await;
}
/// Max number of connections
const CONNECTIONS_MAX: usize = 1;
const L2CAP_CHANNELS_MAX: usize = 1;
async fn ble_scanner_run<C>(controller: C)
where
C: Controller + ControllerCmdSync<LeSetScanParams>,
{
// Using a fixed "random" address can be useful for testing. In real scenarios, one would
// use e.g. the MAC 6 byte array as the address (how to get that varies by the platform).
let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]);
info!("Our address = {:?}", address);
let mut resources: HostResources<DefaultPacketPool, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> =
HostResources::new();
let stack = trouble_host::new(controller, &mut resources).set_random_address(address);
let Host {
central,
mut runner,
..
} = stack.build();
let printer = Printer {
seen: RefCell::new(Deque::new()),
};
let mut scanner = Scanner::new(central);
let _ = join(runner.run_with_handler(&printer), async {
let mut config = ScanConfig::default();
config.active = true;
config.phys = PhySet::M1;
config.interval = Duration::from_secs(1);
config.window = Duration::from_secs(1);
let mut _session = scanner.scan(&config).await.unwrap();
// Scan forever
loop {
Timer::after(Duration::from_secs(1)).await;
}
})
.await;
}
struct Printer {
seen: RefCell<Deque<BdAddr, 128>>,
}
impl EventHandler for Printer {
fn on_adv_reports(&self, mut it: LeAdvReportsIter<'_>) {
let mut seen = self.seen.borrow_mut();
while let Some(Ok(report)) = it.next() {
if seen.iter().find(|b| b.raw() == report.addr.raw()).is_none() {
info!("discovered: {:?}", report.addr);
if seen.is_full() {
seen.pop_front();
}
seen.push_back(report.addr).unwrap();
}
}
}
}

View File

@ -1,29 +0,0 @@
[target.'cfg(target_arch = "riscv32")']
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlinkall.x",
"-C", "force-frame-pointers",
]
[target.'cfg(target_arch = "xtensa")']
runner = "espflash flash --monitor"
rustflags = [
# GNU LD
"-C", "link-arg=-Wl,-Tlinkall.x",
"-C", "link-arg=-nostartfiles",
# LLD
# "-C", "link-arg=-Tlinkall.x",
# "-C", "linker=rust-lld",
]
[env]
ESP_LOG = "info"
SSID = "SSID"
PASSWORD = "PASSWORD"
STATIC_IP = "1.1.1.1 "
GATEWAY_IP = "1.1.1.1"
HOST_IP = "1.1.1.1"
[unstable]
build-std = ["alloc", "core"]

View File

@ -1,80 +0,0 @@
[package]
name = "bench"
version = "0.0.0"
edition = "2024"
publish = false
[dependencies]
blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "b3ecefc" }
embedded-io = { version = "0.6.1", default-features = false }
esp-alloc = { path = "../../../esp-alloc" }
esp-backtrace = { path = "../../../esp-backtrace", features = [
"panic-handler",
"println",
] }
esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }
esp-preempt = { path = "../../../esp-preempt", features = [
"log-04",
] }
esp-radio = { path = "../../../esp-radio", features = [
"log-04",
"smoltcp",
"unstable",
"wifi",
] }
smoltcp = { version = "0.12.0", default-features = false, features = [
"medium-ethernet",
"socket-raw",
] }
[features]
esp32 = [
"esp-backtrace/esp32",
"esp-bootloader-esp-idf/esp32",
"esp-hal/esp32",
"esp-preempt/esp32",
"esp-radio/esp32",
]
esp32c2 = [
"esp-backtrace/esp32c2",
"esp-bootloader-esp-idf/esp32c2",
"esp-hal/esp32c2",
"esp-preempt/esp32c2",
"esp-radio/esp32c2",
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal/esp32c3",
"esp-preempt/esp32c3",
"esp-radio/esp32c3",
]
esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal/esp32c6",
"esp-preempt/esp32c6",
"esp-radio/esp32c6",
]
esp32s2 = [
"esp-backtrace/esp32s2",
"esp-bootloader-esp-idf/esp32s2",
"esp-hal/esp32s2",
"esp-preempt/esp32s2",
"esp-radio/esp32s2",
]
esp32s3 = [
"esp-backtrace/esp32s3",
"esp-bootloader-esp-idf/esp32s3",
"esp-hal/esp32s3",
"esp-preempt/esp32s3",
"esp-radio/esp32s3",
]
[profile.release]
debug = true
debug-assertions = true
lto = "fat"
codegen-units = 1

View File

@ -1,29 +0,0 @@
[target.'cfg(target_arch = "riscv32")']
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlinkall.x",
"-C", "force-frame-pointers",
]
[target.'cfg(target_arch = "xtensa")']
runner = "espflash flash --monitor"
rustflags = [
# GNU LD
"-C", "link-arg=-Wl,-Tlinkall.x",
"-C", "link-arg=-nostartfiles",
# LLD
# "-C", "link-arg=-Tlinkall.x",
# "-C", "linker=rust-lld",
]
[env]
ESP_LOG = "info"
SSID = "SSID"
PASSWORD = "PASSWORD"
STATIC_IP = "1.1.1.1 "
GATEWAY_IP = "1.1.1.1"
HOST_IP = "1.1.1.1"
[unstable]
build-std = ["alloc", "core"]

View File

@ -1,80 +0,0 @@
[package]
name = "csi"
version = "0.0.0"
edition = "2024"
publish = false
[dependencies]
blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "b3ecefc" }
esp-alloc = { path = "../../../esp-alloc" }
esp-backtrace = { path = "../../../esp-backtrace", features = [
"panic-handler",
"println",
] }
esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" }
esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] }
esp-println = { path = "../../../esp-println", features = ["log-04"] }
esp-preempt = { path = "../../../esp-preempt", features = [
"log-04",
] }
esp-radio = { path = "../../../esp-radio", features = [
"csi",
"log-04",
"smoltcp",
"unstable",
"wifi",
] }
smoltcp = { version = "0.12.0", default-features = false, features = [
"medium-ethernet",
"socket-raw",
] }
[features]
esp32 = [
"esp-backtrace/esp32",
"esp-bootloader-esp-idf/esp32",
"esp-hal/esp32",
"esp-preempt/esp32",
"esp-radio/esp32",
]
esp32c2 = [
"esp-backtrace/esp32c2",
"esp-bootloader-esp-idf/esp32c2",
"esp-hal/esp32c2",
"esp-preempt/esp32c2",
"esp-radio/esp32c2",
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal/esp32c3",
"esp-preempt/esp32c3",
"esp-radio/esp32c3",
]
esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal/esp32c6",
"esp-preempt/esp32c6",
"esp-radio/esp32c6",
]
esp32s2 = [
"esp-backtrace/esp32s2",
"esp-bootloader-esp-idf/esp32s2",
"esp-hal/esp32s2",
"esp-preempt/esp32s2",
"esp-radio/esp32s2",
]
esp32s3 = [
"esp-backtrace/esp32s3",
"esp-bootloader-esp-idf/esp32s3",
"esp-hal/esp32s3",
"esp-preempt/esp32s3",
"esp-radio/esp32s3",
]
[profile.release]
debug = true
debug-assertions = true
lto = "fat"
codegen-units = 1

View File

@ -1,29 +0,0 @@
[target.'cfg(target_arch = "riscv32")']
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlinkall.x",
"-C", "force-frame-pointers",
]
[target.'cfg(target_arch = "xtensa")']
runner = "espflash flash --monitor"
rustflags = [
# GNU LD
"-C", "link-arg=-Wl,-Tlinkall.x",
"-C", "link-arg=-nostartfiles",
# LLD
# "-C", "link-arg=-Tlinkall.x",
# "-C", "linker=rust-lld",
]
[env]
ESP_LOG = "info"
SSID = "SSID"
PASSWORD = "PASSWORD"
STATIC_IP = "1.1.1.1 "
GATEWAY_IP = "1.1.1.1"
HOST_IP = "1.1.1.1"
[unstable]
build-std = ["alloc", "core"]

View File

@ -29,6 +29,11 @@ rustflags = [
[env]
ESP_LOG = "info"
ESP_HAL_EMBASSY_CONFIG_TIMER_QUEUE="multiple-integrated"
SSID = "SSID"
PASSWORD = "PASSWORD"
STATIC_IP = "1.1.1.1 "
GATEWAY_IP = "1.1.1.1"
HOST_IP = "1.1.1.1"
[unstable]
build-std = ["alloc", "core"]

View File

@ -6,34 +6,118 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
cfg-if = "1.0.0"
embassy-executor = { version = "0.7.0", features = ["task-arena-size-12288"] }
embassy-time = "0.4.0"
embassy-futures = "0.1.1"
embassy-sync = "0.6.1"
embedded-graphics = "0.8.1"
blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "b3ecefc", optional = true }
cfg-if = "1.0.0"
embassy-executor = { version = "0.7.0", features = ["task-arena-size-12288"] }
embassy-time = "0.4.0"
embassy-futures = "0.1.1"
embassy-net = { version = "0.6.0", features = [ "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-sync = "0.6.1"
embedded-graphics = "0.8.1"
embedded-hal-async = "1.0.0"
esp-alloc = { path = "../esp-alloc" }
esp-backtrace = { path = "../esp-backtrace", features = ["panic-handler", "println"] }
embedded-io = { version = "0.6.1", default-features = false }
esp-alloc = { path = "../esp-alloc" }
esp-backtrace = { path = "../esp-backtrace", features = [
"panic-handler",
"println",
] }
esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" }
esp-hal = { path = "../esp-hal", features = ["unstable", "log-04"] }
esp-hal-embassy = { path = "../esp-hal-embassy" }
esp-println = { path = "../esp-println", features = ["log-04"] }
lis3dh-async = "0.9.3"
ssd1306 = "0.10.0"
esp-hal = { path = "../esp-hal", features = ["log-04", "unstable"] }
esp-hal-embassy = { path = "../esp-hal-embassy" }
esp-preempt = { path = "../esp-preempt", features = [
"log-04",
], optional = true }
esp-println = { path = "../esp-println", features = ["log-04"] }
esp-radio = { path = "../esp-radio", features = [
"log-04",
"smoltcp",
"unstable",
"wifi",
], optional = true }
lis3dh-async = "0.9.3"
ssd1306 = "0.10.0"
smoltcp = { version = "0.12.0", default-features = false, features = [
"medium-ethernet",
"socket-raw",
], optional = true }
static_cell = "2.1.1"
[features]
unstable = []
esp32 = ["esp-backtrace/esp32", "esp-hal/esp32", "esp-hal-embassy/esp32", "esp-println/esp32", "esp-bootloader-esp-idf/esp32"]
esp32c2 = ["esp-backtrace/esp32c2", "esp-hal/esp32c2", "esp-hal-embassy/esp32c2", "esp-println/esp32c2", "esp-bootloader-esp-idf/esp32c2"]
esp32c3 = ["esp-backtrace/esp32c3", "esp-hal/esp32c3", "esp-hal-embassy/esp32c3", "esp-println/esp32c3", "esp-bootloader-esp-idf/esp32c3"]
esp32c6 = ["esp-backtrace/esp32c6", "esp-hal/esp32c6", "esp-hal-embassy/esp32c6", "esp-println/esp32c6", "esp-bootloader-esp-idf/esp32c6"]
esp32h2 = ["esp-backtrace/esp32h2", "esp-hal/esp32h2", "esp-hal-embassy/esp32h2", "esp-println/esp32h2", "esp-bootloader-esp-idf/esp32h2"]
esp32s2 = ["esp-backtrace/esp32s2", "esp-hal/esp32s2", "esp-hal-embassy/esp32s2", "esp-println/esp32s2", "esp-bootloader-esp-idf/esp32s2"]
esp32s3 = ["esp-backtrace/esp32s3", "esp-hal/esp32s3", "esp-hal-embassy/esp32s3", "esp-println/esp32s3", "esp-bootloader-esp-idf/esp32s3"]
esp-radio = [
"dep:blocking-network-stack",
"dep:esp-preempt",
"dep:esp-radio",
"dep:smoltcp",
]
esp32 = [
"esp-backtrace/esp32",
"esp-bootloader-esp-idf/esp32",
"esp-hal-embassy/esp32",
"esp-hal/esp32",
"esp-preempt?/esp32",
"esp-println/esp32",
"esp-radio?/esp32",
]
esp32c2 = [
"esp-backtrace/esp32c2",
"esp-bootloader-esp-idf/esp32c2",
"esp-hal-embassy/esp32c2",
"esp-hal/esp32c2",
"esp-preempt?/esp32c2",
"esp-println/esp32c2",
"esp-radio?/esp32c2",
]
esp32c3 = [
"esp-backtrace/esp32c3",
"esp-bootloader-esp-idf/esp32c3",
"esp-hal-embassy/esp32c3",
"esp-hal/esp32c3",
"esp-preempt?/esp32c3",
"esp-println/esp32c3",
"esp-radio?/esp32c3",
]
esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal-embassy/esp32c6",
"esp-hal/esp32c6",
"esp-preempt?/esp32c6",
"esp-println/esp32c6",
"esp-radio?/esp32c6",
]
esp32h2 = [
"esp-backtrace/esp32h2",
"esp-bootloader-esp-idf/esp32h2",
"esp-hal-embassy/esp32h2",
"esp-hal/esp32h2",
"esp-preempt?/esp32h2",
"esp-println/esp32h2",
"esp-radio?/esp32h2",
]
esp32s2 = [
"esp-backtrace/esp32s2",
"esp-bootloader-esp-idf/esp32s2",
"esp-hal-embassy/esp32s2",
"esp-hal/esp32s2",
"esp-preempt?/esp32s2",
"esp-println/esp32s2",
"esp-radio?/esp32s2",
]
esp32s3 = [
"esp-backtrace/esp32s3",
"esp-bootloader-esp-idf/esp32s3",
"esp-hal-embassy/esp32s3",
"esp-hal/esp32s3",
"esp-preempt?/esp32s3",
"esp-println/esp32s3",
"esp-radio?/esp32s3",
]
[profile.release]
debug = 2
debug = 2
debug-assertions = true
lto = "fat"
codegen-units = 1
lto = "fat"
codegen-units = 1

View File

@ -13,6 +13,9 @@
//! Because of the huge task-arena size configured this won't work on ESP32-S2
//! and ESP32-C2
//% FEATURES: esp-radio esp-radio/wifi esp-radio/unstable esp-hal/unstable
//% CHIPS: esp32 esp32c3 esp32c6 esp32s2 esp32s3
#![allow(static_mut_refs)]
#![no_std]
#![no_main]

View File

@ -10,6 +10,9 @@
//! variable. E.g `HOST_IP="192.168.0.24"` and also set SSID and PASSWORD env
//! variable before running this example.
//% FEATURES: esp-radio esp-radio/wifi esp-radio/smoltcp esp-radio/unstable esp-hal/unstable
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s2 esp32s3
#![no_std]
#![no_main]

View File

@ -1,8 +1,11 @@
//! CSI Example
//!
//!
//! Set SSID and PASSWORD env variable before running this example.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s2 esp32s3
//% FEATURES: esp-radio esp-radio/wifi esp-radio/smoltcp esp-radio/log-04 esp-radio/csi
//% FEATURES: esp-radio/unstable esp-hal/unstable
#![no_std]
#![no_main]