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_FIXED_TIME_INTERVAL			0x01
41 #define ESS_NO_LESS_THAN_SPECIFIED_TIME		0x02
42 #define ESS_VALUE_CHANGED			0x03
43 #define ESS_LESS_THAN_REF_VALUE			0x04
44 #define ESS_LESS_OR_EQUAL_TO_REF_VALUE		0x05
45 #define ESS_GREATER_THAN_REF_VALUE		0x06
46 #define ESS_GREATER_OR_EQUAL_TO_REF_VALUE	0x07
47 #define ESS_EQUAL_TO_REF_VALUE			0x08
48 #define ESS_NOT_EQUAL_TO_REF_VALUE		0x09
49 
read_u16(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)50 static ssize_t read_u16(struct bt_conn *conn, const struct bt_gatt_attr *attr,
51 			void *buf, uint16_t len, uint16_t offset)
52 {
53 	const uint16_t *u16 = attr->user_data;
54 	uint16_t value = sys_cpu_to_le16(*u16);
55 
56 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &value,
57 				 sizeof(value));
58 }
59 
60 /* Environmental Sensing Service Declaration */
61 
62 struct es_measurement {
63 	uint16_t flags; /* Reserved for Future Use */
64 	uint8_t sampling_func;
65 	uint32_t meas_period;
66 	uint32_t update_interval;
67 	uint8_t application;
68 	uint8_t meas_uncertainty;
69 };
70 
71 struct temperature_sensor {
72 	int16_t temp_value;
73 
74 	/* Valid Range */
75 	int16_t lower_limit;
76 	int16_t upper_limit;
77 
78 	/* ES trigger setting - Value Notification condition */
79 	uint8_t condition;
80 	union {
81 		uint32_t seconds;
82 		int16_t ref_val; /* Reference temperature */
83 	};
84 
85 	struct es_measurement meas;
86 };
87 
88 struct humidity_sensor {
89 	int16_t humid_value;
90 
91 	struct es_measurement meas;
92 };
93 
94 static bool simulate_temp;
95 static struct temperature_sensor sensor_1 = {
96 		.temp_value = 1200,
97 		.lower_limit = -10000,
98 		.upper_limit = 10000,
99 		.condition = ESS_VALUE_CHANGED,
100 		.meas.sampling_func = 0x00,
101 		.meas.meas_period = 0x01,
102 		.meas.update_interval = SENSOR_1_UPDATE_IVAL,
103 		.meas.application = 0x1c,
104 		.meas.meas_uncertainty = 0x04,
105 };
106 
107 static struct temperature_sensor sensor_2 = {
108 		.temp_value = 1800,
109 		.lower_limit = -1000,
110 		.upper_limit = 5000,
111 		.condition = ESS_VALUE_CHANGED,
112 		.meas.sampling_func = 0x00,
113 		.meas.meas_period = 0x01,
114 		.meas.update_interval = SENSOR_2_UPDATE_IVAL,
115 		.meas.application = 0x1b,
116 		.meas.meas_uncertainty = 0x04,
117 };
118 
119 static struct humidity_sensor sensor_3 = {
120 		.humid_value = 6233,
121 		.meas.sampling_func = 0x02,
122 		.meas.meas_period = 0x0e10,
123 		.meas.update_interval = SENSOR_3_UPDATE_IVAL,
124 		.meas.application = 0x1c,
125 		.meas.meas_uncertainty = 0x01,
126 };
127 
temp_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)128 static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr,
129 				 uint16_t value)
130 {
131 	simulate_temp = value == BT_GATT_CCC_NOTIFY;
132 }
133 
134 struct read_es_measurement_rp {
135 	uint16_t flags; /* Reserved for Future Use */
136 	uint8_t sampling_function;
137 	uint8_t measurement_period[3];
138 	uint8_t update_interval[3];
139 	uint8_t application;
140 	uint8_t measurement_uncertainty;
141 } __packed;
142 
read_es_measurement(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)143 static ssize_t read_es_measurement(struct bt_conn *conn,
144 				   const struct bt_gatt_attr *attr, void *buf,
145 				   uint16_t len, uint16_t offset)
146 {
147 	const struct es_measurement *value = attr->user_data;
148 	struct read_es_measurement_rp rsp;
149 
150 	rsp.flags = sys_cpu_to_le16(value->flags);
151 	rsp.sampling_function = value->sampling_func;
152 	sys_put_le24(value->meas_period, rsp.measurement_period);
153 	sys_put_le24(value->update_interval, rsp.update_interval);
154 	rsp.application = value->application;
155 	rsp.measurement_uncertainty = value->meas_uncertainty;
156 
157 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &rsp,
158 				 sizeof(rsp));
159 }
160 
read_temp_valid_range(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)161 static ssize_t read_temp_valid_range(struct bt_conn *conn,
162 				     const struct bt_gatt_attr *attr, void *buf,
163 				     uint16_t len, uint16_t offset)
164 {
165 	const struct temperature_sensor *sensor = attr->user_data;
166 	uint16_t tmp[] = {sys_cpu_to_le16(sensor->lower_limit),
167 			  sys_cpu_to_le16(sensor->upper_limit)};
168 
169 	return bt_gatt_attr_read(conn, attr, buf, len, offset, tmp,
170 				 sizeof(tmp));
171 }
172 
173 struct es_trigger_setting_seconds {
174 	uint8_t condition;
175 	uint8_t sec[3];
176 } __packed;
177 
178 struct es_trigger_setting_reference {
179 	uint8_t condition;
180 	int16_t ref_val;
181 } __packed;
182 
read_temp_trigger_setting(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)183 static ssize_t read_temp_trigger_setting(struct bt_conn *conn,
184 					 const struct bt_gatt_attr *attr,
185 					 void *buf, uint16_t len,
186 					 uint16_t offset)
187 {
188 	const struct temperature_sensor *sensor = attr->user_data;
189 
190 	switch (sensor->condition) {
191 	/* Operand N/A */
192 	case ESS_TRIGGER_INACTIVE:
193 		__fallthrough;
194 	case ESS_VALUE_CHANGED:
195 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
196 					 &sensor->condition,
197 					 sizeof(sensor->condition));
198 	/* Seconds */
199 	case ESS_FIXED_TIME_INTERVAL:
200 		__fallthrough;
201 	case ESS_NO_LESS_THAN_SPECIFIED_TIME: {
202 			struct es_trigger_setting_seconds rp;
203 
204 			rp.condition = sensor->condition;
205 			sys_put_le24(sensor->seconds, rp.sec);
206 
207 			return bt_gatt_attr_read(conn, attr, buf, len, offset,
208 						 &rp, sizeof(rp));
209 		}
210 	/* Reference temperature */
211 	default: {
212 			struct es_trigger_setting_reference rp;
213 
214 			rp.condition = sensor->condition;
215 			rp.ref_val = sys_cpu_to_le16(sensor->ref_val);
216 
217 			return bt_gatt_attr_read(conn, attr, buf, len, offset,
218 						 &rp, sizeof(rp));
219 		}
220 	}
221 }
222 
check_condition(uint8_t condition,int16_t old_val,int16_t new_val,int16_t ref_val)223 static bool check_condition(uint8_t condition, int16_t old_val, int16_t new_val,
224 			    int16_t ref_val)
225 {
226 	switch (condition) {
227 	case ESS_TRIGGER_INACTIVE:
228 		return false;
229 	case ESS_FIXED_TIME_INTERVAL:
230 	case ESS_NO_LESS_THAN_SPECIFIED_TIME:
231 		/* TODO: Check time requirements */
232 		return false;
233 	case ESS_VALUE_CHANGED:
234 		return new_val != old_val;
235 	case ESS_LESS_THAN_REF_VALUE:
236 		return new_val < ref_val;
237 	case ESS_LESS_OR_EQUAL_TO_REF_VALUE:
238 		return new_val <= ref_val;
239 	case ESS_GREATER_THAN_REF_VALUE:
240 		return new_val > ref_val;
241 	case ESS_GREATER_OR_EQUAL_TO_REF_VALUE:
242 		return new_val >= ref_val;
243 	case ESS_EQUAL_TO_REF_VALUE:
244 		return new_val == ref_val;
245 	case ESS_NOT_EQUAL_TO_REF_VALUE:
246 		return new_val != ref_val;
247 	default:
248 		return false;
249 	}
250 }
251 
update_temperature(struct bt_conn * conn,const struct bt_gatt_attr * chrc,int16_t value,struct temperature_sensor * sensor)252 static void update_temperature(struct bt_conn *conn,
253 			       const struct bt_gatt_attr *chrc, int16_t value,
254 			       struct temperature_sensor *sensor)
255 {
256 	bool notify = check_condition(sensor->condition,
257 				      sensor->temp_value, value,
258 				      sensor->ref_val);
259 
260 	/* Update temperature value */
261 	sensor->temp_value = value;
262 
263 	/* Trigger notification if conditions are met */
264 	if (notify) {
265 		value = sys_cpu_to_le16(sensor->temp_value);
266 
267 		bt_gatt_notify(conn, chrc, &value, sizeof(value));
268 	}
269 }
270 
271 BT_GATT_SERVICE_DEFINE(ess_svc,
272 	BT_GATT_PRIMARY_SERVICE(BT_UUID_ESS),
273 
274 	/* Temperature Sensor 1 */
275 	BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
276 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
277 			       BT_GATT_PERM_READ,
278 			       read_u16, NULL, &sensor_1.temp_value),
279 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
280 			   read_es_measurement, NULL, &sensor_1.meas),
281 	BT_GATT_CUD(SENSOR_1_NAME, BT_GATT_PERM_READ),
282 	BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
283 			   read_temp_valid_range, NULL, &sensor_1),
284 	BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
285 			   BT_GATT_PERM_READ, read_temp_trigger_setting,
286 			   NULL, &sensor_1),
287 	BT_GATT_CCC(temp_ccc_cfg_changed,
288 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
289 
290 	/* Temperature Sensor 2 */
291 	BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
292 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
293 			       BT_GATT_PERM_READ,
294 			       read_u16, NULL, &sensor_2.temp_value),
295 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
296 			   read_es_measurement, NULL, &sensor_2.meas),
297 	BT_GATT_CUD(SENSOR_2_NAME, BT_GATT_PERM_READ),
298 	BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
299 			   read_temp_valid_range, NULL, &sensor_2),
300 	BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
301 			   BT_GATT_PERM_READ, read_temp_trigger_setting,
302 			   NULL, &sensor_2),
303 	BT_GATT_CCC(temp_ccc_cfg_changed,
304 		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
305 
306 	/* Humidity Sensor */
307 	BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY, BT_GATT_CHRC_READ,
308 			       BT_GATT_PERM_READ,
309 			       read_u16, NULL, &sensor_3.humid_value),
310 	BT_GATT_CUD(SENSOR_3_NAME, BT_GATT_PERM_READ),
311 	BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
312 			   read_es_measurement, NULL, &sensor_3.meas),
313 );
314 
ess_simulate(void)315 static void ess_simulate(void)
316 {
317 	static uint8_t i;
318 	uint16_t val;
319 
320 	if (!(i % SENSOR_1_UPDATE_IVAL)) {
321 		val = 1200 + i;
322 		update_temperature(NULL, &ess_svc.attrs[2], val, &sensor_1);
323 	}
324 
325 	if (!(i % SENSOR_2_UPDATE_IVAL)) {
326 		val = 1800 + i;
327 		update_temperature(NULL, &ess_svc.attrs[9], val, &sensor_2);
328 	}
329 
330 	if (!(i % SENSOR_3_UPDATE_IVAL)) {
331 		sensor_3.humid_value = 6233 + (i % 13);
332 	}
333 
334 	if (!(i % INT8_MAX)) {
335 		i = 0U;
336 	}
337 
338 	i++;
339 }
340 
341 static const struct bt_data ad[] = {
342 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
343 	BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0x00, 0x03),
344 	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
345 		      BT_UUID_16_ENCODE(BT_UUID_ESS_VAL),
346 		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
347 };
348 
connected(struct bt_conn * conn,uint8_t err)349 static void connected(struct bt_conn *conn, uint8_t err)
350 {
351 	if (err) {
352 		printk("Connection failed (err 0x%02x)\n", err);
353 	} else {
354 		printk("Connected\n");
355 	}
356 }
357 
disconnected(struct bt_conn * conn,uint8_t reason)358 static void disconnected(struct bt_conn *conn, uint8_t reason)
359 {
360 	printk("Disconnected (reason 0x%02x)\n", reason);
361 }
362 
363 BT_CONN_CB_DEFINE(conn_callbacks) = {
364 	.connected = connected,
365 	.disconnected = disconnected,
366 };
367 
bt_ready(void)368 static void bt_ready(void)
369 {
370 	int err;
371 
372 	printk("Bluetooth initialized\n");
373 
374 	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
375 	if (err) {
376 		printk("Advertising failed to start (err %d)\n", err);
377 		return;
378 	}
379 
380 	printk("Advertising successfully started\n");
381 }
382 
auth_passkey_display(struct bt_conn * conn,unsigned int passkey)383 static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
384 {
385 	char addr[BT_ADDR_LE_STR_LEN];
386 
387 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
388 
389 	printk("Passkey for %s: %06u\n", addr, passkey);
390 }
391 
auth_cancel(struct bt_conn * conn)392 static void auth_cancel(struct bt_conn *conn)
393 {
394 	char addr[BT_ADDR_LE_STR_LEN];
395 
396 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
397 
398 	printk("Pairing cancelled: %s\n", addr);
399 }
400 
401 static struct bt_conn_auth_cb auth_cb_display = {
402 	.passkey_display = auth_passkey_display,
403 	.passkey_entry = NULL,
404 	.cancel = auth_cancel,
405 };
406 
bas_notify(void)407 static void bas_notify(void)
408 {
409 	uint8_t battery_level = bt_bas_get_battery_level();
410 
411 	battery_level--;
412 
413 	if (!battery_level) {
414 		battery_level = 100U;
415 	}
416 
417 	bt_bas_set_battery_level(battery_level);
418 }
419 
main(void)420 int main(void)
421 {
422 	int err;
423 
424 	err = bt_enable(NULL);
425 	if (err) {
426 		printk("Bluetooth init failed (err %d)\n", err);
427 		return 0;
428 	}
429 
430 	bt_ready();
431 
432 	bt_conn_auth_cb_register(&auth_cb_display);
433 
434 	while (1) {
435 		k_sleep(K_SECONDS(1));
436 
437 		/* Temperature simulation */
438 		if (simulate_temp) {
439 			ess_simulate();
440 		}
441 
442 		/* Battery level simulation */
443 		bas_notify();
444 	}
445 	return 0;
446 }
447