This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to Continue Peripheral Device Advertising After the Connection of a Central Device that Peripheral Device

I am developing firmware for my Peripheral Device (nRF52840) using S140 (v7) and SDK 15.3. In my application, I will advertise (with advert payload changes) and support Smartphone app connect/disconnect to read and write custom characteristic values. Changing the payload was a challenge, but has been resolved using forum posts on this subject. I have created advertising_config(bool firstCall) to set up (firstCall=true) or update (firstCall=false) advertising.  It is not updating with meaningful data yet, but it shows that (conceptually), updates work (toggles data between "SomeData!" and "TomeData!" every 10 sec).

// Config advertising.
static void advertising_config(bool firstCall)
{
  ret_code_t             err_code;

  // Setup var to hold ble_advdata_t advert data. 
  ble_advdata_t advdata;
  memset(&advdata, 0, sizeof(advdata));

  // Setup var to hold ble_advdata_t scan resp data. 
  ble_advdata_t srdata;
  memset(&srdata, 0, sizeof(srdata));

  // Just use srdata to indicate that we want to expose the name provided in gap_params_init().
  srdata.name_type = BLE_ADVDATA_FULL_NAME;
  
  // Setup variable to hold manufacturer specific advert data
  ble_advdata_manuf_data_t mfr_advdata; 
  memset(&mfr_advdata, 0, sizeof(mfr_advdata));

  // Update mfr_data and add to advdata.
  uint8_t data[10];
  if(m_bufIdx == 0) { strcpy(data, "SomeData!"); }
  else { strcpy(data, "TomeData!"); } //Our data to advertise
  mfr_advdata.company_identifier         =  0x0059; //Nordics company ID
  mfr_advdata.data.p_data                = data;
  mfr_advdata.data.size                  = sizeof(data);
  advdata.name_type                    = BLE_ADVDATA_NO_NAME;
  advdata.p_manuf_specific_data        = &mfr_advdata;

  advdata.include_appearance      = true;
  advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
  advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
  advdata.uuids_complete.p_uuids  = m_adv_uuids;

  // Initialize advertising parameters (used when starting advertising).
  memset(&m_adv_params, 0, sizeof(m_adv_params));

  m_adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
  m_adv_params.p_peer_addr     = NULL;    // Undirected advertisement.
  m_adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
  m_adv_params.interval        = APP_ADV_INTERVAL;
  m_adv_params.duration        = 0;       // Never time out.

  // Encode advdata to m_gapData[m_bufIdx]
  err_code = ble_advdata_encode(&advdata, m_gapData[m_bufIdx].adv_data.p_data, (uint16_t *) &m_gapData[m_bufIdx].adv_data.len);
  APP_ERROR_CHECK(err_code);

  // Encode srdata to m_gapData[m_bufIdx]
  err_code = ble_advdata_encode(&srdata, m_gapData[m_bufIdx].scan_rsp_data.p_data, (uint16_t *) &m_gapData[m_bufIdx].scan_rsp_data.len);
  APP_ERROR_CHECK(err_code);

  // Setup adverts. On first call, also set advert params.
  if(firstCall)
  {
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_gapData[m_bufIdx], &m_adv_params);
    APP_ERROR_CHECK(err_code);
  }
  else  // Susequent calls - update payloady only.
  {
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_gapData[m_bufIdx], NULL);
    APP_ERROR_CHECK(err_code);
  }

  // Toggle buffer index to toggle buffer used.
  if(m_bufIdx == 0) {m_bufIdx ++;}
  else {m_bufIdx = 0;}
}

My remaining challenge is getting advertising to restart after a Smartphone app disconnects. I have been using the Android nRF Connect app for testing.

I have tried using the hrs example app (\nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_hrs) as a reference, but it does not work. Whenever I connect to it, read a characteristic value and then disconnect, its advertising does not restart after disconnect (stops when I connect to my dev kit using nRF Connect).

What works (not a feasible solution in the real world due to delay discussed following) is restarting advertising (sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);) when BLE_GAP_EVT_DISCONNECTED is passed to my ble_evt_handler() as shown below. 

// Handle BLE events.
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
  ret_code_t err_code = NRF_SUCCESS;

  switch (p_ble_evt->header.evt_id)
  {
    case BLE_GAP_EVT_DISCONNECTED:
      NRF_LOG_INFO("Disconnected.");
      SEGGER_RTT_printf(0, "Disconnect Evt.\n");
      sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);

      m_conn_handle = BLE_CONN_HANDLE_INVALID;
      break;

    case BLE_GAP_EVT_CONNECTED:
      NRF_LOG_INFO("Connected.");
      SEGGER_RTT_printf(0, "Connect Evt.\n");
      err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
      APP_ERROR_CHECK(err_code);
      m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
      err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
      APP_ERROR_CHECK(err_code);
      break;

The problem with this approach is that BLE_GAP_EVT_DISCONNECTED can come into ble_evt_handler() 3 - 12 minutes after nRF Connect disconnects from the peripheral.

If I can get a faster indication of disconnection, I can stick with the approach that I have now (just call sd_ble_gap_adv_start() after getting an indication of disconnection). Please advise how to change BLE GAP config to support this.

Optionally - if there is a way to setup auto-restart upon disconnection, that approach would be even better.

Maybe also worth consideration .... when I select Disconnect within the nRF Connect app, is it actually disconnecting or am I just seeing a timeout 3-12 min later (i.e. nRF Connect bug)?

Looking forward to a reply and all help and input is appreciated.

Mark J

  • Different applications have different requirements, so the softdevice have tried to make it as flexible as possible. You may start advertising again on the connection event, or at any time during connection if you want, there is no need to wait for the disconnect event. You may also be connected to two central devices and/or disconnect the first connection on the second connection occurs. Again it can pretty much do whatever you want, but you need to call the api for it. 

    I have seen some phones may keep connection even if the application is shut down or want to close connection. There is no way for the peripheral to know that this have occured, but you may for instance add some keep alive data from the app, so once the peripheral have not received any keep alive for a timeout period it may disconnect from the phone and start advertising again. Again, you are free to do this as you see fit.

  • Thank you for the reply . I have added a sd_ble_gap_adv_start() call when BLE_GAP_EVT_CONNECTED hits the ble_evt_handler(), but advertising is not restarting. I suspect that (for some reason) advertising is not permitted when a connection is made.

    Any other options that I should try?

  • You likely need to increase NRF_SDH_BLE_PERIPHERAL_LINK_COUNT and NRF_SDH_BLE_TOTAL_LINK_COUNT, I suspect that the sd_ble_gap_adv_start() return an error code here if you don't.

    Once you do that you will likely get a warning in nrf_sdh_ble_enable() that you need to modify the RAM size required by the softdevice.

  • Thanks again . That did the trick! increased both counts (1->2 for each) and modified RAM start (size was already 0x4000) as required to get past nrf_sdh_ble_enable(). Now, I can see advertising immediately when I disconnect using the nRF Connect app. Thanks much!

Related