-
Notifications
You must be signed in to change notification settings - Fork 8.2k
bluetooth: ANS: Add Alert Notification Service #95578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seankyer
wants to merge
2
commits into
zephyrproject-rtos:main
Choose a base branch
from
seankyer:feat/bt-ans
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+908
−0
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| /* | ||
| * Copyright (c) 2025 Sean Kyer | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_ANS_H_ | ||
| #define ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_ANS_H_ | ||
|
|
||
| /** | ||
| * @brief Alert Notification Service (ANS) | ||
| * @defgroup bt_ans Alert Notification Service (ANS) | ||
| * | ||
| * @since 4.4 | ||
| * @version 0.1.0 | ||
| * | ||
| * @ingroup bluetooth | ||
| * @{ | ||
| */ | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /** | ||
| * @brief Command not supported error code | ||
| */ | ||
| #define BT_ANS_ERR_CMD_NOT_SUP 0xa0 | ||
|
|
||
| /** | ||
| * @brief ANS max text string size in octets | ||
| * | ||
| * This is the max string size in octets to be saved in a New Alert. Text longer than the max is | ||
| * truncated. | ||
| * | ||
| * section 3.165 of | ||
| * https://btprodspecificationrefs.blob.core.windows.net/gatt-specification-supplement/GATT_Specification_Supplement.pdf | ||
| * | ||
| */ | ||
| #define BT_ANS_MAX_TEXT_STR_SIZE 18 | ||
|
|
||
| /** | ||
| * @brief ANS Category ID Enum | ||
| * | ||
| * Enumeration for whether the category is supported. | ||
| */ | ||
| enum bt_ans_cat { | ||
| BT_ANS_CAT_SIMPLE_ALERT, /**< Simple alerts (general notifications). */ | ||
| BT_ANS_CAT_EMAIL, /**< Email messages. */ | ||
| BT_ANS_CAT_NEWS, /**< News updates. */ | ||
| BT_ANS_CAT_CALL, /**< Incoming call alerts. */ | ||
| BT_ANS_CAT_MISSED_CALL, /**< Missed call alerts. */ | ||
| BT_ANS_CAT_SMS_MMS, /**< SMS/MMS text messages. */ | ||
| BT_ANS_CAT_VOICE_MAIL, /**< Voicemail notifications. */ | ||
| BT_ANS_CAT_SCHEDULE, /**< Calendar or schedule alerts. */ | ||
| BT_ANS_CAT_HIGH_PRI_ALERT, /**< High-priority alerts. */ | ||
| BT_ANS_CAT_INSTANT_MESSAGE, /**< Instant messaging alerts. */ | ||
|
|
||
| /** @cond INTERNAL_HIDDEN */ | ||
| BT_ANS_CAT_NUM, /**< Marker for the number of categories. */ | ||
| /** @endcond */ | ||
|
|
||
| /* 10–15 reserved for future use */ | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Set the support for a given new alert category | ||
| * | ||
| * @param mask The bitmask of supported categories | ||
| * | ||
| * @return 0 on success | ||
| * @return negative error codes on failure | ||
| */ | ||
| int bt_ans_set_new_alert_support_category(uint16_t mask); | ||
|
|
||
| /** | ||
| * @brief Set the support for a given unread new alert category | ||
| * | ||
| * @param mask The bitmask of supported categories | ||
| * | ||
| * @return 0 on success | ||
| * @return negative error codes on failure | ||
| */ | ||
| int bt_ans_set_unread_support_category(uint16_t mask); | ||
|
|
||
| /** | ||
| * @brief Send a new alert to remote devices | ||
| * | ||
| * The new alert is transmitted to the remote devices if notifications are enabled. Each category | ||
| * will save the latest call to this function in case an immediate replay is requested via the ANS | ||
| * control point. | ||
| * | ||
| * @note This function waits on a Mutex with @ref K_FOREVER to ensure atomic updates to notification | ||
| * structs. To avoid deadlocks, do not call this function in BT RX or System Workqueue threads. | ||
| * | ||
| * @param conn The connection object to send the alert to | ||
| * @param category The category the notification is for | ||
| * @param num_new Number of new alerts since last alert | ||
| * @param text Text brief of alert, null terminated | ||
| * | ||
| * @return 0 on success | ||
| * @return negative error codes on failure | ||
| */ | ||
| int bt_ans_notify_new_alert(struct bt_conn *conn, enum bt_ans_cat category, uint8_t num_new, | ||
| const char *text); | ||
|
|
||
| /** | ||
| * @brief Set the total unread count for a given category | ||
| * | ||
| * The unread count is transmitted to the remote devices if notifications are enabled. Each category | ||
| * will save the latest call to this function in case an immediate replay is requested via the ANS | ||
| * control point. | ||
| * | ||
| * @note This function waits on a Mutex with @ref K_FOREVER to ensure atomic updates to notification | ||
| * structs. To avoid deadlocks, do not call this function in BT RX or System Workqueue threads. | ||
| * | ||
| * @param conn The connection object to send the alert to | ||
| * @param category The category the unread count is for | ||
| * @param unread Total number of unread alerts | ||
| * | ||
| * @return 0 on success | ||
| * @return negative error codes on failure | ||
| */ | ||
| int bt_ans_set_unread_count(struct bt_conn *conn, enum bt_ans_cat category, uint8_t unread); | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| /** | ||
| * @} | ||
| */ | ||
|
|
||
| #endif /* ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_ANS_H_ */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| cmake_minimum_required(VERSION 3.20.0) | ||
| find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
| project(peripheral_ans) | ||
|
|
||
| target_sources(app PRIVATE | ||
| src/main.c | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| .. zephyr:code-sample:: ble_peripheral_ans | ||
| :name: Peripheral ANS | ||
| :relevant-api: bluetooth | ||
|
|
||
| Send notification using Alert Notification Service (ANS). | ||
|
|
||
| Overview | ||
| ******** | ||
|
|
||
| This sample demonstrates the usage of ANS by acting as a peripheral periodically sending | ||
| notifications to the connected remote device. | ||
|
|
||
| Requirements | ||
| ************ | ||
|
|
||
| * A board with Bluetooth LE support | ||
| * Smartphone with Bluetooth LE app (ADI Attach, nRF Connect, etc.) or dedicated Bluetooth LE sniffer | ||
|
|
||
| Building and Running | ||
| ******************** | ||
|
|
||
| To start receiving alerts over the connection, refer to | ||
| `GATT Specification Supplement <https://btprodspecificationrefs.blob.core.windows.net/gatt-specification-supplement/GATT_Specification_Supplement.pdf>`_ | ||
| section 3.12 for byte array to enable/disable notifications and control the service. | ||
|
|
||
| See :zephyr:code-sample-category:`bluetooth` samples for details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| CONFIG_LOG=y | ||
| CONFIG_UTF8=y | ||
| CONFIG_BT=y | ||
| CONFIG_BT_PERIPHERAL=y | ||
| CONFIG_BT_HCI_ERR_TO_STR=y | ||
| CONFIG_BT_DEVICE_NAME="Zephyr Peripheral ANS Sample" | ||
| CONFIG_BT_ANS=y | ||
| CONFIG_BT_ANS_LOG_LEVEL_DBG=y | ||
| CONFIG_BT_ANS_NALRT_CAT_SIMPLE_ALERT=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| sample: | ||
| name: Bluetooth Peripheral ANS | ||
| description: Demonstrates the Alert Notification Service (ANS) | ||
| tests: | ||
| sample.bluetooth.peripheral_ans: | ||
| harness: bluetooth | ||
| platform_allow: | ||
| - qemu_cortex_m3 | ||
| - qemu_x86 | ||
| - nrf52840dk/nrf52840 | ||
| integration_platforms: | ||
| - qemu_cortex_m3 | ||
| - qemu_x86 | ||
| - nrf52840dk/nrf52840 | ||
| tags: bluetooth |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| /* | ||
| * Copyright (c) 2025 Sean Kyer | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <zephyr/kernel.h> | ||
| #include <zephyr/bluetooth/bluetooth.h> | ||
| #include <zephyr/bluetooth/hci.h> | ||
| #include <zephyr/bluetooth/conn.h> | ||
| #include <zephyr/bluetooth/uuid.h> | ||
| #include <zephyr/bluetooth/gatt.h> | ||
| #include <zephyr/bluetooth/services/ans.h> | ||
| #include <zephyr/logging/log.h> | ||
|
|
||
| LOG_MODULE_REGISTER(peripheral_ans, CONFIG_LOG_DEFAULT_LEVEL); | ||
|
|
||
| /* | ||
| * Sample loops forever, incrementing number of new and unread notifications. Number of new and | ||
| * unread notifications will overflow and loop back around. | ||
| */ | ||
| static uint8_t num_unread; | ||
| static uint8_t num_new; | ||
|
|
||
| static const struct bt_data ad[] = { | ||
| BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), | ||
| BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ANS_VAL))}; | ||
|
|
||
| static const struct bt_data sd[] = { | ||
| BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), | ||
| }; | ||
|
|
||
| static void connected(struct bt_conn *conn, uint8_t err) | ||
| { | ||
| if (err != 0) { | ||
| LOG_ERR("Connection failed, err 0x%02x %s", err, bt_hci_err_to_str(err)); | ||
| return; | ||
| } | ||
|
|
||
| LOG_INF("Connected"); | ||
| } | ||
|
|
||
| static void disconnected(struct bt_conn *conn, uint8_t reason) | ||
| { | ||
| LOG_INF("Disconnected, reason 0x%02x %s", reason, bt_hci_err_to_str(reason)); | ||
| } | ||
|
|
||
| static void start_adv(void) | ||
| { | ||
| int err; | ||
|
|
||
| err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); | ||
| if (err != 0) { | ||
| LOG_ERR("Advertising failed to start (err %d)", err); | ||
| return; | ||
| } | ||
|
|
||
| LOG_INF("Advertising successfully started"); | ||
| } | ||
|
|
||
| BT_CONN_CB_DEFINE(conn_callbacks) = { | ||
| .connected = connected, | ||
| .disconnected = disconnected, | ||
| .recycled = start_adv, | ||
| }; | ||
|
|
||
| int main(void) | ||
| { | ||
| int ret; | ||
|
|
||
| LOG_INF("Sample - Bluetooth Peripheral ANS"); | ||
|
|
||
| ret = bt_enable(NULL); | ||
| if (ret != 0) { | ||
| LOG_ERR("Failed to enable bluetooth: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| start_adv(); | ||
|
|
||
| num_unread = 0; | ||
| num_new = 0; | ||
|
|
||
| /* At runtime, enable support for given categories */ | ||
| uint16_t new_alert_mask = (1 << BT_ANS_CAT_SIMPLE_ALERT) | (1 << BT_ANS_CAT_HIGH_PRI_ALERT); | ||
| uint16_t unread_mask = 1 << BT_ANS_CAT_SIMPLE_ALERT; | ||
|
|
||
| ret = bt_ans_set_new_alert_support_category(new_alert_mask); | ||
| if (ret != 0) { | ||
| LOG_ERR("Unable to set new alert support category mask! (err: %d)", ret); | ||
| } | ||
|
|
||
| ret = bt_ans_set_unread_support_category(unread_mask); | ||
| if (ret != 0) { | ||
| LOG_ERR("Unable to set unread support category mask! (err: %d)", ret); | ||
| } | ||
|
|
||
| while (true) { | ||
| static const char test_msg[] = "Test Alert!"; | ||
| static const char high_pri_msg[] = "Prio Alert!"; | ||
|
|
||
| num_new++; | ||
|
|
||
| ret = bt_ans_notify_new_alert(NULL, BT_ANS_CAT_SIMPLE_ALERT, num_new, test_msg); | ||
| if (ret != 0) { | ||
| LOG_ERR("Failed to push new alert! (err: %d)", ret); | ||
| } | ||
| k_sleep(K_SECONDS(1)); | ||
|
|
||
| ret = bt_ans_notify_new_alert(NULL, BT_ANS_CAT_HIGH_PRI_ALERT, num_new, | ||
| high_pri_msg); | ||
| if (ret != 0) { | ||
| LOG_ERR("Failed to push new alert! (err: %d)", ret); | ||
| } | ||
| k_sleep(K_SECONDS(1)); | ||
|
|
||
| ret = bt_ans_set_unread_count(NULL, BT_ANS_CAT_SIMPLE_ALERT, num_unread); | ||
| if (ret != 0) { | ||
| LOG_ERR("Failed to push new unread count! (err: %d)", ret); | ||
| } | ||
|
|
||
| num_unread++; | ||
|
|
||
| k_sleep(K_SECONDS(5)); | ||
| } | ||
|
|
||
| return 0; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider omitting this particular value from the documentation