We're attempting to get a Bluetooth data stream set up on our nRF52840. We've taken Zephyr's peripheral sample and pared it down somewhat to only allow indications from the board to a phone, a Samsung Galaxy S6 running nRF Connect in our case. The main components seem to work well, but there are some problems regarding pairing/bonding. What we're desiring is to have the phone bonded once and to have it receive data from the board whenever they come in close contact. In order to do this, we'd like to only send indications out if the phone we're connected to is paired, for security reasons (and this is with the understanding that the two units will be paired automatically without asking for a passcode if they're bonded just once). We've not been able to figure out how to do this quite right, unfortunately. The main issue is that the "pairing_complete" callback is never reached after a reconnection of the two bonded units; it only happens right after the passcode is entered, and of course that prompt never reoccurs with a bond present. We tried the "bt_foreach_bond" function, and although it seems to work at first, it keeps getting called even after the bond is deleted in nRF Connect, and even after an incorrect passcode is entered when the prompt inevitably comes up again on the phone. We also tried the "bt_conn_security" function, but its output doesn't seem to make any sense, as described below (we don't have "bt_conn_get_security" in our Zephyr 1.14.99). Is there a simple and reliable way to detect if the phone thinks the connection is secure, either through a recent pairing event or a previous non-deleted bonding event?
Upon trying to get all of this to work, we've come across two other issues. In the interest of space, we've condensed these into steps:
- Have bonding disabled with a static passcode
- Connect, type in passcode as immediately requested
- Device starts indicating as usual
- Device says pairing success and nRF Connect App says bonded
- Disconnect via nRF Connect App
- Device says disconnected (reason 19)
- Try to reconnect via nRF Connect App
- Device says connected, indication fail, then disconnected (reason 61)
- Phone says connected but eventually times out with error 8
- Bonding information is now automatically deleted in nRF Connect App
- Connect, type in passcode as immediately requested, back to beginning
In this first one, we're not sure why bonding succeeds when it's obviously disabled. The disable works the same via "bt_set_bondable(false)" and "CONFIG_BT_BONDABLE=n". This functionality isn't seen if bonding is enabled on our nRF52840. We're not sure if static passcodes are causing either of these issues, but that's just how we have it set currently.
- Have bonding enabled or disabled with a static passcode
- Connect, cancel passcode entry immediately when prompted
- While "Bonding...", close tab for device on nRF Connect App
- No message for pairing cancelled on device; indications keep being transmitted
- Device no longer shows up in list since advertising is no longer happening
- Soon, printed security level is raised to High and indications still flow out
- Have to reset device to make it connectable again
- If instead of closing tab, "Bonding..." is let to time out, printed security level rises but device disconnects properly when asked
This could be an issue with nRF Connect, but it basically bricks our nRF52840 until it's reset, and we can't have this happen for our project. The indications seem to be successful, and we're not sure who's acknowledging them, nor why the chip doesn't detect a disconnection event. This issue might be dependent on the nRF52840's data flash content (sorry, it's all a lot to test).
We're using the "v1.0.0" checkout version of "ncs", however it's termed (we had to revert due to the MQTT sample hanging). Our nRF Connect App is v4.22.3 according to the E-mail feedback subject line. We'd rather not use the SoftDevice as we like Zephyr for the most part, and it would just add more layers of complexity. Our relevant code can be seen below.
static struct bt_uuid_128 vnd_uuid=BT_UUID_INIT_128( 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); static struct bt_uuid_128 vnd_enc_uuid=BT_UUID_INIT_128( 0xF1, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); static const struct bt_data ad[]={ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL|BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12), }; static bool indicating=false; static u8_t simulate_vnd=0, valuething=0; static struct bt_conn *bt_found=NULL; static struct bt_gatt_indicate_params ind_params; static struct bt_gatt_ccc_cfg vnd_ccc_cfg[BT_GATT_CCC_MAX]={}; static void vnd_ccc_cfg_changed(const struct bt_gatt_attr *attr, u16_t value) { simulate_vnd=(value==BT_GATT_CCC_INDICATE)?1:0; } static void indicate_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, u8_t err) { sprintf(string, "Indication %s\r\n", err!=0?"fail":"success"); print(string); indicating=false; } static void connected(struct bt_conn *conn, u8_t err) { char addr[BT_ADDR_LE_STR_LEN]; if(err) { sprintf(string, "Connection failed (err %u)\r\n", err); print(string); } else { bt_found=conn; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); sprintf(string, "Connected to %s\r\n", addr); print(string); } } static void disconnected(struct bt_conn *conn, u8_t reason) { bt_found=NULL; sprintf(string, "Disconnected (reason %u)\r\n", reason); print(string); } static void bt_ready(int err) { if(err) { sprintf(string, "Bluetooth init failed (err %d)\r\n", err); print(string); return; } print("Bluetooth initialized\r\n"); if(IS_ENABLED(CONFIG_SETTINGS)) settings_load(); err=bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); if(err) { sprintf(string, "Advertising failed to start (err %d)\r\n", err); print(string); return; } print("Advertising successfully started\r\n"); } static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); sprintf(string, "Passkey for %s: %06u\r\n", addr, passkey); print(string); } static void auth_cancel(struct bt_conn *conn) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); sprintf(string, "Pairing cancelled: %s\r\n", addr); print(string); } static void auth_paired(struct bt_conn *conn, bool bonded) { sprintf(string, "Paired with %p\r\n", conn); print(string); } void bonded_cb(const struct bt_bond_info *info, void *user_data) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(&info->addr, addr, sizeof(addr)); sprintf(string, "Bonded with: %s\r\n", addr); print(string); } static struct bt_conn_cb conn_callbacks={ .connected=connected, .disconnected=disconnected, }; static struct bt_conn_auth_cb auth_cb_display={ .passkey_display=auth_passkey_display, .cancel=auth_cancel, .pairing_complete=auth_paired }; BT_GATT_SERVICE_DEFINE(vnd_svc, BT_GATT_PRIMARY_SERVICE(&vnd_uuid), BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid, BT_GATT_CHRC_INDICATE, 0, NULL, NULL, NULL), BT_GATT_CCC(vnd_ccc_cfg, vnd_ccc_cfg_changed), ); void main(void) { err=bt_enable(bt_ready); if(err) { sprintf(string, "Bluetooth init failed (err %d)\n", err); print(string); return; } bt_conn_cb_register(&conn_callbacks); bt_conn_auth_cb_register(&auth_cb_display); bt_passkey_set(123456); // bt_set_bondable(false); for(;;) { k_sleep(MSEC_PER_SEC); print(""); if((!indicating)&&(simulate_vnd)&&(bt_found)) { if(bt_conn_security(bt_found, BT_SECURITY_LOW)) { print("At least BT_SECURITY_LOW\r\n"); } if(bt_conn_security(bt_found, BT_SECURITY_MEDIUM)) { print("At least BT_SECURITY_MEDIUM\r\n"); } if(bt_conn_security(bt_found, BT_SECURITY_HIGH)) { print("At least BT_SECURITY_HIGH\r\n"); } bt_foreach_bond(BT_ID_DEFAULT, bonded_cb, NULL); ind_params.attr=&vnd_svc.attrs[2]; ind_params.func=indicate_cb; ind_params.data=&valuething; ind_params.len=sizeof(valuething); valuething++; if(bt_gatt_indicate(NULL, &ind_params)==0) indicating=true; } } }