[蓝牙] 4、Heart Rate Service module

时间:2023-02-05 21:17:48

Detailed Description


Heart Rate Service module.

This module implements the Heart Rate Service with the Heart Rate Measurement, Body Sensor Location and Heart Rate Control Point characteristics. During initialization it adds the Heart Rate Service and Heart Rate Measurement characteristic to the BLE stack database. Optionally it also adds the Body Sensor Location and Heart Rate Control Point characteristics.

If enabled, notification of the Heart Rate Measurement characteristic is performed when the application calls ble_hrs_heart_rate_measurement_send().(这个主要是指notification,HRS主动向客户端发送消息的事件触发,下文在timers_init部分会讲:在周期性timeout回调函数中执行ble_hrs_heart_rate_measurement_send()来触发notification)

The Heart Rate Service also provides a set of functions for manipulating the various fields in the Heart Rate Measurement characteristic, as well as setting the Body Sensor Location characteristic value.

If an event handler is supplied提供 by the application, the Heart Rate Service will generate形成 Heart Rate Service events to the application.(来自应用提供的事件句柄,HRS会形成HRS事件给应用)

Note
The application must propagate传送 BLE stack events to the Heart Rate Service module by calling ble_hrs_on_ble_evt() from the from the ble_stack_handler callback.
Attention! To maintain compliance with Nordic Semiconductor ASA Bluetooth profile qualification listings, this section of source code must not be modified. 

timers_init()中关于hrs的部分


    err_code = app_timer_create(&m_heart_rate_timer_id,
APP_TIMER_MODE_REPEATED,
heart_rate_meas_timeout_handler);
APP_ERROR_CHECK(err_code);
  
[蓝牙] 4、Heart Rate Service module
 
[蓝牙] 4、Heart Rate Service module
 

我对这里的timer暂时的理解是:这里的每次create都会产生一个timer,每个timer绑定一个timerout回调函数,如果是循环执行模式的话,会根据定时周期性触发timeout回调函数执行相关操作~

 /**@brief Function for handling the Heart rate measurement timer timeout.
*
* @details This function will be called each time the heart rate measurement timer expires.
* It will exclude RR Interval data from every third measurement.
*
* @param[in] p_context Pointer used for passing some arbitrary information (context) from the
* app_start_timer() call to the timeout handler.
*/
static void heart_rate_meas_timeout_handler(void * p_context)
{
uint32_t err_code; UNUSED_PARAMETER(p_context); err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, m_cur_heart_rate); 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);
}
}

这里每个周期都会触发timeout_handler函数,并执行其内部的ble_hrs_heart_rate_measurement_send函数

 /**@brief Function for sending heart rate measurement if notification has been enabled.
*
* @details The application calls this function after having performed a heart rate measurement.
* If notification has been enabled, the heart rate measurement data is encoded and sent to
* the client.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] heart_rate New heart rate measurement.
* @param[in] include_expended_energy Determines if expended energy will be included in the
* heart rate measurement data.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate)
{
uint32_t err_code; // Send value if connected and notifying
if (p_hrs->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_hrm[MAX_HRM_LEN];
uint16_t len;
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params; len = hrm_encode(p_hrs, heart_rate, encoded_hrm);
hvx_len = len; memset(&hvx_params, , sizeof(hvx_params)); hvx_params.handle = p_hrs->hrm_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = ;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_hrm; err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
} return err_code;
}

我们重点分析该函数是如何将heart_rate发送出去的,下面是ble_gatts_hvx_params_t的结构体:

 /**@brief GATT HVx parameters. */
typedef struct
{
uint16_t handle; /**< Characteristic Value Handle. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t offset; /**< Offset within the attribute value. */
uint16_t* p_len; /**< Length in bytes to be written, length in bytes written after successful return. */
uint8_t* p_data; /**< Actual data content, use NULL to use the current attribute value. */
} ble_gatts_hvx_params_t;

用这个封装一个notification包,然后调用sd_ble_gatts_hvx将该包发送出去~

//err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);

uint32_t sd_ble_gatts_hvx ( uint16_t conn_handle,

                ble_gatts_hvx_params_t const *const p_hvx_params);

Notify or Indicate an attribute value.

This function checks for the relevant相关的 Client Characteristic Configuration descriptor描述符 value to verify判定 that the relevant operation (notification or indication) has been enabled by the client.

It is also able to update the attribute 属性 value before issuing发出 the PDU(protocol data unit:https://en.wikipedia.org/wiki/Protocol_data_unit  && BLE 包结构及传输速率), so that the application can atomically原子级地 perform a value update and a server initiated开始 transaction事务 with a single API call. (仅仅调用一个API就能够将attribute的value有效地发出)If the application chooses to indicate an attribute value, a BLE_GATTS_EVT_HVC will be sent up as soon as the confirmation arrives from the peer.

Note

The local attribute value may be updated even if an outgoing packet is not sent to the peer due to an error during execution. When receiveing the error codes NRF_ERROR_INVALID_STATE, NRF_ERROR_BUSY, BLE_ERROR_GATTS_SYS_ATTR_MISSING and BLE_ERROR_NO_TX_BUFFERS the ATT table has been updated. The caller can check whether the value has been updated by looking at the contents of *(p_hvx_params->p_len).

 err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}

It is important to note that a notification will consume(消耗) an application buffer, and will therefore generate产生 a BLE_EVT_TX_COMPLETE event when the packet has been transmitted(发送完了会产生一个COMPLETE事件). An indication on the other hand will use the standard server internal buffer and thus will only generate a BLE_GATTS_EVT_HVC event as soon as the confirmation has been received from the peer. Please see the documentation of sd_ble_tx_buffer_count_get for more details.

services_init()中和hrs有关的部分


     hrs_init.evt_handler = hrs_event_handler;
hrs_init.is_sensor_contact_supported = false;
hrs_init.p_body_sensor_location = &body_sensor_location; // Here the sec level for the Heart Rate Service can be changed/increased.
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_hrm_attr_md.cccd_write_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.write_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_bsl_attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_bsl_attr_md.write_perm); err_code = ble_hrs_init(&m_hrs, &hrs_init);

最后一个黄线部分阐明了事件如何关联——

  application必须在ble_stack_handler的回调函数中调用ble_hrs_on_ble_evt()函数来将BLE stack events传送给Heart Rate Serice

下面这个函数负责分派 a BLE stack event to all modules 模块 with a BLE stack event handler.

是由ble_stack_handler的回调函数调用的,发生在:This function is called from the BLE Stack event interrupt handler after a BLE stack event has been received.

 /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
*
* @details This function is called from the BLE Stack event interrupt handler after a BLE stack
* event has been received.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
ble_bondmngr_on_ble_evt(p_ble_evt);
ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);
ble_bas_on_ble_evt(&bas, p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
on_ble_evt(p_ble_evt);
}

这样一旦有ble_stack_handler的回调收到一个BLE stack的事件就会将事件派送到ble_evt_dispatch函数,该函数将该事件派送到每个具体服务的on_ble_evt函数,实现消息传递

 void ble_hrs_on_ble_evt(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_hrs, p_ble_evt);
break; case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_hrs, p_ble_evt);
break; case BLE_GATTS_EVT_WRITE:
on_write(p_hrs, p_ble_evt);
break; default:
// No implementation needed.
break;
}
}
 /**@brief Function for handling the Connect event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
p_hrs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
} /**@brief Function for handling the Disconnect event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_hrs->conn_handle = BLE_CONN_HANDLE_INVALID;
} /**@brief Function for handling write events to the Heart Rate Measurement characteristic.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_hrm_cccd_write(ble_hrs_t * p_hrs, ble_gatts_evt_write_t * p_evt_write)
{
if (p_evt_write->len == )
{
// CCCD written, update notification state
if (p_hrs->evt_handler != NULL)
{
ble_hrs_evt_t evt; if (ble_srv_is_notification_enabled(p_evt_write->data))
{
evt.evt_type = BLE_HRS_EVT_NOTIFICATION_ENABLED;
}
else
{
evt.evt_type = BLE_HRS_EVT_NOTIFICATION_DISABLED;
} p_hrs->evt_handler(p_hrs, &evt);
}
}
} /**@brief Function for handling the Write event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; if (p_evt_write->handle == p_hrs->hrm_handles.cccd_handle)
{
on_hrm_cccd_write(p_hrs, p_evt_write);
}
}

注:

本篇讲了心率检测服务的业务流程

下一篇分析电量检测服务的业务流程
 
More:

[蓝牙] 4、Heart Rate Service module的更多相关文章

  1. &lbrack;蓝牙&rsqb; 5、Battery Service module

    Detailed Description This module implements the Battery Service with the Battery Level characteristi ...

  2. FreeBSD修改root密码错误passwd&colon; pam&lowbar;chau&lpar;www&period;111cn&period;net&rpar;thtok&lpar;&rpar;&colon; error in service module from&colon;http&colon;&sol;&sol;www&period;111cn&period;net&sol;sys&sol;freebsd&sol;66713&period;htm

    在FreeBSD中修改帐号密码有时候会出现一些错误,针对passwd: pam_chauthtok(): error in service module这样的错误提示,简单整理了以下解决方案:错误提示 ...

  3. 低功耗蓝牙ATT&sol;GATT&sol;Profile&sol;Service&sol;Characteristic规格解读

    什么是蓝牙service和characteristic?到底怎么理解蓝牙profile?ATT和GATT两者如何区分?什么又是attribute?attribute和characteristic的区别 ...

  4. 蓝牙发现服务UUID&lpar;service UUID&rpar;

    出至<蓝牙标准Core_V4.0>2.5.1 uuid(1576页) 其中 Bluetooth_Base_UUID定义为 00000000-0000-1000-8000-00805F9B3 ...

  5. Drupal service module 介绍

    https://www.ostraining.com/blog/drupal/services/ https://www.drupal.org/node/1246470 https://www.dru ...

  6. &lbrack;蓝牙&rsqb; 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

    开机初始化Log Log编号 函数名   所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...

  7. &lbrack;蓝牙&rsqb; 3、 剖析BLE心率检测工程

    位于:<KEIL path> \ARM\Device\Nordic\nrf51822\Board\pca10001\s110\ble_app_hrs Heart Rate Example ...

  8. &lbrack;编译&rsqb; 4、在Linux下搭建nRF51822的开发烧写环境(makefile版)

    星期日, 09. 九月 2018 07:51下午 - beautifulzzzz 1.安装步骤 1) 从GNU Arm Embedded Toolchain官网下载最新的gcc-arm工具链,写文章时 ...

  9. 低功耗蓝牙4&period;0BLE编程-nrf51822开发&lpar;2&rpar;

    相关下载:http://download.csdn.net/detail/xgbing/9565708 首先看的示例是心率计一个示例程序:<KEIL path> \ARM\Device\N ...

随机推荐

  1. 。Java中的一些小细节

    1.main方法. ------任何一个Java程序都有一个main方法,它是程序的入口. ------当执行  “ java + 类名 “  这个命令时,JVM就会去加载这个类,并且寻找这个类中的m ...

  2. C&plus;&plus; 类的成员函数指针 &lpar; function&sol;bind &rpar;

    这个概念主要用在C++中去实现"委托"的特性. 但现在C++11 中有了 更好用的function/bind 功能.但对于类的成员函数指针的概念我们还是应该掌握的. 类函数指针 就 ...

  3. Linux常用解压文件

    tar.gz    tar -zxvf filename.tar.gz tar.bz2  tar -vxjf filename.tar.bz2

  4. Eclipse中配置约束&lpar;DTD&comma;XSD&rpar;

    在Eclipse中本地配置schema约束(xsd): 1.比如配置spring的applicationContext.xml中的约束条件: 复制applicationContext.xml中如图: ...

  5. 万能选项卡,tab选项卡

    //万能选项卡 function PaPtabs(thisObj, num) { if (thisObj.className == "active") return; var ta ...

  6. xpath 中 &lbrack;&lt&semi;Element a at 3985984dj343&gt&semi;&rsqb;

    在写爬虫用xpath抓取数据的时候出现了这个问题,列表中都是很多个 < element > 首先这不是报错,也不是你的xpath语法有错. 将这个数据列表循环,循环出的item就是你想要的 ...

  7. EF Core Fluent API

    多对多配置 先安装 Install-Package MySql.Data.EntityFrameworkCore 创建3个表 创建类 public class Role { public long I ...

  8. go标准库的学习-mime

    参考:https://studygolang.com/pkgdoc 导入方法: import "mime" mime实现了MIME的部分规定. 什么是MIME: MIME(Mult ...

  9. SVN三种合并类型

    https://blog.csdn.net/zht666/article/details/36178117 转自:http://wenku.baidu.com/link?url=pnALYESJnX0 ...

  10. LPC43xx SGPIO Configuration -- Why not use GPDMA &quest;

    LPC43xx SGPIO Configuration The LPC43xx SGPIO peripheral is used to move samples between USB and the ...