bluer

repository·master·Indexed 19 days ago

https://github.com/bluez/bluer

The official Rust interface to the Linux Bluetooth protocol stack (BlueZ), version 0.17.4. It provides idiomatic Rust bindings for managing Bluetooth adapters, devices, GATT services, L2CAP/RFCOMM sockets, and Bluetooth Mesh. The project includes the bluer-tools suite of command-line utilities: bluadv for LE advertisements, blumon for device monitoring, gattcat for GATT services, l2cat for L2CAP sockets, and rfcat for RFCOMM sockets.

Tokens
22.8K
Snippets
73
Records
95
Agent score
66%

What's inside bluer

  1. Overview of BlueR tools command line utilities

    master

    The bluer-tools crate provides several specialized command-line utilities for interacting with Bluetooth on Linux. Each tool supports the --help flag for detailed usage instructions.

    Available Tools:

    • bluadv: Used to send Bluetooth LE advertisements.
    • blumon: A monitoring tool that scans for and monitors Bluetooth devices (similar to the top command).
    • gattcat: A comprehensive tool for Bluetooth LE GATT services. It can:
      • Discover devices and services.
      • Handle pairing.
      • Resolve well-known UUIDs and manufacturer IDs.
      • Perform operations on GATT services.
      • Connect to remote GATT services (via notify and write).
      • Serve a local program over a GATT service (via notify and write).
      • Implement the Nordic UART service (NUS) as both client and server.
    • l2cat: A netcat-like tool for Bluetooth classic (BR/EDR) and LE L2CAP sockets. It supports connecting to/listening on L2CAP PSMs, serving local programs, and performing speed tests.
    • rfcat: A netcat-like tool for Bluetooth RFCOMM sockets. It supports connecting to/listening on RFCOMM channels, serving local programs, resolving/publishing SDP records, and performing speed tests.
  2. Install BlueR as a dependency

    master

    To add BlueR to your Rust project with all features enabled, use the cargo add command with the full feature flag. This is the recommended way to ensure all functionality (GATT, L2CAP, RFCOMM, Mesh, etc.) is available.

    cargo add -F full bluer
  3. Build and push meshd container images

    master

    You can build a custom container image for the meshd daemon using podman. Run these commands from the doc/meshd-example/ directory to build the image from the repository root and push it to the specified registry.

    podman build  ../.. -f infra/meshd/Dockerfile -t quay.io/eclipsecon-2022/meshd:latest
    podman push quay.io/eclipsecon-2022/meshd:latest
  4. Start the bluetooth-meshd daemon

    master

    To run a mesh network on a Linux host using the bluetooth mesh daemon, create a directory for the library files and execute the daemon with the required --config, --storage, and --debug flags. Ensure you provide absolute or relative paths to your configuration and storage directories.

    mkdir -p ${PWD}/lib
    sudo /usr/libexec/bluetooth/bluetooth-meshd --config ${PWD}/config --storage ${PWD}/lib --debug
  5. Install BlueR tools

    master

    To install the bluer-tools command line utilities, you must first ensure that D-Bus and Bluetooth development libraries are installed on your system. On Debian-based systems, use apt. Once the dependencies are met, use cargo install to install the tools.

    Prerequisites:

    • A running Bluetooth daemon (BlueZ).
    • Cargo (Rust package manager) installed via rustup.
    # Install dependencies (Debian/Ubuntu)
    sudo apt install libdbus-1-dev
    
    # Install BlueR tools
    cargo install bluer-tools
  6. Build BlueR from source

    master

    If you are cloning the repository directly to build it, you must use the --recursive flag to ensure all submodules are included, otherwise the build will fail with file not found errors.

    Note: D-Bus development headers are required for the build process.

    git clone --recursive https://github.com/bluez/bluer.git
  7. Use l2cat to manage L2CAP connections

    master

    The l2cat CLI tool allows for arbitrary Bluetooth BR/EDR and LE L2CAP connections and listening. It supports connecting to remote devices, listening for incoming connections, serving programs over an established connection, and performing speed tests.

    l2cat [COMMAND]
    
    Possible commands:
      connect       Connect to remote device.
      listen        Listen for connection from remote device.
      serve         Listen for connection and serve a program.
      speed-client  Speed test client.
      speed-server  Speed test server.
  8. Use AdvertisementHandle to manage advertisement lifecycle

    master

    The AdvertisementHandle is a RAII (Resource Acquisition Is Initialization) guard returned by Adapter::advertise.

    • To keep advertising: Keep the AdvertisementHandle instance in scope.
    • To stop advertising: Drop the AdvertisementHandle (e.g., by letting it go out of scope or using drop(_handle)).

    This handle is marked #[must_use], meaning the compiler will warn you if you ignore the return value of an advertisement registration, which would result in the advertisement being immediately stopped.

  9. Handle errors in Bluetooth Mesh agent requests

    master

    When implementing callbacks for a ProvisionAgent, your asynchronous functions return a ReqResult<T>, which is an alias for std::result::Result<T, ReqError>.

    If the agent cannot or will not fulfill a request, you should return one of the following ReqError variants:

    • Rejected: The request was explicitly rejected.
    • Canceled: The request was canceled.

    These errors are automatically converted into appropriate D-Bus MethodErr responses by the BlueR mesh implementation.

  10. Handle characteristic IO requests

    master

    If a characteristic is configured with CharacteristicWriteMethod::Io or CharacteristicNotifyMethod::Io, you can handle data transfers via file descriptors instead of callbacks. This provides lower overhead for high-frequency data.

    1. Writes: When a client writes to an Io characteristic, a CharacteristicControlEvent::Write(CharacteristicWriteIoRequest) is emitted on the CharacteristicControl stream. Call request.accept() to get a CharacteristicReader for the data.
    2. Notifications: When a client starts a notification session on an Io characteristic, a CharacteristicControlEvent::Notify(CharacteristicWriter) is emitted. Use the CharacteristicWriter to send data.
    // In your event loop processing CharacteristicControl events:
    while let Some(event) = control.next().await {
        match event {
            CharacteristicControlEvent::Write(req) => {
                let mut reader = req.accept().map_err(|e| ...)?;
                // Read from reader.socket...
            }
            CharacteristicControlEvent::Notify(mut writer) => {
                // Write to writer.socket...
            }
        }
    }
  11. Configure L2CAP Socket Addresses

    master

    L2CAP socket addresses (SocketAddr) are used to identify Bluetooth devices and Protocol Service Multiplexors (PSM).

    • PSM Requirements:

      • For Bluetooth Classic (BR/EDR), listening on a PSM below PSM_BR_EDR_DYN_START (0x1001) requires CAP_NET_BIND_SERVICE.
      • For Bluetooth LE, listening on a PSM below PSM_LE_DYN_START (0x80) requires CAP_NET_BIND_SERVICE. The maximum PSM for LE is PSM_LE_MAX (0xff).
      • The PSM must be odd and follow the bit pattern xxxxxxx0_xxxxxxx1.
      • Setting PSM to 0 when binding allows the system to assign an available PSM.
    • Binding to any local adapter:

      • Use SocketAddr::any_br_edr() for Classic Bluetooth with a dynamic PSM.
      • Use SocketAddr::any_le() for Bluetooth LE with a dynamic PSM.
    use bluer::l2cap::{SocketAddr, AddressType};
    use bluer::Address;
    
    // Create a specific address
    let addr = SocketAddr::new(device_address, AddressType::LePublic, 0x80);
    
    // Create an address for binding to any local adapter (LE)
    let bind_addr = SocketAddr::any_le();
  12. Define a local GATT characteristic

    master

    A Characteristic defines how a specific GATT characteristic behaves. It includes a UUID, an optional handle, and configuration for:

    • Read: Using CharacteristicRead to define the read function and security requirements.
    • Write: Using CharacteristicWrite to define write permissions and the method (Fun for a callback or Io for asynchronous socket-based IO).
    • Notify/Indicate: Using CharacteristicNotify to define notification/indication behavior and the method (Fun or Io).

    To control a characteristic and receive events (like IO requests) after registration, use characteristic_control() to obtain a CharacteristicControl object and a CharacteristicControlHandle. Store the handle in Characteristic::control_handle.

    let (control, control_handle) = characteristic_control();
    
    let mut characteristic = Characteristic {
        uuid: Uuid::new_v4(),
        handle: None,
        broadcast: false,
        writable_auxiliaries: false,
        authorize: false,
        descriptors: vec![],
        read: Some(CharacteristicRead {
            read: true,
            ..Default::default()
        }),
        write: Some(CharacteristicWrite {
            write: true,
            method: CharacteristicWriteMethod::Fun(Box::new(|value, options| {
                Box::pin(async move { Ok(()) })
            })),
            ..Default::default()
        }),
        notify: None,
        control_handle,
        _non_exhaustive: (),
    };