nRF5 SDK v11.0.0
Usage

The following code examples show the typical usage of the Peer Manager in an application.

Initializing

Initializing the Peer Manager typically consists of three steps:

  1. Call pm_init once to initialize the module.
  2. Optionally, call pm_sec_params_set to set the security parameters. If you do not call this function, pairing and bonding is not supported. See Security parameters.
  3. Subscribe to the Peer Manager events by calling pm_register.

The following code example shows how the Peer Manager is initialized in the Experimental: BLE Relay Example:

static void peer_manager_init(bool erase_bonds)
{
ret_code_t err_code;
err_code = pm_init();
APP_ERROR_CHECK(err_code);
if (erase_bonds)
{
}
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.lesc = SEC_PARAM_LESC;
sec_param.keypress = SEC_PARAM_KEYPRESS;
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);
}

Security parameters

The function pm_sec_params_set configures how the Peer Manager behaves when securing the link, thus it configures bonding, pairing, and encryption. The configuration is given by security parameters (ble_gap_sec_params_t). These security parameters are also used in the BLE Device Manager and directly in the SoftDevice security API and contain the parameters that are sent over-the-air during the bonding procedure. See the Bluetooth Core Specification (sections 3.H.3.5.1 and 3.H.3.5.2) for more information.

pm_sec_params_set rejects invalid security parameters. See the mentioned Bluetooth specification sections or the verification function in the Peer Manager source code for the constraints on the parameters.

The following list shows the required security parameters for common use cases:

Event handling

How to handle Peer Manager events depends on the application. The Peer Manager provides different kinds of events; some must be handled, while others can be disregarded.

The following code example provides a starting point for handling Peer Manager events:

static void peer_manager_event_handler(pm_evt_t const * p_evt)
{
ret_code_t err_code;
switch(p_evt->evt_id)
{
// Update the rank of the peer.
err_code = pm_peer_rank_highest(p_evt->peer_id);
break;//PM_EVT_BONDED_PEER_CONNECTED
break;//PM_EVT_CONN_SEC_START
// Update the rank of the peer.
err_code = pm_peer_rank_highest(p_evt->peer_id);
break;//PM_EVT_CONN_SEC_SUCCEEDED
// In some cases, when securing fails, 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.
switch (p_evt->params.conn_sec_failed.error)
{
// 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;//PM_EVT_CONN_SEC_FAILED
{
// A connected peer (central) is trying to pair, but the Peer Manager already has a bond
// for that peer. Setting allow_repairing to false rejects the pairing request.
// If this event is ignored (pm_conn_sec_config_reply is not called in the event
// handler), the Peer Manager assumes allow_repairing to be false.
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
}break;//PM_EVT_CONN_SEC_CONFIG_REQ
// 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;//PM_EVT_STORAGE_FULL
// Assert.
break;//PM_EVT_ERROR_UNEXPECTED
break;//PM_EVT_PEER_DATA_UPDATE_SUCCEEDED
// Assert.
break;//PM_EVT_PEER_DATA_UPDATE_FAILED
break;//PM_EVT_PEER_DELETE_SUCCEEDED
// Assert.
break;//PM_EVT_PEER_DELETE_FAILED
// At this point it is safe to start advertising or scanning.
break;//PM_EVT_PEERS_DELETE_SUCCEEDED
// Assert.
break;//PM_EVT_PEERS_DELETE_FAILED
break;//PM_EVT_LOCAL_DB_CACHE_APPLIED
// The local database has likely changed, send service changed indications.
break;//PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED
break;//PM_EVT_SERVICE_CHANGED_IND_SENT
break;//PM_EVT_SERVICE_CHANGED_IND_CONFIRMED
}
}

Storing data

The Peer Manager stores and retrieves data autonomously and does not require you to manually store any data. However, if you want to manually change, add, or remove data, the Peer Manager provides API functions to manipulate all data that is associated with its bonded peers.

The data is stored in chunks. For example, all bonding data (keys and identities) is stored together. A chunk cannot be partially stored or updated, but each chunk can be stored or updated independently of the other chunks. The only restrictions are that there must always be valid bonding data for a peer in flash and that there is only one instance of each chunk for each bonded peer. Two of the chunks, the remote GATT database and the application data, are not used internally by the Peer Manager. They are solely meant to be used through the Peer Manager API.

The following code example shows how to store a remote GATT database (in array_of_services). Note that array_of_services must be available for the duration of the (asynchronous) store operation. The store operation is finished when the event PM_EVT_PEER_DATA_UPDATE_SUCCEEDED or PM_EVT_PEER_DATA_UPDATE_FAILED is received.

ret_code_t err_code;
pm_store_token_t store_token;
err_code = pm_peer_data_remote_db_store(peer_id, array_of_services, number_of_services, &store_token);
if (err_code != NRF_ERROR_BUSY)
{
APP_ERROR_CHECK(err_code);
}

pm_peer_data_remote_db_store, as well as pm_peer_data_bonding_store and pm_peer_data_app_data_store, call pm_peer_data_store. pm_peer_data_store can also be used directly, as in the following example:

ret_code_t err_code;
pm_store_token_t store_token;
err_code = pm_peer_data_store(peer_id, PM_PEER_DATA_ID_GATT_REMOTE, array_of_services, number_of_services, &store_token);
if (err_code != NRF_ERROR_BUSY)
{
APP_ERROR_CHECK(err_code);
}

Making a whitelist

The Peer Manager must be aware of any whitelist that is being used. Therefore, when a whitelist is needed, use the pm_whitelist_create function in Peer Manager to construct a whitelist out of the stored peer bonding data. The following example shows how to use pm_whitelist_create to construct a whitelist based on the first (up to 8) bonded peers, and how to apply it when using Advertising Module:

static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
switch (ble_adv_evt)
{
...
{
ret_code_t err_code;
// Storage for the whitelist.
ble_gap_irk_t * irks[8];
ble_gap_addr_t * addrs[8];
ble_gap_whitelist_t whitelist = {.pp_irks = irks, .pp_addrs = addrs};
// Construct a list of peer IDs.
pm_peer_id_t peer_ids[8];
uint32_t n_peer_ids = 0;
while((peer_id != PM_PEER_ID_INVALID) && (n_peer_ids < 8))
{
peer_ids[n_peer_ids++] = peer_id;
peer_id = pm_next_peer_id_get(peer_id);
}
// Create the whitelist.
err_code = pm_whitelist_create(peer_ids, n_peer_ids, &whitelist);
APP_ERROR_CHECK(err_code);
// Apply the whitelist.
err_code = ble_advertising_whitelist_reply(&whitelist);
APP_ERROR_CHECK(err_code);
}
...
}
}

Documentation feedback | Developer Zone | Updated