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. Theid
is aPeripheralId
, which you can use to get the fullPeripheral
object 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). Thestate
is aCentralState
enum.
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.