[藍牙] 四、Heart Rate Service module

 

 

Detailed Description


Heart Rate Service module.html

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)app

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.ide

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的部分post


 

1    err_code = app_timer_create(&m_heart_rate_timer_id,
2                                 APP_TIMER_MODE_REPEATED,
3                                 heart_rate_meas_timeout_handler);
4     APP_ERROR_CHECK(err_code);
  
 
 

我對這裏的timer暫時的理解是:這裏的每次create都會產生一個timer,每一個timer綁定一個timerout回調函數,若是是循環執行模式的話,會根據定時週期性觸發timeout回調函數執行相關操做~ui

 1 /**@brief Function for handling the Heart rate measurement timer timeout.
 2  *
 3  * @details This function will be called each time the heart rate measurement timer expires.
 4  *          It will exclude RR Interval data from every third measurement.
 5  *
 6  * @param[in]   p_context   Pointer used for passing some arbitrary information (context) from the
 7  *                          app_start_timer() call to the timeout handler.
 8  */
 9 static void heart_rate_meas_timeout_handler(void * p_context)
10 {
11     uint32_t err_code;
12 
13     UNUSED_PARAMETER(p_context);
14 
15     err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, m_cur_heart_rate);
16 
17     if (
18         (err_code != NRF_SUCCESS)
19         &&
20         (err_code != NRF_ERROR_INVALID_STATE)
21         &&
22         (err_code != BLE_ERROR_NO_TX_BUFFERS)
23         &&
24         (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
25     )
26     {
27         APP_ERROR_HANDLER(err_code);
28     }
29 }

 

這裏每一個週期都會觸發timeout_handler函數,並執行其內部的ble_hrs_heart_rate_measurement_send函數this

 1 /**@brief Function for sending heart rate measurement if notification has been enabled.
 2  *
 3  * @details The application calls this function after having performed a heart rate measurement.
 4  *          If notification has been enabled, the heart rate measurement data is encoded and sent to
 5  *          the client.
 6  *
 7  * @param[in]   p_hrs                    Heart Rate Service structure.
 8  * @param[in]   heart_rate               New heart rate measurement.
 9  * @param[in]   include_expended_energy  Determines if expended energy will be included in the
10  *                                       heart rate measurement data.
11  *
12  * @return      NRF_SUCCESS on success, otherwise an error code.
13  */
14 uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate)
15 {
16     uint32_t err_code;
17     
18     // Send value if connected and notifying
19     if (p_hrs->conn_handle != BLE_CONN_HANDLE_INVALID)
20     {
21         uint8_t                encoded_hrm[MAX_HRM_LEN];
22         uint16_t               len;
23         uint16_t               hvx_len;
24         ble_gatts_hvx_params_t hvx_params;
25         
26         len     = hrm_encode(p_hrs, heart_rate, encoded_hrm);
27         hvx_len = len;
28 
29         memset(&hvx_params, 0, sizeof(hvx_params));
30         
31         hvx_params.handle   = p_hrs->hrm_handles.value_handle;
32         hvx_params.type     = BLE_GATT_HVX_NOTIFICATION;
33         hvx_params.offset   = 0;
34         hvx_params.p_len    = &hvx_len;
35         hvx_params.p_data   = encoded_hrm;
36         
37         err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);
38         if ((err_code == NRF_SUCCESS) && (hvx_len != len))
39         {
40             err_code = NRF_ERROR_DATA_SIZE;
41         }
42     }
43     else
44     {
45         err_code = NRF_ERROR_INVALID_STATE;
46     }
47 
48     return err_code;
49 }

 

咱們重點分析該函數是如何將heart_rate發送出去的,下面是ble_gatts_hvx_params_t的結構體:atom

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

用這個封裝一個notification包,而後調用sd_ble_gatts_hvx將該包發送出去~url

 

//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).

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

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有關的部分


 

 1     hrs_init.evt_handler = hrs_event_handler;
 2     hrs_init.is_sensor_contact_supported = false;
 3     hrs_init.p_body_sensor_location      = &body_sensor_location;
 4 
 5     // Here the sec level for the Heart Rate Service can be changed/increased.
 6     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_hrm_attr_md.cccd_write_perm);
 7     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.read_perm);
 8     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.write_perm);
 9 
10     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_bsl_attr_md.read_perm);
11     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_bsl_attr_md.write_perm);
12 
13     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.

 1 /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 2  *
 3  * @details This function is called from the BLE Stack event interrupt handler after a BLE stack
 4  *          event has been received.
 5  *
 6  * @param[in]   p_ble_evt   Bluetooth stack event.
 7  */
 8 static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
 9 {
10     ble_bondmngr_on_ble_evt(p_ble_evt);
11     ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);
12     ble_bas_on_ble_evt(&bas, p_ble_evt);
13     ble_conn_params_on_ble_evt(p_ble_evt);
14     on_ble_evt(p_ble_evt);
15 }

 

這樣一旦有ble_stack_handler的回調收到一個BLE stack的事件就會將事件派送到ble_evt_dispatch函數,該函數將該事件派送到每一個具體服務的on_ble_evt函數,實現消息傳遞

 1 void ble_hrs_on_ble_evt(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
 2 {
 3     switch (p_ble_evt->header.evt_id)
 4     {
 5         case BLE_GAP_EVT_CONNECTED:
 6             on_connect(p_hrs, p_ble_evt);
 7             break;
 8             
 9         case BLE_GAP_EVT_DISCONNECTED:
10             on_disconnect(p_hrs, p_ble_evt);
11             break;
12             
13         case BLE_GATTS_EVT_WRITE:
14             on_write(p_hrs, p_ble_evt);
15             break;
16             
17         default:
18             // No implementation needed.
19             break;
20     }
21 }
 1 /**@brief Function for handling the Connect event.
 2  *
 3  * @param[in]   p_hrs       Heart Rate Service structure.
 4  * @param[in]   p_ble_evt   Event received from the BLE stack.
 5  */
 6 static void on_connect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
 7 {
 8     p_hrs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
 9 }
10 
11 
12 /**@brief Function for handling the Disconnect event.
13  *
14  * @param[in]   p_hrs       Heart Rate Service structure.
15  * @param[in]   p_ble_evt   Event received from the BLE stack.
16  */
17 static void on_disconnect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
18 {
19     UNUSED_PARAMETER(p_ble_evt);
20     p_hrs->conn_handle = BLE_CONN_HANDLE_INVALID;
21 }
22 
23 
24 /**@brief Function for handling write events to the Heart Rate Measurement characteristic.
25  *
26  * @param[in]   p_hrs         Heart Rate Service structure.
27  * @param[in]   p_evt_write   Write event received from the BLE stack.
28  */
29 static void on_hrm_cccd_write(ble_hrs_t * p_hrs, ble_gatts_evt_write_t * p_evt_write)
30 {
31     if (p_evt_write->len == 2)
32     {
33         // CCCD written, update notification state
34         if (p_hrs->evt_handler != NULL)
35         {
36             ble_hrs_evt_t evt;
37             
38             if (ble_srv_is_notification_enabled(p_evt_write->data))
39             {
40                 evt.evt_type = BLE_HRS_EVT_NOTIFICATION_ENABLED;
41             }
42             else
43             {
44                 evt.evt_type = BLE_HRS_EVT_NOTIFICATION_DISABLED;
45             }
46             
47             p_hrs->evt_handler(p_hrs, &evt);
48         }
49     }
50 }
51 
52 
53 /**@brief Function for handling the Write event.
54  *
55  * @param[in]   p_hrs       Heart Rate Service structure.
56  * @param[in]   p_ble_evt   Event received from the BLE stack.
57  */
58 static void on_write(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
59 {
60     ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
61     
62     if (p_evt_write->handle == p_hrs->hrm_handles.cccd_handle)
63     {
64         on_hrm_cccd_write(p_hrs, p_evt_write);
65     }
66 }

 

注:

本篇講了心率檢測服務的業務流程
下一篇分析電量檢測服務的業務流程
 
More:
相關文章
相關標籤/搜索