1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21 #include <string.h>
22 #include <stdio.h>
23
24 #include <rtthread.h>
25
26 #include "nimble/ble.h"
27 #include "host/ble_hs.h"
28 #include "services/gap/ble_svc_gap.h"
29 #include "blehr_sens.h"
30
31 static bool notify_state;
32 static uint16_t notify_conn_handle;
33
34 static const char *device_name = "blehr_sensor";
35
36 static int blehr_gap_event(struct ble_gap_event *event, void *arg);
37
38 static uint8_t blehr_addr_type;
39
40 /* Sending notify data timer */
41 static struct ble_npl_callout blehr_tx_timer;
42
43 /* Variable to simulate heart beats */
44 static uint8_t heartrate = 90;
45
46 /*
47 * Enables advertising with parameters:
48 * o General discoverable mode
49 * o Undirected connectable mode
50 */
51 static void
blehr_advertise(void)52 blehr_advertise(void)
53 {
54 struct ble_gap_adv_params adv_params;
55 struct ble_hs_adv_fields fields;
56 int rc;
57
58 /*
59 * Set the advertisement data included in our advertisements:
60 * o Flags (indicates advertisement type and other general info)
61 * o Advertising tx power
62 * o Device name
63 */
64 memset(&fields, 0, sizeof(fields));
65
66 /*
67 * Advertise two flags:
68 * o Discoverability in forthcoming advertisement (general)
69 * o BLE-only (BR/EDR unsupported)
70 */
71 fields.flags = BLE_HS_ADV_F_DISC_GEN |
72 BLE_HS_ADV_F_BREDR_UNSUP;
73
74 /*
75 * Indicate that the TX power level field should be included; have the
76 * stack fill this value automatically. This is done by assigning the
77 * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
78 */
79 fields.tx_pwr_lvl_is_present = 1;
80 fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
81
82 fields.name = (uint8_t *)device_name;
83 fields.name_len = strlen(device_name);
84 fields.name_is_complete = 1;
85
86 rc = ble_gap_adv_set_fields(&fields);
87 if (rc != 0) {
88 MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
89 return;
90 }
91
92 /* Begin advertising */
93 memset(&adv_params, 0, sizeof(adv_params));
94 adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
95 adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
96 rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER,
97 &adv_params, blehr_gap_event, NULL);
98 if (rc != 0) {
99 MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
100 return;
101 }
102 }
103
104 static void
blehr_tx_hrate_stop(void)105 blehr_tx_hrate_stop(void)
106 {
107 ble_npl_callout_stop(&blehr_tx_timer);
108 }
109
110 /* Reset heartrate measurment */
111 static void
blehr_tx_hrate_reset(void)112 blehr_tx_hrate_reset(void)
113 {
114 int rc;
115
116 rc = ble_npl_callout_reset(&blehr_tx_timer, RT_TICK_PER_SECOND);
117 assert(rc == 0);
118 }
119
120 /* This functions simulates heart beat and notifies it to the client */
121 static void
blehr_tx_hrate(struct ble_npl_event * ev)122 blehr_tx_hrate(struct ble_npl_event *ev)
123 {
124 static uint8_t hrm[2];
125 int rc;
126 struct os_mbuf *om;
127
128 if (!notify_state) {
129 blehr_tx_hrate_stop();
130 heartrate = 90;
131 return;
132 }
133
134 hrm[0] = 0x06; /* contact of a sensor */
135 hrm[1] = heartrate; /* storing dummy data */
136
137 /* Simulation of heart beats */
138 heartrate++;
139 if (heartrate == 160) {
140 heartrate = 90;
141 }
142
143 om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm));
144
145 rc = ble_gattc_notify_custom(notify_conn_handle, hrs_hrm_handle, om);
146
147 assert(rc == 0);
148 blehr_tx_hrate_reset();
149 }
150
151 static int
blehr_gap_event(struct ble_gap_event * event,void * arg)152 blehr_gap_event(struct ble_gap_event *event, void *arg)
153 {
154 switch (event->type) {
155 case BLE_GAP_EVENT_CONNECT:
156 /* A new connection was established or a connection attempt failed */
157 MODLOG_DFLT(INFO, "connection %s; status=%d\n",
158 event->connect.status == 0 ? "established" : "failed",
159 event->connect.status);
160
161 if (event->connect.status != 0) {
162 /* Connection failed; resume advertising */
163 blehr_advertise();
164 }
165 break;
166
167 case BLE_GAP_EVENT_DISCONNECT:
168 MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
169
170 /* Connection terminated; resume advertising */
171 blehr_advertise();
172 break;
173
174 case BLE_GAP_EVENT_ADV_COMPLETE:
175 MODLOG_DFLT(INFO, "adv complete\n");
176 blehr_advertise();
177 break;
178
179 case BLE_GAP_EVENT_SUBSCRIBE:
180 MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "
181 "val_handle=%d\n",
182 event->subscribe.cur_notify, hrs_hrm_handle);
183 if (event->subscribe.attr_handle == hrs_hrm_handle) {
184 notify_state = event->subscribe.cur_notify;
185 notify_conn_handle = event->subscribe.conn_handle;
186 blehr_tx_hrate_reset();
187 } else if (event->subscribe.attr_handle != hrs_hrm_handle) {
188 notify_state = event->subscribe.cur_notify;
189 notify_conn_handle = 0;
190 blehr_tx_hrate_stop();
191 }
192 break;
193
194 case BLE_GAP_EVENT_MTU:
195 MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
196 event->mtu.conn_handle,
197 event->mtu.value);
198 break;
199
200 }
201
202 return 0;
203 }
204
205 static void
blehr_on_sync(void)206 blehr_on_sync(void)
207 {
208 int rc;
209
210 /* Use privacy */
211 rc = ble_hs_id_infer_auto(0, &blehr_addr_type);
212 assert(rc == 0);
213
214 /* Begin advertising */
215 blehr_advertise();
216 }
217
218 extern int nimble_ble_enable(void);
219 extern struct ble_npl_eventq *nimble_port_get_dflt_eventq(void);
ble_hr(void)220 static int ble_hr(void)
221 {
222 int rc;
223 static int init_flag = 0;
224
225 if (init_flag)
226 return 0;
227 init_flag = 1;
228
229 /* Initialize the NimBLE host configuration */
230 ble_hs_cfg.sync_cb = blehr_on_sync;
231
232 ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),
233 blehr_tx_hrate, NULL);
234
235 rc = gatt_svr_init();
236
237 /* Set the default device name */
238 rc = ble_svc_gap_device_name_set(device_name);
239 RT_ASSERT(rc == 0);
240
241 /* startup bluetooth host stack*/
242 ble_hs_thread_startup();
243
244 return 0;
245 }
246 MSH_CMD_EXPORT_ALIAS(ble_hr, ble_hr, "bluetoooth heartrate senson sample");
247