1 /*
2  * Copyright (c) 2025 Sean Kyer
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/hci.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/uuid.h>
12 #include <zephyr/bluetooth/gatt.h>
13 #include <zephyr/bluetooth/services/ans.h>
14 #include <zephyr/logging/log.h>
15 
16 LOG_MODULE_REGISTER(peripheral_ans, CONFIG_LOG_DEFAULT_LEVEL);
17 
18 /*
19  * Sample loops forever, incrementing number of new and unread notifications. Number of new and
20  * unread notifications will overflow and loop back around.
21  */
22 static uint8_t num_unread;
23 static uint8_t num_new;
24 
25 static const struct bt_data ad[] = {
26 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
27 	BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ANS_VAL))};
28 
29 static const struct bt_data sd[] = {
30 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
31 };
32 
connected(struct bt_conn * conn,uint8_t err)33 static void connected(struct bt_conn *conn, uint8_t err)
34 {
35 	if (err != 0) {
36 		LOG_ERR("Connection failed, err 0x%02x %s", err, bt_hci_err_to_str(err));
37 		return;
38 	}
39 
40 	LOG_INF("Connected");
41 }
42 
disconnected(struct bt_conn * conn,uint8_t reason)43 static void disconnected(struct bt_conn *conn, uint8_t reason)
44 {
45 	LOG_INF("Disconnected, reason 0x%02x %s", reason, bt_hci_err_to_str(reason));
46 }
47 
start_adv(void)48 static void start_adv(void)
49 {
50 	int err;
51 
52 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
53 	if (err != 0) {
54 		LOG_ERR("Advertising failed to start (err %d)", err);
55 		return;
56 	}
57 
58 	LOG_INF("Advertising successfully started");
59 }
60 
61 BT_CONN_CB_DEFINE(conn_callbacks) = {
62 	.connected = connected,
63 	.disconnected = disconnected,
64 	.recycled = start_adv,
65 };
66 
main(void)67 int main(void)
68 {
69 	int ret;
70 
71 	LOG_INF("Sample - Bluetooth Peripheral ANS");
72 
73 	ret = bt_enable(NULL);
74 	if (ret != 0) {
75 		LOG_ERR("Failed to enable bluetooth: %d", ret);
76 		return ret;
77 	}
78 
79 	start_adv();
80 
81 	num_unread = 0;
82 	num_new = 0;
83 
84 	/* At runtime, enable support for given categories */
85 	uint16_t new_alert_mask = (1 << BT_ANS_CAT_SIMPLE_ALERT) | (1 << BT_ANS_CAT_HIGH_PRI_ALERT);
86 	uint16_t unread_mask = 1 << BT_ANS_CAT_SIMPLE_ALERT;
87 
88 	ret = bt_ans_set_new_alert_support_category(new_alert_mask);
89 	if (ret != 0) {
90 		LOG_ERR("Unable to set new alert support category mask! (err: %d)", ret);
91 	}
92 
93 	ret = bt_ans_set_unread_support_category(unread_mask);
94 	if (ret != 0) {
95 		LOG_ERR("Unable to set unread support category mask! (err: %d)", ret);
96 	}
97 
98 	while (true) {
99 		static const char test_msg[] = "Test Alert!";
100 		static const char high_pri_msg[] = "Prio Alert!";
101 
102 		num_new++;
103 
104 		ret = bt_ans_notify_new_alert(NULL, BT_ANS_CAT_SIMPLE_ALERT, num_new, test_msg);
105 		if (ret != 0) {
106 			LOG_ERR("Failed to push new alert! (err: %d)", ret);
107 		}
108 		k_sleep(K_SECONDS(1));
109 
110 		ret = bt_ans_notify_new_alert(NULL, BT_ANS_CAT_HIGH_PRI_ALERT, num_new,
111 					      high_pri_msg);
112 		if (ret != 0) {
113 			LOG_ERR("Failed to push new alert! (err: %d)", ret);
114 		}
115 		k_sleep(K_SECONDS(1));
116 
117 		ret = bt_ans_set_unread_count(NULL, BT_ANS_CAT_SIMPLE_ALERT, num_unread);
118 		if (ret != 0) {
119 			LOG_ERR("Failed to push new unread count! (err: %d)", ret);
120 		}
121 
122 		num_unread++;
123 
124 		k_sleep(K_SECONDS(5));
125 	}
126 
127 	return 0;
128 }
129