LE Central
The LE central sample demonstrates how to use the LE GAP Central role to develop many different central-role based applications.
LE Central role features:
Scan for advertising events.
Initiate connection and become a Central role in the link.
Connect with more than one peripheral device.
The sample uses LE host and LE manager module. The above figure shows the relationships between several modules in the sample.
More information about these modules can be found in the LE Host and LE Manager documentation.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
Build Target |
---|---|---|
RTL87x3E HDK |
RTL87x3E EVB |
ble_central_4M_bank0 ble_central_16M_bank0 |
RTL87x3D HDK |
RTL87x3D EVB |
ble_central_8M_bank0 ble_central_16M_bank0 ble_central_cs_16M_bank0 |
This sample project can be found under board\evb\ble_central
in the SDK folder structure.
Developers can choose the project according to the Board Name
and choose the Build Target
according to the flash map.
When built for an xxx_4M_xxx
build target, the sample is configured to compile and run with a 4M flash map.
When built for an xxx_8M_xxx
build target, the sample is configured to compile and run with an 8M flash map.
When built for an xxx_16M_xxx
build target, the sample is configured to compile and run with a 16M flash map.
To quickly set up the development environment, please refer to the detailed instructions provided in Quick Start.
Wiring
Please refer to EVB Interfaces and Modules in Quick Start.
The sample requires support for user command interface. For specific wiring instructions, please refer to Data UART Connection in User Command Interface.
Configurations
Configurable Items
All contents that can be configured for the sample are in
src\sample\ble_central\app_central_flags.h
,
developers can configure according to actual needs.
/** @brief Config APP LE link number */
#define APP_MAX_LINKS 2
/** @brief Config GATT services storage: 0-Not save, 1-Save to flash
*
* If configured to 1, the GATT services discovery results will be saved to the flash.
*/
#define F_BT_GATT_SRV_HANDLE_STORAGE 0
Developers can open or close the GATT Service Handle Storage Module through the macro F_BT_GATT_SRV_HANDLE_STORAGE
. For more information, please refer to GATT Services Handle Storage in Code Overview.
Generating System Config File
Developers shall configure the following items through MCUConfig Tool:
Configurable Item |
Value |
---|---|
LE link number |
≥ APP_MAX_LINKS |
LE master link number |
≥ APP_MAX_LINKS |
For more information about MCUConfig Tool Configuration, please refer to MCUConfig Tool.
Building and Downloading
Take the project rtl87x3e_ble_central.uvprojx
and target ble_central_4M_bank0
as an example. To build and run the sample with Keil development environment, follow the steps listed below:
Open
rtl87x3e_ble_central.uvprojx
.Choose the build target
ble_central_4M_bank0
.Build the target.
After a successful build, the APP bin file
ble_central_bank0_MP-v0.0.0.0-xxx.bin
will be generated in the directorybin\rtl87x3e\flash_4M_dualbank\bank0
.Download the APP bin into the EVB.
Press the reset button on the EVB and it will start running.
Experimental Verification
After downloading the sample bin to the EVB, developers can test it by using another kit that is running the LE Peripheral sample.
Testing with Another Kit
Prepare two development boards named DUT and Tester respectively. Developers can capture SoC logs using Debug Analyzer.
Preparation Phase
Use MCUConfig Tool to set the DUT address to [00:11:22:33:44:85], and then build the LE central sample, and download images into DUT. Developers can enter the user command in the serial port assistant tool on PC. For more information, please refer to How to Use Commands in User Command Interface.
Use MCUConfig Tool to set the Tester address to [00:11:22:33:44:80], and then build the LE peripheral sample, and download images into Tester. For more information, please refer to Building and Downloading in LE Peripheral.
For details about how to change the Bluetooth Address, please refer to MCUConfig Tool.
Testing Phase
Press the reset button on Tester and Tester will start sending connectable undirected advertising events.
If the advertisement is successfully enabled, the following Debug Analyzer log will be printed. If developers don’t see the following log, it means that the advertisement failed to start. Please check if the software and hardware environment is configured correctly.
[APP] !**app_peripheral_adv_callback: BLE_EXT_ADV_MGR_ADV_ENABLED
Press the reset button on DUT and developers can enter the user command in the serial port assistant tool on PC.
If the DUT successfully boots up and the serial port assistant tool configuration is successful, developers will see the following log. The serial port assistant tool will display the local address. If developers don’t see the following log, please check if the software and hardware environment is configured correctly.
local bd addr: xx:xx:xx:xx:xx:xx
One link test flow is shown as below:
Step
DUT User Command
Description
DUT Log
1
scan 0
Start scanning and viewing information on LE device nearby discovered.
Debug Analyzer shows:
[APP] !**GAP scan start
[APP] app_scan_cb: BLE_SCAN_REPORT event_type 0x13, bd_addr 00::11::22::33::44::80, addr_type 0, rssi -17, data status 0x0
2
stopscan
Stop scanning.
Debug Analyzer shows:
[APP] !**GAP scan stop
3
showdev
Show scan device list, the device list is filtered by simple LE service UUID.
Serial port assistant tool shows:
Advertising and Scan response: filter uuid = 0xA00A dev list RemoteBd[0] = [00:11:22:33:44:80] type = 0
4
condev 0
Initiate connection.
Serial port assistant tool shows:
Connected success conn_id 0
5
showcon
Show connection information.
Serial port assistant tool shows:
ShowCon conn_id 0 state 0x00000002 role 1
RemoteBd = [00:11:22:33:44:80] type = 0
6
conupdreq 0 1
Initiate connection parameter update request.
Debug Analyzer shows:
[APP] !**app_handle_conn_param_update_evt update success:conn_id 0, conn_interval 0xa, conn_slave_latency 0x0, conn_supervision_timeout 0x3e8
7
sauth 0
Send authentication request.
Serial port assistant tool shows:
Pair success
8
basread 0 0
Read the value of battery level value.
Debug Analyzer shows:
[APP] !**”app_central_client_callback: BAS_READ_BATTERY_LEVEL battery level 90
9
gapread 0 0
Read the value of device name.
Debug Analyzer shows:
[APP] !**app_central_client_callback: GAPS_READ_DEVICE_NAME device name BLE_PERIPHERAL
10
disc 0
Disconnect with the peripheral device.
Serial port assistant tool shows:
Disconnect conn_id 0
Code Overview
The main purpose of this chapter is to help sample developers become familiar with the development process related to Central Role. This chapter will be introduced according to the following several parts:
The directories of the project and source code files will be introduced in chapter Source Code Directory.
The main function will be introduced in chapter Initialization.
The configurable GAP parameters of this sample will be introduced in chapter GAP Parameter Initialization.
The link control block will be introduced in chapter Multilink Manager.
The GAP message handler of this sample will be introduced in chapter GAP Message Handler.
The GAP callback handler of this sample will be introduced in chapter GAP Callback Handler.
The procedure of start scanning will be introduced in chapter Start Scanning.
The profile message callback of this sample will be introduced in chapter Profile Message Callback.
The procedure of discover GATT services will be introduced in chapter Discover GATT Services Procedure.
The GATT services handle storage will be introduced in chapter GATT Services Handle Storage.
Source Code Directory
Project directory:
board\evb\ble_central
.Source code directory:
src\sample\ble_central
.
Source files in the sample project are currently categorized into several groups as below:
└── ble_central_4M_bank0
├── include ROM UUID header files. Developers do not need to modify it.
├── lib Includes all binary symbol files that user application is built on.
├── gap_utils.lib
├── ROM.lib
├── upperstack_4M.lib
├── hal_utils.lib
├── ble_mgr.lib
└── sysm.lib
├── cmsis The cmsis source code. Developers do not need to modify it.
├── profile The LE profiles source code.
├── bas_client.c
├── gaps_client.c
└── simple_ble_client.c
├── app The application source code.
├── data_uart.c
├── user_cmd_parse.c
├── app_central_main.c Main entry
├── app_central_gap.c LE GAP initialization and message handler
├── app_central_client.c LE profile client role initialization
├── app_central_link_mgr.c LE link utility
├── app_central_user_cmd.c User command
├── app_central_scan.c Scan manager
└── app_central_task.c APP task
└── io_hal
Initialization
main()
function is invoked when the EVB is powered on and the chip boots up,
and it performs the following initialization functions:
int main(void)
{
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_gap_init();
app_central_client_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
le_gap_init()
function is used to initialize GAP and configure link number.app_central_client_init()
function is used to initialize Profile.app_gap_init()
function is used to initialize the GAP parameters.
GAP Parameter Initialization
The app_gap_init()
function is used to initialize the GAP parameters.
Developers can easily customize the sample by modifying the following parameter values.
void app_gap_init(void)
{
/* Device name and device appearance */
uint8_t device_name[GAP_DEVICE_NAME_LEN] = "BLE_CENTRAL";
uint16_t appearance = GAP_GATT_APPEARANCE_UNKNOWN;
le_set_gap_param(GAP_PARAM_DEVICE_NAME, GAP_DEVICE_NAME_LEN, device_name);
le_set_gap_param(GAP_PARAM_APPEARANCE, sizeof(appearance), &appearance);
/* Use LE Advertising Extensions */
uint8_t use_extended = true;
le_set_gap_param(GAP_PARAM_USE_EXTENDED_ADV, sizeof(use_extended), &use_extended);
/* Setup the GAP Bond Manager */
gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode);
gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags);
gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap);
gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey),
&auth_use_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags),
&auth_sec_req_flags);
/* register GAP message callback */
le_register_app_cb(app_gap_callback);
/* ble manager module initialize*/
app_gap_ble_mgr_init();
}
-
Configure device name with the parameter
GAP_PARAM_DEVICE_NAME
.Configure appearance with the parameter
GAP_PARAM_APPEARANCE
.Configure the sample to use LE Advertising Extensions with the parameter
GAP_PARAM_USE_EXTENDED_ADV
.
gap_set_param()
andle_bond_set_param()
Initialize GAP pairing parameters.
-
Register the GAP message callback function
app_gap_callback()
, and all GAP callback messages will be handled in this callback. app_gap_ble_mgr_init()
Initialize the LE manager library. More information on LE manager library initialization can be found in the chapter LE Manager Initialization of LE Manager.
void app_gap_ble_mgr_init(void) { BLE_MGR_PARAMS param = {0}; param.ble_scan.enable = true; ble_mgr_init(¶m); }
More information on LE GAP initialization and startup flow can be found in the chapter GAP Parameters Initialization of LE Host.
Multilink Manager
Link control block is defined in app_central_link_mgr.h
.
typedef struct
{
T_GAP_CONN_STATE conn_state; /**< Connection state. */
uint8_t discovered_flags; /**< discovered flags. */
uint8_t srv_found_flags; /**< service found flags. */
T_GAP_REMOTE_ADDR_TYPE bd_type; /**< remote BD type*/
uint8_t bd_addr[GAP_BD_ADDR_LEN]; /**< remote BD */
} T_APP_LINK;
extern T_APP_LINK app_link_table[APP_MAX_LINKS];
app_link_table
is used to save the connection link related information.
GAP Message Handler
app_handle_gap_msg()
function is invoked whenever a GAP message is received from the Bluetooth Host.
More information on GAP messages can be found in the chapter Bluetooth LE GAP Message of LE Host.
When the sample handles GAP message, the ble_mgr_handle_gap_msg()
shall be called to handle the GAP message.
void app_handle_gap_msg(T_IO_MSG *p_gap_msg)
{
T_LE_GAP_MSG gap_msg;
uint8_t conn_id;
memcpy(&gap_msg, &p_gap_msg->u.param, sizeof(p_gap_msg->u.param));
ble_mgr_handle_gap_msg(p_gap_msg->subtype, &gap_msg);
APP_PRINT_TRACE1("app_handle_gap_msg: subtype %d", p_gap_msg->subtype);
switch (p_gap_msg->subtype)
{
case GAP_MSG_LE_DEV_STATE_CHANGE:
{
app_handle_dev_state_evt(gap_msg.msg_data.gap_dev_state_change.new_state,
gap_msg.msg_data.gap_dev_state_change.cause);
}
break;
......
default:
APP_PRINT_ERROR1("app_handle_gap_msg: unknown subtype %d", p_gap_msg->subtype);
break;
}
}
GAP Callback Handler
app_gap_callback()
function is used to handle GAP callback messages.
More information on GAP callback can be found in the chapter Bluetooth LE GAP Callback of LE Host.
When the sample handles GAP callback, the ble_mgr_handle_gap_cb()
shall be called to handle the callback message.
T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data;
ble_mgr_handle_gap_cb(cb_type, p_cb_data);
......
}
Start Scanning
ble_scan_start()
function is used to start scanning.
If the device starts extended scanning and receives advertising data or scan response data, the LE manager library will use BLE_SCAN_REPORT
message to inform sample. Sample code is listed as below:
void app_scan_start(uint16_t scan_interval, uint16_t scan_window)
{
BLE_SCAN_PARAM param;
BLE_SCAN_FILTER scan_filter;
APP_PRINT_TRACE2("app_scan_start: scan_interval 0x%x, scan_window 0x%x",
scan_interval, scan_window);
memset(¶m, 0, sizeof(param));
memset(&scan_filter, 0, sizeof(scan_filter));
param.scan_param_1m.scan_type = GAP_SCAN_MODE_ACTIVE;
param.scan_param_1m.scan_interval = scan_interval;
param.scan_param_1m.scan_window = scan_window;
param.scan_param_coded.scan_type = GAP_SCAN_MODE_ACTIVE;
param.scan_param_coded.scan_interval = scan_interval;
param.scan_param_coded.scan_window = scan_window;
param.ext_filter_duplicate = GAP_SCAN_FILTER_DUPLICATE_DISABLE;
param.ext_filter_policy = GAP_SCAN_FILTER_ANY;
param.own_addr_type = GAP_LOCAL_ADDR_LE_PUBLIC;
param.phys = GAP_EXT_SCAN_PHYS_1M_BIT;
scan_filter.filter_flags = BLE_SCAN_FILTER_ADV_DATA_BIT;
scan_filter.ad_len = 0x03;
scan_filter.ad_struct = filter_ad_struct;
ble_scan_start(&app_scan_hdl, app_scan_cb, ¶m, &scan_filter);
}
static void app_scan_cb(BLE_SCAN_EVT evt, BLE_SCAN_EVT_DATA *data)
{
uint8_t scan_state = ble_scan_get_cur_state();
switch (evt)
{
case BLE_SCAN_REPORT:
APP_PRINT_INFO6("app_scan_cb: BLE_SCAN_REPORT event_type 0x%x, bd_addr %s, addr_type %d, rssi %d, data_len %d, data_status 0x%x",
data->report->event_type,
TRACE_BDADDR(data->report->bd_addr),
data->report->addr_type,
data->report->rssi,
data->report->data_len,
data->report->data_status);
link_mgr_add_device(data->report->bd_addr, data->report->addr_type);
break;
default:
break;
}
}
Profile Message Callback
When xxx_add_client() is used to register a specific client, The callback function shall be registered to handle the message from the specific client.
Developers can register different callback functions to handle messages from different clients or register the general callback function to handle all the messages from specific clients.
app_central_client_callback()
function is the general callback function. app_central_client_callback()
can distinguish different clients by client ID.
More information can be found in the chapter GATT Profile Client of LE Host.
void app_central_client_init(void)
{
client_init(3);
gaps_client_id = gaps_add_client(app_central_client_callback, APP_MAX_LINKS);
simple_ble_client_id = simp_ble_add_client(app_central_client_callback, APP_MAX_LINKS);
bas_client_id = bas_add_client(app_central_client_callback, APP_MAX_LINKS);
}
GAP service client
gaps_client_id
is the client ID of GAP service client.
T_APP_RESULT app_central_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
APP_PRINT_INFO2("app_central_client_callback: client_id %d, conn_id %d",
client_id, conn_id);
if (client_id == gaps_client_id)
{
T_GAPS_CLIENT_CB_DATA *p_gaps_cb_data = (T_GAPS_CLIENT_CB_DATA *)p_data;
switch (p_gaps_cb_data->cb_type)
{
case GAPS_CLIENT_CB_TYPE_DISC_STATE:
......
}
}
}
Battery Service Client
bas_client_id
is the client ID of battery service client.
T_APP_RESULT app_central_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
APP_PRINT_INFO2("app_central_client_callback: client_id %d, conn_id %d",
client_id, conn_id);
else if (client_id == bas_client_id)
{
T_BAS_CLIENT_CB_DATA *p_bas_cb_data = (T_BAS_CLIENT_CB_DATA *)p_data;
switch (p_bas_cb_data->cb_type)
{
case BAS_CLIENT_CB_TYPE_DISC_STATE:
......
}
}
}
Simple LE Service Client
T_APP_RESULT app_central_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
APP_PRINT_INFO2("app_central_client_callback: client_id %d, conn_id %d",
client_id, conn_id);
else if (client_id == simple_ble_client_id)
{
T_SIMP_CLIENT_CB_DATA *p_simp_client_cb_data = (T_SIMP_CLIENT_CB_DATA *)p_data;
uint16_t value_size;
uint8_t *p_value;
switch (p_simp_client_cb_data->cb_type)
{
case SIMP_CLIENT_CB_TYPE_DISC_STATE:
......
}
}
}
Discover GATT Services Procedure
The central sample will automatically discover services after receiving the message GAP_MSG_LE_CONN_MTU_INFO
. Discover GATT services procedure is defined in app_central_client_discov_services()
. More information can be found in app_central_client_discov_services()
in app_central_gap.c
.
void app_handle_conn_mtu_info_evt(uint8_t conn_id, uint16_t mtu_size)
{
APP_PRINT_INFO2("app_handle_conn_mtu_info_evt: conn_id %d, mtu_size %d", conn_id, mtu_size);
app_central_client_discov_services(conn_id, true);
}
The sample will call app_central_client_callback()
when it receives DISC_GAPS_DONE
, DISC_SIMP_DONE
, and DISC_BAS_DONE
. Reference code is shown as below:
T_APP_RESULT app_central_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data)
{
......
case DISC_GAPS_DONE:
app_link_table[conn_id].discovered_flags |= APP_DISCOV_GAPS_FLAG;
app_link_table[conn_id].srv_found_flags |= APP_DISCOV_GAPS_FLAG;
app_discov_services(conn_id, false);
/* Discovery Simple LE service procedure successfully done. */
APP_PRINT_INFO0("app_central_client_callback: discover gaps procedure done.");
break;
......
case DISC_SIMP_DONE:
/* Discovery Simple LE service procedure successfully done. */
app_link_table[conn_id].discovered_flags |= APP_DISCOV_SIMP_FLAG;
app_link_table[conn_id].srv_found_flags |= APP_DISCOV_SIMP_FLAG;
app_discov_services(conn_id, false);
APP_PRINT_INFO0("app_central_client_callback: discover simp procedure done.");
break;
......
case DISC_BAS_DONE:
/* Discovery BAS procedure successfully done. */
app_link_table[conn_id].discovered_flags |= APP_DISCOV_BAS_FLAG;
app_link_table[conn_id].srv_found_flags |= APP_DISCOV_BAS_FLAG;
app_discov_services(conn_id, false);
APP_PRINT_INFO0("app_central_client_callback: discover bas procedure done");
break;
......
}
GATT Services Handle Storage
Reference code is shown as below:
typedef struct
{
uint8_t srv_found_flags;
uint8_t bd_type; /**< remote BD type*/
uint8_t bd_addr[GAP_BD_ADDR_LEN]; /**< remote BD */
uint32_t reserved;
uint16_t gaps_hdl_cache[HDL_GAPS_CACHE_LEN];
uint16_t simp_hdl_cache[HDL_SIMBLE_CACHE_LEN];
uint16_t bas_hdl_cache[HDL_BAS_CACHE_LEN];
} T_APP_SRVS_HDL_TABLE;
#if F_BT_GATT_SRV_HANDLE_STORAGE
uint32_t app_save_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info);
uint32_t app_load_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info);
#endif
app_save_srvs_hdl_table()
is used to save the information to flash.app_load_srvs_hdl_table()
is used to load the information from flash.