Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use notifications (start_notify) #9

Open
monorkin opened this issue May 8, 2017 · 12 comments
Open

How to use notifications (start_notify) #9

monorkin opened this issue May 8, 2017 · 12 comments

Comments

@monorkin
Copy link

monorkin commented May 8, 2017

Hi!

I'm working on a project on the RPi that connects to an Arduino using Bluetooth. I'm using the HM11 BLE chip on the Arduino side. That chip, for some reason, insists on communicating using notifications. When I try to read a value using read_value i just get gibberish (always this exact string Èý� �lÄ or as bytes [200, 253, 25, 130, 108, 196]).

I went through the source and noticed the start_notify and stop_notify methods. Though I can't figure out how to use them. Any pointers? Are they implemented at all?

Also, pardons for asking a question here. I didn't find any guide which outlines the desired method for asking questions about the project.

@dati91
Copy link
Member

dati91 commented May 11, 2017

Hi,
Sorry for the late response!
I don't think the start_notify/start_notify will be useful. It should be reworked like the DiscoverySession.
The problem is that we currently drop the session after a call, which is usually fine, but when you try to use something session related, like doing a discovery or in you case using notification, the connection should be kept.
We are planning to do this, but I can't promise that it will be in the near future. Hopefully before the end of this year.

@monorkin
Copy link
Author

@dati91 thanks for the response.

Do you think that this feature would be difficult to implement? If it isn't too much hassle I'd like to contribute.

@dati91
Copy link
Member

dati91 commented May 15, 2017

@stankec My main concern is that somehow we need to receive the messages (we can do this with a loop, but i hope there are some callback functions), then store them, and somehow propagate them through an API (maybe register a callback, or a poll mechanism).

@monorkin
Copy link
Author

I went through some other libraries that implement this functionality (ble [Python] and ruby-ble [Ruby]) and there doesn't seem to be a consensus on how to do this.

The Python library pipes notification to the characteristic's value attribute and encourages the use of a loop to read them (I could be wrong here since my Python knowledge is not the best). While the Ruby library uses lambdas attached to DBus signal callbacks.

From snooping around dbus-rs I see that signals are supported (at least it says so in the readme) and there is an example of how to use them. I think it's possible to implement something similar in interface and functionality to the Ruby library using closures in Rust.

To be a bit more specific. We could add a function like on_notify which receives a closure as an argument and spawns a detached thread which does something similar to the example from DBus but in a loop.

@dati91 You opinion?

@dati91
Copy link
Member

dati91 commented May 16, 2017

Sounds promising!
If you need any help with it, just let me know.

@jamesmunns
Copy link

I have started working on this, and have successfully read from a notify using something like this in bluetooth_characteristic.rs:

pub fn acquire_notify<F>(&self, func: F) -> Result<()>
    where F: FnOnce(u16, OwnedFd)
{
    let c = Connection::get_private(BusType::System)?;
    let mut m = Message::new_method_call(
        SERVICE_NAME,
        &self.object_path,
        GATT_CHARACTERISTIC_INTERFACE,
        "AcquireNotify"
    )?;
    let reply = c.send_with_reply_and_block(m, 1000)?;
    let (opt_fd, opt_mtu) = reply.get2::<OwnedFd, u16>();
    func(opt_mtu.unwrap(), opt_fd.unwrap());
    Ok(())
}

paired with something like this:

// UGLY PROTOTYPE
println!("{:?}", charac.acquire_notify(
    |mtu, ofd| {
        println!("{:?}", mtu);
        println!("{:?}", ofd);

        use std::fs::File;

        let mut buf = [0u8; 512];

        // TODO, don't leak fd
        let fd = ofd.into_fd();

        for i in 0..5 {
            thread::sleep(Duration::from_millis(1500));
            if let Ok(num_read) = read(&fd, &mut buf[..]) {
                let hbs = buf.iter()
                    .take(num_read)
                    .map(|b| format!("{:02X}", b))
                    .collect::<Vec<_>>();

                println!("PING: {:?}", hbs);
            } else {
                println!(":(");
            }
        }


    }
));

fn read(fd: &RawFd, buf: &mut [u8]) -> Result<usize> {
    use libc::c_void;

    let ret = unsafe {
        libc::read(fd.clone(),
                   buf.as_mut_ptr() as *mut c_void,
                   buf.len())
    };

    if ret < 0 {
        println!("{:?}", ret);
        bail!("Fail")
    } else {
        Ok(ret as usize)
    }
}

which gives me this:

Pairing...
"00000001-c001-de30-cabb-785feabcd123"
        "0000c01d-c001-de30-cabb-785feabcd123"
23
OwnedFd { fd: 5 }
PING: ["0E", "00", "00", "00"]
PING: ["0F", "00", "00", "00"]
PING: ["10", "00", "00", "00"]
PING: ["11", "00", "00", "00"]
PING: ["12", "00", "00", "00"]
Ok(())

I'm working on some other stuff first, and right now this still requires periodic polling of the file descriptor, but it's a good proof of concept :)

Hopefully I can clean this up to a better interface, and contribute it back sooner than later.

@jamesmunns
Copy link

Also as a note, this requires BlueZ 5.46 or above, and at least on 5.46, this requires you to set the --experimental flag for bluetoothd.

@denysvitali
Copy link

@jamesmunns Have you had time to look further into this issue? Do you know another workaround or can you make a PR?

@jamesmunns
Copy link

Hey @denysvitali, I didn't end up going further with this project, so the comment I left before is basically as far as I got.

denysvitali added a commit to denysvitali/blurz that referenced this issue Jul 25, 2018
@bsphere
Copy link
Contributor

bsphere commented Oct 23, 2018

in #21 you could use start_notify, map incoming dbus messages to BluetoothEvent and use BluetoothEvent::Value. (`examples/test5.rs)

@visceralfield
Copy link

Has there been any more work on this? My use case also relies on BLE notifications.

@bsphere
Copy link
Contributor

bsphere commented Jul 10, 2019

see #21, GATT notifications work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants