本文描述为nrf52810设备添加以下属性服务:
- 电池服务
- 设备信息服务
- 一个触摸按键属性(附加在 uart tx/rx服务中)
开发环境准备
- SDK:
nRF5_SDK_15.3.0_59ac345.zip - 选择工程:
nRF5_SDK_15.3.0\examples\ble_peripheral\ble_app_uart\pca10040
添加电池服务
- 在 sdk_config.h 中 打开宏:
#define BLE_BAS 1
- 在 main.c 找到函数 services_init,添加如下代码:
ble_bas_init_t bas_init;
// Initialize BAS.
memset(&bas_init, 0, sizeof(bas_init));
// Here the sec level for the Battery Service can be changed/increased.
bas_init.bl_cccd_wr_sec = SEC_OPEN;
bas_init.bl_rd_sec = SEC_OPEN;
bas_init.bl_report_rd_sec = SEC_OPEN;
bas_init.evt_handler = NULL;
bas_init.support_notification = true;
bas_init.p_report_ref = NULL;
bas_init.initial_batt_level = 100;
err_code = ble_bas_init(&m_bas, &bas_init);
APP_ERROR_CHECK(err_code);
- 创建一个app timer 周期性地向客户端播报电池电量:
在ble_bas.c中找到函数:
ret_code_t ble_bas_battery_level_update(ble_bas_t * p_bas,
uint8_t battery_level,
uint16_t conn_handle)
创建 app 周期性任务调度,5秒一次:
app_timer_create(&task_5s_id, APP_TIMER_MODE_REPEATED, task_5s_timer_handler);
app_timer_start(task_5s_id, APP_TIMER_TICKS(5000), NULL);//定时5s
static void task_5s_timer_handler(void *p_context)
{
ble_bas_battery_level_update(&m_bas, bat_level, m_conn_handle);
}
其中 bat_level 的值是通过ADC测量到的电池曲线,然后转换成电池百分比。
添加设备信息服务
- 在 sdk_config.h 中 打开宏,并定义属性服务字段宏:
#define BLE_DIS 1
//属性服务字段宏
#define MANUFACTURER_NAME "XXXXX"
#define MODEL "MMMMM"
#define HW_VER "HW" /**< Hardware Revision String. */
#define FW_VER "FW" /**< Firmware Revision String. */
#define SW_VER "SW" /**< Software Revision String. */
#define SN "SN"
- 在 main.c 函数 services_init 中,添加如下代码:
// Initialize Device Information Service.
memset(&dis_init, 0, sizeof(dis_init));
ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *)MANUFACTURER_NAME);
ble_srv_ascii_to_utf8(&dis_init.model_num_str, MODEL);
ble_srv_ascii_to_utf8(&dis_init.serial_num_str, SN);
ble_srv_ascii_to_utf8(&dis_init.hw_rev_str, HW_VER);
ble_srv_ascii_to_utf8(&dis_init.fw_rev_str, FW_VER);
ble_srv_ascii_to_utf8(&dis_init.sw_rev_str, SW_VER);
dis_init.dis_char_rd_sec = SEC_OPEN;
err_code = ble_dis_init(&dis_init);
APP_ERROR_CHECK(err_code);
添加按键通知属性
该属性附加在 uart服务中,由于我的项目是一个触摸片模拟的按键,所以取名 touchpanel 的缩写 TP
- 找到uart tx/rx服务对应文件 ble_nus.c,添加如下代码
/**< The UUID of the TP Characteristic. */
#define BLE_UUID_NUS_TP_CHARACTERISTIC 0x0005
- 在结构体 struct ble_nus_s 中添加如下成员:
struct ble_nus_s
{
...
/**< Handles related to the TP characteristic (as provided by the SoftDevice). */
ble_gatts_char_handles_t tp_handles;
...
};
- 在函数 ble_nus_init 中添加如下代码:
// Add TP characteristic.
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_NUS_TP_CHARACTERISTIC;
add_char_params.uuid_type = p_nus->uuid_type;
add_char_params.init_len = sizeof(uint8_t);
add_char_params.max_len = sizeof(uint8_t);
add_char_params.char_props.read = 1;
add_char_params.char_props.notify = 1;//notification 属性
add_char_params.read_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
err_code = characteristic_add(
p_nus->service_handle,
&add_char_params,
&p_nus->tp_handles);
if (err_code != NRF_SUCCESS) {
return err_code;
}
- 在 ble_nus.c 中实现如下函数:
//将按键状态改动通知协议栈
uint32_t nus_notification_state_change(uint16_t conn_handle, uint16_t handle, uint8_t state)
{
ble_gatts_hvx_params_t params;
uint16_t len = sizeof(state);
memset(¶ms, 0, sizeof(params));
params.type = BLE_GATT_HVX_NOTIFICATION;
params.handle = handle;
params.p_data = &state;
params.p_len = &len;
return sd_ble_gatts_hvx(conn_handle, ¶ms);
}
#define BUTTON_DETECTION_DELAY APP_TIMER_TICKS(50)
#define TP_DET_PIN 25
//tp模拟按键事件处理函数
void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
uint32_t err_code;
if (!is_ble_connect()) {
debug_print(
"ble disconnect(pin_no=%d,button_action=%d)\r\n",
pin_no,
button_action);
return;
}
switch(pin_no) {
case TP_DET_PIN:
err_code = nus_notification_state_change(
m_conn_handle,
m_nus.tp_handles.value_handle,
button_action);
debug_print("tp button_action=%d \r\n",button_action);
if (err_code != NRF_SUCCESS &&
err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
err_code != NRF_ERROR_INVALID_STATE &&
err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) {
debug_print("tp button_event_handler err_code=%d\r\n",err_code);
}
break;
default:
debug_print("APP_ERROR_HANDLER(%d)\r\n",pin_no);
break;
}
}
//TP按键初始化
void tp_button_init()
{
uint32_t err_code;
static app_button_cfg_t buttons[] = {
{TP_DET_PIN, APP_BUTTON_ACTIVE_HIGH, NRF_GPIO_PIN_PULLDOWN, button_event_handler}
};
err_code = app_button_init(buttons,sizeof(buttons)/sizeof(buttons[0]), BUTTON_DETECTION_DELAY);
APP_ERROR_CHECK(err_code);
app_button_enable();
}
5.最后在 main函数中调用 tp_button_init 。
测试工具
可以采用NRF官方提供的测试工具 nRF connect 进行测试,目前最新版本为 v4.19.0,但官方不提供该工具的源码(后面会有文章介绍android客户端<参考nRF Toolbox的源码>实现本文服务及属性的访问 ):
使用方法
- 点击 SCANNER 选项卡, 扫描到目标设备,点击 CONNECT选项
- 点击已连设备所在的选项卡,然后选择竖着的 三个点 展开菜单,选择 Enable CCCDs 选项
- 点击属性菜单右边的更新小箭头
- 电池服务
- 设备信息服务
- 触摸按键属性,手指按住的时候 value 显示 为 (0x)01