deviceplug / btleplug

Rust Cross-Platform Host-Side Bluetooth LE Access Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Notification fired multiple times

jjungo opened this issue · comments

Bug happens since c032d66

Describe the bug
On Linux a notification is fired multiple time instead of one.

Expected behavior
A client is registered to a notify property of a characteristic; when the characteristic value is updated from the device, the client is notified only once when polling the stream given by peripheral::notifications.

Actual behavior
The client is notified multiple time depending on the number of subscribed characteristics. Let's imagine we've subscribed to 3 characteristics. When a notification for a characteristics is fired, the event is fired 3 time. If we have only 1 char, notify is fired 1 time, if we have 4 char, notify is fired 4 times... So a notification is fired up a number of time depending on the number of subscribed characteristics.

Additional context
This is my very first rust project, I might have made some mistake...
I didn't try on Windows or MacOS.

The following snippet is able to reproduce the bug:

const FEATURE_1: Uuid = Uuid::from_u128(0xff751000_362f_11ec_8d3d_0242ac130003);
const FEATURE_2: Uuid = Uuid::from_u128(0xff751001_362f_11ec_8d3d_0242ac130003);

async fn get_first_adapter(manager: Manager) -> Result<Adapter, Box<dyn Error>> {
    todo!()
}

async fn find_dev(addr: &str, adapter: &Adapter) -> Option<Peripheral> {
    todo!()
}

async fn find_char(uuid: Uuid, discovered_char: &BTreeSet<Characteristic>) -> Result<&Characteristic, Box<dyn Error>> {
    todo!()
}

fn notification_handler(data: ValueNotification) -> Result<(), Box<dyn Error>> {
    match data.uuid {
        FEATURE_1 => {
            println!("feature 1: {:x?}", data.value);
        }
        FEATURE_2 => {
            println!("feature 2: {:x?}", data.value);
        }
        _ => Err("wtf").unwrap()
    }

    Ok(())
}

async fn char_subscribe(char: &Characteristic, peripheral: &Peripheral) -> Result<(), Box<dyn Error>> {
    peripheral.subscribe(&char).await?;
    let mut result = peripheral.notifications().await?;

    while let Some(data) = result.next().await {
        notification_handler(data).unwrap();
    }

    Ok(())
}


#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let address = "01:02:03:04:05:06";
    let manager = Manager::new().await?;
    let adapter = get_first_adapter(manager).await?;
    let dev = find_dev(address, &adapter).await.expect("Device not found!");

    dev.connect().await?;

    dev.discover_services().await?;
    let chars = dev.characteristics();
    let feature_1_fut = char_subscribe(find_char(FEATURE_1, &chars).await?, &dev);
    let feature_2_fut = char_subscribe(find_char(FEATURE_2, &chars).await?, &dev);

    let _ = futures::join!(
        feature_1_fut,
        feature_2_fut,
    );

    Ok(())
}

Are you getting multiple copies of the same notification from each notification stream, or do you just mean that you're seeing the notifications in both the streams that you get in your two calls to char_subscribe? If it's the latter, then this is working as intended. Maybe you want to do something like this instead:

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let address = "01:02:03:04:05:06";
    let manager = Manager::new().await?;
    let adapter = get_first_adapter(manager).await?;
    let dev = find_dev(address, &adapter).await.expect("Device not found!");

    dev.connect().await?;

    dev.discover_services().await?;
    let chars = dev.characteristics();
    dev.subscribe(find_char(FEATURE_1, &chars).await?).await?;
    dev.subscribe(find_char(FEATURE_2, &chars).await?).await?;

    let mut notifications = dev.notifications().await?;
    while let Some(data) = notifications.next().await {
        notification_handler(data).unwrap();
    }

    Ok(())
}

Hi @qwandor,

Ouch... sorry you're totally right, my usage of the notification future was totally stupid...

Thank you for your help.