Hi.
I want my peripheral, an nRF52832, to accept a connection from the first peer it sees and bond with that peer, and thereafter accept connections only from that peer (but still be visible to other centrals scanning). I need this to work for both Android and iOS centrals.
Here's my whitelist reply code, running on the BLE_ADV_EVT_WHITELIST_REQUEST
event, but only when we're bonded already:
void whitelist_reply(void)
{
ret_code_t err_code = 0;
// Get the first and only peer ID. We must have one by now, because we only send a whitelist reply when we're bonded.
pm_peer_id_t peer_ids[1];
pm_peer_id_t peer_id = pm_next_peer_id_get(PM_PEER_ID_INVALID);
if (peer_id == PM_PEER_ID_INVALID)
{
// We should never call whitelist_reply() unless we're bonded and therefore have a peer ID.
NRF_LOG_WARNING("whitelist_reply(): No peer ID. Not sending whitelist.");
return;
}
// What we have now is a single pm_peer_id_t. But what we need to pass to ble_advertising_whitelist_reply() is a list of ble_gap_addr_t. To get from one to the other, use pm_whitelist_set(), then pm_whitelist_get().
peer_ids[0] = peer_id;
err_code = pm_whitelist_set(peer_ids, 1);
APP_ERROR_CHECK(err_code);
ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
ble_gap_irk_t whitelist_irks[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
uint32_t addr_cnt = 1;
uint32_t irk_cnt = 1;
err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt, whitelist_irks, &irk_cnt);
APP_ERROR_CHECK(err_code);
// Because the NRF_LOG macros only support up to five varargs after the format string, we need to break this into two calls.
NRF_LOG_RAW_INFO("pm_whitelist_get() returned address: ");
NRF_LOG_RAW_INFO("%02X:%02X:%02X:", whitelist_addrs[0].addr[0], whitelist_addrs[0].addr[1], whitelist_addrs[0].addr[2]);
NRF_LOG_RAW_INFO("%02X:%02X:%02X\n", whitelist_addrs[0].addr[3], whitelist_addrs[0].addr[4], whitelist_addrs[0].addr[5]);
// From BLE_GAP_ADDR_TYPES in ble_gap.h:
// #define BLE_GAP_ADDR_TYPE_PUBLIC 0x00 /**< Public address. */
// #define BLE_GAP_ADDR_TYPE_RANDOM_STATIC 0x01 /**< Random static address. */
// #define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE 0x02 /**< Random private resolvable address. */
// #define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE 0x03 /**< Random private non-resolvable address. */
NRF_LOG_INFO("pm_whitelist_get() returned address type: %d", whitelist_addrs[0].addr_type);
// The IRK size is BLE_GAP_SEC_KEY_LEN = 16.
NRF_LOG_RAW_INFO("pm_whitelist_get() returned IRK: ");
NRF_LOG_RAW_INFO("%02X\n", whitelist_irks[0].irk);
// Apply the whitelist.
err_code = ble_advertising_whitelist_reply(&m_advertising, whitelist_addrs, addr_cnt, whitelist_irks, irk_cnt);
APP_ERROR_CHECK(err_code);
}
This code does seem to have found the address for my bonded Android central:
D ble_periph Adv event: Whitelist requested
pm_whitelist_get() returned address: 46:1D:36:6D:1B:04
pm_whitelist_get() returned IRK: 2000FDD0
D ble_periph Adv event: Slow (whitelist)
The byte order of the address logged above is the reverse of what I see on the UI in the Android Bluetooth Settings app: 04:1B:6D:36:1D:46
. I presume this is harmless - it's just the way it's being logged and not the way it's being stored and used in the whitelist?
I see this in the logcat on the Android app side:
com.android.bluetooth E/bt_btm: btm_ble_read_remote_features_complete: failed for handle: 0x0006, status 0x3e
And here's the logcat from the nRFConnect Android app, if I use that instead to make the second connection attempt.
12-31 14:30:48.146 28856-28856/no.nordicsemi.android.mcp D/BluetoothGatt: connect() - device: C7:56:60:F0:56:58, auto: false
12-31 14:30:48.146 28856-28856/no.nordicsemi.android.mcp D/BluetoothGatt: registerApp()
12-31 14:30:48.146 28856-28856/no.nordicsemi.android.mcp D/BluetoothGatt: registerApp() - UUID=2f63c9df-a8bd-4c58-bfdd-71cce668bc60
12-31 14:30:48.147 1552-2145/com.android.bluetooth I/bt_stack: [INFO:gatt_api.cc(1004)] GATT_Register
12-31 14:30:48.147 1552-2145/com.android.bluetooth I/bt_stack: [INFO:gatt_api.cc(1027)] allocated gatt_if=10
12-31 14:30:48.147 28856-29057/no.nordicsemi.android.mcp D/BluetoothGatt: onClientRegistered() - status=0 clientIf=10
12-31 14:30:48.148 1552-1603/com.android.bluetooth D/bt_btif_config: btif_get_address_type: Device [c7:56:60:f0:56:58] address type 1
12-31 14:30:48.148 1552-1603/com.android.bluetooth D/bt_btif_config: btif_get_device_type: Device [c7:56:60:f0:56:58] type 2
12-31 14:30:48.148 1552-2145/com.android.bluetooth I/bt_stack: [INFO:gatt_api.cc(1168)] GATT_Connectgatt_if=10 c7:56:60:f0:56:58
12-31 14:30:48.496 1552-2145/com.android.bluetooth E/bt_btm: btm_ble_read_remote_features_complete: failed for handle: 0x0001, status 0x3e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=3 connected=0 conn_id=3 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=4 connected=0 conn_id=4 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=5 connected=0 conn_id=5 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=6 connected=0 conn_id=6 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=7 connected=0 conn_id=7 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=8 connected=0 conn_id=8 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=9 connected=0 conn_id=9 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth W/bt_btif: bta_gattc_conn_cback() - cif=10 connected=0 conn_id=10 reason=0x003e
12-31 14:30:48.497 1552-2145/com.android.bluetooth I/bt_btm_sec: btm_sec_disconnected clearing pending flag handle:1 reason:62
12-31 14:30:48.498 28856-29057/no.nordicsemi.android.mcp D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=10 device=C7:56:60:F0:56:58
12-31 14:30:48.498 28856-29057/no.nordicsemi.android.mcp E/BluetoothLeBasicConn: Connection state changing error: 133
Here's a Wireshark/Sniffer capture that shows the CONNECT_REQ simply being ignored. See packet 947 or so.
bike-tracker-nRF52-whitelist-connection-req-ignored.pcapng
So, why can't the central connect? If I turn the whitelist off, it can connect immediately. With it on, it never connects.
[edit]
Here's the logging of the address and address type on the very first connection:
I ble_periph Connected.
connection from address: B0:77:9A:67:7C:4D
I ble_periph connection from address type: 2
And here's the logging on the whitelist reply:
D ble_periph Adv event: Whitelist requested
pm_whitelist_get() returned address: C6:00:20:D4:C8:00
I ble_periph pm_whitelist_get() returned address type: 90
pm_whitelist_get() returned IRK: 2000FDEC
The address type in the whitelist reply makes no sense - it should be in the range 0 - 3.
[edit 2]
But the address type on the second connection, once we're already bonded and are going to to the whitelist reply, is OK:
connection from address: A3:36:7D:6C:18:70
I ble_periph connection from address type: 2