GATT Over BR/EDR User Guide

GATT Over BR/EDR allows the GATT protocol, originally part of BLE, to be transmitted and used over classic Bluetooth (BR/EDR), thereby enhancing data rate and device compatibility. This document uses the AirSync service as an example to introduce how to implement GATT over BR/EDR based on the SDK.

Note

  • Directly enable the global macro F_APP_AIRSYNC_SERVICE_SUPPORT, as the following configurations are already included in the SDK.

  1. Configure the ADV_TYPE_FLAGS for BLE advertising without GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED.

    static  uint8_t adv_data[31] =
    {
       0x02,
       GAP_ADTYPE_FLAGS,
       GAP_ADTYPE_FLAGS_GENERAL,
    
       ...
    }
    
  2. The source file and header file for the AirSync service are airsync_ble_service.c and airsync_ble_service.h, respectively. Unlike a regular BLE service, the Attribute flags in the service table need to be configured with 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. Add the service in app_ble_service.c and call bt_att_init() to initialize ATT and register the callback.

    #include "bt_att.h"
    #include "airsync_ble_service.h"
    
    // add service id
    T_SERVER_ID  air_ser_id;
    
    //callback function
    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);
    }
    
  4. Add the SDP table for the AirSync service in app_sdp.c.

    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);
    
    }
    
    • For the above SDP table, you need to fill in the start handle and end handle, introducing two methods to obtain the handle:

      • Use the function server_get_start_handle(service id) to get the start handle, and calculate the end handle based on the registered service table.

      • Open the .cfa file in Ellisys Bluetooth Analyzer.exe, select ATT Read Type Transaction, and read the values in sequence 3 in the figure to fill in the initial handle position of the SDP table. If the registered service changes, it needs to be refilled.

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

        Start Handle

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

        End Handle

  5. Call server_ext_send_data() to send 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);
    

Note

  • If there is a situation where ATT cannot connect properly, try using HFP to connect and then actively initiate an ATT connection after a delay of approximately 5 seconds by calling bt_att_connect_req().

  • The above AirSync service, transmitted via GATT over BR/EDR, is suitable for Android WeChat Sports, while iOS WeChat Sports can still use BLE.