bluer 0.17.4

BlueR: official Rust interface to the Linux Bluetooth protocol stack (BlueZ)
Documentation
//! Opens a listening L2CAP socket, accepts connections and echos incoming data.

use bluer::{
    adv::Advertisement,
    l2cap::{SocketAddr, StreamListener, PSM_LE_DYN_START},
};
use std::time::Duration;
use tokio::{
    io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
    time::sleep,
};

const SERVICE_UUID: uuid::Uuid = uuid::Uuid::from_u128(0xFEED0000F00D);

include!("l2cap.inc");

#[tokio::main]
async fn main() -> bluer::Result<()> {
    env_logger::init();
    let session = bluer::Session::new().await?;
    let adapter = session.default_adapter().await?;
    adapter.set_powered(true).await?;
    let adapter_addr = adapter.address().await?;
    let adapter_addr_type = adapter.address_type().await?;

    // Advertising is necessary for device to be connectable.
    println!(
        "Advertising on Bluetooth adapter {} with {} address {}",
        adapter.name(),
        &adapter_addr_type,
        &adapter_addr
    );
    let le_advertisement = Advertisement {
        service_uuids: vec![SERVICE_UUID].into_iter().collect(),
        discoverable: Some(true),
        local_name: Some("l2cap_server".to_string()),
        ..Default::default()
    };
    let adv_handle = adapter.advertise(le_advertisement).await?;

    let local_sa = SocketAddr::new(adapter_addr, adapter_addr_type, PSM);
    let listener = StreamListener::bind(local_sa).await?;

    println!("Listening on PSM {}. Press enter to quit.", listener.as_ref().local_addr()?.psm);
    let stdin = BufReader::new(tokio::io::stdin());
    let mut lines = stdin.lines();

    loop {
        println!("\nWaiting for connection...");

        let (mut stream, sa) = tokio::select! {
            l = listener.accept() => {
                match l {
                    Ok(v) => v,
                    Err(err) => {
                        println!("Accepting connection failed: {}", &err);
                        continue;
                    }}
            },
            _ = lines.next_line() => break,
        };
        let recv_mtu = stream.as_ref().recv_mtu()?;

        println!("Accepted connection from {:?} with receive MTU {} bytes", &sa, &recv_mtu);

        println!("Sending hello");
        if let Err(err) = stream.write_all(HELLO_MSG).await {
            println!("Write failed: {}", &err);
            continue;
        }

        let mut n = 0;
        loop {
            n += 1;

            // Vary buffer size between MTU and smaller value to test
            // partial reads.
            let buf_size = if n % 5 == 0 { recv_mtu - 70 } else { recv_mtu };
            let mut buf = vec![0; buf_size as _];

            let n = match stream.read(&mut buf).await {
                Ok(0) => {
                    println!("Stream ended");
                    break;
                }
                Ok(n) => n,
                Err(err) => {
                    println!("Read failed: {}", &err);
                    continue;
                }
            };
            let buf = &buf[..n];

            println!("Echoing {} bytes", buf.len());
            if let Err(err) = stream.write_all(buf).await {
                println!("Write failed: {}", &err);
                continue;
            }
        }
    }

    println!("Removing advertisement");
    drop(adv_handle);
    sleep(Duration::from_secs(1)).await;

    Ok(())
}