Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/releases/release-notes-4.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ New APIs and options
* :c:struct:`bt_iso_sync_receiver_info` now contains a ``big_handle`` and a ``bis_number`` field
* :c:struct:`bt_le_ext_adv_info` now contains an ``sid`` field with the Advertising Set ID.

* Services

* Introduced Alert Notification Service (ANS) :kconfig:option:`CONFIG_BT_ANS`

* CPUFreq

* Introduced experimental dynamic CPU frequency scaling subsystem
Expand Down
134 changes: 134 additions & 0 deletions include/zephyr/bluetooth/services/ans.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* 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.3
* @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. */

BT_ANS_CAT_NUM, /**< Marker for the number of categories. */

/* 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_ */
9 changes: 9 additions & 0 deletions samples/bluetooth/peripheral_ans/CMakeLists.txt
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
)
26 changes: 26 additions & 0 deletions samples/bluetooth/peripheral_ans/README.rst
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 BLE app (ADI Attach, nRF Connect, etc.) or dedicated BLE 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.
9 changes: 9 additions & 0 deletions samples/bluetooth/peripheral_ans/prj.conf
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
15 changes: 15 additions & 0 deletions samples/bluetooth/peripheral_ans/sample.yaml
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
128 changes: 128 additions & 0 deletions samples/bluetooth/peripheral_ans/src/main.c
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;
}
1 change: 1 addition & 0 deletions subsys/bluetooth/services/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_sources_ifdef(CONFIG_BT_ANS ans.c)

zephyr_sources_ifdef(CONFIG_BT_DIS dis.c)

Expand Down
2 changes: 2 additions & 0 deletions subsys/bluetooth/services/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
menu "GATT Services"
depends on BT_CONN

rsource "Kconfig.ans"

rsource "Kconfig.dis"

rsource "Kconfig.cts"
Expand Down
Loading