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