Reputation: 9365
I'm playing around with this library and reading through the Apple Notification Center Service(ANCS) but still have a hard time getting message and title of an iOs notification forwarded to my esp32 board and output them to the Serial Monitor inside Arduino Studio. With this code, I can pair my iPhone with the esp32 board and when notifications get send over but all I got is the category like this:
20:01:50.225 -> ********************
20:01:50.225 -> **Device connected**
20:01:50.225 -> 30:9a:77:**:**:**
20:01:50.225 -> ********************
20:01:53.567 -> New notification!
20:01:53.567 -> Category: News
Could someone tell me what am I do wrong with this code? (Arduino_ESP32_ANCS.ino, the Task.h and Task.cpp files are available on github)
P.S: I use the default "Shortcuts" app on iPhone to trigger fake notification to test if anyone is interested in how you can have notifications to test.
// Original: https://github.com/S-March/esp32_ANCS
// fixed for Arduino15/packages/esp32/hardware/esp32/1.0.3
#include <Arduino.h>
#include "BLEDevice.h"
#include "BLEServer.h"
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLE2902.h"
#include <esp_log.h>
#include <esp_bt_main.h>
#include <string>
#include "Task.h"
#include <sys/time.h>
#include <time.h>
#include "sdkconfig.h"
static char LOG_TAG[] = "SampleServer";
static BLEUUID ancsServiceUUID("7905F431-B5CE-4E99-A40F-4B1E122D00D0");
static BLEUUID notificationSourceCharacteristicUUID("9FBF120D-6301-42D9-8C58-25E699A21DBD");
static BLEUUID controlPointCharacteristicUUID("69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9");
static BLEUUID dataSourceCharacteristicUUID("22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB");
BLERemoteCharacteristic* pControlPointCharacteristic;
class MySecurity : public BLESecurityCallbacks {
uint32_t onPassKeyRequest(){
ESP_LOGI(LOG_TAG, "PassKeyRequest");
return 123456; // Returning a default passkey
}
void onPassKeyNotify(uint32_t pass_key){
ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key);
}
bool onSecurityRequest(){
ESP_LOGI(LOG_TAG, "On Security Request");
return true;
}
bool onConfirmPIN(uint32_t pin){
ESP_LOGI(LOG_TAG, "On Confirm Pin Request with pin: %d", pin);
return true; // You should return true to confirm the pin
}
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){
ESP_LOGI(LOG_TAG, "Starting BLE work!");
if(cmpl.success){
uint16_t length;
esp_ble_gap_get_whitelist_size(&length);
ESP_LOGD(LOG_TAG, "size: %d", length);
}
}
};
static void dataSourceNotifyCallback(
BLERemoteCharacteristic* pDataSourceCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("Notify callback for characteristic ");
Serial.print(pDataSourceCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
// Parse the response
size_t index = 0;
uint8_t commandID = pData[index++];
uint32_t notificationUID = pData[index++] | (pData[index++] << 8) | (pData[index++] << 16) | (pData[index++] << 24);
while (index < length) {
uint8_t attributeID = pData[index++];
uint16_t attributeLength = pData[index++] | (pData[index++] << 8);
if (attributeID == 1) { // Title
Serial.print("Title: ");
} else if (attributeID == 2) { // Message
Serial.print("Message: ");
} else if (attributeID == 3) { // App Identifier
Serial.print("App Identifier: ");
}
for (uint16_t i = 0; i < attributeLength; i++) {
Serial.print((char)pData[index + i]);
}
Serial.println();
index += attributeLength;
}
}
static void NotificationSourceNotifyCallback(
BLERemoteCharacteristic* pNotificationSourceCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
if (pData[0] == 0) {
Serial.println("New notification!");
Serial.print(" ");
switch (pData[2]) {
case 0: Serial.println("Category: Other"); break;
case 1: Serial.println("Category: Incoming call"); break;
case 2: Serial.println("Category: Missed call"); break;
case 3: Serial.println("Category: Voicemail"); break;
case 4: Serial.println("Category: Social"); break;
case 5: Serial.println("Category: Schedule"); break;
case 6: Serial.println("Category: Email"); break;
case 7: Serial.println("Category: News"); break;
case 8: Serial.println("Category: Health"); break;
case 9: Serial.println("Category: Business"); break;
case 10: Serial.println("Category: Location"); break;
case 11: Serial.println("Category: Entertainment"); break;
default: break;
}
// Call the function to get notification attributes
getNotificationAttributes(pControlPointCharacteristic, *(uint32_t*)(pData + 4));
}
}
/**
* Become a BLE client to a remote BLE server. We are passed in the address of the BLE server
* as the input parameter when the task is created.
*/
class MyClient : public Task {
void run(void* data) {
BLEAddress* pAddress = (BLEAddress*)data;
BLEClient* pClient = BLEDevice::createClient();
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
BLEDevice::setSecurityCallbacks(new MySecurity());
BLESecurity* pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
pSecurity->setCapability(ESP_IO_CAP_IO);
pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
// Connect to the remote BLE Server.
pClient->connect(*pAddress);
/** BEGIN ANCS SERVICE **/
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pAncsService = pClient->getService(ancsServiceUUID);
if (pAncsService == nullptr) {
ESP_LOGD(LOG_TAG, "Failed to find our service UUID: %s", ancsServiceUUID.toString().c_str());
return;
}
// Obtain a reference to the characteristics in the service of the remote BLE server.
BLERemoteCharacteristic* pNotificationSourceCharacteristic = pAncsService->getCharacteristic(notificationSourceCharacteristicUUID);
if (pNotificationSourceCharacteristic == nullptr) {
ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", notificationSourceCharacteristicUUID.toString().c_str());
return;
}
pControlPointCharacteristic = pAncsService->getCharacteristic(controlPointCharacteristicUUID);
if (pControlPointCharacteristic == nullptr) {
ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", controlPointCharacteristicUUID.toString().c_str());
return;
}
BLERemoteCharacteristic* pDataSourceCharacteristic = pAncsService->getCharacteristic(dataSourceCharacteristicUUID);
if (pDataSourceCharacteristic == nullptr) {
ESP_LOGD(LOG_TAG, "Failed to find our characteristic UUID: %s", dataSourceCharacteristicUUID.toString().c_str());
return;
}
const uint8_t v[] = {0x1, 0x0};
pDataSourceCharacteristic->registerForNotify(dataSourceNotifyCallback);
pDataSourceCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)v, 2, true);
pNotificationSourceCharacteristic->registerForNotify(NotificationSourceNotifyCallback);
pNotificationSourceCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)v, 2, true);
/** END ANCS SERVICE **/
} // run
}; // MyClient
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) {
Serial.println(" ");
Serial.println("********************");
Serial.println("**Device connected**");
Serial.println(BLEAddress(param->connect.remote_bda).toString().c_str());
Serial.println("********************");
MyClient* pMyClient = new MyClient();
pMyClient->setStackSize(18000);
pMyClient->start(new BLEAddress(param->connect.remote_bda));
};
void onDisconnect(BLEServer* pServer) {
Serial.println(" ");
Serial.println("************************");
Serial.println("**Device disconnected**");
Serial.println("************************");
}
};
class MainBLEServer: public Task {
void run(void *data) {
ESP_LOGD(LOG_TAG, "Starting BLE work!");
esp_log_buffer_char(LOG_TAG, LOG_TAG, sizeof(LOG_TAG));
esp_log_buffer_hex(LOG_TAG, LOG_TAG, sizeof(LOG_TAG));
// Initialize device
BLEDevice::init("ANCS");
BLEServer* pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
BLEDevice::setSecurityCallbacks(new MySecurity());
// Advertising parameters:
// Soliciting ANCS
BLEAdvertising *pAdvertising = pServer->getAdvertising();
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
oAdvertisementData.setFlags(0x01);
_setServiceSolicitation(&oAdvertisementData, BLEUUID("7905F431-B5CE-4E99-A40F-4B1E122D00D0"));
pAdvertising->setAdvertisementData(oAdvertisementData);
// Set security
BLESecurity *pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
pSecurity->setCapability(ESP_IO_CAP_OUT);
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
//Start advertising
pAdvertising->start();
ESP_LOGD(LOG_TAG, "Advertising started!");
delay(portMAX_DELAY);
}
/**
* @brief Set the service solicitation (UUID)
* @param [in] uuid The UUID to set with the service solicitation data. Size of UUID will be used.
*/
void _setServiceSolicitation(BLEAdvertisementData *a, BLEUUID uuid)
{
char cdata[2];
switch(uuid.bitSize()) {
case 16: {
// [Len] [0x14] [UUID16] data
cdata[0] = 3;
cdata[1] = ESP_BLE_AD_TYPE_SOL_SRV_UUID; // 0x14
String uuidString = String((char *)&uuid.getNative()->uuid.uuid16, 2);
a->addData(String(cdata[0], DEC) + String(cdata[1], DEC) + uuidString);
break;
}
case 128: {
// [Len] [0x15] [UUID128] data
cdata[0] = 17;
cdata[1] = ESP_BLE_AD_TYPE_128SOL_SRV_UUID; // 0x15
String uuidString = String((char *)uuid.getNative()->uuid.uuid128, 16);
a->addData(String(cdata[0], DEC) + String(cdata[1], DEC) + uuidString);
break;
}
default:
return;
}
}
};
// New from 19:44
void getNotificationAttributes(BLERemoteCharacteristic* pControlPointCharacteristic, uint32_t notificationUID) {
// Command ID for Get Notification Attributes
const uint8_t commandID = 0;
// Attributes we want to retrieve (Title, Message, AppIdentifier)
const uint8_t attributes[] = {1, 2, 3};
// Create the command to send
std::vector<uint8_t> command;
command.push_back(commandID);
// Add the notification UID
command.push_back(notificationUID & 0xFF);
command.push_back((notificationUID >> 8) & 0xFF);
command.push_back((notificationUID >> 16) & 0xFF);
command.push_back((notificationUID >> 24) & 0xFF);
// Add the attributes and their max lengths (e.g., 100 bytes)
for (uint8_t attr : attributes) {
command.push_back(attr);
command.push_back(100);
command.push_back(0);
}
// Send the command
pControlPointCharacteristic->writeValue(command.data(), command.size(), true);
}
void SampleSecureServer(void)
{
MainBLEServer* pMainBleServer = new MainBLEServer();
pMainBleServer->setStackSize(20000);
pMainBleServer->start();
Serial.print("");
Serial.print("BLEServer started.");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
}
void setup()
{
Serial.print("ANCS started.");
Serial.begin(115200);
SampleSecureServer();
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
}
Upvotes: 0
Views: 56