Skip to content

Commit a045046

Browse files
committed
fix(nimble): Added support for ANCS in nimble
1 parent 4ce1da7 commit a045046

File tree

10 files changed

+1210
-0
lines changed

10 files changed

+1210
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
7+
idf_build_set_property(MINIMAL_BUILD ON)
8+
project(ble_ancs)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
2+
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
3+
4+
# ESP-IDF NimBLE ANCS Example
5+
6+
The purpose of the Apple Notification Center Service (ANCS) is to give Bluetooth accessories (that connect to iOS devices through a Bluetooth low-energy link) a simple and convenient way to access many kinds of notifications that are generated on iOS devices.
7+
8+
## How to Use Example
9+
10+
Before project configuration and build, be sure to set the correct chip target using:
11+
12+
```bash
13+
idf.py set-target <chip_name>
14+
```
15+
16+
The Apple Notification Center Service is a primary service whose service UUID is:
17+
18+
`7905F431-B5CE-4E99-A40F-4B1E122D00D0`
19+
20+
Only one instance of the ANCS may be present on an NP. Due to the nature of iOS, the ANCS is not guaranteed to always be present. As a result, the NC should look for and subscribe to the Service Changed characteristic of the GATT service in order to monitor for the potential publishing and unpublishing of the ANCS at any time.
21+
22+
In its basic form, the ANCS exposes three characteristics:
23+
Notification Source: UUID `9FBF120D-6301-42D9-8C58-25E699A21DBD` (notifiable)
24+
Control Point: UUID `69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9` (writeable with response)
25+
Data Source: UUID `22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB` (notifiable)
26+
27+
All these characteristics require authorization for access.
28+
29+
### Hardware Required
30+
31+
* A development board with ESP32/ESP32-C3/ESP32-H2/ESP32-C2/ESP32-S3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
32+
* A USB cable for power supply and programming
33+
34+
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it.
35+
36+
### Build and Flash
37+
38+
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
39+
40+
(To exit the serial monitor, type ``Ctrl-]``.)
41+
42+
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
43+
44+
## Example Output
45+
46+
```
47+
I (466) NimBLE ANCS: BLE Host Task Started
48+
I (466) NimBLE: GAP procedure initiated: stop advertising.
49+
50+
I (476) NimBLE: Device Address:
51+
I (476) NimBLE: 48:27:e2:e9:0e:a6
52+
I (476) NimBLE:
53+
54+
I (476) NimBLE: GAP procedure initiated: advertise;
55+
I (476) NimBLE: disc_mode=2
56+
I (476) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=0 adv_itvl_max=0
57+
I (486) NimBLE:
58+
59+
I (496) uart: queue free spaces: 8
60+
I (496) main_task: Returned from app_main()
61+
```
62+
63+
## Troubleshooting
64+
65+
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
set(srcs "main.c"
2+
"ble_ancs.c")
3+
4+
idf_component_register(SRCS "${srcs}"
5+
INCLUDE_DIRS ".")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
menu "Example Configuration"
2+
3+
config EXAMPLE_EXTENDED_ADV
4+
bool
5+
depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT
6+
default y if SOC_ESP_NIMBLE_CONTROLLER
7+
select BT_NIMBLE_EXT_ADV
8+
prompt "Enable Extended Adv"
9+
help
10+
Use this option to enable extended advertising in the example.
11+
If this option is disabled, ensure config BT_NIMBLE_EXT_ADV is
12+
also disabled from Nimble stack menuconfig
13+
14+
endmenu
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
7+
#include <stdlib.h>
8+
#include <string.h>
9+
#include <inttypes.h>
10+
#include "esp_log.h"
11+
#include "ble_ancs.h"
12+
13+
#define NimBLE_ANCS_TAG "NimBLE_ANCS"
14+
15+
/*
16+
| EventID(1 Byte) | EventFlags(1 Byte) | CategoryID(1 Byte) | CategoryCount(1 Byte) | NotificationUID(4 Bytes) |
17+
18+
A GATT notification delivered through the Notification Source characteristic contains the following information:
19+
* EventID: This field informs the accessory whether the given iOS notification was added, modified, or removed. The enumerated values for this field are defined
20+
in EventID Values.
21+
* EventFlags: A bitmask whose set bits inform an NC of specificities with the iOS notification. For example, if an iOS notification is considered “important”,
22+
the NC may want to display a more aggressive user interface (UI) to make sure the user is properly alerted. The enumerated bits for this field
23+
are defined in EventFlags.
24+
* CategoryID: A numerical value providing a category in which the iOS notification can be classified. The NP will make a best effort to provide an accurate category
25+
for each iOS notification. The enumerated values for this field are defined in CategoryID Values.
26+
* CategoryCount: The current number of active iOS notifications in the given category. For example, if two unread emails are sitting in a user’s email inbox, and a new
27+
email is pushed to the user’s iOS device, the value of CategoryCount is 3.
28+
* NotificationUID: A 32-bit numerical value that is the unique identifier (UID) for the iOS notification. This value can be used as a handle in commands sent to the
29+
Control Point characteristic to interact with the iOS notification.
30+
*/
31+
32+
char *EventID_to_String(uint8_t EventID)
33+
{
34+
char *str = NULL;
35+
switch (EventID)
36+
{
37+
case EventIDNotificationAdded:
38+
str = "New message";
39+
break;
40+
case EventIDNotificationModified:
41+
str = "Modified message";
42+
break;
43+
case EventIDNotificationRemoved:
44+
str = "Removed message";
45+
break;
46+
default:
47+
str = "unknown EventID";
48+
break;
49+
}
50+
return str;
51+
}
52+
53+
char *CategoryID_to_String(uint8_t CategoryID)
54+
{
55+
char *Cidstr = NULL;
56+
switch(CategoryID) {
57+
case CategoryIDOther:
58+
Cidstr = "Other";
59+
break;
60+
case CategoryIDIncomingCall:
61+
Cidstr = "IncomingCall";
62+
break;
63+
case CategoryIDMissedCall:
64+
Cidstr = "MissedCall";
65+
break;
66+
case CategoryIDVoicemail:
67+
Cidstr = "Voicemail";
68+
break;
69+
case CategoryIDSocial:
70+
Cidstr = "Social";
71+
break;
72+
case CategoryIDSchedule:
73+
Cidstr = "Schedule";
74+
break;
75+
case CategoryIDEmail:
76+
Cidstr = "Email";
77+
break;
78+
case CategoryIDNews:
79+
Cidstr = "News";
80+
break;
81+
case CategoryIDHealthAndFitness:
82+
Cidstr = "HealthAndFitness";
83+
break;
84+
case CategoryIDBusinessAndFinance:
85+
Cidstr = "BusinessAndFinance";
86+
break;
87+
case CategoryIDLocation:
88+
Cidstr = "Location";
89+
break;
90+
case CategoryIDEntertainment:
91+
Cidstr = "Entertainment";
92+
break;
93+
default:
94+
Cidstr = "Unknown CategoryID";
95+
break;
96+
}
97+
return Cidstr;
98+
}
99+
100+
/*
101+
| EventID(1 Byte) | EventFlags(1 Byte) | CategoryID(1 Byte) | CategoryCount(1 Byte) | NotificationUID(4 Bytes) |
102+
*/
103+
104+
void ble_receive_apple_notification_source(uint8_t *message, uint16_t message_len)
105+
{
106+
if (!message || message_len < 5) {
107+
return;
108+
}
109+
110+
uint8_t EventID = message[0];
111+
char *EventIDS = EventID_to_String(EventID);
112+
uint8_t EventFlags = message[1];
113+
uint8_t CategoryID = message[2];
114+
char *Cidstr = CategoryID_to_String(CategoryID);
115+
uint8_t CategoryCount = message[3];
116+
uint32_t NotificationUID = (message[4]) | (message[5]<< 8) | (message[6]<< 16) | (message[7] << 24);
117+
ESP_LOGI(NimBLE_ANCS_TAG, "EventID:%s EventFlags:0x%x CategoryID:%s CategoryCount:%d NotificationUID:%" PRIu32, EventIDS, EventFlags, Cidstr, CategoryCount, NotificationUID);
118+
}
119+
120+
void ble_receive_apple_data_source(uint8_t *message, uint16_t message_len)
121+
{
122+
if (!message || message_len == 0) {
123+
return;
124+
}
125+
uint8_t Command_id = message[0];
126+
switch (Command_id)
127+
{
128+
case CommandIDGetNotificationAttributes: {
129+
uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24);
130+
uint32_t remian_attr_len = message_len - 5;
131+
uint8_t *attrs = &message[5];
132+
ESP_LOGI(NimBLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID);
133+
while(remian_attr_len > 0) {
134+
uint8_t AttributeID = attrs[0];
135+
uint16_t len = attrs[1] | (attrs[2] << 8);
136+
if(len > (remian_attr_len -3)) {
137+
ESP_LOGE(NimBLE_ANCS_TAG, "data error");
138+
break;
139+
}
140+
switch (AttributeID)
141+
{
142+
case NotificationAttributeIDAppIdentifier:
143+
ESP_LOG_BUFFER_CHAR("Identifier", &attrs[3], len);
144+
break;
145+
case NotificationAttributeIDTitle:
146+
ESP_LOG_BUFFER_CHAR("Title", &attrs[3], len);
147+
break;
148+
case NotificationAttributeIDSubtitle:
149+
ESP_LOG_BUFFER_CHAR("Subtitle", &attrs[3], len);
150+
break;
151+
case NotificationAttributeIDMessage:
152+
ESP_LOG_BUFFER_CHAR("Message", &attrs[3], len);
153+
break;
154+
case NotificationAttributeIDMessageSize:
155+
ESP_LOG_BUFFER_CHAR("MessageSize", &attrs[3], len);
156+
break;
157+
case NotificationAttributeIDDate:
158+
//yyyyMMdd'T'HHmmSS
159+
ESP_LOG_BUFFER_CHAR("Date", &attrs[3], len);
160+
break;
161+
case NotificationAttributeIDPositiveActionLabel:
162+
ESP_LOG_BUFFER_HEX("PActionLabel", &attrs[3], len);
163+
break;
164+
case NotificationAttributeIDNegativeActionLabel:
165+
ESP_LOG_BUFFER_HEX("NActionLabel", &attrs[3], len);
166+
break;
167+
default:
168+
ESP_LOG_BUFFER_HEX("unknownAttributeID", &attrs[3], len);
169+
break;
170+
}
171+
172+
attrs += (1 + 2 + len);
173+
remian_attr_len -= (1 + 2 + len);
174+
}
175+
176+
break;
177+
}
178+
case CommandIDGetAppAttributes:
179+
ESP_LOGI(NimBLE_ANCS_TAG, "recevice APP Attributes response");
180+
break;
181+
case CommandIDPerformNotificationAction:
182+
ESP_LOGI(NimBLE_ANCS_TAG, "recevice Perform Notification Action");
183+
break;
184+
default:
185+
ESP_LOGI(NimBLE_ANCS_TAG, "unknown Command ID");
186+
break;
187+
}
188+
}
189+
190+
char *Errcode_to_String(uint16_t status)
191+
{
192+
char *Errstr = NULL;
193+
switch (status) {
194+
case Unknown_command:
195+
Errstr = "Unknown_command";
196+
break;
197+
case Invalid_command:
198+
Errstr = "Invalid_command";
199+
break;
200+
case Invalid_parameter:
201+
Errstr = "Invalid_parameter";
202+
break;
203+
case Action_failed:
204+
Errstr = "Action_failed";
205+
break;
206+
default:
207+
Errstr = "unknown_failed";
208+
break;
209+
}
210+
return Errstr;
211+
212+
}

0 commit comments

Comments
 (0)