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