Event-Driven Usage
While polling for peripherals with adapter.peripherals() is useful for simple applications, a more robust and efficient approach for long-running applications is to use btleplug's event-driven API. This allows your application to react to Bluetooth events in real-time as they occur.
The Central Event Stream
The Central trait (implemented by Adapter) provides an events() method that returns a stream of CentralEvent enums.
By listening to this stream, you can be notified of:
- Device discovery
- Device property updates (e.g., name or RSSI changes)
- Connections and disconnections
- Changes in advertisement data
Example: Event-Driven Discovery
The following example demonstrates how to set up an event listener and handle different types of central events.
use btleplug::api::{Central, CentralEvent, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::{Adapter, Manager};
use futures::stream::StreamExt;
async fn get_central(manager: &Manager) -> Adapter {
let adapters = manager.adapters().await.unwrap();
adapters.into_iter().nth(0).unwrap()
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let manager = Manager::new().await?;
let central = get_central(&manager).await;
// Get the stream of events
let mut events = central.events().await?;
// Start scanning
central.start_scan(ScanFilter::default()).await?;
println!("Listening for events...");
// Process events from the stream
while let Some(event) = events.next().await {
match event {
CentralEvent::DeviceDiscovered(id) => {
println!("Device Discovered: {:?}", id);
}
CentralEvent::DeviceUpdated(id) => {
println!("Device Updated: {:?}", id);
}
CentralEvent::DeviceConnected(id) => {
println!("Device Connected: {:?}", id);
}
CentralEvent::DeviceDisconnected(id) => {
println!("Device Disconnected: {:?}", id);
}
CentralEvent::ManufacturerDataAdvertisement { id, manufacturer_data } => {
println!(
"Manufacturer Data Advertisement: {:?}, {:?}",
id, manufacturer_data
);
}
CentralEvent::ServiceDataAdvertisement { id, service_data } => {
println!("Service Data Advertisement: {:?}, {:?}", id, service_data);
}
CentralEvent::ServicesAdvertisement { id, services } => {
println!("Services Advertisement: {:?}, {:?}", id, services);
}
_ => {}
}
}
Ok(())
}
Understanding Central Events
Here's a breakdown of the most common events and what they mean:
-
CentralEvent::DeviceDiscovered(id): Fired the first time a peripheral is seen during a scan. Theidis aPeripheralId, which you can use to get the fullPeripheralobject usingcentral.peripheral(&id).await?. -
CentralEvent::DeviceUpdated(id): Fired when an already-discovered peripheral sends a new advertisement packet, which might contain updated information like its name or RSSI value. -
CentralEvent::DeviceConnected(id): Fired when a connection to a peripheral is successfully established. -
CentralEvent::DeviceDisconnected(id): Fired when a peripheral disconnects, either intentionally or due to signal loss. -
CentralEvent::ManufacturerDataAdvertisement { ... }: Fired when a peripheral's advertisement includes manufacturer-specific data. This is often used for custom protocols or device identification. -
CentralEvent::ServiceDataAdvertisement { ... }: Fired when an advertisement contains data associated with a specific service UUID. -
CentralEvent::ServicesAdvertisement { ... }: Fired when an advertisement includes a list of service UUIDs the peripheral supports. -
CentralEvent::StateUpdate(state): Fired when the Bluetooth adapter's state changes (e.g., powered on or off). Thestateis aCentralStateenum.
Why Use the Event-Driven Approach?
- Efficiency: Your application doesn't need to repeatedly poll
adapter.peripherals(), which can be resource-intensive. - Responsiveness: You can react to new devices and state changes instantly.
- Correctness: It's the most reliable way to track the lifecycle of peripherals, especially their connection status.