On Wed, Jun 01, 2016 at 07:04:02AM +0100, Wayne Keenan wrote: > Because I'm creating a 'ble_gatt_svc_def' at runtime these are the steps > I'm having todo to be able to keep hold of the characteristic handle in > order to be able to call 'ble_gatts_chr_updated(handle)' to send the > notification when needed: > > 1. When initialising the 'ble_gatt_chr_def' the 'arg' field is set to a > marker value. > > 2. In my 'gatt_svr_register_cb' callback if a ble_gatt_chr_def.arg marker > value is found then the 'ctxt->chr_reg.def_handle' is stashed in a global > var.
That will work, but as you say, the need for the "marker arg" is a bit unwieldy. I think you are correct - there isn't currently a good way to do this. There is an alternative approach that I came up with, but I am not sure it is an improvement over what you are currently doing, so it has been relegated to the bottom of this email! After reading your email and looking at the code some more, I think there are a few changes that need to be made to the nimble host immediately: 1. The characteristic-registration-callback should receive a pointers to both the service definition and the characteristic definition (currently it only receives a pointer to the characteristic definition). Without the service in hand, it is impossible to identify the characteristic by UUIDs. 2. (As you mentioned) A function to retrieve the attribute handles corresponding to the characteristic with a specified service-UUID/characteristic-UUID pair. In addition, this is not directly related to the problem being discussed, but I noticed we need a third change: 3. The characteristic access callback also needs to receive a pointer to the service definition (rather than just a pointer to the characteristic definition). This chnage is needed for the same reason as 1 above: so that the characteristic can be reliably identified by UUID. [...] > Is it safe to cache the 'union ble_gatt_register_ctxt *ctxt' pointer > given as a parameter 'gatt_svr_register_cb' to use later? No, it is not safe. The ctxt object is located on the stack, so you need to make copies of anything you might need later. > Being able to call Newt GATT API's like the following would help alleviate > steps 1,2 and help with step 3: > > a) uuit16_t gatt_get_handle_for_chr(serviceUUID, charUUID) Agreed. > b) int gatt_write_chr(uint16_t handle, void* data, uint len, bool > sendNotificationIfEnabled); There is the following function: int ble_att_svr_write_local(uint16_t attr_handle, void *data, uint16_t data_len); When a characteristic is written via this function call, the subsequent characteristic access callback indicates that the write was done locally by specifying a connection handle of BLE_HS_CONN_HANDLE_NONE (0xffff). The above function will always send notifications to all clients who are subscribed to the corresponding characteristic. Perhaps the characteristic access callback should have the option of disabling notifications for a particular write. Alternatively, If you want to update a characteristic without sending any notifications, you can always bypass the nimble stack entirely and just write directly to the attribute data. [...] Thanks again for you feedback. It is very much appreciated. Chris ----- As promised, below is a technique that eliminates the need for the marker arg. As I said, it is a bit too ugly for me to recommend with a straight face, but I thought I should include it for.. I don't know... some reason :). The technique identifies characteristics by memory address. #define SVC_IDX_GAP 0 #define CHR_IDX_GAP_DEV_NAME 0 #define CHR_IDX_GAP_APPEARANCE 1 static const struct ble_gatt_svc_def my_svcs[] = { [SVC_IDX_GAP] = { /*** Service: GAP. */ .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid128 = BLE_UUID16(BLE_GAP_SVC_UUID16), .characteristics = (struct ble_gatt_chr_def[]) { [CHR_IDX_GAP_DEV_NAME] = { /*** Characteristic: Device Name. */ .uuid128 = BLE_UUID16(BLE_GAP_CHR_UUID16_DEVICE_NAME), .access_cb = gatt_svr_chr_access_gap, .flags = BLE_GATT_CHR_F_READ, }, [CHR_IDX_GAP_APPEARANCE] = { /*** Characteristic: Appearance. */ .uuid128 = BLE_UUID16(BLE_GAP_CHR_UUID16_APPEARANCE), .access_cb = gatt_svr_chr_access_gap, .flags = BLE_GATT_CHR_F_READ, }, /* [...] */ 0, /* No more characteristics in this service. */ } 0, /* No more services. */ }, }; static void gatt_svr_register_cb(uint8_t op, union ble_gatt_register_ctxt *ctxt, void *arg) { switch (op) { case BLE_GATT_REGISTER_OP_CHR: if (ctxt->chr_reg.chr == &gatt_svr_svcs[SVC_IDX_GAP]. characteristics[CHR_IDX_GAP_DEV_NAME]) { /* Stash handles. */ } break; }