xref: /nrf52832-nimble/packages/NimBLE-latest/apps/blecsc/src/blecsc_sens.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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 #include <errno.h>
24 
25 #include <rtthread.h>
26 
27 #include "config/config.h"
28 #include "nimble/ble.h"
29 #include "host/ble_hs.h"
30 #include "services/gap/ble_svc_gap.h"
31 #include "blecsc_sens.h"
32 
33 /* Wheel size for simulation calculations */
34 #define CSC_SIM_WHEEL_CIRCUMFERENCE_MM            2000
35 /* Simulated cadence lower limit */
36 #define CSC_SIM_CRANK_RPM_MIN                     20
37 /* Simulated cadence upper limit */
38 #define CSC_SIM_CRANK_RPM_MAX                     100
39 /* Simulated speed lower limit */
40 #define CSC_SIM_SPEED_KPH_MIN                     0
41 /* Simulated speed upper limit */
42 #define CSC_SIM_SPEED_KPH_MAX                     35
43 
44 /* Noticication status */
45 static bool notify_state = false;
46 
47 /* Connection handle */
48 static uint16_t conn_handle;
49 
50 static uint8_t blecsc_addr_type;
51 
52 /* Advertised device name  */
53 static const char *device_name = "blecsc_sensor";
54 
55 /* Measurement and notification timer */
56 static struct ble_npl_callout blecsc_measure_timer;
57 
58 /* Variable holds current CSC measurement state */
59 static struct ble_csc_measurement_state csc_measurement_state;
60 
61 /* Variable holds simulted speed (kilometers per hour) */
62 static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
63 
64 /* Variable holds simulated cadence (RPM) */
65 static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
66 
67 static int blecsc_gap_event(struct ble_gap_event *event, void *arg);
68 
69 
70 /*
71  * Enables advertising with parameters:
72  *     o General discoverable mode
73  *     o Undirected connectable mode
74  */
75 static void
blecsc_advertise(void)76 blecsc_advertise(void)
77 {
78     struct ble_gap_adv_params adv_params;
79     struct ble_hs_adv_fields fields;
80     int rc;
81 
82     /*
83      *  Set the advertisement data included in our advertisements:
84      *     o Flags (indicates advertisement type and other general info)
85      *     o Advertising tx power
86      *     o Device name
87      */
88     memset(&fields, 0, sizeof(fields));
89 
90     /*
91      * Advertise two flags:
92      *      o Discoverability in forthcoming advertisement (general)
93      *      o BLE-only (BR/EDR unsupported)
94      */
95     fields.flags = BLE_HS_ADV_F_DISC_GEN |
96                    BLE_HS_ADV_F_BREDR_UNSUP;
97 
98     /*
99      * Indicate that the TX power level field should be included; have the
100      * stack fill this value automatically.  This is done by assigning the
101      * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
102      */
103     fields.tx_pwr_lvl_is_present = 1;
104     fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
105 
106     fields.name = (uint8_t *)device_name;
107     fields.name_len = strlen(device_name);
108     fields.name_is_complete = 1;
109 
110     /*
111      * Set appearance.
112      */
113     fields.appearance = ble_svc_gap_device_appearance();
114     fields.appearance_is_present = 1;
115 
116     rc = ble_gap_adv_set_fields(&fields);
117     if (rc != 0) {
118         MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
119         return;
120     }
121 
122     /* Begin advertising */
123     memset(&adv_params, 0, sizeof(adv_params));
124     adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
125     adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
126     rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER,
127                            &adv_params, blecsc_gap_event, NULL);
128     if (rc != 0) {
129         MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
130         return;
131     }
132 }
133 
134 
135 /* Update simulated CSC measurements.
136  * Each call increments wheel and crank revolution counters by one and
137  * computes last event time in order to match simulated candence and speed.
138  * Last event time is expressedd in 1/1024th of second units.
139  *
140  *                 60 * 1024
141  * crank_dt =    --------------
142  *                cadence[RPM]
143  *
144  *
145  *                circumference[mm] * 1024 * 60 * 60
146  * wheel_dt =    -------------------------------------
147  *                         10^6 * speed [kph]
148  */
149 static void
blecsc_simulate_speed_and_cadence()150 blecsc_simulate_speed_and_cadence()
151 {
152     uint16_t wheel_rev_period;
153     uint16_t crank_rev_period;
154 
155     /* Update simulated crank and wheel rotation speed */
156     csc_sim_speed_kph++;
157     if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) {
158          csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
159     }
160 
161     csc_sim_crank_rpm++;
162     if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) {
163          csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
164     }
165 
166     /* Calculate simulated measurement values */
167     if (csc_sim_speed_kph > 0){
168         wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) /
169                            (625*csc_sim_speed_kph);
170         csc_measurement_state.cumulative_wheel_rev++;
171         csc_measurement_state.last_wheel_evt_time += wheel_rev_period;
172     }
173 
174     if (csc_sim_crank_rpm > 0){
175         crank_rev_period = (60*1024) / csc_sim_crank_rpm;
176         csc_measurement_state.cumulative_crank_rev++;
177         csc_measurement_state.last_crank_evt_time += crank_rev_period;
178     }
179 
180     MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n",
181                 csc_sim_speed_kph, csc_sim_crank_rpm);
182 }
183 
184 /* Run CSC measurement simulation and notify it to the client */
185 static void
blecsc_measurement(struct ble_npl_event * ev)186 blecsc_measurement(struct ble_npl_event *ev)
187 {
188     int rc;
189 
190     rc = ble_npl_callout_reset(&blecsc_measure_timer, RT_TICK_PER_SECOND);
191     assert(rc == 0);
192 
193     blecsc_simulate_speed_and_cadence();
194 
195     if (notify_state) {
196         rc = gatt_svr_chr_notify_csc_measurement(conn_handle);
197         assert(rc == 0);
198     }
199 }
200 
201 static int
blecsc_gap_event(struct ble_gap_event * event,void * arg)202 blecsc_gap_event(struct ble_gap_event *event, void *arg)
203 {
204     switch (event->type) {
205     case BLE_GAP_EVENT_CONNECT:
206         /* A new connection was established or a connection attempt failed */
207         MODLOG_DFLT(INFO, "connection %s; status=%d\n",
208                     event->connect.status == 0 ? "established" : "failed",
209                     event->connect.status);
210 
211         if (event->connect.status != 0) {
212             /* Connection failed; resume advertising */
213             blecsc_advertise();
214             conn_handle = 0;
215         }
216         else {
217           conn_handle = event->connect.conn_handle;
218         }
219         break;
220 
221     case BLE_GAP_EVENT_DISCONNECT:
222         MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
223         conn_handle = 0;
224         /* Connection terminated; resume advertising */
225         blecsc_advertise();
226         break;
227 
228     case BLE_GAP_EVENT_ADV_COMPLETE:
229         MODLOG_DFLT(INFO, "adv complete\n");
230         break;
231 
232     case BLE_GAP_EVENT_SUBSCRIBE:
233         MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n",
234                     event->subscribe.attr_handle);
235 
236         if (event->subscribe.attr_handle == csc_measurement_handle) {
237             notify_state = event->subscribe.cur_notify;
238             MODLOG_DFLT(INFO, "csc measurement notify state = %d\n",
239                         notify_state);
240         }
241         else if (event->subscribe.attr_handle == csc_control_point_handle) {
242             gatt_svr_set_cp_indicate(event->subscribe.cur_indicate);
243             MODLOG_DFLT(INFO, "csc control point indicate state = %d\n",
244                         event->subscribe.cur_indicate);
245         }
246         break;
247 
248     case BLE_GAP_EVENT_MTU:
249         MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
250                     event->mtu.conn_handle,
251                     event->mtu.value);
252         break;
253 
254     }
255 
256     return 0;
257 }
258 
259 static void
blecsc_on_sync(void)260 blecsc_on_sync(void)
261 {
262     int rc;
263 
264     /* Figure out address to use while advertising (no privacy) */
265     rc = ble_hs_id_infer_auto(0, &blecsc_addr_type);
266     assert(rc == 0);
267 
268     /* Begin advertising */
269     blecsc_advertise();
270 }
271 
272 extern struct ble_npl_eventq *nimble_port_get_dflt_eventq(void);
273 /*
274  * main
275  *
276  * The main task for the project. This function initializes the packages,
277  * then starts serving events from default event queue.
278  *
279  * @return int NOTE: this function should never return!
280  */
blecsc_sens_entry(void)281 int blecsc_sens_entry(void)
282 {
283     int rc;
284 
285 
286     /* Initialize the NimBLE host configuration */
287     ble_hs_cfg.sync_cb = blecsc_on_sync;
288 
289     /* Initialize measurement and notification timer */
290     ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(),
291                     blecsc_measurement, NULL);
292     rc = ble_npl_callout_reset(&blecsc_measure_timer, RT_TICK_PER_SECOND);
293     assert(rc == 0);
294 
295     rc = gatt_svr_init(&csc_measurement_state);
296     assert(rc == 0);
297 
298     /* Set the default device name */
299     rc = ble_svc_gap_device_name_set(device_name);
300     assert(rc == 0);
301 
302     /* startup bluetooth host stack*/
303     ble_hs_thread_startup();
304 
305     return 0;
306 }
307 
308 MSH_CMD_EXPORT_ALIAS(blecsc_sens_entry, blecsc_sens, "bluetooth cycle sensor sample");
309