Zephyr, NRFConnect 2.3, and BLE reading/writing

Can someone please, for the love of all that is holy, point me towards some guidance on how to read values of custom characteristics set by another device?

I cannot find this ANYWHERE.

Parents Reply Children
  • Apologies, Amanda - I solved this and then had to rush to solve another problem.

    If I recall correctly, I was attempting to discover services and characteristics; this was challenging at the time, and I suspect the documentation wasn't really that hot.

    That said, if anyone else is looking for help here's what I found:

    There are many ways you can connect to a device that offers services over BLE.

    1) You can use a premade discover manager (NCS and/or Zephyr have one as far as I know). This can be very, very slow and drain your battery if you're disconnecting and reconnecting often. If you don't know what services are available, this may be your best option.
     
    2) You can can quickly "discover" services and characteristics for which you already have the UUIDs. With NCS/Zephyr, you can do this by calling bt_gatt_discover() with a set of discovery parameters you populate yourself. Using this method, you would ensure that you set a callback function that iteratively discovers any subsequent services using the UUIDs you have in hand once a connection is made and the first service is discovered.

    Though more involved than the discovery manager, it can be much faster.

    For more info, I would consult the hr_central example in the zephyr github repo.

    3) You can hard set the handles for all the services you want to use if you have foreknowledge of what they'll be. This only works if you have complete control of both sides of the connection as far as I understand, but it's extremely fast since you can effectively skip all discovery and just call read/write/notify/etc at will.

    In that case, you would want to create a set of read/write/subscribe params somewhere on your device (I would suggest the stack rather than heap, though both work fine), manually set them up, and use them to interact with your connected device after connections have been established.

    Something like

    static struct bt_gatt_read_params my_bt_gatt_read_params[2];
    static struct bt_gatt_write_params my_bt_gatt_write_params[2];
    static struct bt_gatt_subscribe_params my_bt_gatt_subscribe_params;
    
    
    uint8_t my_gatt_write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
    {
        LOG_INF("We tried to send data over ble.");
        if (err){
            LOG_ERR("Error While Sending Data: err = %d\n", err);
        }
        
    }
    
    //... similar for reads, notifies, etc...
    my_bt_gatt_write_params[0].func = my_gatt_write_cb;
    my_bt_gatt_write_params[0].handle = <handle of char here>;
    my_bt_gatt_write_params[0].data = <data goes here>;
    my_bt_gatt_write_params[0].length = <length of data goes here>;
    
    
    // once connected, with a proper connection handle - in this case 'my_conn'...
    
    int err = bt_gatt_write(my_conn, my_bt_gatt_write_params[0]);
    if(err) {
        LOG_ERR("Error in bt_gatt_write: %d", err);
    };



    That said, I would love clearer documentation here - this was a bit of a bear to figure out unaided, but ended up being very simple to implement once understood.


    Cheers.

Related