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);
}
Related