Usage
Install and use the SimpleRsBLE Rust binding.
SimpleRsBLE exposes SimpleBLE to Rust applications with Rust-facing types and async event streams.
Requirements
- Rust 1.82 or newer.
- CMake 3.21 or newer.
- The platform requirements from Installation.
- On Linux, DBus headers:
sudo apt-get install libdbus-1-devInstall from crates.io
cargo add simplersbleThe crate builds the native SimpleBLE components during the Cargo build.
Use a local checkout
When developing SimpleRsBLE or testing unreleased changes:
[dependencies]
simplersble = { path = "path/to/simpleble" }First scan
#[tokio::main]
async fn main() {
let mut adapters = simplersble::Adapter::get_adapters().unwrap();
if adapters.is_empty() {
println!("No Bluetooth adapters found.");
return;
}
let adapter = adapters.remove(0);
adapter.scan_for(5000).unwrap();
for peripheral in adapter.scan_get_results().unwrap() {
println!(
"{} [{}]",
peripheral.identifier().unwrap(),
peripheral.address().unwrap()
);
}
}For event streams and notification examples, see Quickstart, Read, Write, Notify, and the SimpleRsBLE Examples.
Several handles can refer to the same adapter or device, such as a peripheral from a scan event and another from scan_get_results(). Each kind of event goes to the handle that last subscribed to it with on_scan_event(), on_power_event(), or on_connection_event(), and dropping a handle doesn't affect the others.
Configure backends
simplersble::config holds process-wide settings, grouped by backend: simplebluez (Linux), winrt (Windows), corebluetooth (macOS), and dongl. Set them at startup, before the first Backend::get_backends() or Adapter::get_adapters() call; changes made afterwards may not take effect.
use std::time::Duration;
simplersble::config::simplebluez::set_connection_timeout(Duration::from_secs(5));Each setting has a getter and a setter, such as use_system_bus() and set_use_system_bus(); timeouts use std::time::Duration. Each backend module has a reset() that restores its defaults, and config::reset_all() restores them all.
Use a Dongl
A Dongl is a USB Bluetooth adapter made for SimpleBLE. Its backend is on by default; take the adapters of the Dongl backend:
let adapters = simplersble::Backend::get_dongl().unwrap().adapters().unwrap();
if adapters.is_empty() {
println!("No Dongl found.");
return;
}A Dongl adapter works like any other adapter. Pairing through it happens without the operating system, so your application answers pairing requests. Register the callbacks your device needs with simplersble::advanced::dongl before connect():
set_passkey_request_callbacktakes anFn() -> Option<String>: return the six digits the user typed, orNoneto reject.set_passkey_display_callbacktakes anFn(&str): show the passkey so the user can type it on the device.set_numeric_comparison_callbacktakes anFn(&str) -> bool: returntrueonly if the device shows the same six digits.
use std::io::Write;
simplersble::advanced::dongl::set_numeric_comparison_callback(&peripheral, |passkey| {
print!("Does the device show {}? [y/N] ", passkey);
std::io::stdout().flush().unwrap();
let mut answer = String::new();
std::io::stdin().read_line(&mut answer).unwrap();
answer.trim() == "y"
})
.unwrap();
peripheral.connect().unwrap();The callbacks run synchronously on a SimpleBLE worker thread, possibly while connect() is still running, and can block while the user answers. A request without a callback, or whose callback panics, is rejected. A callback stays registered until you clear it with the matching clear_*_callback() or drop the last clone of the Peripheral. Peripherals of other adapters return an error.
Host a local peripheral
adapter.create_local_peripheral() returns a LocalPeripheral that hosts GATT services on that adapter. Add advertised service UUIDs, services, and characteristics, then call start(); the layout cannot change while started.
use futures::stream::StreamExt;
use simplersble::{CharacteristicCapability, LocalCharacteristicEvent};
let peripheral = adapter.create_local_peripheral().unwrap();
peripheral.add_advertised_service("12345678-1234-5678-1234-56789abcdef0").unwrap();
let service = peripheral.add_service("12345678-1234-5678-1234-56789abcdef0").unwrap();
let characteristic = service
.add_characteristic(
"12345678-1234-5678-1234-56789abcdef1",
&[CharacteristicCapability::Read, CharacteristicCapability::WriteRequest, CharacteristicCapability::Notify],
)
.unwrap();
characteristic.set_value(b"ready").unwrap();
let mut events = characteristic.on_event();
let echo = characteristic.clone();
tokio::spawn(async move {
while let Some(Ok(event)) = events.next().await {
if let LocalCharacteristicEvent::Written(value) = event {
// Echo the written value to subscribed clients.
echo.set_value(&value).unwrap();
}
}
});
peripheral.start().unwrap();set_value() updates the stored value and publishes it to subscribed clients when the characteristic has the Notify or Indicate capability. characteristic.on_event() streams Written(value), Subscribed, and Unsubscribed events; the stored value is updated before Written is delivered. peripheral.on_client_event() streams ClientEvent::Connected(address) and ClientEvent::Disconnected(address).
To compute values on demand, characteristic.set_callback_on_read() takes an Fn() -> Vec<u8> that answers client reads instead of the stored value, until clear_callback_on_read(). It runs synchronously on a native thread while the client waits, so return promptly and do not call blocking BLE operations from it.
set_advertised_manufacturer_data(company_id, data) adds manufacturer specific data to the advertisement, and set_connectable(false) makes it non-connectable, for example to advertise a beacon. Configure both before start().
Clones of a LocalPeripheral, LocalService, or LocalCharacteristic refer to the same object. Dropping the last clone of the LocalPeripheral stops it.
Platform-specific features
simplersble::advanced exposes features of a single platform:
- On macOS and Linux,
advanced::macos::set_advertisement_local_name(&peripheral, Some("Name"))andadvanced::linux::set_advertisement_local_name(...)set the local name in a local peripheral's advertisement beforestart().Noneremoves it. This changes only the advertisement, not the system Bluetooth name. - On macOS,
advanced::macos::retrieve_cached_peripherals(&adapter, &identifiers)returns peripherals that CoreBluetooth already knows, by the identifiersaddress()returned earlier, without scanning. Unknown identifiers are skipped, and a returned peripheral may be out of range.
