1 /* main.c - Bluetooth Cycling Speed and Cadence app main entry point */
2 
3 /*
4  * Copyright (c) 2016 Intel Corporation
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <stdbool.h>
10 #include <zephyr/types.h>
11 #include <stddef.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <zephyr/random/random.h>
15 #include <zephyr/sys/printk.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/kernel.h>
18 
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/hci.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/uuid.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/hci.h>
25 #include <zephyr/bluetooth/services/bas.h>
26 
27 #define CSC_SUPPORTED_LOCATIONS		{ CSC_LOC_OTHER, \
28 					  CSC_LOC_FRONT_WHEEL, \
29 					  CSC_LOC_REAR_WHEEL, \
30 					  CSC_LOC_LEFT_CRANK, \
31 					  CSC_LOC_RIGHT_CRANK }
32 #define CSC_FEATURE			(CSC_FEAT_WHEEL_REV | \
33 					 CSC_FEAT_CRANK_REV | \
34 					 CSC_FEAT_MULTI_SENSORS)
35 
36 /* CSC Sensor Locations */
37 #define CSC_LOC_OTHER			0x00
38 #define CSC_LOC_TOP_OF_SHOE		0x01
39 #define CSC_LOC_IN_SHOE			0x02
40 #define CSC_LOC_HIP			0x03
41 #define CSC_LOC_FRONT_WHEEL		0x04
42 #define CSC_LOC_LEFT_CRANK		0x05
43 #define CSC_LOC_RIGHT_CRANK		0x06
44 #define CSC_LOC_LEFT_PEDAL		0x07
45 #define CSC_LOC_RIGHT_PEDAL		0x08
46 #define CSC_LOC_FRONT_HUB		0x09
47 #define CSC_LOC_REAR_DROPOUT		0x0a
48 #define CSC_LOC_CHAINSTAY		0x0b
49 #define CSC_LOC_REAR_WHEEL		0x0c
50 #define CSC_LOC_REAR_HUB		0x0d
51 #define CSC_LOC_CHEST			0x0e
52 
53 /* CSC Application error codes */
54 #define CSC_ERR_IN_PROGRESS		0x80
55 #define CSC_ERR_CCC_CONFIG		0x81
56 
57 /* SC Control Point Opcodes */
58 #define SC_CP_OP_SET_CWR		0x01
59 #define SC_CP_OP_CALIBRATION		0x02
60 #define SC_CP_OP_UPDATE_LOC		0x03
61 #define SC_CP_OP_REQ_SUPP_LOC		0x04
62 #define SC_CP_OP_RESPONSE		0x10
63 
64 /* SC Control Point Response Values */
65 #define SC_CP_RSP_SUCCESS		0x01
66 #define SC_CP_RSP_OP_NOT_SUPP		0x02
67 #define SC_CP_RSP_INVAL_PARAM		0x03
68 #define SC_CP_RSP_FAILED		0x04
69 
70 /* CSC Feature */
71 #define CSC_FEAT_WHEEL_REV		BIT(0)
72 #define CSC_FEAT_CRANK_REV		BIT(1)
73 #define CSC_FEAT_MULTI_SENSORS		BIT(2)
74 
75 /* CSC Measurement Flags */
76 #define CSC_WHEEL_REV_DATA_PRESENT	BIT(0)
77 #define CSC_CRANK_REV_DATA_PRESENT	BIT(1)
78 
79 /* Cycling Speed and Cadence Service declaration */
80 
81 static uint32_t c_wheel_revs; /* Cumulative Wheel Revolutions */
82 static uint8_t supported_locations[] = CSC_SUPPORTED_LOCATIONS;
83 static uint8_t sensor_location; /* Current Sensor Location */
84 static bool csc_simulate;
85 static bool ctrl_point_configured;
86 
csc_meas_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)87 static void csc_meas_ccc_cfg_changed(const struct bt_gatt_attr *attr,
88 				     uint16_t value)
89 {
90 	csc_simulate = value == BT_GATT_CCC_NOTIFY;
91 }
92 
ctrl_point_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)93 static void ctrl_point_ccc_cfg_changed(const struct bt_gatt_attr *attr,
94 				       uint16_t value)
95 {
96 	ctrl_point_configured = value == BT_GATT_CCC_INDICATE;
97 }
98 
read_location(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)99 static ssize_t read_location(struct bt_conn *conn,
100 			     const struct bt_gatt_attr *attr, void *buf,
101 			     uint16_t len, uint16_t offset)
102 {
103 	uint8_t *value = attr->user_data;
104 
105 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
106 				 sizeof(*value));
107 }
108 
read_csc_feature(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)109 static ssize_t read_csc_feature(struct bt_conn *conn,
110 				const struct bt_gatt_attr *attr, void *buf,
111 				uint16_t len, uint16_t offset)
112 {
113 	uint16_t csc_feature = CSC_FEATURE;
114 
115 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
116 				 &csc_feature, sizeof(csc_feature));
117 }
118 
119 static void ctrl_point_ind(struct bt_conn *conn, uint8_t req_op, uint8_t status,
120 			   const void *data, uint16_t data_len);
121 
122 struct write_sc_ctrl_point_req {
123 	uint8_t op;
124 	union {
125 		uint32_t cwr;
126 		uint8_t location;
127 	};
128 } __packed;
129 
write_ctrl_point(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)130 static ssize_t write_ctrl_point(struct bt_conn *conn,
131 				const struct bt_gatt_attr *attr,
132 				const void *buf, uint16_t len, uint16_t offset,
133 				uint8_t flags)
134 {
135 	const struct write_sc_ctrl_point_req *req = buf;
136 	uint8_t status;
137 	int i;
138 
139 	if (!ctrl_point_configured) {
140 		return BT_GATT_ERR(CSC_ERR_CCC_CONFIG);
141 	}
142 
143 	if (!len) {
144 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
145 	}
146 
147 	switch (req->op) {
148 	case SC_CP_OP_SET_CWR:
149 		if (len != sizeof(req->op) + sizeof(req->cwr)) {
150 			status = SC_CP_RSP_INVAL_PARAM;
151 			break;
152 		}
153 
154 		c_wheel_revs = sys_le32_to_cpu(req->cwr);
155 		status = SC_CP_RSP_SUCCESS;
156 		break;
157 	case SC_CP_OP_UPDATE_LOC:
158 		if (len != sizeof(req->op) + sizeof(req->location)) {
159 			status = SC_CP_RSP_INVAL_PARAM;
160 			break;
161 		}
162 
163 		/* Break if the requested location is the same as current one */
164 		if (req->location == sensor_location) {
165 			status = SC_CP_RSP_SUCCESS;
166 			break;
167 		}
168 
169 		/* Pre-set status */
170 		status = SC_CP_RSP_INVAL_PARAM;
171 
172 		/* Check if requested location is supported */
173 		for (i = 0; i < ARRAY_SIZE(supported_locations); i++) {
174 			if (supported_locations[i] == req->location) {
175 				sensor_location = req->location;
176 				status = SC_CP_RSP_SUCCESS;
177 				break;
178 			}
179 		}
180 
181 		break;
182 	case SC_CP_OP_REQ_SUPP_LOC:
183 		if (len != sizeof(req->op)) {
184 			status = SC_CP_RSP_INVAL_PARAM;
185 			break;
186 		}
187 
188 		/* Indicate supported locations and return */
189 		ctrl_point_ind(conn, req->op, SC_CP_RSP_SUCCESS,
190 			       &supported_locations,
191 			       sizeof(supported_locations));
192 
193 		return len;
194 	default:
195 		status = SC_CP_RSP_OP_NOT_SUPP;
196 	}
197 
198 	ctrl_point_ind(conn, req->op, status, NULL, 0);
199 
200 	return len;
201 }
202 
203 BT_GATT_SERVICE_DEFINE(csc_svc,
204 	BT_GATT_PRIMARY_SERVICE(BT_UUID_CSC),
205 	BT_GATT_CHARACTERISTIC(BT_UUID_CSC_MEASUREMENT, BT_GATT_CHRC_NOTIFY,
206 			       0x00, NULL, NULL, NULL),
207 	BT_GATT_CCC(csc_meas_ccc_cfg_changed,
208 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
209 	BT_GATT_CHARACTERISTIC(BT_UUID_SENSOR_LOCATION, BT_GATT_CHRC_READ,
210 			       BT_GATT_PERM_READ, read_location, NULL,
211 			       &sensor_location),
212 	BT_GATT_CHARACTERISTIC(BT_UUID_CSC_FEATURE, BT_GATT_CHRC_READ,
213 			       BT_GATT_PERM_READ, read_csc_feature, NULL, NULL),
214 	BT_GATT_CHARACTERISTIC(BT_UUID_SC_CONTROL_POINT,
215 			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE,
216 			       BT_GATT_PERM_WRITE, NULL, write_ctrl_point,
217 			       &sensor_location),
218 	BT_GATT_CCC(ctrl_point_ccc_cfg_changed,
219 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
220 );
221 
222 struct sc_ctrl_point_ind {
223 	uint8_t op;
224 	uint8_t req_op;
225 	uint8_t status;
226 	uint8_t data[];
227 } __packed;
228 
ctrl_point_ind(struct bt_conn * conn,uint8_t req_op,uint8_t status,const void * data,uint16_t data_len)229 static void ctrl_point_ind(struct bt_conn *conn, uint8_t req_op, uint8_t status,
230 			   const void *data, uint16_t data_len)
231 {
232 	struct sc_ctrl_point_ind *ind;
233 	uint8_t buf[sizeof(*ind) + data_len];
234 
235 	ind = (void *) buf;
236 	ind->op = SC_CP_OP_RESPONSE;
237 	ind->req_op = req_op;
238 	ind->status = status;
239 
240 	/* Send data (supported locations) if present */
241 	if (data && data_len) {
242 		memcpy(ind->data, data, data_len);
243 	}
244 
245 	bt_gatt_notify(conn, &csc_svc.attrs[8], buf, sizeof(buf));
246 }
247 
248 struct csc_measurement_nfy {
249 	uint8_t flags;
250 	uint8_t data[];
251 } __packed;
252 
253 struct wheel_rev_data_nfy {
254 	uint32_t cwr;
255 	uint16_t lwet;
256 } __packed;
257 
258 struct crank_rev_data_nfy {
259 	uint16_t ccr;
260 	uint16_t lcet;
261 } __packed;
262 
measurement_nfy(struct bt_conn * conn,uint32_t cwr,uint16_t lwet,uint16_t ccr,uint16_t lcet)263 static void measurement_nfy(struct bt_conn *conn, uint32_t cwr, uint16_t lwet,
264 			    uint16_t ccr, uint16_t lcet)
265 {
266 	struct csc_measurement_nfy *nfy;
267 	uint8_t buf[sizeof(*nfy) +
268 		    (cwr ? sizeof(struct wheel_rev_data_nfy) : 0) +
269 		    (ccr ? sizeof(struct crank_rev_data_nfy) : 0)];
270 	uint16_t len = 0U;
271 
272 	nfy = (void *) buf;
273 	nfy->flags = 0U;
274 
275 	/* Send Wheel Revolution data is present */
276 	if (cwr) {
277 		struct wheel_rev_data_nfy data;
278 
279 		nfy->flags |= CSC_WHEEL_REV_DATA_PRESENT;
280 		data.cwr = sys_cpu_to_le32(cwr);
281 		data.lwet = sys_cpu_to_le16(lwet);
282 
283 		memcpy(nfy->data, &data, sizeof(data));
284 		len += sizeof(data);
285 	}
286 
287 	/* Send Crank Revolution data is present */
288 	if (ccr) {
289 		struct crank_rev_data_nfy data;
290 
291 		nfy->flags |= CSC_CRANK_REV_DATA_PRESENT;
292 		data.ccr = sys_cpu_to_le16(ccr);
293 		data.lcet = sys_cpu_to_le16(lcet);
294 
295 		memcpy(nfy->data + len, &data, sizeof(data));
296 	}
297 
298 	bt_gatt_notify(NULL, &csc_svc.attrs[1], buf, sizeof(buf));
299 }
300 
301 static uint16_t lwet; /* Last Wheel Event Time */
302 static uint16_t ccr;  /* Cumulative Crank Revolutions */
303 static uint16_t lcet; /* Last Crank Event Time */
304 
csc_simulation(void)305 static void csc_simulation(void)
306 {
307 	static uint8_t i;
308 	uint8_t rnd = sys_rand8_get();
309 	bool nfy_crank = false, nfy_wheel = false;
310 
311 	/* Measurements don't have to be updated every second */
312 	if (!(i % 2)) {
313 		lwet += 1050 + rnd % 50;
314 		c_wheel_revs += 2U;
315 		nfy_wheel = true;
316 	}
317 
318 	if (!(i % 3)) {
319 		lcet += 1000 + rnd % 50;
320 		ccr += 1U;
321 		nfy_crank = true;
322 	}
323 
324 	/*
325 	 * In typical applications, the CSC Measurement characteristic is
326 	 * notified approximately once per second. This interval may vary
327 	 * and is determined by the Server and not required to be configurable
328 	 * by the Client.
329 	 */
330 	measurement_nfy(NULL, nfy_wheel ? c_wheel_revs : 0, nfy_wheel ? lwet : 0,
331 			nfy_crank ? ccr : 0, nfy_crank ? lcet : 0);
332 
333 	/*
334 	 * The Last Crank Event Time value and Last Wheel Event Time roll over
335 	 * every 64 seconds.
336 	 */
337 	if (!(i % 64)) {
338 		lcet = 0U;
339 		lwet = 0U;
340 		i = 0U;
341 	}
342 
343 	i++;
344 }
345 
connected(struct bt_conn * conn,uint8_t err)346 static void connected(struct bt_conn *conn, uint8_t err)
347 {
348 	if (err) {
349 		printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
350 	} else {
351 		printk("Connected\n");
352 	}
353 }
354 
disconnected(struct bt_conn * conn,uint8_t reason)355 static void disconnected(struct bt_conn *conn, uint8_t reason)
356 {
357 	printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
358 }
359 
360 BT_CONN_CB_DEFINE(conn_callbacks) = {
361 	.connected = connected,
362 	.disconnected = disconnected,
363 };
364 
365 static const struct bt_data ad[] = {
366 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
367 	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
368 		      BT_UUID_16_ENCODE(BT_UUID_CSC_VAL),
369 		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL))
370 };
371 
372 static const struct bt_data sd[] = {
373 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
374 };
375 
bt_ready(void)376 static void bt_ready(void)
377 {
378 	int err;
379 
380 	printk("Bluetooth initialized\n");
381 
382 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
383 	if (err) {
384 		printk("Advertising failed to start (err %d)\n", err);
385 		return;
386 	}
387 
388 	printk("Advertising successfully started\n");
389 }
390 
bas_notify(void)391 static void bas_notify(void)
392 {
393 	uint8_t battery_level = bt_bas_get_battery_level();
394 
395 	battery_level--;
396 
397 	if (!battery_level) {
398 		battery_level = 100U;
399 	}
400 
401 	bt_bas_set_battery_level(battery_level);
402 }
403 
main(void)404 int main(void)
405 {
406 	int err;
407 
408 	err = bt_enable(NULL);
409 	if (err) {
410 		printk("Bluetooth init failed (err %d)\n", err);
411 		return 0;
412 	}
413 
414 	bt_ready();
415 
416 	while (1) {
417 		k_sleep(K_SECONDS(1));
418 
419 		/* CSC simulation */
420 		if (csc_simulate) {
421 			csc_simulation();
422 		}
423 
424 		/* Battery level simulation */
425 		bas_notify();
426 	}
427 	return 0;
428 }
429