This blog is to use Nordic nRF52840 as the BLE keyboard with multiple peripheral role. Those keyboard would connect to multiple mobile (BLE central) for demo.
I would use the HOGP Keyboard example on this blog.
HID over GATT Profile Specification (HOGP)
This profile requires the Generic Attribute Profile (GATT), the Battery Service, the Device Information Service, and the Scan Parameters Profile.
This specification can be used with Bluetooth Core Specification Version 4.0 or later.


Nordic nRF5 SDK
The nRF5 SDK provides a rich developing environment for nRF5 Series devices by including a broad selection of drivers, libraries, examples for peripherals, SoftDevices, and proprietary radio protocols.
The SDK is delivered as a plain .zip-archive, which makes it easy to install as well as giving you the freedom to choose the IDE and compiler of your choice.
All code examples included in the SDK are tailored to compile for and run on Nordic Semiconductor’s nRF5 Development Kits.
To download the nRF5 SDK, go to www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF5-SDK.
HID Keyboard Application
All provided HID examples conform with USB HID usage tables, http://www.usb.org/developers/hidpage.
https://infocenter.nordicsemi.com/topic/sdk_nrf5_v16.0.0/ble_sdk_app_hids_keyboard.html
The HID Keyboard Application is an example that implements the HID over GATT profile for keyboard using the hardware delivered in the nRF5 Development Kit.
The application includes the three mandatory services needed for the HID over GATT profile:
How to create BLE Dual Peripheral role
- Configure the multiple link inside the sdk_config.h

Initialize the BLE Stack
- add the number link configuration during the ble_stack_init

Modify the RAM configuration if using the Segger Embedded Studio
- direct modify the project file (ble_app_hids_keyboard_pca10056_s140.emProject)

GATT Server Table
Inside Multiple BLE Peripheral, it consists of the Battery services, Device Information Service and HID services.

Use the BLE Connection State module to get all the link status.
For example on the ble_evt_handler, it needs to store all the links status and do the advertising if need.
/**@brief Function for handling the Connected event.
*
* @param[in] p_gap_evt GAP event received from the BLE stack.
*/
static void on_connected(const ble_gap_evt_t * const p_gap_evt)
{
ret_code_t err_code;
uint32_t periph_link_cnt = ble_conn_state_peripheral_conn_count(); // Number of peripheral links.
NRF_LOG_INFO("Connection with link 0x%x established.", p_gap_evt->conn_handle);
// Assign connection handle to available instance of QWR module.
for (uint32_t i = 0; i < NRF_SDH_BLE_PERIPHERAL_LINK_COUNT; i++)
{
if (m_qwr[i].conn_handle == BLE_CONN_HANDLE_INVALID)
{
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr[i], p_gap_evt->conn_handle);
APP_ERROR_CHECK(err_code);
break;
}
}
err_code = app_button_enable();
APP_ERROR_CHECK(err_code);
// Update LEDs
bsp_board_led_on(CONNECTED_LED);
if (periph_link_cnt == NRF_SDH_BLE_PERIPHERAL_LINK_COUNT)
{
bsp_board_led_off(ADVERTISING_LED);
}
else
{
// Continue advertising. More connections can be established because the maximum link count has not been reached.
//advertising_start();
advertising_start(false);
}
}
/**@brief Function for handling the Disconnected event.
*
* @param[in] p_gap_evt GAP event received from the BLE stack.
*/
static void on_disconnected(ble_gap_evt_t const * const p_gap_evt)
{
ret_code_t err_code;
uint32_t periph_link_cnt = ble_conn_state_peripheral_conn_count(); // Number of peripheral links.
NRF_LOG_INFO("Connection 0x%x has been disconnected. Reason: 0x%X",
p_gap_evt->conn_handle,
p_gap_evt->params.disconnected.reason);
if (periph_link_cnt == 0)
{
bsp_board_led_off(CONNECTED_LED);
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
}
if (periph_link_cnt == (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - 1))
{
NRF_LOG_INFO("on_disconnected peripheral link cnt = %d", periph_link_cnt);
// Advertising is not running when all connections are taken, and must therefore be started.
advertising_start(false);
}
}
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected");
// err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
// APP_ERROR_CHECK(err_code);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
// err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
// APP_ERROR_CHECK(err_code);
on_connected(&p_ble_evt->evt.gap_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected");
// Dequeue all keys without transmission.
(void) buffer_dequeue(false, p_ble_evt->evt.gap_evt.conn_handle);
on_disconnected(&p_ble_evt->evt.gap_evt);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
// Reset m_caps_on variable. Upon reconnect, the HID host will re-send the Output
// report containing the Caps lock state.
m_caps_on = false;
// disabling alert 3. signal - used for capslock ON
// err_code = bsp_indication_set(BSP_INDICATE_ALERT_OFF);
// APP_ERROR_CHECK(err_code);
break; // BLE_GAP_EVT_DISCONNECTED
How to test with two difference Mobile phones
- Bonding with Mobile through bluetooth setting menu
- Press the button 1 or button 2 to send the keyboard data to mobile.


Here is the demo to use the nRF52840 DK to play as BLE multiple peripheral role x 4 and connect to 4 difference mobile phones.
Please leave the message to me if you would like to get such demo code.
Thanks for your interests on my blog. Since 2019, I have created this blog and shared the idea how to do some funny stuffs. I am very pleasure that I get quite a lot of positive feedback. I really hope that this blog helps your own embedded solution development. May I get support from you to keep it in order to maintain the wordpress host service? Your appreciation would be very helpful.
https://jimmywongiot.com/2021/05/26/asking-for-support/

Hey Jimmy, thank you for writing the blog on this, amazing !. I aim to try it on a nRF52840 dongle so if it would be possible I would love to test your demo code.
Thank you for your consideration.
LikeLike
Hey Jimmy,Thank you for your article. Can you send me a reference to see the program?
Can you send me a program?
LikeLike
I aim to try it on a nRF52840 dongle so if it would be possible I would love to test your demo code.
Thank you for your consideration.
LikeLike
What values did you use for the memory map?
I need to increase the ram for my application and I don’t know how to do it.
LikeLike
You can find the material at
https://devzone.nordicsemi.com/nordic/short-range-guides/b/getting-started/posts/adjustment-of-ram-and-flash-memory
in my case, I am using
linker_section_placement_macros=”FLASH_PH_START=0x0;FLASH_PH_SIZE=0x100000;RAM_PH_START=0x20000000;RAM_PH_SIZE=0x40000;FLASH_START=0x27000;FLASH_SIZE=0xd9000;RAM_START=0x20006000;RAM_SIZE=0x3A000″
LikeLike
Like!! I blog frequently and I really thank you for your content. The article has truly peaked my interest.
LikeLike
Thank you for your good article.Can you send me a demo code??
LikeLike
Hey Jimmy, we would like to get your demo code for a university project. We have some troubles integrating your part in our code to connect multiple devices.
LikeLike
Please send an email on this request.
LikeLike
Thank you for your good article.
Can you send me a demo code??
LikeLike
Hi Jlee,
You can use the example from the HID keyboard in the nRF5 SDK and modify the multiple peripheral.
LikeLike