embassy-rs / nrf-softdevice

Apache License 2.0
259 stars 79 forks source link

How to use channels with ble server & connection? #264

Closed Gibbz closed 1 month ago

Gibbz commented 1 month ago

I want to write and notify values on the ble server using pubsub channels. Ive tried storing the softdevice and connection objects. And ive tried setting up a new subtask. Both ways have not worked. Im happy for either the bluetooth to be subscribed. Or for my objects to access a write/notify function in the softdevice.

It looks like the gattserver consumes the softdevice and connection references. So I cannot share them or use them.

Any ideas how I can get this working?

Gibbz commented 1 month ago

I think i found what I need here to make an updater task: https://github.com/embassy-rs/nrf-softdevice/blob/master/examples/src/bin/ble_bas_peripheral_notify.rs#L169

Gibbz commented 1 month ago

I'm not having much luck setting up my code like the example. I need access to sd, however it is consumed by the connection. Ive got the following code at the end of my main loop:

        let conn: Connection = unwrap!(peripheral::advertise_connectable(sd, adv, &config).await); // <- sd is consumed here
        info!("advertising done!");

        let update_fut = update_task(&server, &conn);
        let gatt_fut = gatt_server::run(&conn, &server, |_| {});
        pin_mut!(update_fut, gatt_fut);

        let _ = match select(update_fut, gatt_fut).await {
            Either::Left((_, _)) => {
                info!("BLE update task encountered an error and stopped!")
            }
            Either::Right((e, _)) => {
                info!("gatt_server run exited with error: {:?}", e);
            }
        };

And my update function:

// update task
async fn update_task<'a>(server: &'a Server, connection: &'a Connection) {
    let mut sub_throttle_in = signals::THROTTLE_IN.subscriber().unwrap();
    loop {
        let val = sub_throttle_in.next_message_pure().await;
        info!("ble pudate task throttle: {}", val);
        let t = server.data.throttle_input_voltage_set(&sd, val); // <- function requires sd, which we cant use

        match server.data.throttle_input_voltage_notify(connection, val) {
            Ok(_) => info!("notified client"),
            Err(_) => unwrap!(server.data.throttle_input_voltage_set(&sd, val)), // <- function requires sd, which we cant use
        };
    }
}

Edit: ive noticed gatt_server::set_value(sd, self.throttle_input_voltage, &split) does not even use the sd reference in the code... Is there a way to pass null to this as its not even used?

Also I noticed in this example that its setup with a macro, so there must be some way I can copy the macro to get this working:

        // Try and notify the connected client of the new ADC value.
        match server.bas.battery_level_notify(connection, &adc_raw_value) {
            Ok(_) => info!("Battery adc_raw_value: {=i16}", &adc_raw_value),
            Err(_) => unwrap!(server.bas.battery_level_set(&adc_raw_value)),
        };

//.... SNIP ....

#[nrf_softdevice::gatt_service(uuid = "180f")] // <- how is this macro setting up server.bas.battery_level_set(&adc_raw_value) ??
struct BatteryService {
    #[characteristic(uuid = "2a19", read, notify)]
    battery_level: i16,
}
Gibbz commented 1 month ago

Ive kind of got this working with a bit of hacking around...