1+ // Original: https://github.com/mathcampbell/ANCS
2+ #include " NimBLEDevice.h"
3+ #include " driver/uart.h"
4+
5+ static NimBLEUUID ancsServiceUUID (" 7905F431-B5CE-4E99-A40F-4B1E122D00D0" );
6+ static NimBLEUUID notificationSourceCharacteristicUUID (" 9FBF120D-6301-42D9-8C58-25E699A21DBD" );
7+ static NimBLEUUID controlPointCharacteristicUUID (" 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9" );
8+ static NimBLEUUID dataSourceCharacteristicUUID (" 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB" );
9+
10+ static NimBLEClient *pClient;
11+
12+ uint8_t latestMessageID[4 ];
13+ bool pendingNotification = false ;
14+ bool incomingCall = false ;
15+ uint8_t acceptCall = 0 ;
16+
17+ static void initUart ()
18+ {
19+ uart_config_t uartConfig{};
20+ uartConfig.baud_rate = 115200 ;
21+ uartConfig.data_bits = UART_DATA_8_BITS;
22+ uartConfig.parity = UART_PARITY_DISABLE;
23+ uartConfig.stop_bits = UART_STOP_BITS_1;
24+ uartConfig.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
25+ uartConfig.source_clk = UART_SCLK_DEFAULT;
26+
27+ uart_driver_install (UART_NUM_0, 256 , 0 , 0 , nullptr , 0 );
28+ uart_param_config (UART_NUM_0, &uartConfig);
29+ }
30+
31+ static void dataSourceNotifyCallback (NimBLERemoteCharacteristic *pDataSourceCharacteristic,
32+ uint8_t *pData,
33+ size_t length,
34+ bool isNotify)
35+ {
36+ for (int i = 0 ; i < length; i++)
37+ {
38+ if (i > 7 )
39+ {
40+ printf (" %c" , pData[i]);
41+ }
42+ else
43+ {
44+ printf (" %02X " , pData[i]);
45+ }
46+ }
47+ printf (" \n " );
48+ }
49+
50+ static void NotificationSourceNotifyCallback (NimBLERemoteCharacteristic *pNotificationSourceCharacteristic,
51+ uint8_t *pData,
52+ size_t length,
53+ bool isNotify)
54+ {
55+ if (pData[0 ] == 0 )
56+ {
57+ printf (" New notification!\n " );
58+ latestMessageID[0 ] = pData[4 ];
59+ latestMessageID[1 ] = pData[5 ];
60+ latestMessageID[2 ] = pData[6 ];
61+ latestMessageID[3 ] = pData[7 ];
62+
63+ switch (pData[2 ])
64+ {
65+ case 0 :
66+ printf (" Category: Other\n " );
67+ break ;
68+ case 1 :
69+ incomingCall = true ;
70+ printf (" Category: Incoming call\n " );
71+ break ;
72+ case 2 :
73+ printf (" Category: Missed call\n " );
74+ break ;
75+ case 3 :
76+ printf (" Category: Voicemail\n " );
77+ break ;
78+ case 4 :
79+ printf (" Category: Social\n " );
80+ break ;
81+ case 5 :
82+ printf (" Category: Schedule\n " );
83+ break ;
84+ case 6 :
85+ printf (" Category: Email\n " );
86+ break ;
87+ case 7 :
88+ printf (" Category: News\n " );
89+ break ;
90+ case 8 :
91+ printf (" Category: Health\n " );
92+ break ;
93+ case 9 :
94+ printf (" Category: Business\n " );
95+ break ;
96+ case 10 :
97+ printf (" Category: Location\n " );
98+ break ;
99+ case 11 :
100+ printf (" Category: Entertainment\n " );
101+ break ;
102+ default :
103+ break ;
104+ }
105+ }
106+ else if (pData[0 ] == 1 )
107+ {
108+ printf (" Notification Modified!\n " );
109+ if (pData[2 ] == 1 )
110+ {
111+ printf (" Call Changed!\n " );
112+ }
113+ }
114+ else if (pData[0 ] == 2 )
115+ {
116+ printf (" Notification Removed!\n " );
117+ if (pData[2 ] == 1 )
118+ {
119+ printf (" Call Gone!\n " );
120+ }
121+ }
122+ pendingNotification = true ;
123+ }
124+
125+ class ServerCallbacks : public NimBLEServerCallbacks
126+ {
127+ void onConnect (NimBLEServer *pServer, NimBLEConnInfo &connInfo)
128+ {
129+ printf (" Client connected: %s\n " , connInfo.getAddress ().toString ().c_str ());
130+ pClient = pServer->getClient (connInfo);
131+ printf (" Client connected!\n " );
132+ }
133+
134+ void onDisconnect (NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
135+ {
136+ printf (" Client disconnected: %s, reason: %d\n " , connInfo.getAddress ().toString ().c_str (), reason);
137+ }
138+ } serverCallbacks;
139+
140+ extern " C" void app_main ()
141+ {
142+ initUart ();
143+ printf (" Starting setup...\n " );
144+
145+ NimBLEDevice::init (" ANCS" );
146+ NimBLEDevice::setSecurityAuth (true , true , true );
147+ NimBLEDevice::setSecurityIOCap (BLE_HS_IO_DISPLAY_YESNO);
148+ NimBLEDevice::setPower (9 );
149+
150+ NimBLEServer *pServer = NimBLEDevice::createServer ();
151+ pServer->setCallbacks (&serverCallbacks);
152+ pServer->advertiseOnDisconnect (true );
153+
154+ NimBLEAdvertising *pAdvertising = pServer->getAdvertising ();
155+ NimBLEAdvertisementData advData{};
156+ advData.setFlags (0x06 );
157+ advData.addServiceUUID (ancsServiceUUID);
158+ pAdvertising->setAdvertisementData (advData);
159+ pAdvertising->start ();
160+
161+ printf (" Advertising started!\n " );
162+
163+ while (1 )
164+ {
165+ if (pClient != nullptr && pClient->isConnected ())
166+ {
167+ auto pAncsService = pClient->getService (ancsServiceUUID);
168+ if (pAncsService == nullptr )
169+ {
170+ printf (" Failed to find our service UUID: %s\n " , ancsServiceUUID.toString ().c_str ());
171+ continue ;
172+ }
173+ // Obtain a reference to the characteristic in the service of the remote BLE server.
174+ auto pNotificationSourceCharacteristic = pAncsService->getCharacteristic (notificationSourceCharacteristicUUID);
175+ if (pNotificationSourceCharacteristic == nullptr )
176+ {
177+ printf (" Failed to find our characteristic UUID: %s\n " ,
178+ notificationSourceCharacteristicUUID.toString ().c_str ());
179+ continue ;
180+ }
181+ // Obtain a reference to the characteristic in the service of the remote BLE server.
182+ auto pControlPointCharacteristic = pAncsService->getCharacteristic (controlPointCharacteristicUUID);
183+ if (pControlPointCharacteristic == nullptr )
184+ {
185+ printf (" Failed to find our characteristic UUID: %s\n " ,
186+ controlPointCharacteristicUUID.toString ().c_str ());
187+ continue ;
188+ }
189+ // Obtain a reference to the characteristic in the service of the remote BLE server.
190+ auto pDataSourceCharacteristic = pAncsService->getCharacteristic (dataSourceCharacteristicUUID);
191+ if (pDataSourceCharacteristic == nullptr )
192+ {
193+ printf (" Failed to find our characteristic UUID: %s\n " , dataSourceCharacteristicUUID.toString ().c_str ());
194+ continue ;
195+ }
196+ pDataSourceCharacteristic->subscribe (true , dataSourceNotifyCallback);
197+ pNotificationSourceCharacteristic->subscribe (true , NotificationSourceNotifyCallback);
198+
199+ while (1 )
200+ {
201+ if (pendingNotification || incomingCall)
202+ {
203+ // CommandID: CommandIDGetNotificationAttributes
204+ // 32bit uid
205+ // AttributeID
206+ printf (" Requesting details...\n " );
207+ uint8_t val[8 ] =
208+ {0x0 , latestMessageID[0 ], latestMessageID[1 ], latestMessageID[2 ], latestMessageID[3 ], 0x0 , 0x0 , 0x10 };
209+ pControlPointCharacteristic->writeValue (val, 6 , true ); // Identifier
210+ val[5 ] = 0x1 ;
211+ pControlPointCharacteristic->writeValue (val, 8 , true ); // Title
212+ val[5 ] = 0x3 ;
213+ pControlPointCharacteristic->writeValue (val, 8 , true ); // Message
214+ val[5 ] = 0x5 ;
215+ pControlPointCharacteristic->writeValue (val, 6 , true ); // Date
216+
217+ while (incomingCall)
218+ {
219+ int bytesRead = uart_read_bytes (UART_NUM_0, &acceptCall, 1 , 0 );
220+ if (bytesRead > 0 )
221+ {
222+ printf (" %c\n " , (char )acceptCall);
223+ }
224+
225+ if (acceptCall == 49 )
226+ { // call accepted , get number 1 from serial
227+ const uint8_t vResponse[] =
228+ {0x02 , latestMessageID[0 ], latestMessageID[1 ], latestMessageID[2 ], latestMessageID[3 ], 0x00 };
229+ pControlPointCharacteristic->writeValue ((uint8_t *)vResponse, 6 , true );
230+
231+ acceptCall = 0 ;
232+ // incomingCall = false;
233+ }
234+ else if (acceptCall == 48 )
235+ { // call rejected , get number 0 from serial
236+ const uint8_t vResponse[] =
237+ {0x02 , latestMessageID[0 ], latestMessageID[1 ], latestMessageID[2 ], latestMessageID[3 ], 0x01 };
238+ pControlPointCharacteristic->writeValue ((uint8_t *)vResponse, 6 , true );
239+
240+ acceptCall = 0 ;
241+ incomingCall = false ;
242+ }
243+ vTaskDelay (10 / portTICK_PERIOD_MS);
244+ }
245+
246+ pendingNotification = false ;
247+ }
248+ vTaskDelay (10 / portTICK_PERIOD_MS);
249+ }
250+ }
251+ vTaskDelay (10 / portTICK_PERIOD_MS);
252+ }
253+ }
0 commit comments