EDIT: 27 OCT 2018 - Narrowed down my issue quite substantially and have it working for hacked up peripherals but the hacks aren't useful except they confirm the problem.
The problem is exposed when the second and third peripherals retrieve the NUS service attribute handles: The values in those handles are identical to those from the first connection, and therein is the problem.
Product has nrf52832s in all components. The central is built around NUS hacked for multiconnect central service, the peripherals are mostly straightforward NUS peripherals The central is literally a serial server. The periphs await notifications for 60 or so character records coming once every 3 seconds.
Problem I'm now running into is that only the first periph to connect gets notifications. The central calls to ble_nus_data_send return NRF_ERROR_INVALID_STATE and I'm confirming the client service structures don't have is_notification_enabled, for all but the first connecting periph.
Central/NUS server
static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt) { ret_code_t err_code; ble_nus_evt_t evt; ble_nus_client_context_t * p_client; ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; // Check if the event if on the link for this instance if (p_nus->conn_handle != p_ble_evt->evt.gatts_evt.conn_handle) { return; } err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, p_ble_evt->evt.gatts_evt.conn_handle, (void *) &p_client); NRF_LOG_DEBUG("GATTS WRITE for handle %d/%d obj %p entered",p_ble_evt->evt.gatts_evt.conn_handle, p_nus->conn_handle,p_nus); if (err_code != NRF_SUCCESS) { NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.", p_ble_evt->evt.gatts_evt.conn_handle); } memset(&evt, 0, sizeof(ble_nus_evt_t)); evt.p_nus = p_nus; evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle; evt.p_link_ctx = p_client; if ( (p_evt_write->handle == p_nus->tx_handles.cccd_handle) && (p_evt_write->len == 2)) { NRF_LOG_DEBUG("NOTIF setting written for %p/handle %d/%d",p_nus, p_ble_evt->evt.gatts_evt.conn_handle,p_nus->conn_handle); if (p_client != NULL) { if (ble_srv_is_notification_enabled(p_evt_write->data)) { p_client->is_notification_enabled = true; evt.type = BLE_NUS_EVT_COMM_STARTED; } else { p_client->is_notification_enabled = false; evt.type = BLE_NUS_EVT_COMM_STOPPED; } if (p_nus->data_handler != NULL) { p_nus->data_handler(&evt); } } } else if ((p_evt_write->handle == p_nus->rx_handles.value_handle) && (p_nus->data_handler != NULL)) { evt.type = BLE_NUS_EVT_RX_DATA; evt.params.rx_data.p_data = p_evt_write->data; evt.params.rx_data.length = p_evt_write->len; p_nus->data_handler(&evt); } else if ( p_evt_write->len == 2 ) { // debug for busted notifs NRF_LOG_DEBUG(" gatts write handle %d looking for %d, no goodski",p_evt_write->handle,p_nus->tx_handles.cccd_handle); asm("nop"); } else { // Do Nothing. This event is not relevant for this service. asm("nop"); } }
The bit where "(p_evt_write->handle == p_nus->tx_handles.cccd_handle)" runs is true when the periph is successfully gets notifications. All is well and good there, for the first peripheral to connect.
But not at the 2nd to nth consecutive connectees, they attempt writes using attribute handle values that area already allocated for use by the first peripheral. Their writes fail (good on the central! It should see those as faulty, and it does, because in its ble_nus_t object (of which there are multiple) it has the correct values. And those *should* have returned to the periph during the discovery phase.
And this begs the question as to how I'm accidentally polluting the central's gatt with duplicate handle values? I'm initializing each ble_nus_t object with a unique context (or am I? The context manager is deceptively concise but critical if there's to be any clean way to provide context that doesn't involve the preprocessor. (I'm looking at you and BLE_NUS_DEF) Fitting actual values to this code: All of the peripherals attempt write have p_evt_write->handle all requesting the p_nus->tx_handles.cccd_handle that's correct for the first link (16 in my case), but the central thinks its p_nus->tx_handles.cccd_handle is actually 22 (and 28, and so on for successive instances). In other words the attribute handles values are offset +6 for each successive instance of nus service but the peripheral in its wisdom / p_evt_write->handle has set ->handle with the value that is only appropriate for the first instance.
The more I read this the more it will read as confusing, but better as well. Edit 28 Oct 18 - Perhaps some more code will help (!), but I think it's something dopey I left in the central's BLE_NUS service array definition and its runtime init.
Periph/NUS client from lightly modified ble_nus_c.c:
static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable) { uint8_t buf[BLE_CCCD_VALUE_LEN]; buf[0] = enable ? BLE_GATT_HVX_NOTIFICATION : 0; buf[1] = 0; ble_gattc_write_params_t const write_params = { .write_op = BLE_GATT_OP_WRITE_REQ, .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, .handle = cccd_handle, .offset = 0, .len = sizeof(buf), .p_value = buf }; return sd_ble_gattc_write(conn_handle, &write_params); }
FWIW .handle = cccd_handle is always 16 no matter how many peripherals I have.
It's like the peripheral is getting the characteristics for the first instance instead of the other instances. I wish I could explain this better than I am, but I'm trying in lieu of dumping all the components into a .zip and sending it to Norway for help. Which I may still yet do if any more blood spills out of my earholes
Thanks for reading and in advance for your guidance Howard
PS How are these links created ? Maybe that's the problem. The central scans for advertisements where ble_advdata_name_find(p_adv_report->data.p_data, p_adv_report->data.len,m_target_periph_name) returns true, matching the hardcoded device name in the peripherals. From within on_adv_report (called out of the BLE_GAP_EVT_ADV_REPORT on_ble_central_evt case) I create the link by calling sd_ble_gap_connect(&p_adv_report->peer_addr,
&m_scan_params,
&m_connection_param,
APP_BLE_CONN_CFG_TAG); When it returns NRF_SUCCESS I don't explicitly do anything to configure the gatts correctly. I have a feeling I need to prime it with new handles lest it supply old handles, like it seems to be doing.;...
PPS Don't work on this on the weekend. Have fun.