1 /* main.c - Application 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/sys/printk.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/kernel.h>
17 
18 #include <zephyr/bluetooth/bluetooth.h>
19 #include <zephyr/bluetooth/hci.h>
20 #include <zephyr/bluetooth/conn.h>
21 #include <zephyr/bluetooth/uuid.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/services/bas.h>
24 
25 #define SENSOR_1_NAME				"Temperature Sensor 1"
26 #define SENSOR_2_NAME				"Temperature Sensor 2"
27 #define SENSOR_3_NAME				"Humidity Sensor"
28 
29 /* Sensor Internal Update Interval [seconds] */
30 #define SENSOR_1_UPDATE_IVAL			5
31 #define SENSOR_2_UPDATE_IVAL			12
32 #define SENSOR_3_UPDATE_IVAL			60
33 
34 /* ESS error definitions */
35 #define ESS_ERR_WRITE_REJECT					0x80
36 #define ESS_ERR_COND_NOT_SUPP					0x81
37 
38 /* ESS Trigger Setting conditions */
39 #define ESS_TRIGGER_INACTIVE					0x00
40 #define ESS_TRIGGER_FIXED_TIME_INTERVAL				0x01
41 #define ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME			0x02
42 #define ESS_TRIGGER_VALUE_CHANGED				0x03
43 #define ESS_TRIGGER_LESS_THAN_REF_VALUE				0x04
44 #define ESS_TRIGGER_LESS_OR_EQUAL_TO_REF_VALUE			0x05
45 #define ESS_TRIGGER_GREATER_THAN_REF_VALUE			0x06
46 #define ESS_TRIGGER_GREATER_OR_EQUAL_TO_REF_VALUE		0x07
47 #define ESS_TRIGGER_EQUAL_TO_REF_VALUE				0x08
48 #define ESS_TRIGGER_NOT_EQUAL_TO_REF_VALUE			0x09
49 
50 /* ESS Measurement Descriptor – Sampling Functions */
51 #define ESS_DESC_SAMPLING_UNSPECIFIED				0x00
52 #define ESS_DESC_SAMPLING_INSTANTANEOUS				0x01
53 #define ESS_DESC_SAMPLING_ARITHMETIC_MEAN			0x02
54 #define ESS_DESC_SAMPLING_RMS					0x03
55 #define ESS_DESC_SAMPLING_MAXIMUM				0x04
56 #define ESS_DESC_SAMPLING_MINIMUM				0x05
57 #define ESS_DESC_SAMPLING_ACCUMULATED				0x06
58 #define ESS_DESC_SAMPLING_COUNT					0x07
59 
60 /* ES Measurement Descriptor - Applications */
61 #define ESS_DESC_APP_UNSPECIFIED				0x00
62 #define ESS_DESC_APP_AIR					0x01
63 #define ESS_DESC_APP_WATER					0x02
64 #define ESS_DESC_APP_BAROMETRIC					0x03
65 #define ESS_DESC_APP_SOIL					0x04
66 #define ESS_DESC_APP_INFRARED					0x05
67 #define ESS_DESC_APP_MAP_DATABASE				0x06
68 #define ESS_DESC_APP_BAROMETRIC_ELEVATION_SOURCE		0x07
69 #define ESS_DESC_APP_GPS_ONLY_ELEVATION_SOURCE			0x08
70 #define ESS_DESC_APP_GPS_AND_MAP_DATABASE_ELEVATION_SOURCE	0x09
71 #define ESS_DESC_APP_VERTICAL_DATUM_ELEVATION_SOURCE		0x0A
72 #define ESS_DESC_APP_ONSHORE					0x0B
73 #define ESS_DESC_APP_ONBOARD_VESSEL_OR_VEHICLE			0x0C
74 #define ESS_DESC_APP_FRONT					0x0D
75 #define ESS_DESC_APP_BACK_REAR					0x0E
76 #define ESS_DESC_APP_UPPER					0x0F
77 #define ESS_DESC_APP_LOWER					0x10
78 #define ESS_DESC_APP_PRIMARY					0x11
79 #define ESS_DESC_APP_SECONDARY					0x12
80 #define ESS_DESC_APP_OUTDOOR					0x13
81 #define ESS_DESC_APP_INDOOR					0x14
82 #define ESS_DESC_APP_TOP					0x15
83 #define ESS_DESC_APP_BOTTOM					0x16
84 #define ESS_DESC_APP_MAIN					0x17
85 #define ESS_DESC_APP_BACKUP					0x18
86 #define ESS_DESC_APP_AUXILIARY					0x19
87 #define ESS_DESC_APP_SUPPLEMENTARY				0x1A
88 #define ESS_DESC_APP_INSIDE					0x1B
89 #define ESS_DESC_APP_OUTSIDE					0x1C
90 #define ESS_DESC_APP_LEFT					0x1D
91 #define ESS_DESC_APP_RIGHT					0x1E
92 #define ESS_DESC_APP_INTERNAL					0x1F
93 #define ESS_DESC_APP_EXTERNAL					0x20
94 #define ESS_DESC_APP_SOLAR					0x21
95 
read_u16(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)96 static ssize_t read_u16(struct bt_conn *conn, const struct bt_gatt_attr *attr,
97 			void *buf, uint16_t len, uint16_t offset)
98 {
99 	const uint16_t *u16 = attr->user_data;
100 	uint16_t value = sys_cpu_to_le16(*u16);
101 
102 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &value,
103 				 sizeof(value));
104 }
105 
106 /* Environmental Sensing Service Declaration */
107 
108 struct es_measurement {
109 	uint16_t flags; /* Reserved for Future Use */
110 	uint8_t sampling_func;
111 	uint32_t meas_period;
112 	uint32_t update_interval;
113 	uint8_t application;
114 	uint8_t meas_uncertainty;
115 };
116 
117 struct temperature_sensor {
118 	int16_t temp_value;
119 
120 	/* Valid Range */
121 	int16_t lower_limit;
122 	int16_t upper_limit;
123 
124 	/* ES trigger setting - Value Notification condition */
125 	uint8_t condition;
126 	union {
127 		uint32_t seconds;
128 		int16_t ref_val; /* Reference temperature */
129 	};
130 
131 	struct es_measurement meas;
132 };
133 
134 struct humidity_sensor {
135 	int16_t humid_value;
136 
137 	struct es_measurement meas;
138 };
139 
140 static bool simulate_temp;
141 static struct temperature_sensor sensor_1 = {
142 		.temp_value = 1200,
143 		.lower_limit = -10000,
144 		.upper_limit = 10000,
145 		.condition = ESS_TRIGGER_VALUE_CHANGED,
146 		.meas.sampling_func = ESS_DESC_SAMPLING_UNSPECIFIED,
147 		.meas.meas_period = 0x01,
148 		.meas.update_interval = SENSOR_1_UPDATE_IVAL,
149 		.meas.application = ESS_DESC_APP_OUTSIDE,
150 		.meas.meas_uncertainty = 0x04,
151 };
152 
153 static struct temperature_sensor sensor_2 = {
154 		.temp_value = 1800,
155 		.lower_limit = -1000,
156 		.upper_limit = 5000,
157 		.condition = ESS_TRIGGER_VALUE_CHANGED,
158 		.meas.sampling_func = ESS_DESC_SAMPLING_UNSPECIFIED,
159 		.meas.meas_period = 0x01,
160 		.meas.update_interval = SENSOR_2_UPDATE_IVAL,
161 		.meas.application = ESS_DESC_APP_INSIDE,
162 		.meas.meas_uncertainty = 0x04,
163 };
164 
165 static struct humidity_sensor sensor_3 = {
166 		.humid_value = 6233,
167 		.meas.sampling_func = ESS_DESC_SAMPLING_ARITHMETIC_MEAN,
168 		.meas.meas_period = 0x0e10,
169 		.meas.update_interval = SENSOR_3_UPDATE_IVAL,
170 		.meas.application = ESS_DESC_APP_OUTSIDE,
171 		.meas.meas_uncertainty = 0x01,
172 };
173 
temp_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)174 static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr,
175 				 uint16_t value)
176 {
177 	simulate_temp = value == BT_GATT_CCC_NOTIFY;
178 }
179 
180 struct read_es_measurement_rp {
181 	uint16_t flags; /* Reserved for Future Use */
182 	uint8_t sampling_function;
183 	uint8_t measurement_period[3];
184 	uint8_t update_interval[3];
185 	uint8_t application;
186 	uint8_t measurement_uncertainty;
187 } __packed;
188 
read_es_measurement(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)189 static ssize_t read_es_measurement(struct bt_conn *conn,
190 				   const struct bt_gatt_attr *attr, void *buf,
191 				   uint16_t len, uint16_t offset)
192 {
193 	const struct es_measurement *value = attr->user_data;
194 	struct read_es_measurement_rp rsp;
195 
196 	rsp.flags = sys_cpu_to_le16(value->flags);
197 	rsp.sampling_function = value->sampling_func;
198 	sys_put_le24(value->meas_period, rsp.measurement_period);
199 	sys_put_le24(value->update_interval, rsp.update_interval);
200 	rsp.application = value->application;
201 	rsp.measurement_uncertainty = value->meas_uncertainty;
202 
203 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &rsp,
204 				 sizeof(rsp));
205 }
206 
read_temp_valid_range(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)207 static ssize_t read_temp_valid_range(struct bt_conn *conn,
208 				     const struct bt_gatt_attr *attr, void *buf,
209 				     uint16_t len, uint16_t offset)
210 {
211 	const struct temperature_sensor *sensor = attr->user_data;
212 	uint16_t tmp[] = {sys_cpu_to_le16(sensor->lower_limit),
213 			  sys_cpu_to_le16(sensor->upper_limit)};
214 
215 	return bt_gatt_attr_read(conn, attr, buf, len, offset, tmp,
216 				 sizeof(tmp));
217 }
218 
219 struct es_trigger_setting_seconds {
220 	uint8_t condition;
221 	uint8_t sec[3];
222 } __packed;
223 
224 struct es_trigger_setting_reference {
225 	uint8_t condition;
226 	int16_t ref_val;
227 } __packed;
228 
read_temp_trigger_setting(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)229 static ssize_t read_temp_trigger_setting(struct bt_conn *conn,
230 					 const struct bt_gatt_attr *attr,
231 					 void *buf, uint16_t len,
232 					 uint16_t offset)
233 {
234 	const struct temperature_sensor *sensor = attr->user_data;
235 
236 	switch (sensor->condition) {
237 	/* Operand N/A */
238 	case ESS_TRIGGER_INACTIVE:
239 		__fallthrough;
240 	case ESS_TRIGGER_VALUE_CHANGED:
241 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
242 					 &sensor->condition,
243 					 sizeof(sensor->condition));
244 	/* Seconds */
245 	case ESS_TRIGGER_FIXED_TIME_INTERVAL:
246 		__fallthrough;
247 	case ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME: {
248 			struct es_trigger_setting_seconds rp;
249 
250 			rp.condition = sensor->condition;
251 			sys_put_le24(sensor->seconds, rp.sec);
252 
253 			return bt_gatt_attr_read(conn, attr, buf, len, offset,
254 						 &rp, sizeof(rp));
255 		}
256 	/* Reference temperature */
257 	default: {
258 			struct es_trigger_setting_reference rp;
259 
260 			rp.condition = sensor->condition;
261 			rp.ref_val = sys_cpu_to_le16(sensor->ref_val);
262 
263 			return bt_gatt_attr_read(conn, attr, buf, len, offset,
264 						 &rp, sizeof(rp));
265 		}
266 	}
267 }
268 
check_condition(uint8_t condition,int16_t old_val,int16_t new_val,int16_t ref_val)269 static bool check_condition(uint8_t condition, int16_t old_val, int16_t new_val,
270 			    int16_t ref_val)
271 {
272 	switch (condition) {
273 	case ESS_TRIGGER_INACTIVE:
274 		return false;
275 	case ESS_TRIGGER_FIXED_TIME_INTERVAL:
276 	case ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME:
277 		/* TODO: Check time requirements */
278 		return false;
279 	case ESS_TRIGGER_VALUE_CHANGED:
280 		return new_val != old_val;
281 	case ESS_TRIGGER_LESS_THAN_REF_VALUE:
282 		return new_val < ref_val;
283 	case ESS_TRIGGER_LESS_OR_EQUAL_TO_REF_VALUE:
284 		return new_val <= ref_val;
285 	case ESS_TRIGGER_GREATER_THAN_REF_VALUE:
286 		return new_val > ref_val;
287 	case ESS_TRIGGER_GREATER_OR_EQUAL_TO_REF_VALUE:
288 		return new_val >= ref_val;
289 	case ESS_TRIGGER_EQUAL_TO_REF_VALUE:
290 		return new_val == ref_val;
291 	case ESS_TRIGGER_NOT_EQUAL_TO_REF_VALUE:
292 		return new_val != ref_val;
293 	default:
294 		return false;
295 	}
296 }
297 
update_temperature(struct bt_conn * conn,const struct bt_gatt_attr * chrc,int16_t value,struct temperature_sensor * sensor)298 static void update_temperature(struct bt_conn *conn,
299 			       const struct bt_gatt_attr *chrc, int16_t value,
300 			       struct temperature_sensor *sensor)
301 {
302 	bool notify = check_condition(sensor->condition,
303 				      sensor->temp_value, value,
304 				      sensor->ref_val);
305 
306 	/* Update temperature value */
307 	sensor->temp_value = value;
308 
309 	/* Trigger notification if conditions are met */
310 	if (notify) {
311 		value = sys_cpu_to_le16(sensor->temp_value);
312 
313 		bt_gatt_notify(conn, chrc, &value, sizeof(value));
314 	}
315 }
316 
317 BT_GATT_SERVICE_DEFINE(ess_svc,
318 	BT_GATT_PRIMARY_SERVICE(BT_UUID_ESS),
319 
320 	/* Temperature Sensor 1 */
321 	BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
322 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
323 			       BT_GATT_PERM_READ,
324 			       read_u16, NULL, &sensor_1.temp_value),
325 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
326 			   read_es_measurement, NULL, &sensor_1.meas),
327 	BT_GATT_CUD(SENSOR_1_NAME, BT_GATT_PERM_READ),
328 	BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
329 			   read_temp_valid_range, NULL, &sensor_1),
330 	BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
331 			   BT_GATT_PERM_READ, read_temp_trigger_setting,
332 			   NULL, &sensor_1),
333 	BT_GATT_CCC(temp_ccc_cfg_changed,
334 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
335 
336 	/* Temperature Sensor 2 */
337 	BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
338 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
339 			       BT_GATT_PERM_READ,
340 			       read_u16, NULL, &sensor_2.temp_value),
341 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
342 			   read_es_measurement, NULL, &sensor_2.meas),
343 	BT_GATT_CUD(SENSOR_2_NAME, BT_GATT_PERM_READ),
344 	BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
345 			   read_temp_valid_range, NULL, &sensor_2),
346 	BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
347 			   BT_GATT_PERM_READ, read_temp_trigger_setting,
348 			   NULL, &sensor_2),
349 	BT_GATT_CCC(temp_ccc_cfg_changed,
350 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
351 
352 	/* Humidity Sensor */
353 	BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY, BT_GATT_CHRC_READ,
354 			       BT_GATT_PERM_READ,
355 			       read_u16, NULL, &sensor_3.humid_value),
356 	BT_GATT_CUD(SENSOR_3_NAME, BT_GATT_PERM_READ),
357 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
358 			   read_es_measurement, NULL, &sensor_3.meas),
359 );
360 
ess_simulate(void)361 static void ess_simulate(void)
362 {
363 	static uint8_t i;
364 	uint16_t val;
365 
366 	if (!(i % SENSOR_1_UPDATE_IVAL)) {
367 		val = 1200 + i;
368 		update_temperature(NULL, &ess_svc.attrs[2], val, &sensor_1);
369 	}
370 
371 	if (!(i % SENSOR_2_UPDATE_IVAL)) {
372 		val = 1800 + i;
373 		update_temperature(NULL, &ess_svc.attrs[9], val, &sensor_2);
374 	}
375 
376 	if (!(i % SENSOR_3_UPDATE_IVAL)) {
377 		sensor_3.humid_value = 6233 + (i % 13);
378 	}
379 
380 	if (!(i % INT8_MAX)) {
381 		i = 0U;
382 	}
383 
384 	i++;
385 }
386 
387 static const struct bt_data ad[] = {
388 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
389 	BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0x00, 0x03),
390 	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
391 		      BT_UUID_16_ENCODE(BT_UUID_ESS_VAL),
392 		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
393 };
394 
395 static const struct bt_data sd[] = {
396 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
397 };
398 
connected(struct bt_conn * conn,uint8_t err)399 static void connected(struct bt_conn *conn, uint8_t err)
400 {
401 	if (err) {
402 		printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
403 	} else {
404 		printk("Connected\n");
405 	}
406 }
407 
disconnected(struct bt_conn * conn,uint8_t reason)408 static void disconnected(struct bt_conn *conn, uint8_t reason)
409 {
410 	printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
411 }
412 
413 BT_CONN_CB_DEFINE(conn_callbacks) = {
414 	.connected = connected,
415 	.disconnected = disconnected,
416 };
417 
bt_ready(void)418 static void bt_ready(void)
419 {
420 	int err;
421 
422 	printk("Bluetooth initialized\n");
423 
424 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
425 	if (err) {
426 		printk("Advertising failed to start (err %d)\n", err);
427 		return;
428 	}
429 
430 	printk("Advertising successfully started\n");
431 }
432 
auth_passkey_display(struct bt_conn * conn,unsigned int passkey)433 static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
434 {
435 	char addr[BT_ADDR_LE_STR_LEN];
436 
437 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
438 
439 	printk("Passkey for %s: %06u\n", addr, passkey);
440 }
441 
auth_cancel(struct bt_conn * conn)442 static void auth_cancel(struct bt_conn *conn)
443 {
444 	char addr[BT_ADDR_LE_STR_LEN];
445 
446 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
447 
448 	printk("Pairing cancelled: %s\n", addr);
449 }
450 
451 static struct bt_conn_auth_cb auth_cb_display = {
452 	.passkey_display = auth_passkey_display,
453 	.passkey_entry = NULL,
454 	.cancel = auth_cancel,
455 };
456 
bas_notify(void)457 static void bas_notify(void)
458 {
459 	uint8_t battery_level = bt_bas_get_battery_level();
460 
461 	battery_level--;
462 
463 	if (!battery_level) {
464 		battery_level = 100U;
465 	}
466 
467 	bt_bas_set_battery_level(battery_level);
468 }
469 
main(void)470 int main(void)
471 {
472 	int err;
473 
474 	err = bt_enable(NULL);
475 	if (err) {
476 		printk("Bluetooth init failed (err %d)\n", err);
477 		return 0;
478 	}
479 
480 	bt_ready();
481 
482 	bt_conn_auth_cb_register(&auth_cb_display);
483 
484 	while (1) {
485 		k_sleep(K_SECONDS(1));
486 
487 		/* Temperature simulation */
488 		if (simulate_temp) {
489 			ess_simulate();
490 		}
491 
492 		/* Battery level simulation */
493 		bas_notify();
494 	}
495 	return 0;
496 }
497