网上有很多关于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");
    }
}

对照着这个协议图示,是不是一目了然; 

ios 蓝牙data iOS 蓝牙协议_ci

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;

ios 蓝牙data iOS 蓝牙协议_ios 蓝牙data_02

 所以,nordic的例程每次收到notify后是把所有的id详情每次都申请了

ios 蓝牙data iOS 蓝牙协议_ci_03

这样去看这个协议是不是就一目了然了;

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);

ios 蓝牙data iOS 蓝牙协议_iphone_04

 

再对比一下协议,很容易就能理解了;

ios 蓝牙data iOS 蓝牙协议_ci_05

获取到所有详细的信息后,ble设备能够对当前信息作出反应,比如进来一个电话,iPhone通知消息notify到ble设备,并将这个消息定义为消极,那ble设备回复一个消极notify其实就通知iPhone接这个电话;同理iPhone收到一个qq消息在通知栏并将其定义为消息,ble设备回复消极notify的话表示消息已经在ble设备读取,那么iPhone就会在通知栏消除这条通知,并且回复通知remove了一条消息;所以通知有变动都会通知到ble设备,不管是增加还是减少;

ios 蓝牙data iOS 蓝牙协议_ci_06

需要说明的是这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);