### Main Function for L2CAP Examples Source: https://context7.com/bluez/bluer/llms.txt The main entry point for the L2CAP examples. It parses command-line arguments to determine whether to run the client or server. If an argument is provided, it's treated as the target address for the client; otherwise, the server is started. ```rust @tokio::main async fn main() -> bluer::Result<()> { // Run either server or client based on arguments let args: Vec = std::env::args().collect(); if args.len() > 1 { let addr: Address = args[1].parse()?; l2cap_client(addr).await } else { l2cap_server().await } } ``` -------------------------------- ### Implement RFCOMM Server, Client, and Profile Registration Source: https://context7.com/bluez/bluer/llms.txt Provides examples for creating an RFCOMM server, a client, and a profile-based server that uses SDP for service discovery. ```rust use bluer::rfcomm::{Socket, SocketAddr, Stream, Listener, Profile, Role}; use bluer::Address; use tokio::io::{AsyncReadExt, AsyncWriteExt}; const CHANNEL: u8 = 5; // RFCOMM Server using raw sockets async fn rfcomm_server() -> bluer::Result<()> { let local_addr = SocketAddr::new(Address::any(), CHANNEL); let listener = Listener::bind(local_addr).await?; let actual_channel = listener.as_ref().local_addr()?.channel; println!("Listening on channel {}", actual_channel); loop { let (mut stream, peer_addr) = listener.accept().await?; println!("Connection from {}", peer_addr); // Echo loop let mut buf = vec![0u8; 1024]; loop { match stream.read(&mut buf).await { Ok(0) => break, Ok(n) => { println!("Received: {}", String::from_utf8_lossy(&buf[..n])); stream.write_all(&buf[..n]).await?; } Err(e) => { println!("Error: {}", e); break; } } } } } // RFCOMM Client async fn rfcomm_client(target: Address) -> bluer::Result<()> { let addr = SocketAddr::new(target, CHANNEL); println!("Connecting to {}", addr); let mut stream = Stream::connect(addr).await?; // Send and receive data stream.write_all(b"Hello RFCOMM!").await?; let mut response = vec![0u8; 256]; let n = stream.read(&mut response).await?; println!("Response: {}", String::from_utf8_lossy(&response[..n])); Ok(()) } // RFCOMM with Profile registration (uses SDP) async fn rfcomm_profile_server() -> bluer::Result<()> { let session = bluer::Session::new().await?; let profile = Profile { uuid: uuid::Uuid::from_u128(0x00001101_0000_1000_8000_00805f9b34fb), // Serial Port name: Some("My Serial Service".to_string()), role: Some(Role::Server), require_authentication: Some(false), require_authorization: Some(false), channel: Some(CHANNEL.into()), ..Default::default() }; let mut profile_handle = session.register_profile(profile).await?; println!("Profile registered, waiting for connections..."); // Handle incoming connection requests while let Some(req) = profile_handle.next().await { match req { Ok(req) => { println!("Connection from {}", req.device()); let stream = req.accept()?; // Handle stream... } Err(e) => println!("Request error: {}", e), } } Ok(()) } #[tokio::main] async fn main() -> bluer::Result<()> { rfcomm_server().await } ``` -------------------------------- ### Start Bluetooth Mesh Daemon Source: https://github.com/bluez/bluer/blob/master/doc/meshd-example/README.md Use this command to start the bluetooth mesh daemon. Ensure the necessary directories for configuration and storage are created. ```shell mkdir -p ${PWD}/lib sudo /usr/libexec/bluetooth/bluetooth-meshd --config ${PWD}/config --storage ${PWD}/lib --debug ``` -------------------------------- ### L2CAP Stream Server Example Source: https://context7.com/bluez/bluer/llms.txt Sets up an L2CAP stream server to listen for incoming connections on a specified PSM. It echoes received data back to the client. Requires a Bluetooth adapter and the bluer crate. ```rust use bluer::l2cap::{Socket, SocketAddr, Stream, StreamListener, SeqPacket, SeqPacketListener}; use bluer::{Address, AddressType}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; const PSM: u16 = 0x80; // Dynamic PSM for LE // L2CAP Server async fn l2cap_server() -> bluer::Result<()> { let session = bluer::Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; let local_addr = SocketAddr::new( adapter.address().await?, adapter.address_type().await?, PSM ); // Create stream listener let listener = StreamListener::bind(local_addr).await?; let actual_psm = listener.as_ref().local_addr()?.psm; println!("Listening on PSM {}", actual_psm); // Accept connections loop { let (mut stream, peer_addr) = listener.accept().await?; println!("Connection from {:?}", peer_addr); println!("Recv MTU: {}", stream.as_ref().recv_mtu()?); println!("Send MTU: {:?}", stream.as_ref().send_mtu()); // Echo received data let mut buf = vec![0u8; 1024]; loop { match stream.read(&mut buf).await { Ok(0) => break, Ok(n) => { println!("Received {} bytes", n); stream.write_all(&buf[..n]).await?; } Err(e) => { println!("Read error: {}", e); break; } } } } } ``` -------------------------------- ### Discover Bluetooth Devices with Filters Source: https://context7.com/bluez/bluer/llms.txt Starts a device discovery session that streams device addresses. Supports custom discovery filters for transport type, RSSI threshold, and UUID filtering. Ensure the adapter is powered on before starting discovery. ```rust use bluer::{Session, Adapter, AdapterEvent, Address, DiscoveryFilter, DiscoveryTransport}; use futures::{pin_mut, StreamExt}; use std::collections::HashSet; #[tokio::main] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; // Configure discovery filter (optional) let filter = DiscoveryFilter { transport: DiscoveryTransport::Le, // LE only, or Auto, BrEdr rssi: Some(-70), // Minimum RSSI threshold duplicate_data: true, // Receive duplicate advertisements discoverable: false, uuids: HashSet::new(), // Filter by service UUIDs pattern: None, // Filter by name/address prefix ..Default::default() }; adapter.set_discovery_filter(filter).await?; // Start discovery println!("Starting device discovery on {}", adapter.name()); let discover = adapter.discover_devices().await?; pin_mut!(discover); while let Some(event) = discover.next().await { match event { AdapterEvent::DeviceAdded(addr) => { let device = adapter.device(addr)?; println!("Device found: {}", addr); println!(" Name: {:?}", device.name().await?); println!(" RSSI: {:?}", device.rssi().await?); println!(" Paired: {}", device.is_paired().await?); println!(" Connected: {}", device.is_connected().await?); println!(" UUIDs: {:?}", device.uuids().await?.unwrap_or_default()); println!(" Manufacturer data: {:?}", device.manufacturer_data().await?); } AdapterEvent::DeviceRemoved(addr) => { println!("Device removed: {}", addr); } AdapterEvent::PropertyChanged(prop) => { println!("Adapter property changed: {:?}", prop); } } } Ok(()) } ``` -------------------------------- ### L2CAP Stream Client Example Source: https://context7.com/bluez/bluer/llms.txt Connects to an L2CAP stream server at a given address and PSM. It sends a message and then reads the echoed response. Requires a running L2CAP server. ```rust // L2CAP Client async fn l2cap_client(target_addr: Address) -> bluer::Result<()> { let target = SocketAddr::new(target_addr, AddressType::LePublic, PSM); println!("Connecting to {:?}", target); let mut stream = Stream::connect(target).await?; println!("Connected!"); println!("Local: {:?}", stream.as_ref().local_addr()?); println!("Remote: {:?}", stream.peer_addr()?); println!("Security: {:?}", stream.as_ref().security()?); // Send data let data = b"Hello, Bluetooth!"; stream.write_all(data).await?; println!("Sent {} bytes", data.len()); // Receive echo let mut buf = vec![0u8; data.len()]; stream.read_exact(&mut buf).await?; println!("Received: {}", String::from_utf8_lossy(&buf)); Ok(()) } ``` -------------------------------- ### L2CAP Sequential Packet Example Source: https://context7.com/bluez/bluer/llms.txt Demonstrates L2CAP sequential packet mode for sending and receiving discrete messages. It binds a socket, sets security, and exchanges a packet. This mode ensures message boundaries. ```rust // Sequential Packet mode for message boundaries async fn l2cap_seqpacket_example() -> bluer::Result<()> { let session = bluer::Session::new().await?; let adapter = session.default_adapter().await?; let socket = Socket::::new_seq_packet()?; socket.bind(SocketAddr::any_le())?; socket.set_recv_mtu(512)?; // Set security level socket.set_security(bluer::l2cap::Security { level: bluer::l2cap::SecurityLevel::Medium, key_size: 0, })?; let listener = socket.listen(1)?; let (seqpacket, _addr) = listener.accept().await?; // Send/receive individual packets let send_mtu = seqpacket.send_mtu()?; let recv_mtu = seqpacket.recv_mtu()?; let packet = vec![0xAA; send_mtu]; seqpacket.send(&packet).await?; let mut recv_buf = vec![0u8; recv_mtu]; let len = seqpacket.recv(&mut recv_buf).await?; println!("Received packet of {} bytes", len); Ok(()) } ``` -------------------------------- ### POST /discover_devices - Scan for Bluetooth Devices Source: https://context7.com/bluez/bluer/llms.txt Starts a device discovery session that streams device addresses as new devices are found. Supports custom discovery filters for transport type, RSSI threshold, and UUID filtering. ```APIDOC ## POST /discover_devices ### Description Starts a device discovery session that streams device addresses as new devices are found. Supports custom discovery filters for transport type, RSSI threshold, and UUID filtering. ### Method POST ### Endpoint /discover_devices ### Parameters #### Query Parameters - **transport** (DiscoveryTransport) - Optional - Specifies the transport type for discovery (e.g., Le, Auto, BrEdr). - **rssi** (i8) - Optional - Minimum RSSI threshold for discovered devices. - **duplicate_data** (bool) - Optional - Whether to receive duplicate advertisements. - **discoverable** (bool) - Optional - Whether to filter for discoverable devices. - **uuids** (HashSet) - Optional - Filter by service UUIDs. - **pattern** (String) - Optional - Filter by name or address prefix. ### Request Example ```json { "transport": "Le", "rssi": -70, "duplicate_data": true, "discoverable": false, "uuids": [], "pattern": null } ``` ### Response #### Success Response (200) - **AdapterEvent** (stream) - A stream of AdapterEvent, including DeviceAdded, DeviceRemoved, and PropertyChanged. #### Response Example ```json { "type": "DeviceAdded", "device": "00:11:22:33:44:55" } ``` ``` -------------------------------- ### Create Bluetooth Session and Manage Adapters Source: https://context7.com/bluez/bluer/llms.txt Establishes a connection to the system Bluetooth daemon and demonstrates how to retrieve or list available adapters. ```rust use bluer::{Session, Adapter}; #[tokio::main] async fn main() -> bluer::Result<()> { // Create a new Bluetooth session let session = Session::new().await?; // Get the default adapter (usually hci0) let adapter = session.default_adapter().await?; println!("Using adapter: {}", adapter.name()); // Or get a specific adapter by name let adapter = session.adapter("hci0")?; // List all available adapters let adapter_names = session.adapter_names().await?; for name in adapter_names { println!("Found adapter: {}", name); } Ok(()) } ``` -------------------------------- ### Configure Adapter Properties Source: https://context7.com/bluez/bluer/llms.txt Demonstrates how to power on an adapter, query its current state, and modify configuration settings like alias and discoverability. ```rust use bluer::{Session, Address}; #[tokio::main] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; // Power on the adapter adapter.set_powered(true).await?; // Query adapter properties println!("Address: {}", adapter.address().await?); println!("Address type: {}", adapter.address_type().await?); println!("System name: {}", adapter.system_name().await?); println!("Alias: {}", adapter.alias().await?); println!("Powered: {}", adapter.is_powered().await?); println!("Discoverable: {}", adapter.is_discoverable().await?); println!("Pairable: {}", adapter.is_pairable().await?); println!("Discovering: {}", adapter.is_discovering().await?); // Configure adapter adapter.set_alias("MyBluetoothDevice".to_string()).await?; adapter.set_discoverable(true).await?; adapter.set_discoverable_timeout(180).await?; // 3 minutes adapter.set_pairable(true).await?; // Get advertising capabilities println!("Active advertising instances: {}", adapter.active_advertising_instances().await?); println!("Supported advertising instances: {}", adapter.supported_advertising_instances().await?); Ok(()) } ``` -------------------------------- ### Build and Push Mesh Daemon Image Source: https://github.com/bluez/bluer/blob/master/doc/meshd-example/README.md Commands to build a new container image for the mesh daemon using Podman and then push it to a registry. Assumes Dockerfile is in infra/meshd/. ```shell podman build ../.. -f infra/meshd/Dockerfile -t quay.io/eclipsecon-2022/meshd:latest ``` ```shell podman push quay.io/eclipsecon-2022/meshd:latest ``` -------------------------------- ### Register Custom Bluetooth Authorization Agent Source: https://context7.com/bluez/bluer/llms.txt Demonstrates how to register an agent to handle pairing, passkey entry, and authorization requests. The agent remains active as long as the returned handle is in scope. ```rust use bluer::agent::{Agent, AgentHandle, ReqResult, RequestPasskey, RequestConfirmation, RequestAuthorization}; #[tokio::main] async fn main() -> bluer::Result<()> { let session = bluer::Session::new().await?; // Create custom agent with callbacks let agent = Agent { request_default: true, // Set as default agent request_passkey: Some(Box::new(|req: RequestPasskey| { Box::pin(async move { println!("Passkey requested for device {}", req.device); // Return a 6-digit passkey Ok(123456) }) })), request_confirmation: Some(Box::new(|req: RequestConfirmation| { Box::pin(async move { println!("Confirm passkey {} for device {}?", req.passkey, req.device); // Accept the pairing Ok(()) }) })), request_authorization: Some(Box::new(|req: RequestAuthorization| { Box::pin(async move { println!("Authorization requested for device {}", req.device); // Authorize the device Ok(()) }) })), ..Default::default() }; // Register agent - active as long as handle exists let _handle: AgentHandle = session.register_agent(agent).await?; println!("Agent registered"); // Keep agent active tokio::signal::ctrl_c().await?; Ok(()) } ``` -------------------------------- ### Configure GATT settings Source: https://github.com/bluez/bluer/blob/master/README.md Recommended configuration for /etc/bluetooth/main.conf to disable GATT caching and EATT for more reliable data transmission. ```ini [GATT] Cache = no Channels = 1 ``` -------------------------------- ### Adapter Properties and Configuration Source: https://context7.com/bluez/bluer/llms.txt The Adapter struct provides methods to query and configure Bluetooth adapter properties including power state, discoverability, and pairing settings. ```APIDOC ## Adapter Properties and Configuration ### Description The Adapter struct provides methods to query and configure Bluetooth adapter properties including power state, discoverability, and pairing settings. ### Method GET / POST / PUT (for setting properties) ### Endpoint /adapters/{adapter_name} ### Parameters #### Path Parameters - **adapter_name** (string) - Required - The name of the Bluetooth adapter (e.g., "hci0"). #### Query Parameters None #### Request Body None for querying properties. For setting properties, the value to be set is passed as an argument to the respective method. ### Request Example ```rust use bluer::{Session, Address}; #[tokio::main] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; // Power on the adapter adapter.set_powered(true).await?; // Query adapter properties println!("Address: {}", adapter.address().await?); println!("Address type: {}", adapter.address_type().await?); println!("System name: {}", adapter.system_name().await?); println!("Alias: {}", adapter.alias().await?); println!("Powered: {}", adapter.is_powered().await?); println!("Discoverable: {}", adapter.is_discoverable().await?); println!("Pairable: {}", adapter.is_pairable().await?); println!("Discovering: {}", adapter.is_discovering().await?); // Configure adapter adapter.set_alias("MyBluetoothDevice".to_string()).await?; adapter.set_discoverable(true).await?; adapter.set_discoverable_timeout(180).await?; adapter.set_pairable(true).await?; // Get advertising capabilities println!("Active advertising instances: {}", adapter.active_advertising_instances().await?); println!("Supported advertising instances: {}", adapter.supported_advertising_instances().await?); Ok(()) } ``` ### Response #### Success Response (200) - **address** (Address) - The Bluetooth address of the adapter. - **address_type** (string) - The address type (e.g., "public", "random"). - **system_name** (string) - The system name of the adapter. - **alias** (string) - The alias of the adapter. - **powered** (boolean) - Indicates if the adapter is powered on. - **discoverable** (boolean) - Indicates if the adapter is discoverable. - **pairable** (boolean) - Indicates if the adapter is pairable. - **discovering** (boolean) - Indicates if the adapter is currently discovering devices. - **active_advertising_instances** (integer) - The number of active advertising instances. - **supported_advertising_instances** (integer) - The number of supported advertising instances. #### Response Example ```json { "address": "00:1A:7D:DA:71:00", "address_type": "public", "system_name": "MyComputer", "alias": "MyBluetoothDevice", "powered": true, "discoverable": true, "pairable": true, "discovering": false, "active_advertising_instances": 0, "supported_advertising_instances": 1 } ``` ``` -------------------------------- ### Session::new - Create Bluetooth Session Source: https://context7.com/bluez/bluer/llms.txt Creates a new Bluetooth session by establishing a connection to the system Bluetooth daemon over D-Bus. This is the entry point for all BlueR operations. ```APIDOC ## Session::new - Create Bluetooth Session ### Description Creates a new Bluetooth session by establishing a connection to the system Bluetooth daemon over D-Bus. This is the entry point for all BlueR operations. ### Method POST (Implied by session creation) ### Endpoint / ### Parameters #### Query Parameters None #### Request Body None ### Request Example ```rust use bluer::{Session, Adapter}; #[tokio::main] async fn main() -> bluer::Result<()> { // Create a new Bluetooth session let session = Session::new().await?; // Get the default adapter (usually hci0) let adapter = session.default_adapter().await?; println!("Using adapter: {}", adapter.name()); // Or get a specific adapter by name let adapter = session.adapter("hci0")?; // List all available adapters let adapter_names = session.adapter_names().await?; for name in adapter_names { println!("Found adapter: {}", name); } Ok(()) } ``` ### Response #### Success Response (200) - **session** (Session) - A new Bluetooth session object. - **adapter** (Adapter) - The default Bluetooth adapter. - **adapter_names** (Vec) - A list of available adapter names. #### Response Example ```json { "session": "", "adapter": "", "adapter_names": ["hci0", "hci1"] } ``` ``` -------------------------------- ### Monitor BLE advertisements with MonitorManager Source: https://context7.com/bluez/bluer/llms.txt Registers a monitor to detect devices based on specific advertisement patterns without active scanning. Requires an active adapter and appropriate pattern configuration. ```rust use bluer::monitor::{Monitor, Pattern, RssiSamplingPeriod}; use futures::StreamExt; #[tokio::main] async fn main() -> bluer::Result<()> { let session = bluer::Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; // Create advertisement monitor let mut monitor_manager = adapter.monitor().await?; // Register pattern to match specific advertisements let monitor = Monitor { monitor_type: bluer::monitor::Type::OrPatterns, rssi_low_threshold: Some(-70), rssi_high_threshold: Some(-50), rssi_low_timeout: Some(std::time::Duration::from_secs(5)), rssi_high_timeout: Some(std::time::Duration::from_secs(5)), rssi_sampling_period: Some(RssiSamplingPeriod::All), patterns: Some(vec![ Pattern { start_position: 0, ad_data_type: 0xFF, // Manufacturer data content_of_pattern: vec![0x4C, 0x00], // Apple company ID }, ]), ..Default::default() }; let mut handle = monitor_manager.register(monitor).await?; println!("Monitor registered"); // Receive matching advertisement events while let Some(event) = handle.next().await { println!("Advertisement event: {:?}", event); } Ok(()) } ``` -------------------------------- ### Publish Local GATT Service with IO Model Source: https://context7.com/bluez/bluer/llms.txt Registers a GATT service with characteristics configured for IO-based read/write/notify operations. Requires the tokio runtime and the bluer crate. ```rust use bluer::{ adv::Advertisement, gatt::local::{ Application, Characteristic, CharacteristicControlEvent, CharacteristicNotify, CharacteristicNotifyMethod, CharacteristicWrite, CharacteristicWriteMethod, Service, characteristic_control, service_control, }, }; use futures::{future, pin_mut, StreamExt}; use std::collections::BTreeMap; use tokio::io::{AsyncReadExt, AsyncWriteExt}; const SERVICE_UUID: uuid::Uuid = uuid::Uuid::from_u128(0xFEEDC0DE); const CHARACTERISTIC_UUID: uuid::Uuid = uuid::Uuid::from_u128(0xF00DC0DE00001); #[tokio::main] async fn main() -> bluer::Result<()> { let session = bluer::Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; // Create advertisement let mut manufacturer_data = BTreeMap::new(); manufacturer_data.insert(0xf00d_u16, vec![0x01, 0x02, 0x03]); let le_advertisement = Advertisement { service_uuids: vec![SERVICE_UUID].into_iter().collect(), manufacturer_data, discoverable: Some(true), local_name: Some("BlueR GATT Server".to_string()), ..Default::default() }; let _adv_handle = adapter.advertise(le_advertisement).await?; // Create GATT service with control handles let (service_control, service_handle) = service_control(); let (char_control, char_handle) = characteristic_control(); let app = Application { services: vec![Service { uuid: SERVICE_UUID, primary: true, characteristics: vec![Characteristic { uuid: CHARACTERISTIC_UUID, write: Some(CharacteristicWrite { write: true, write_without_response: true, method: CharacteristicWriteMethod::Io, ..Default::default() }), notify: Some(CharacteristicNotify { notify: true, method: CharacteristicNotifyMethod::Io, ..Default::default() }), control_handle: char_handle, ..Default::default() }], control_handle: service_handle, ..Default::default() }], ..Default::default() }; let _app_handle = adapter.serve_gatt_application(app).await?; println!("GATT service registered. Service handle: 0x{:x}", service_control.handle()?); // Handle characteristic events pin_mut!(char_control); let mut reader_opt = None; let mut writer_opt = None; loop { tokio::select! { evt = char_control.next() => { match evt { Some(CharacteristicControlEvent::Write(req)) => { println!("Write request from {} with MTU {}", req.device_address(), req.mtu()); reader_opt = Some(req.accept()?); } Some(CharacteristicControlEvent::Notify(notifier)) => { println!("Notify request from {} with MTU {}", notifier.device_address(), notifier.mtu()); writer_opt = Some(notifier); } None => break, } } read_res = async { match &mut reader_opt { Some(reader) => { let mut buf = vec![0u8; 512]; reader.read(&mut buf).await } None => future::pending().await, } } => { match read_res { Ok(0) => { println!("Write stream ended"); reader_opt = None; } Ok(n) => { println!("Received {} bytes from client", n); // Echo back via notify if available if let Some(writer) = writer_opt.as_mut() { let _ = writer.write(&[0x01, 0x02, 0x03]).await; } } Err(e) => { println!("Read error: {}", e); reader_opt = None; } } } } } Ok(()) } ``` -------------------------------- ### POST /connect - Connect to Remote Device Source: https://context7.com/bluez/bluer/llms.txt Connects to a remote Bluetooth device and initiates service discovery. The connection supports automatic profile connection and pairing. ```APIDOC ## POST /connect ### Description Connects to a remote Bluetooth device and initiates service discovery. The connection supports automatic profile connection and pairing. ### Method POST ### Endpoint /connect ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device to connect to. #### Request Body *This endpoint does not explicitly define a request body in the provided text. Connection parameters might be implicitly handled or configured elsewhere.* ### Request Example ```json { "address": "00:11:22:33:44:55" } ``` ### Response #### Success Response (200) - **status** (string) - Indicates the connection status (e.g., "Connected"). #### Response Example ```json { "status": "Connected" } ``` ``` -------------------------------- ### Add BlueR dependency Source: https://github.com/bluez/bluer/blob/master/README.md Command to add the latest version of BlueR with all features enabled to your project. ```bash cargo add -F full bluer ``` -------------------------------- ### Connect and Interact with Remote GATT Services Source: https://context7.com/bluez/bluer/llms.txt Connects to a specified Bluetooth device, iterates through its GATT services and characteristics, and performs read, write, and notification operations. Requires the bluer and tokio crates. Ensure the device address is correct and the device is discoverable. ```rust use bluer::{Session, Address, gatt::remote::Characteristic}; use futures::{pin_mut, StreamExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; const SERVICE_UUID: uuid::Uuid = uuid::Uuid::from_u128(0xFEEDC0DE); const CHARACTERISTIC_UUID: uuid::Uuid = uuid::Uuid::from_u128(0xF00DC0DE00001); #[tokio::main] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; let address: Address = "00:11:22:33:44:55".parse()?; let device = adapter.device(address)?; // Connect to device device.connect().await?; // Enumerate GATT services (waits for service resolution) for service in device.services().await? { let uuid = service.uuid().await?; println!("Service: {} (primary: {})", uuid, service.primary().await?); if uuid == SERVICE_UUID { // Enumerate characteristics for char in service.characteristics().await? { let char_uuid = char.uuid().await?; let flags = char.flags().await?; println!(" Characteristic: {} (flags: {:?})", char_uuid, flags); if char_uuid == CHARACTERISTIC_UUID { // Read characteristic value if flags.read { let value = char.read().await?; println!(" Read value: {:02x?}", value); } // Write characteristic value if flags.write { let data = vec![0x01, 0x02, 0x03, 0x04]; char.write(&data).await?; println!(" Written: {:02x?}", data); } // Subscribe to notifications if flags.notify { println!(" Starting notifications..."); let notify = char.notify().await?; pin_mut!(notify); // Receive 5 notifications for _ in 0..5 { if let Some(value) = notify.next().await { println!(" Notification: {:02x?}", value); } } } // Use low-overhead IO for write operations if flags.write_without_response { let mut write_io = char.write_io().await?; println!(" Write IO MTU: {}", write_io.mtu()); write_io.write_all(&[0xAA, 0xBB, 0xCC]).await?; } // Use low-overhead IO for notifications if flags.notify { let mut notify_io = char.notify_io().await?; println!(" Notify IO MTU: {}", notify_io.mtu()); let mut buf = vec![0u8; notify_io.mtu()]; let n = notify_io.read(&mut buf).await?; println!(" Received {} bytes: {:02x?}", n, &buf[..n]); } } // Enumerate descriptors for desc in char.descriptors().await? { println!(" Descriptor: {}", desc.uuid().await?); } } } } device.disconnect().await?; Ok(()) } ``` -------------------------------- ### Connect to Remote Bluetooth Device Source: https://context7.com/bluez/bluer/llms.txt Connects to a remote Bluetooth device by its address and initiates service discovery. Automatically handles profile connection and pairing if necessary. Ensure the adapter is powered on and the device address is valid. ```rust use bluer::{Session, Address}; use std::time::Duration; use tokio::time::sleep; #[tokio::main] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; // Get device by address let address: Address = "00:11:22:33:44:55".parse()?; let device = adapter.device(address)?; // Connect to device println!("Connecting to {}...", address); device.connect().await?; println!("Connected!"); // Query device properties println!("Name: {:?}", device.name().await?); println!("Address type: {}", device.address_type().await?); println!("Paired: {}", device.is_paired().await?); println!("Trusted: {}", device.is_trusted().await?); println!("Services resolved: {}", device.is_services_resolved().await?); // Set device as trusted device.set_trusted(true).await?; // Initiate pairing (if not already paired) if !device.is_paired().await? { println!("Pairing..."); device.pair().await?; println!("Paired!"); } // Keep connection for a while sleep(Duration::from_secs(10)).await; // Disconnect device.disconnect().await?; println!("Disconnected"); Ok(()) } ``` -------------------------------- ### Send BLE Advertisements Source: https://context7.com/bluez/bluer/llms.txt Registers and broadcasts Bluetooth Low Energy advertisements to make the device discoverable. Ensure the adapter is powered on before advertising. The advertisement remains active as long as the returned handle exists. ```rust use bluer::adv::{Advertisement, Type, Feature}; use std::collections::BTreeMap; use std::time::Duration; use tokio::time::sleep; #[tokio::main] async fn main() -> bluer::Result<()> { let session = bluer::Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; println!("Advertising on {} with address {}", adapter.name(), adapter.address().await?); // Create manufacturer-specific data let mut manufacturer_data = BTreeMap::new(); manufacturer_data.insert(0x0001_u16, vec![0x01, 0x02, 0x03, 0x04]); // Create service-specific data let mut service_data = BTreeMap::new(); let service_uuid: uuid::Uuid = "0000180f-0000-1000-8000-00805f9b34fb".parse().unwrap(); service_data.insert(service_uuid, vec![0x64]); // Battery level 100% let advertisement = Advertisement { advertisement_type: Type::Peripheral, service_uuids: vec![ "0000180f-0000-1000-8000-00805f9b34fb".parse().unwrap(), // Battery Service "0000180a-0000-1000-8000-00805f9b34fb".parse().unwrap(), // Device Information ].into_iter().collect(), manufacturer_data, service_data, discoverable: Some(true), local_name: Some("My BLE Device".to_string()), appearance: Some(0x0080), // Generic tag duration: Some(Duration::from_secs(0)), // Advertise indefinitely timeout: Some(Duration::from_secs(0)), // No timeout tx_power: Some(7), // TX power in dBm ..Default::default() }; // Register advertisement - it's active as long as handle exists let handle = adapter.advertise(advertisement).await?; println!("Advertisement active. Press Ctrl+C to stop."); sleep(Duration::from_secs(60)).await; // Drop handle to stop advertising drop(handle); println!("Advertisement stopped"); Ok(()) } ``` -------------------------------- ### Device Properties API Source: https://context7.com/bluez/bluer/llms.txt APIs for querying and modifying properties of a connected Bluetooth device. ```APIDOC ## Device Properties API ### GET /device/{address}/name ### Description Retrieves the name of the remote Bluetooth device. ### Method GET ### Endpoint /device/{address}/name ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **name** (string) - The name of the device. ### GET /device/{address}/rssi ### Description Retrieves the current RSSI (Received Signal Strength Indicator) value of the remote Bluetooth device. ### Method GET ### Endpoint /device/{address}/rssi ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **rssi** (i8) - The RSSI value. ### GET /device/{address}/is_paired ### Description Checks if the remote Bluetooth device is paired. ### Method GET ### Endpoint /device/{address}/is_paired ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **is_paired** (bool) - True if the device is paired, false otherwise. ### GET /device/{address}/is_connected ### Description Checks if the remote Bluetooth device is currently connected. ### Method GET ### Endpoint /device/{address}/is_connected ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **is_connected** (bool) - True if the device is connected, false otherwise. ### GET /device/{address}/uuids ### Description Retrieves the list of UUIDs supported by the remote Bluetooth device. ### Method GET ### Endpoint /device/{address}/uuids ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **uuids** (array) - An array of strings representing the UUIDs. ### GET /device/{address}/manufacturer_data ### Description Retrieves the manufacturer-specific data for the remote Bluetooth device. ### Method GET ### Endpoint /device/{address}/manufacturer_data ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **manufacturer_data** (object) - An object containing manufacturer data. ### POST /device/{address}/set_trusted ### Description Sets the trusted status of the remote Bluetooth device. ### Method POST ### Endpoint /device/{address}/set_trusted ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. #### Request Body - **trusted** (bool) - Required - The desired trusted status (true or false). ### Response #### Success Response (200) - **status** (string) - Indicates the success of the operation. ### POST /device/{address}/pair ### Description Initiates the pairing process with the remote Bluetooth device. ### Method POST ### Endpoint /device/{address}/pair ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **status** (string) - Indicates the success of the pairing operation. ### POST /device/{address}/disconnect ### Description Disconnects from the remote Bluetooth device. ### Method POST ### Endpoint /device/{address}/disconnect ### Parameters #### Path Parameters - **address** (Address) - Required - The Bluetooth address of the device. ### Response #### Success Response (200) - **status** (string) - Indicates the success of the disconnection. ``` -------------------------------- ### Handle Bluetooth errors with ErrorKind Source: https://context7.com/bluez/bluer/llms.txt Matches specific BlueR error kinds to handle common Bluetooth failure scenarios like connection issues or authentication failures. ```rust use bluer::{Session, ErrorKind, Error}; #[tokio::main] async fn main() { match run_bluetooth().await { Ok(()) => println!("Success"), Err(e) => { // Handle specific error kinds match e.kind { ErrorKind::NotFound => { println!("Device or adapter not found"); } ErrorKind::ConnectionAttemptFailed => { println!("Failed to connect to device"); } ErrorKind::AuthenticationFailed | ErrorKind::AuthenticationRejected => { println!("Pairing/authentication failed"); } ErrorKind::NotReady => { println!("Bluetooth adapter not ready"); } ErrorKind::NotSupported => { println!("Operation not supported"); } ErrorKind::AlreadyConnected => { println!("Device already connected"); } ErrorKind::ServicesUnresolved => { println!("GATT services not yet discovered"); } _ => { println!("Bluetooth error: {} - {}", e.kind, e.message); } } } } } async fn run_bluetooth() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; adapter.set_powered(true).await?; Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.