GATT Over BR/EDR 使用说明

GATT Over BR/EDR 让原本属于 BLEGATT 协议,也能够在经典蓝牙( BR/EDR )上传输和使用,从而提升数据速率和设备兼容性。 本文以 AirSync 服务为例,介绍基于 SDK 如何实现 GATT over BR/EDR。

备注

  • 直接打开全局宏 F_APP_AIRSYNC_SERVICE_SUPPORT ,如下的配置 SDK 中都已包含。

  1. 配置 BLE 广播的 ADV_TYPE_FLAGS 不能带有 GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED

    static  uint8_t adv_data[31] =
    {
       0x02,
       GAP_ADTYPE_FLAGS,
       GAP_ADTYPE_FLAGS_GENERAL,
    
       ...
    }
    
  2. AirSync 服务的源文件和头文件分别为 airsync_ble_service.cairsync_ble_service.h 。 区别于普通 BLE 服务,service table 中 Attribute flags 需要配置 ATTRIB_FLAG_BREDR

    const T_ATTRIB_APPL airsync_ble_service_tbl[] =
    {
       /* <<Primary Service>>, 0.. */
       {
          (ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_LE | ATTRIB_FLAG_BREDR),  /* wFlags     */
          {   /* bTypeValue */
                LO_WORD(GATT_UUID_PRIMARY_SERVICE),
                HI_WORD(GATT_UUID_PRIMARY_SERVICE),
                LO_WORD(GATT_UUID_AIRSYNC_SERVICE),      /* service UUID */
                HI_WORD(GATT_UUID_AIRSYNC_SERVICE)
          },
          UUID_16BIT_SIZE,                            /* bValueLen     */
          NULL,                                       /* pValueContext */
          GATT_PERM_READ                              /* wPermissions  */
       },
       ...
    };
    
  3. app_ble_service.c中添加服务,并调用 bt_att_init() 初始化 ATT 注册 callback。

    #include "bt_att.h"
    #include "airsync_ble_service.h"
    
    // add service id
    T_SERVER_ID  air_ser_id;
    
    //callback 函数
    void att_callback(uint8_t bd_addr[6], T_BT_ATT_MSG_TYPE msg_type, void *p_msg)
    {
       APP_PRINT_INFO1("att_callback msg_type = 0x%x", msg_type);
    }
    
    void app_ble_service_init(void)
    {
       //init bt att
       bt_att_init(att_callback);
    
       //add service
       air_ser_id = airsync_add_service((void *)app_profile_callback);
    }
    
  1. app_sdp.c 中添加 AirSync service 的 SDP table。

    static const uint8_t att_sdp_record[] =
    {
       SDP_DATA_ELEM_SEQ_HDR,
       0x2b,
       //attribute SDP_ATTR_SRV_CLASS_ID_LIST
       SDP_UNSIGNED_TWO_BYTE,
       (uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
       (uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
       SDP_DATA_ELEM_SEQ_HDR,
       0x03,
       SDP_UUID16_HDR,
       //According to UUID
       (uint8_t)(0xFEE7 >> 8),
       (uint8_t)(0xFEE7),
       //attribute SDP_ATTR_PROTO_DESC_LIST
       SDP_UNSIGNED_TWO_BYTE,
       (uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
       (uint8_t)SDP_ATTR_PROTO_DESC_LIST,
       SDP_DATA_ELEM_SEQ_HDR,
       0x13,
       SDP_DATA_ELEM_SEQ_HDR,
       0x06,
       SDP_UUID16_HDR,
       (uint8_t)(UUID_L2CAP >> 8),
       (uint8_t)(UUID_L2CAP),
       SDP_UNSIGNED_TWO_BYTE,
       (uint8_t)(PSM_ATT >> 8),
       (uint8_t)(PSM_ATT),
       SDP_DATA_ELEM_SEQ_HDR,
       0x09,
       SDP_UUID16_HDR,
       (uint8_t)(UUID_ATT >> 8),
       (uint8_t)(UUID_ATT),
       SDP_UNSIGNED_TWO_BYTE,
       //According to service start handle
       0x00,
       0x10,
       SDP_UNSIGNED_TWO_BYTE,
       //According to service end handle
       0x00,
       0x18,
       //attribute SDP_ATTR_BROWSE_GROUP_LIST
       SDP_UNSIGNED_TWO_BYTE,
       (uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
       (uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
       SDP_DATA_ELEM_SEQ_HDR,
       0x03,
       SDP_UUID16_HDR,
       (uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
       (uint8_t)(UUID_PUBLIC_BROWSE_GROUP),
    };
    
    void app_sdp_init(void)
    {
    
       ......
       bt_sdp_record_add((void *)att_sdp_record);
    
    }
    
    • 如上 SDP table,需要填写起始 handle 和结束 handle, 介绍获取 handle 的两种方法:

      • 使用函数 server_get_start_handle(service id) 获取起始 handle,结束 handle 根据注册的 service table 计算。

      • Ellisys Bluetooth Analyzer.exe 中打开 .cfa 文件,选择 ATT Read Type Transaction , 读取图中序号3的数值分别填入 SDP table 的初始 handle 位置, 如果注册的服务有变化,需要重新填写。

        ../../../_images/Gatt_over_bredr_Starting_Handle.png

        起始 Handle

        ../../../_images/Gatt_over_bredr_Ending_Handle.png

        结束 Handle

  2. 调用 server_ext_send_data() 去发 Notification/Indication。

    uint16_t conn_handle;
    uint16_t cid;
    uint8_t cid_num;
    for (uint8_t i = 0; i < MAX_BR_LINK_NUM; i++)
    {
       if (app_db.br_link[i].used == true)
       {
             gap_chann_get_handle(app_db.br_link[i].bd_addr, 0x10/*BTIF_REMOTE_ADDR_CLASSIC*/, &conn_handle);
             gap_chann_get_cid(conn_handle, 1, &cid, &cid_num);
    
             server_ext_send_data(conn_handle,
                                  cid,
                                  airsync_ser_id,
                                  2,
                                  p_data,
                                  data_len,
                                  GATT_PDU_TYPE_ANY);
             break;
       }
    }
    //le notification
    server_send_data(conn_id,
                      airsync_ser_id,
                      2,
                      p_data,
                      data_len,
                      GATT_PDU_TYPE_NOTIFICATION);
    

备注

  • 如果存在 ATT 无法正常连线情况下, 可尝试 HFP 连上后 delay 5s 左右的时间主动发起 ATT 连线,调用: bt_att_connect_req()

  • 上述 AirSync 服务通过 GATT over BR/EDR 的传输方式适用于 Android 微信运动, iOS 的微信运动可以仍然通过 BLE。