前言
由于蓝牙BLE就是为低功耗手持设备存在的,那电池电量的监测服务就不可或缺了。由于电量服务是蓝牙兴趣小组指定的服务,不需要用户去创建私有服务,所以这个实验我们直接使用官方提供的驱动文件即可。
实验分析
由于我的开发板没有提供AD口检测电量,所以实验直接使用一个全局变量代表电量值,每隔一秒更新一次。
首先,我们需要把官方驱动例程添加到我们的工程目录里面:
路径是:工程目录\components\ble\ble_services\ble_bas
接着,我们去看看官方提供给我们的电量更新接口函数:
ble_bas.c:
uint32_t ble_bas_battery_level_update(ble_bas_t * p_bas, uint8_t battery_level) { if (p_bas == NULL) { return NRF_ERROR_NULL; } uint32_t err_code = NRF_SUCCESS; ble_gatts_value_t gatts_value; if (battery_level != p_bas->battery_level_last) { // Initialize value struct. memset(&gatts_value, 0, sizeof(gatts_value)); gatts_value.len = sizeof(uint8_t); gatts_value.offset = 0; gatts_value.p_value = &battery_level; // Update database. err_code = sd_ble_gatts_value_set(p_bas->conn_handle, p_bas->battery_level_handles.value_handle, &gatts_value); if (err_code == NRF_SUCCESS) { // Save new battery value. p_bas->battery_level_last = battery_level; } else { return err_code; } // Send value if connected and notifying. if ((p_bas->conn_handle != BLE_CONN_HANDLE_INVALID) && p_bas->is_notification_supported) { ble_gatts_hvx_params_t hvx_params; memset(&hvx_params, 0, sizeof(hvx_params)); hvx_params.handle = p_bas->battery_level_handles.value_handle; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; hvx_params.offset = gatts_value.offset; hvx_params.p_len = &gatts_value.len; hvx_params.p_data = gatts_value.p_value; err_code = sd_ble_gatts_hvx(p_bas->conn_handle, &hvx_params); } else { err_code = NRF_ERROR_INVALID_STATE; } } return err_code; }
分析这个函数可以知道,执行更新数据之后,会调用sd_ble_gatts_hvx函数通知手机;
接下来,我们直接开始应用层的设计。既然我们要做到定时更新,那么定时器就首先要创建:
//定时器初始化
APP_TIMER_DEF(m_battery_timer_id);//宏定义一个电量测量定时器id
static void timers_init(void)
{
uint32_t err_code;
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
err_code = app_timer_create(&m_battery_timer_id, APP_TIMER_MODE_REPEATED, battery_level_meas_timeout_handler);
APP_ERROR_CHECK(err_code);
}
err_code = app_timer_start(m_battery_timer_id, APP_TIMER_TICKS(1000,APP_TIMER_PRESCALER), NULL);
APP_ERROR_CHECK(err_code);
在main函数启动定时器。
然后,我们需要创建一个定时器超时的回调函数:
uint8_t adc = 0;
static void battery_level_update(void)
{
uint32_t err_code;
uint8_t battery_level;
battery_level = adc;
adc++;
if(adc >= 100) adc = 0;
err_code = ble_bas_battery_level_update(&m_bas, battery_level);
if((err_code != NRF_SUCCESS) &&
(err_code != NRF_ERROR_INVALID_STATE) &&
(err_code != BLE_ERROR_NO_TX_BUFFERS) &&
(err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING))
{
APP_ERROR_HANDLER(err_code);
}
}
static void battery_level_meas_timeout_handler(void *p_context)
{
UNUSED_PARAMETER(p_context);
//数据处理
battery_level_update();
}
接着,我们要把电池服务注册到服务初始化里面:
/**@brief Function for initializing services that will be used by the application.
*/
static void services_init(void)
{
uint32_t err_code;
ble_bas_init_t bas_init;
//电池服务
memset(&bas_init, 0, sizeof(bas_init));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm);
bas_init.evt_handler = NULL;
bas_init.support_notification = 1;
bas_init.p_report_ref = NULL;
bas_init.initial_batt_level = 100;
m_bas.is_notification_supported = 1;
err_code = ble_bas_init(&m_bas, &bas_init);
APP_ERROR_CHECK(err_code);
}
需要注意的是,一定要把上面的几个安全模式设置打开,因为官方驱动代码里面没有添加,所以需要我们自行添加。
最后,再把电池服务的事件派发函数添加到BLE事件派发函数里即可:
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
ble_conn_params_on_ble_evt(p_ble_evt);
on_ble_evt(p_ble_evt);
ble_advertising_on_ble_evt(p_ble_evt);
ble_bas_on_ble_evt(&m_bas, p_ble_evt);
}
结果验证
- 手机连接蓝牙,我们可以看到一个Battery Service的服务
- 这个服务包含read和notify特性
- 打开调试app的监听功能后,发现电池电量1秒更新一次
总结
通过这个实验,我们学会如何开发一个电池电量的监听服务。