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

Peer Manager disconnect after connection with NRF52 and Android 7.0

I'm attempting to use Peer Manager with my nRF52832 module as a peripheral with an Android 7.0 phone as the central device.

My program is a mix between the NUS example and the Template (for the PM side). What I'm experiencing is this:

On startup, I see "Nordic UART" advertising. On my Android device if I use the System Bluetooth icon and click "Pair" I get the following printouts:

  • PM_EVT_CONN_SEC_START
  • Data len is set to 0x3D(61)
  • BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST
  • Connection secured: role: 1, conn_handle: 0x0, procedure: 1
  • Disconnected: Reason 13 (BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)

I use the Nrf Toolbox app (UART tab) and I attempt to connect after I think I've bonded. I get "Error: (0x85): GATT ERROR)"

However, If I use the Nrf Toolbox app before 'pairing' or 'bonding' with Android I successfully connect, discover services, send/receive UART data, etc... The difference is I'm not 'pairing' or 'bonding' in this scenario, I'm simply connecting.

  • Connected
  • Data len is set to 0x3D(61)
  • BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST
  • Note: No disconnect

It seems as if my 'disconnect after connect' is only occurring when I'm attempting to 'bond'.

Thoughts?

Relevant source code is below.

#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 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                                         /**< Maximum encryption key size. */

....

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
		ble_conn_state_on_ble_evt(p_ble_evt);
		pm_on_ble_evt(p_ble_evt);
		nrf_ble_gatt_on_ble_evt(&m_gatt, p_ble_evt);
		ble_advertising_on_ble_evt( p_ble_evt);
		on_ble_evt(p_ble_evt);
		ble_conn_params_on_ble_evt(p_ble_evt);
    ble_nus_on_ble_evt(&m_nus, p_ble_evt);
	
}

...

static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    uint32_t err_code;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
						ButtonAndLEDController_indicateConnected();
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;						
            printf("Connected\r\n");

						advertising_state =  ADVERT_STATE_IDLE;
            break; // BLE_GAP_EVT_CONNECTED

        case BLE_GAP_EVT_DISCONNECTED:
						ButtonAndLEDController_indicateDisconnected();
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
						printf("Disconnected: %x\r\n", p_ble_evt->evt.gap_evt.params.disconnected.reason);
						advertising_state =  ADVERT_STATE_IDLE;
						mDeviceIsConnected = false;
						
						Bluetooth_StartAdvertising();
				
            break; // BLE_GAP_EVT_DISCONNECTED

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
						printf("BLE_GAP_EVT_SEC_PARAMS_REQUEST\r\n");
					  break; 

         case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
        {
						printf("BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST\r\n");
            ble_gap_data_length_params_t dl_params;

            // Clearing the struct will effectivly set members to @ref BLE_GAP_DATA_LENGTH_AUTO
            memset(&dl_params, 0, sizeof(ble_gap_data_length_params_t));
            err_code = sd_ble_gap_data_length_update(p_ble_evt->evt.gap_evt.conn_handle, &dl_params, NULL);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
					printf("BLE_GATTS_EVT_SYS_ATTR_MISSING\r\n");
            // No system attributes have been stored.
            err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTS_EVT_SYS_ATTR_MISSING

        case BLE_GATTC_EVT_TIMEOUT:
					printf("BLE_GATTC_EVT_TIMEOUT\r\n");
            // Disconnect on GATT Client timeout event.
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTC_EVT_TIMEOUT

        case BLE_GATTS_EVT_TIMEOUT:
					printf("BLE_GATTS_EVT_TIMEOUT\r\n");
            // Disconnect on GATT Server timeout event.
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break; // BLE_GATTS_EVT_TIMEOUT

        case BLE_EVT_USER_MEM_REQUEST:
					printf("BLE_EVT_USER_MEM_REQUEST\r\n");
            err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
            APP_ERROR_CHECK(err_code);
            break; // BLE_EVT_USER_MEM_REQUEST

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
					printf("BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST\r\n");
            ble_gatts_evt_rw_authorize_request_t  req;
            ble_gatts_rw_authorize_reply_params_t auth_reply;

            req = p_ble_evt->evt.gatts_evt.params.authorize_request;

            if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
            {
                if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                {
                    if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                    }
                    else
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                    }
                    auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                               &auth_reply);
                    APP_ERROR_CHECK(err_code);
                }
            }
        } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

        default:
            // No implementation needed.
            break;
    }
}

...

static void pm_evt_handler(pm_evt_t const * p_evt)
{
    ret_code_t err_code;

    switch (p_evt->evt_id)
    {
        case PM_EVT_BONDED_PEER_CONNECTED:
        {
            printf("Connected to a previously bonded device.\r\n");
						err_code = pm_peer_rank_highest(p_evt->peer_id);
						mDeviceIsConnected = true;
        } break;

        case PM_EVT_CONN_SEC_SUCCEEDED:
        {
            printf("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.\r\n",
                         ble_conn_state_role(p_evt->conn_handle),
                         p_evt->conn_handle,
                         p_evt->params.conn_sec_succeeded.procedure);
					err_code = pm_peer_rank_highest(p_evt->peer_id);
					mDeviceIsConnected = true;
					
        } break;

        case PM_EVT_CONN_SEC_FAILED:
        {
            /* Often, when securing fails, it shouldn't be restarted, for security reasons.
             * Other times, it can be restarted directly.
             * Sometimes it can be restarted, but only after changing some Security Parameters.
             * Sometimes, it cannot be restarted until the link is disconnected and reconnected.
             * Sometimes it is impossible, to secure the link, or the peer device does not support it.
             * How to handle this error is highly application dependent. */
					printf("PM_EVT_CONN_SEC_FAILED (error: %d, error_src: %d, procedure: %d)\r\n", p_evt->params.conn_sec_failed.error, p_evt->params.conn_sec_failed.error_src, p_evt->params.conn_sec_failed.procedure);
					switch (p_evt->params.conn_sec_failed.error)
					{
							case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING:
									// Rebond if one party has lost its keys.
									err_code = pm_conn_secure(p_evt->conn_handle, true);
									if (err_code != NRF_ERROR_INVALID_STATE)
									{
											APP_ERROR_CHECK(err_code);
									}
									break;//PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING
							default:
									break;
					}
        } break;

        case PM_EVT_CONN_SEC_CONFIG_REQ:
        {
						printf("PM_EVT_CONN_SEC_CONFIG_REQ\r\n");
            // Reject pairing request from an already bonded peer.
            pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};
            pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
        } break;

        case PM_EVT_STORAGE_FULL:
        {
						printf("PM_EVT_STORAGE_FULL\r\n");
            // Run garbage collection on the flash.
            err_code = fds_gc();
            if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
            {
                // Retry.
            }
            else
            {
                APP_ERROR_CHECK(err_code);
            }
        } break;

        case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
        {
					printf("PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED\r\n");
            // The local database has likely changed, send service changed indications.
            pm_local_database_has_changed();
        } break;

        case PM_EVT_PEER_DATA_UPDATE_FAILED:
        {
						printf("PM_EVT_PEER_DATA_UPDATE_FAILED\r\n");
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);
        } break;

        case PM_EVT_PEER_DELETE_FAILED:
        {
						printf("PM_EVT_PEER_DELETE_FAILED\r\n");
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
        } break;

        case PM_EVT_PEERS_DELETE_FAILED:
        {					
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
        } break;

        case PM_EVT_ERROR_UNEXPECTED:
        {
						printf("PM_EVT_ERROR_UNEXPECTED\r\n");
            // Assert.
            APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
        } break;

        case PM_EVT_CONN_SEC_START:
					printf("PM_EVT_CONN_SEC_START\r\n");
					break;
        case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
        case PM_EVT_PEER_DELETE_SUCCEEDED:
        case PM_EVT_PEERS_DELETE_SUCCEEDED:
        case PM_EVT_LOCAL_DB_CACHE_APPLIED:
        case PM_EVT_SERVICE_CHANGED_IND_SENT:
        case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
        default:
            break;
    }
}

...

static void peer_manager_init(bool erase_bonds)
{
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    if (erase_bonds)
    {
        err_code = pm_peers_delete();
        APP_ERROR_CHECK(err_code);
    }

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = fds_register(fds_evt_handler);
    APP_ERROR_CHECK(err_code);
}
Parents
  • In the HCI Log i'm getting a successful connection, pairing, encryption, etc... but I'm getting "Bluetooth Attribute Protocol error, Attribute Not Found for Nordic Uart Service (blah blah)" I'm using the generic application on my phone to do the pairing, not something that that would support the Nordic Uart Service. Shouldn't Android simply be able to bond with the device and show me the services it found?

Reply
  • In the HCI Log i'm getting a successful connection, pairing, encryption, etc... but I'm getting "Bluetooth Attribute Protocol error, Attribute Not Found for Nordic Uart Service (blah blah)" I'm using the generic application on my phone to do the pairing, not something that that would support the Nordic Uart Service. Shouldn't Android simply be able to bond with the device and show me the services it found?

Children
No Data
Related