Skip to content

Commit ccc67f8

Browse files
sondrepasilz
authored andcommitted
bluetooth: services: hrs_c: add heart rate central service and sample
Add heart rate central service and sample. Co-authored-by: Asil Zogby <[email protected]> Co-authored-by: Sondre Pettersen <[email protected]> Signed-off-by: Eivind Jølsgard <[email protected]>
1 parent 8b531cb commit ccc67f8

File tree

14 files changed

+1477
-7
lines changed

14 files changed

+1477
-7
lines changed

doc/nrf-bm/libraries/bluetooth/ble_scan.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ It is required when using scan filters or the whitelist.
101101
},
102102
.conn_params = BLE_SCAN_CONN_PARAMS_DEFAULT,
103103
.connect_if_match = true,
104-
.conn_cfg_tag = APP_BLE_CONN_CFG_TAG,
104+
.conn_cfg_tag = CONFIG_NRF_SDH_BLE_CONN_TAG,
105105
.evt_handler = scan_event_handler_func,
106106
};
107107
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* Copyright (c) 2012 - 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
/**
7+
* @file
8+
*
9+
* @defgroup ble_hrs_c Heart Rate Service Client
10+
* @{
11+
* @brief Heart Rate Service Client.
12+
*
13+
* @details This library contains the APIs and types exposed by the Heart Rate Service Client
14+
* library. The application can use these APIs and types to perform the discovery of
15+
* Heart Rate Service at the peer and to interact with it.
16+
*
17+
* @warning Currently, this library only supports the Heart Rate Measurement characteristic. This
18+
* means that it is able to enable notification of the characteristic at the peer and
19+
* is able to receive Heart Rate Measurement notifications from the peer. It does not
20+
* support the Body Sensor Location and the Heart Rate Control Point characteristics.
21+
* When a Heart Rate Measurement is received, this library decodes only the
22+
* Heart Rate Measurement value field (both 8-bit and 16-bit) and provides it to
23+
* the application.
24+
*/
25+
26+
#ifndef BLE_HRS_C_H__
27+
#define BLE_HRS_C_H__
28+
29+
#include <stdint.h>
30+
#include <ble.h>
31+
#include <bm/bluetooth/ble_db_discovery.h>
32+
#include <bm/bluetooth/ble_gq.h>
33+
#include <bm/softdevice_handler/nrf_sdh_ble.h>
34+
35+
#ifdef __cplusplus
36+
extern "C" {
37+
#endif
38+
39+
/**
40+
* @brief Macro for defining a ble_hrs_c instance.
41+
*
42+
* @param _name Name of the instance.
43+
* @hideinitializer
44+
*/
45+
#define BLE_HRS_C_DEF(_name) \
46+
static struct ble_hrs_c _name; \
47+
NRF_SDH_BLE_OBSERVER(_name##_obs, ble_hrs_c_on_ble_evt, &_name, HIGH)
48+
49+
/**
50+
* @brief HRS Client event type.
51+
*/
52+
enum ble_hrs_c_evt_type {
53+
/** Event indicating that the Heart Rate Service was discovered at the peer. */
54+
BLE_HRS_C_EVT_DISCOVERY_COMPLETE,
55+
/** Event indicating that a notification of the Heart Rate Measurement characteristic was
56+
* received from the peer.
57+
*/
58+
BLE_HRS_C_EVT_HRM_NOTIFICATION,
59+
/** Error. */
60+
BLE_HRS_C_EVT_ERROR,
61+
};
62+
63+
/**
64+
* @brief Heart Rate Measurement received from the peer.
65+
*/
66+
struct ble_hrm {
67+
/** Heart Rate Value. */
68+
uint16_t hr_value;
69+
/** Number of RR intervals. */
70+
uint8_t rr_intervals_cnt;
71+
/** RR intervals. */
72+
uint16_t rr_intervals[CONFIG_BLE_HRS_C_RR_INTERVALS_MAX_COUNT];
73+
};
74+
75+
/**
76+
* @brief Database for handles related to the Heart Rate Service found on the peer.
77+
*/
78+
struct hrs_db {
79+
/** Handle of the CCCD of the Heart Rate Measurement characteristic. */
80+
uint16_t hrm_cccd_handle;
81+
/** Handle of the Heart Rate Measurement characteristic, as provided by the
82+
* SoftDevice. */
83+
uint16_t hrm_handle;
84+
};
85+
86+
/**
87+
* @brief Heart Rate Event.
88+
*/
89+
struct ble_hrs_c_evt {
90+
/** Type of the event. */
91+
enum ble_hrs_c_evt_type evt_type;
92+
/** Connection handle on which the Heart Rate service was discovered on the peer
93+
* device. */
94+
uint16_t conn_handle;
95+
union {
96+
/** Handles related to the Heart Rate, found on the peer device.
97+
This is filled if the evt_type is @ref
98+
BLE_HRS_C_EVT_DISCOVERY_COMPLETE.*/
99+
struct hrs_db peer_db;
100+
/** Heart Rate Measurement received. This is filled if the evt_type
101+
is @ref BLE_HRS_C_EVT_HRM_NOTIFICATION. */
102+
struct ble_hrm hrm;
103+
104+
struct {
105+
/* Error reason */
106+
uint32_t reason;
107+
} error;
108+
} params;
109+
};
110+
111+
/** Forward declaration of the struct ble_hrs_c type. */
112+
struct ble_hrs_c;
113+
114+
/**
115+
* @brief Event handler type.
116+
*
117+
* @details This is the type of the event handler that is to be provided by the application
118+
* of this module to receive events.
119+
*/
120+
typedef void (*ble_hrs_c_evt_handler_t)(struct ble_hrs_c *ble_hrs_c, struct ble_hrs_c_evt *evt);
121+
122+
/**
123+
* @brief Heart Rate Client.
124+
*/
125+
struct ble_hrs_c {
126+
/** Connection handle, as provided by the SoftDevice. */
127+
uint16_t conn_handle;
128+
/** Handles related to HRS on the peer. */
129+
struct hrs_db peer_hrs_db;
130+
/** Application event handler to be called when there
131+
is an event related to the Heart Rate Service. */
132+
ble_hrs_c_evt_handler_t evt_handler;
133+
/** Pointer to the BLE GATT Queue instance. */
134+
const struct ble_gq *gatt_queue;
135+
};
136+
137+
/**
138+
* @brief Heart Rate Client configuration structure.
139+
*/
140+
struct ble_hrs_c_config {
141+
/** Event handler to be called by the Heart Rate Client module when
142+
there is an event related to the Heart Rate Service. */
143+
ble_hrs_c_evt_handler_t evt_handler;
144+
/** Pointer to the BLE GATT Queue instance. */
145+
const struct ble_gq *gatt_queue;
146+
/** Database discovery instance. */
147+
struct ble_db_discovery *db_discovery;
148+
};
149+
150+
/**
151+
* @brief Initialize the Heart Rate Client module.
152+
*
153+
* @details This function registers with the Database Discovery module for the Heart Rate Service.
154+
* The module looks for the presence of a Heart Rate Service instance at the peer
155+
* when a discovery is started.
156+
*
157+
* @param[in] ble_hrs_c Pointer to the Heart Rate Client structure.
158+
* @param[in] ble_hrs_c_init Pointer to the Heart Rate initialization structure that
159+
* contains the initialization information.
160+
*
161+
* @retval NRF_SUCCESS On successful initialization.
162+
* @return Otherwise, this function propagates the error code returned by the
163+
* Database Discovery module API @ref ble_db_discovery_service_register.
164+
*/
165+
uint32_t ble_hrs_c_init(struct ble_hrs_c *ble_hrs_c, struct ble_hrs_c_config *ble_hrs_c_init);
166+
167+
/**
168+
* @brief Handle BLE events from the SoftDevice.
169+
*
170+
* @details This function handles the BLE events received from the SoftDevice. If a BLE
171+
* event is relevant to the Heart Rate Client module, the function uses the
172+
* event's data to update interval variables and, if necessary, send events to the
173+
* application.
174+
*
175+
* @param[in] ble_evt Pointer to the BLE event.
176+
* @param[in] ctx Pointer to the Heart Rate Client structure.
177+
*/
178+
void ble_hrs_c_on_ble_evt(ble_evt_t const *ble_evt, void *ctx);
179+
180+
/**
181+
* @brief Request the peer to start sending notification of Heart Rate Measurement.
182+
*
183+
* @details This function enables notification of the Heart Rate Measurement at the peer
184+
* by writing to the CCCD of the Heart Rate Measurement characteristic.
185+
*
186+
* @param ble_hrs_c Pointer to the Heart Rate Client structure.
187+
*
188+
* @retval NRF_SUCCESS If the SoftDevice is requested to write to the CCCD of the peer.
189+
* @return Error code returned by the SoftDevice API @ref sd_ble_gattc_write.
190+
*/
191+
uint32_t ble_hrs_c_hrm_notif_enable(struct ble_hrs_c *ble_hrs_c);
192+
193+
/**
194+
* @brief Handle events from the Database Discovery module.
195+
*
196+
* @details Call this function when you get a callback event from the Database Discovery
197+
* module. This function handles an event from the Database Discovery module and determines
198+
* whether it relates to the discovery of Heart Rate Service at the peer. If it
199+
* does, the function calls the application's event handler to indicate that the Heart Rate
200+
* Service was discovered at the peer. The function also populates the event with
201+
* service-related information before providing it to the application.
202+
*
203+
* @param[in] ble_hrs_c Pointer to the Heart Rate Client structure instance for
204+
* associating the link.
205+
* @param[in] evt Pointer to the event received from the Database Discovery module.
206+
*
207+
*/
208+
void ble_hrs_on_db_disc_evt(struct ble_hrs_c *ble_hrs_c,
209+
const struct ble_db_discovery_evt *evt);
210+
211+
/**
212+
* @brief Assign handles to an instance of hrs_c.
213+
*
214+
* @details Call this function when a link has been established with a peer to
215+
* associate the link to this instance of the module. This association makes it
216+
* possible to handle several links and associate each link to a particular
217+
* instance of this module. The connection handle and attribute handles are
218+
* provided from the discovery event @ref BLE_HRS_C_EVT_DISCOVERY_COMPLETE.
219+
*
220+
* @param[in] ble_hrs_c Pointer to the Heart Rate Client structure instance for
221+
* associating the link.
222+
* @param[in] conn_handle Connection handle to associate with the given Heart Rate
223+
* Client Instance.
224+
* @param[in] peer_hrs_handles Attribute handles for the HRS server you want this HRS_C
225+
* client to interact with.
226+
*/
227+
uint32_t ble_hrs_c_handles_assign(struct ble_hrs_c *ble_hrs_c, uint16_t conn_handle,
228+
const struct hrs_db *peer_hrs_handles);
229+
230+
#ifdef __cplusplus
231+
}
232+
#endif
233+
234+
#endif /* BLE_HRS_C_H__ */
235+
236+
/** @} */

lib/bluetooth/ble_db_discovery/ble_db_discovery.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static void discovery_error_evt_trigger(struct ble_db_discovery *db_discovery, u
7676

7777
evt_handler = registered_uuid_handler_get(db_discovery, &(srv_being_discovered->srv_uuid));
7878

79-
if (evt_handler != NULL) {
79+
if (evt_handler) {
8080
struct ble_db_discovery_evt evt = {
8181
.conn_handle = conn_handle,
8282
.evt_type = BLE_DB_DISCOVERY_ERROR,
@@ -97,7 +97,7 @@ static void discovery_error_handler(uint16_t conn_handle, uint32_t nrf_error, vo
9797
discovery_available_evt_trigger(db_discovery, conn_handle);
9898
}
9999

100-
static void discovery_event_handler(const struct ble_gq_req *req, struct ble_gq_evt *evt)
100+
static void discovery_gq_event_handler(const struct ble_gq_req *req, struct ble_gq_evt *evt)
101101
{
102102
if (evt->evt_type != BLE_GQ_EVT_ERROR) {
103103
return;
@@ -186,7 +186,7 @@ static void on_srv_disc_completion(struct ble_db_discovery *db_discovery, uint16
186186
db_srv_disc_req.gattc_srv_disc.start_handle = CONFIG_SRV_DISC_START_HANDLE;
187187
db_srv_disc_req.gattc_srv_disc.srvc_uuid = srv_being_discovered->srv_uuid;
188188
db_srv_disc_req.ctx = db_discovery;
189-
db_srv_disc_req.evt_handler = discovery_event_handler;
189+
db_srv_disc_req.evt_handler = discovery_gq_event_handler;
190190

191191
nrf_err = ble_gq_item_add(db_discovery->gatt_queue, &db_srv_disc_req, conn_handle);
192192

@@ -292,7 +292,7 @@ static uint32_t characteristics_discover(struct ble_db_discovery *db_discovery,
292292
db_char_disc_req.type = BLE_GQ_REQ_CHAR_DISCOVERY;
293293
db_char_disc_req.gattc_char_disc = handle_range;
294294
db_char_disc_req.ctx = db_discovery;
295-
db_char_disc_req.evt_handler = discovery_event_handler;
295+
db_char_disc_req.evt_handler = discovery_gq_event_handler;
296296

297297
return ble_gq_item_add(db_discovery->gatt_queue, &db_char_disc_req, conn_handle);
298298
}
@@ -357,7 +357,7 @@ static uint32_t descriptors_discover(struct ble_db_discovery *db_discovery,
357357
db_desc_disc_req.type = BLE_GQ_REQ_DESC_DISCOVERY;
358358
db_desc_disc_req.gattc_desc_disc = handle_range;
359359
db_desc_disc_req.ctx = db_discovery;
360-
db_desc_disc_req.evt_handler = discovery_event_handler;
360+
db_desc_disc_req.evt_handler = discovery_gq_event_handler;
361361

362362
return ble_gq_item_add(db_discovery->gatt_queue, &db_desc_disc_req, conn_handle);
363363
}
@@ -650,7 +650,7 @@ static uint32_t discovery_start(struct ble_db_discovery *const db_discovery, uin
650650
db_srv_disc_req.gattc_srv_disc.start_handle = CONFIG_SRV_DISC_START_HANDLE;
651651
db_srv_disc_req.gattc_srv_disc.srvc_uuid = srv_being_discovered->srv_uuid;
652652
db_srv_disc_req.ctx = db_discovery;
653-
db_srv_disc_req.evt_handler = discovery_event_handler;
653+
db_srv_disc_req.evt_handler = discovery_gq_event_handler;
654654

655655
nrf_err = ble_gq_item_add(db_discovery->gatt_queue, &db_srv_disc_req, conn_handle);
656656
if (nrf_err) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
10+
project(ble_hrs)
11+
12+
target_sources(app PRIVATE src/main.c)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
menu "BLE HRS Central sample"
8+
9+
config APP_USE_TARGET_PERIPH_NAME
10+
bool "Use target peripheral name"
11+
default y
12+
13+
if APP_USE_TARGET_PERIPH_NAME
14+
15+
config APP_TARGET_PERIPH_NAME
16+
string "Target peripheral name"
17+
default "nRF_BM_HRS"
18+
19+
endif # APP_USE_TARGET_PERIPH_NAME
20+
21+
config APP_USE_TARGET_PERIPH_ADDR
22+
bool "Use target peripheral address"
23+
default y
24+
25+
if APP_USE_TARGET_PERIPH_ADDR
26+
27+
config APP_TARGET_PERIPH_ADDR
28+
hex "Target peripheral address"
29+
default 0x78E7F806C5D8
30+
range 0x0 0xffffffffffff
31+
32+
endif # APP_USE_TARGET_PERIPH_ADDR
33+
34+
module=APP_BLE_HRS_C_SAMPLE
35+
module-dep=LOG
36+
module-str=BLE Heart Rate Central Service Sample
37+
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"
38+
39+
endmenu # "BLE HRS sample"
40+
41+
source "Kconfig.zephyr"

0 commit comments

Comments
 (0)