网上有很多关于ancs的文章,但是翻译过来的可能会看的没头没脑,建议还是看苹果的官方文档:
https://developer.apple.com/library/archive/documentation/CoreBluetooth/Reference/AppleNotificationCenterServiceSpecification/Specification/Specification.html#//apple_ref/doc/uid/TP40013460-CH1-SW61
ancs过程:
1.ble设备进行ancs服务广播,iphone 连接ble设备并绑定;
2.iphone有事件(电话、短信)到来,通过notify通知ble设备;
3.ble设备收到notify之后发出控制notify给iPhone,请求具体数据;
4.iPhone收到ble请求后notify给ble具体数据;
下面以52832的代码对ancs进行程序解析:
1.ble设备收到iPhone notify
static void on_ancs_c_evt(ble_ancs_c_evt_t * p_evt)
{
ret_code_t ret = NRF_SUCCESS;
switch (p_evt->evt_type)
{
case BLE_ANCS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_DEBUG("Apple Notification Center Service discovered on the server.\r\n");
ret = nrf_ble_ancs_c_handles_assign(&m_ancs_c, p_evt->conn_handle, &p_evt->service);
APP_ERROR_CHECK(ret);
apple_notification_setup();
break;
case BLE_ANCS_C_EVT_NOTIF://notify解析完毕
m_notification_latest = p_evt->notif;
notif_print(&m_notification_latest);
break;
case BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE://从iPhone端获取到的属性值解析完毕
m_notif_attr_latest = p_evt->attr;
notif_attr_print(&m_notif_attr_latest);
if(p_evt->attr.attr_id == BLE_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER)
{
m_notif_attr_app_id_latest = p_evt->attr;
}
break;
case BLE_ANCS_C_EVT_DISCOVERY_FAILED:
NRF_LOG_DEBUG("Apple Notification Center Service not discovered on the server.\r\n");
break;
case BLE_ANCS_C_EVT_APP_ATTRIBUTE:
app_attr_print(&p_evt->attr);
break;
case BLE_ANCS_C_EVT_NP_ERROR:
err_code_print(p_evt->err_code_np);
break;
default:
// No implementation needed.
break;
}
}
static void on_ancs_c_evt(ble_ancs_c_evt_t * p_evt)函数是ancs服务初始化的时候注册的一个回调,当iPhone有notify来的时候就会执行这个回调;
当iPhone有电话进来的时候,通过ancs服务通知ble设备,数据解析完毕后进入on_ancs_c_evt函数的 BLE_ANCS_C_EVT_NOTIF 事件中,执行了打印函数static void notif_print(ble_ancs_c_evt_notif_t * p_notif);
Event:代表是事件类型,比如收到qq消息,他是add一个消息到信息中心,你在手机上查看了这个qq消息那就是remove这个信息到信息中心,所以收到和已经查看消息ble设备都能知悉;
Category id:apple公司将消息源做了很多分类,微信qq这些是放在social这一类的;
Category cnt,就是累计在iPhone通知栏没有被处理的历史消息总和数;
Flags,比如iPhone将未读的消息定义为消极,那么你ble设备后续回复消极notify的话就会通知这条信息已经在ble设备端被读取;
static void notif_print(ble_ancs_c_evt_notif_t * p_notif)
{
NRF_LOG_INFO("\r\nNotification\r\n");
NRF_LOG_INFO("Event: %s\r\n", (uint32_t)lit_eventid[p_notif->evt_id]);
NRF_LOG_INFO("Category ID: %s\r\n", (uint32_t)lit_catid[p_notif->category_id]);
NRF_LOG_INFO("Category Cnt:%u\r\n", (unsigned int) p_notif->category_count);
NRF_LOG_INFO("UID: %u\r\n", (unsigned int) p_notif->notif_uid);
//uuid
NRF_LOG_INFO("Flags:\r\n");
if(p_notif->evt_flags.silent == 1)
{
NRF_LOG_INFO(" Silent\r\n");
}
if(p_notif->evt_flags.important == 1)
{
NRF_LOG_INFO(" Important\r\n");
}
if(p_notif->evt_flags.pre_existing == 1)
{
NRF_LOG_INFO(" Pre-existing\r\n");
}
if(p_notif->evt_flags.positive_action == 1)
{
NRF_LOG_INFO(" Positive Action\r\n");
}
if(p_notif->evt_flags.negative_action == 1)
{
NRF_LOG_INFO(" Negative Action\r\n");
}
}
对照着这个协议图示,是不是一目了然;
2.ble设备向iPhone发送获取详细信息的notify;
在官方的ancs例程中这个发送是通过开发板上的按键KEY0来实现的:
static void bsp_event_handler(bsp_event_t event)
{
ret_code_t ret;
switch (event)
{
case BSP_EVENT_SLEEP:
sleep_mode_enter();
break;
case BSP_EVENT_DISCONNECT:
ret = sd_ble_gap_disconnect(m_cur_conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
if (ret != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(ret);
}
break;
case BSP_EVENT_WHITELIST_OFF:
if (m_ancs_c.conn_handle == BLE_CONN_HANDLE_INVALID)
{
ret = ble_advertising_restart_without_whitelist();
if (ret != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(ret);
}
}
break;
case BSP_EVENT_KEY_0:
ret = nrf_ble_ancs_c_request_attrs(&m_ancs_c, &m_notification_latest);//发送获取详细信息notify请求
APP_ERROR_CHECK(ret);
break;
case BSP_EVENT_KEY_1:
if(m_notif_attr_app_id_latest.attr_id == BLE_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER
&& m_notif_attr_app_id_latest.attr_len != 0)
{
NRF_LOG_INFO("Request for %s: \r\n", (uint32_t)m_notif_attr_app_id_latest.p_attr_data);
ret = nrf_ble_ancs_c_app_attr_request(&m_ancs_c,
m_notif_attr_app_id_latest.p_attr_data,
m_notif_attr_app_id_latest.attr_len);
APP_ERROR_CHECK(ret);
}
break;
case BSP_EVENT_KEY_2://回复积极指令
if(m_notification_latest.evt_flags.positive_action == true)
{
NRF_LOG_INFO("Performing Positive Action.\r\n");
ret = nrf_ancs_perform_notif_action(&m_ancs_c,
m_notification_latest.notif_uid,
ACTION_ID_POSITIVE);
APP_ERROR_CHECK(ret);
}
break;
case BSP_EVENT_KEY_3://回复消极指令
if(m_notification_latest.evt_flags.negative_action == true)
{
NRF_LOG_INFO("Performing Negative Action.\r\n");
ret = nrf_ancs_perform_notif_action(&m_ancs_c,
m_notification_latest.notif_uid,
ACTION_ID_NEGATIVE);
APP_ERROR_CHECK(ret);
}
break;
default:
break;
}
}
我们详细的来看这个 ret = nrf_ble_ancs_c_request_attrs(&m_ancs_c, &m_notification_latest)函数:
ret_code_t nrf_ble_ancs_c_request_attrs(ble_ancs_c_t * p_ancs,
const ble_ancs_c_evt_notif_t * p_notif)
{
uint32_t err_code;
err_code = ble_ancs_verify_notification_format(p_notif);
VERIFY_SUCCESS(err_code);
err_code = ble_ancs_get_notif_attrs(p_ancs, p_notif->notif_uid);
p_ancs->parse_info.parse_state = COMMAND_ID;
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}
继续下探:
uint32_t ble_ancs_get_notif_attrs(ble_ancs_c_t * p_ancs,
const uint32_t p_uid)
{
tx_message_t p_msg;
memset(&p_msg, 0, sizeof(tx_message_t));
uint32_t index = 0;
p_ancs->number_of_requested_attr = 0;
p_msg.req.write_req.gattc_params.handle = p_ancs->service.control_point_char.handle_value;
p_msg.req.write_req.gattc_params.p_value = p_msg.req.write_req.gattc_value;
p_msg.req.write_req.gattc_params.offset = 0;
p_msg.req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
//Encode Command ID.
p_msg.req.write_req.gattc_value[index++] = BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES;
//Encode Notification UID.
index += uint32_encode(p_uid, &(p_msg.req.write_req.gattc_value[index]));
//Encode Attribute ID.
for (uint32_t attr = 0; attr < BLE_ANCS_NB_OF_NOTIF_ATTR; attr++)//添加需要获悉详情的attribute id
{
if (p_ancs->ancs_notif_attr_list[attr].get == true)
{
p_msg.req.write_req.gattc_value[index++] = attr;
if ((attr == BLE_ANCS_NOTIF_ATTR_ID_TITLE) ||
(attr == BLE_ANCS_NOTIF_ATTR_ID_SUBTITLE) ||
(attr == BLE_ANCS_NOTIF_ATTR_ID_MESSAGE))
{
//Encode Length field, only applicable for Title, Subtitle and Message
index += uint16_encode(p_ancs->ancs_notif_attr_list[attr].attr_len,
&(p_msg.req.write_req.gattc_value[index]));
}
p_ancs->number_of_requested_attr++;
}
}
p_msg.req.write_req.gattc_params.len = index;
p_msg.conn_handle = p_ancs->conn_handle;
p_msg.type = WRITE_REQ;
p_ancs->parse_info.expected_number_of_attrs = p_ancs->number_of_requested_attr;
tx_buffer_insert(&p_msg);
tx_buffer_process();
return NRF_SUCCESS;
}
BLE_ANCS_NB_OF_NOTIF_ATTR的值为8,再看看各个attribute id的值:
typedef enum
{
BLE_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER = 0, /**< Identifies that the attribute data is of an "App Identifier" type. */
BLE_ANCS_NOTIF_ATTR_ID_TITLE, /**< Identifies that the attribute data is a "Title". */
BLE_ANCS_NOTIF_ATTR_ID_SUBTITLE, /**< Identifies that the attribute data is a "Subtitle". */
BLE_ANCS_NOTIF_ATTR_ID_MESSAGE, /**< Identifies that the attribute data is a "Message". */
BLE_ANCS_NOTIF_ATTR_ID_MESSAGE_SIZE, /**< Identifies that the attribute data is a "Message Size". */
BLE_ANCS_NOTIF_ATTR_ID_DATE, /**< Identifies that the attribute data is a "Date". */
BLE_ANCS_NOTIF_ATTR_ID_POSITIVE_ACTION_LABEL, /**< The notification has a "Positive action" that can be executed associated with it. */
BLE_ANCS_NOTIF_ATTR_ID_NEGATIVE_ACTION_LABEL, /**< The notification has a "Negative action" that can be executed associated with it. */
} ble_ancs_c_notif_attr_id_val_t;
所以,nordic的例程每次收到notify后是把所有的id详情每次都申请了
这样去看这个协议是不是就一目了然了;
3.iPhone通过notify发送详情给ble设备;
ble设备对notify解析后就在这里进行打印:
static void notif_attr_print(ble_ancs_c_attr_t * p_attr)
{
if (p_attr->attr_len != 0)
{
NRF_LOG_INFO("%s: %s\r\n", (uint32_t)lit_attrid[p_attr->attr_id], nrf_log_push((char *)p_attr->p_attr_data));
//打印id号 打印id号下的内容
}
else if (p_attr->attr_len == 0)
{
NRF_LOG_INFO("%s: (N/A)\r\n", (uint32_t)lit_attrid[p_attr->attr_id]);
}
}
这里看一下其调试的打印数据:
因为数据量太大所有两次notify上报过来,就出现了两次(N/A);
再对比一下协议,很容易就能理解了;
获取到所有详细的信息后,ble设备能够对当前信息作出反应,比如进来一个电话,iPhone通知消息notify到ble设备,并将这个消息定义为消极,那ble设备回复一个消极notify其实就通知iPhone接这个电话;同理iPhone收到一个qq消息在通知栏并将其定义为消息,ble设备回复消极notify的话表示消息已经在ble设备读取,那么iPhone就会在通知栏消除这条通知,并且回复通知remove了一条消息;所以通知有变动都会通知到ble设备,不管是增加还是减少;
需要说明的是这8个attribute id 信息通过notify下达后,是一个一个attribute id解析然后再进入on_ancs_c_evt回调的,所以写应用层的话需要全部回调完后再对信息打包处理;
attribute id是从0到8的索引标号,ancs赋予他们意义,后边的attribute值就是他们的意义,是id的内容;ancs规定有8个attribute id也就是iPhone一个notify事件产生后能生产8个于此有关的标签信息,你需要获取8个中的哪一个在获取详情那里填充对应的attribute id值;
ancs有一个重要的特点就是使用前一定需要配对绑定,官方的ancs例程里面还使用了白名单机制,ble设备第一次被绑定之后就会往flash写入绑定信息,设置白名单,其他手机就没有办法连接绑定;针对此写应用可能用到的函数有
1.删除绑定:delete_bonds();
2.设置白名单: ret = ble_advertising_whitelist_reply(&m_advertising,
whitelist_addrs,
addr_cnt,
whitelist_irks,
irk_cnt);
3.去除白名单:ret = ble_advertising_restart_without_whitelist(&m_advertising);