Closed albertskog closed 11 months ago
It doesn't look like notifications are currently supported. You would need to add handling for the BLE_GATTC_EVT_HVX event to gatt_client.rs and add support for writing to the Client Configuration Characteristic Descriptor to enable/disable notifications. PRs are welcome.
Is this still an open issue?
As in, if I use the NRF as a client and connect to a peripheral, I cannot receive notifications from that peripheral?
Okay, just in case anyone runs into this, there is a bit of a hack in the meantime to get some raw data out (I've never used Rust macros, so the "correct" solution will take me some time to wrap my head around before I can PR).
Given this gatt client:
#[nrf_softdevice::gatt_client(uuid = "00726f62-6f74-7061-6a61-6d61732e6361")]
struct MyClient {
#[characteristic(uuid = "01726f62-6f74-7061-6a61-6d61732e6361", write)]
tx: u8,
#[characteristic(uuid = "02726f62-6f74-7061-6a61-6d61732e6361", read, notify)]
rx: u8,
}
The existing macros will create what you need to set up notifications.
let mut config = central::ConnectConfig::default();
config.scan_config.whitelist = Some(addresses);
let connection = unwrap!(central::connect(sd, &config).await);
let my_client: MyClient = unwrap!(gatt_client::discover(&connection).await);
So, write to the characteristic descriptor with your notification/indication request:
// Write to the generated notify cccd handle with a 16-bit little-endian 0x0001 to setup notifications (0x0002 for indications)
unwrap!(gatt_client::write(&connection, my_client.rx_cccd_handle, &[0x01, 0x00]).await);
Then, use this new function to consume the raw ble_evt_t
:
let result = gatt_client::run(&connection, &client, |e| {
info!("Event: {:?}", e.header.evt_id);
}).await;
In gatt_client.rs, I've created a run
function that currently just passes the BLE event straight down for manual consumption (setting this up with macro-generated events Ala gatt_server::run would be better).
pub async fn run<'m, F, C>(conn: &Connection, client: &C, mut f: F) -> DisconnectedError
where
// HACK: Passing the event straight down
F: FnMut(&ble_evt_t),
C: Client,
{
let conn_handle = match conn.with_state(|state| state.check_connected()) {
Ok(handle) => handle,
Err(DisconnectedError) => return DisconnectedError,
};
portal(conn_handle)
.wait_many(|ble_evt| unsafe {
info!("run: ble_evt={:?}", ble_evt);
let ble_evt = &*ble_evt;
if u32::from(ble_evt.header.evt_id) == raw::BLE_GAP_EVTS_BLE_GAP_EVT_DISCONNECTED {
return Some(DisconnectedError);
}
// If evt_id is not BLE_GAP_EVTS_BLE_GAP_EVT_DISCONNECTED, then it must be a GATTC event
let gattc_evt = get_union_field(ble_evt, &ble_evt.evt.gattc_evt);
let conn = unwrap!(Connection::from_handle(gattc_evt.conn_handle));
let evt: Option<ble_evt_t> = match ble_evt.header.evt_id as u32 {
raw::BLE_GATTC_EVTS_BLE_GATTC_EVT_HVX => {
let params = get_union_field(ble_evt, &gattc_evt.params.hvx);
let v = get_flexarray(ble_evt, ¶ms.data, params.len as usize);
trace!("GATT_HVX write handle={:?} data={:?}", params.handle, v);
None
}
_ => None,
};
// HACK: Just passing the event straight down
f(ble_evt);
None
})
.await
}
Hopefully this helps someone, and if anyone would like to provide me some suggestions on how this API should look and how I should consider setting up the macros - I'd appreciate it. I can then run with it and submit a PR to do this properly.
Is there a way for
gatt_client
to receive notifications? I am trying to understand nrf-softdevice-macro and I see there is some sort of event generated innrf-softdevice-macro/src/lib.rs
:But how do I capture it, there is no
gatt_client::run()
like there is withgatt_server
? Maybe I'm looking in the wrong places..Happy to contribute documentation and/or code if I can just get a little hint!