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

Trouble with directed advertising

Hello, I've been trying to get directed advertising and auto-connection working on an NRF51822 peripheral.

I'm using SDK V11.0 with SD130 v2.0.1

This is the scenario I'd like to get working:

  • Start normal (fast) advertising.
  • Pair & bond with central.
  • Upon disconnection, try automatically connecting using directed advertising in case the disconnect wasn't intentional.
  • (Ideally) start direct advertising on next startup if bond info is present.

This is my advertising_init() function

void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    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;

    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS;
    options.ble_adv_directed_enabled = true;        // Rapidly try to reconnect to the last peer for a short while upon disconnect
    options.ble_adv_directed_slow_enabled = false;  // TODO: Do we want slow directed advertising?
    options.ble_adv_directed_slow_interval = APP_ADV_DIRECT_INTERVAL;
    options.ble_adv_directed_slow_timeout = APP_ADV_DIRECT_TIMEOUT_IN_SECONDS;
    options.ble_adv_whitelist_enabled  = false;     // TODO: Might want to use whitelists later on.

    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}

And this is how I'm handling the advertising events:

void on_adv_evt(ble_adv_evt_t ble_adv_evt) 
{
    uint32_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_DIRECTED:
            nrf_gpio_pin_write(LED_RED, 1);

            break;

        case BLE_ADV_EVT_FAST:
        case BLE_ADV_EVT_FAST_WHITELIST:
            nrf_gpio_pin_write(LED_RED, 0);
            err_code = app_timer_start(adv_led_timer_id, ADV_LED_INTERVAL, NULL);
            APP_ERROR_CHECK(err_code);

            break;

        case BLE_ADV_EVT_IDLE:
            err_code = app_timer_stop(adv_led_timer_id);
            APP_ERROR_CHECK(err_code);

            shutdown_flag = 1;

            break;

        // Handle whitelist requests from the advertising module.
        // This happens when using FAST or SLOW advertising modes and 
        // whitelisting is enabled when ble_advertising_init() is called
        case BLE_ADV_EVT_WHITELIST_REQUEST: {
            ble_gap_whitelist_t whitelist;
            ble_gap_addr_t* p_whitelist_addr[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
            ble_gap_irk_t* p_whitelist_irk[BLE_GAP_WHITELIST_IRK_MAX_COUNT];

            whitelist.addr_count = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
            whitelist.irk_count = BLE_GAP_WHITELIST_IRK_MAX_COUNT;
            whitelist.pp_addrs = p_whitelist_addr;
            whitelist.pp_irks = p_whitelist_irk;

            err_code = dm_whitelist_create(&m_app_handle, &whitelist);

            if(err_code == NRF_SUCCESS) {
                err_code = ble_advertising_whitelist_reply(&whitelist);
                APP_ERROR_CHECK(err_code);                
            }

            break;
        }

        // Handle peer address requests from advertising module.
        // This happens when using DIRECTED or DIRECTED_SLOW advertising modes.
        // The device will then try to reconnect to the last connected peer.
        case BLE_ADV_EVT_PEER_ADDR_REQUEST: {
            if(m_bonded_peer_handle.appl_id != DM_INVALID_ID) {
                ble_gap_addr_t addr;
                memset(&addr, 0, sizeof(addr));

                err_code = dm_peer_addr_get(&m_bonded_peer_handle, &addr);

                if(err_code != (NRF_ERROR_NOT_FOUND | DEVICE_MANAGER_ERR_BASE)) {
                    err_code = ble_advertising_peer_addr_reply(&addr);
                    APP_ERROR_CHECK(err_code);
                }
            }

            break;
        }

        default:
            break;
    }
}

I'm using "just works" bonding with these parameters:

#define SEC_PARAM_BOND                   1                                          /**< Perform bonding. */
#define SEC_PARAM_MITM                   0                                          /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC                   0                                          /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS               0                                          /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES        BLE_GAP_IO_CAPS_NONE                       /**< No I/O capabilities. */
#define SEC_PARAM_OOB                    0                                          /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE           7                                          /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE           16  

I've confirmed that the address returned by dm_peer_addr_get is the correct one and the advertising module is entering directed advertising mode (the red LED lights up for about a second indicating that the BLE_ADV_EVT_DIRECTED event is received).

I'm trying this with a Linux central. The pairing/bonding is successful and a connection is established. But when I kill the connection from the peripheral side (restart or reset), the peripheral starts direct advertising (red LED is lit). But a connection is not automatically established, instead the peripheral just starts fast advertising when the directed advertising times out.

I've got a Logitech MX Master Bluetooth mouse connected to the same computer. Paired & bonded. Immediately when I turn on the mouse, a connection is established with the computer. This is the kind of behavior I'd like to get with my peripheral. Is there a different protocol since it is an HID?

My understanding is that just entering directed advertising mode should be enough. Is there something else I'm missing? Do I have to handle the BLE_ADV_EVT_DIRECTED event and manually try to connect to the bonded central?

I'm also having trouble getting a connection working on my Android phone, but that may be a problem with the phone also. What happens is that pairing & bonding seems to complete, and then after about 2-3 seconds the Android central kills the connection with the reason BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION. In that case, I'd think properly working directed advertising would automatically reconnect to the central. In my case, the peripheral advertises in directed mode until it times out and falls down to fast advertising.

Related