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