如今的社会已经从IT时代过渡到了DT时代,数据的重要性不言而喻。将数据安全快速的传输给对方是一件非常重要的事情,如今诞生了很多不同的传输技术,每一种传输技术都是为了和对方进行数据交互。BLE技术也是这样,它的最终目的就是为了在两个设备间进行数据交互。我们从BLE的本质出发,搞清楚它是如何实现数据交互的,也就真正搞清楚了BLE的工作原理。

下面从3个方面,逐步讲解BLE的数据收发过程。

本文结合nordic的代码和蓝牙核心规范5.2来进行介绍。本文认为你对BLE协议栈的各个层已经有了一个大概认识,对BLE协议栈还不太熟悉的朋友可以参看拙作:蓝牙低功耗(BLE)协议栈

一 广播 1.1 从设备 从设备想要别人能够发现自己就需要不停的进行广播。

Host层通过HCI层定义的接口来设置广播数据。

HCI层的命令格式如下图:

android 耳机数据传输 安卓蓝牙数据传输_GAP

设置广播数据的命令格式如下图:

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_02

 OpCode,0x0008,两个字节
parameter Total Length,一个字节,也就是Advertising_Data_Length,根据Advertising_Data计算得来
Advertising_Data, 由HCI 以上层传下来
事实上只有Advertising_Data会通过空中发送。Advertising_Data的格式如下:

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_03

详细介绍参看:蓝牙核心卷 5.2 , Vol 3, Part C, 11

可以看到广播数据可能有多个元素:AD Structure 1,AD Structure 2 … AD Structure N,每一个AD Structure的格式为,1个字节长度,一个字节AD Type,n个字节的AD Data。广播数据包也遵循一定的格式,广播包的数据格式参看:AD Type和Core Specification Supplement

LL层定义的广播报文的格式如下图:

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_04

1.用户设置广播参数和广播内容
ble_gap.h
        //设置广播参数,以及广播类容
        uint32_t sd_ble_gap_adv_set_configure(uint8_t *p_adv_handle, ble_gap_adv_data_t const         *p_adv_data, ble_gap_adv_params_t const *p_adv_params)
设置广播参数,ocf=0x006,ogf=0x08
命令格式:

android 耳机数据传输 安卓蓝牙数据传输_GAP_05

android 耳机数据传输 安卓蓝牙数据传输_GAP_05

 gap协议层会根据HCI层提供的接口来设置广播参数

android 耳机数据传输 安卓蓝牙数据传输_ble_07

 设置广播数据,ocf=0x0008,ogf=0x08
gap协议层会根据HCI层提供的接口来设置广播数据

android 耳机数据传输 安卓蓝牙数据传输_ble_08

2. 开启广播
命令格式:

android 耳机数据传输 安卓蓝牙数据传输_android 耳机数据传输_09

 gap层提供的函数接口:uint32_t sd_ble_gap_adv_start(uint8_t adv_handle, uint8_t conn_cfg_tag) HCI层提供的接口,ocf=0x00a,ogf=0x08

android 耳机数据传输 安卓蓝牙数据传输_GAP_10

 3、广播的具体数据

通过抓包工具得到的广播包的数据:

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_11


android 耳机数据传输 安卓蓝牙数据传输_ble_12

aa, 前导码

d6 be 89 8e,接入地址:0x8e89bed6。(蓝牙传输均使用小端模式)

40 19,报头

        0000b,PDU Type,ADV_IND,普通广播. 详见:Vol 6, Part B, 2.3, Table 2.3
        0b, RFU,未使用
        0b, ChSel, 通道选择,未使用
        1b,TxAdd,广播者的mac地址类型,1表示随机地址
        0b,RxAdd, 扫描者mac地址类型,未知
        19,数据长度,指的是Payload的长度
8b 89 e6 26 c9 4c: 0x4cc926e6898b mac地址, Payload的一部分

02 01 1a , AD Structure 1,广播报文的第一个元素

        02,长度
        01,类型,«Flags»
        1a,数据

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_13

02 0a 0c

  • 02, 长度
  • 0a,类型,«Tx Power Level»
  • 0x,数据

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_14

 从设备设置的广播数据流向:用户调用GAP提供的接口,GAP调用HCI层接口,HCI层再传给LL层。

android 耳机数据传输 安卓蓝牙数据传输_android 耳机数据传输_15

1.2 主设备

  • 设置扫描参数
  • 开启扫描
**@brief GAP scanning parameters. */
typedef struct
{
  uint8_t               extended               : 1; /**< If 1, the scanner will accept extended advertising packets.
                                                         If set to 0, the scanner will not receive advertising packets
                                                         on secondary advertising channels, and will not be able
                                                         to receive long advertising PDUs.
                                                         @note Extended scanning is only supported as an experimental feature in this
                                                               SoftDevice. */
  uint8_t               report_incomplete_evts : 1; /**< If 1, events of type @ref ble_gap_evt_adv_report_t may have
                                                         @ref ble_gap_adv_report_type_t::status set to
                                                         @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA.
                                                         This parameter is ignored when used with @ref sd_ble_gap_connect
                                                         @note This may be used to abort receiving more packets from an extended
                                                               advertising event, and is only available for extended
                                                               scanning, see @ref sd_ble_gap_scan_start.
                                                         @note This feature is not supported by this SoftDevice. */
  uint8_t               active                 : 1; /**< If 1, perform active scanning by sending scan requests.
                                                         This parameter is ignored when used with @ref sd_ble_gap_connect. */
  uint8_t               filter_policy          : 2; /**< Scanning filter policy. @sa BLE_GAP_SCAN_FILTER_POLICIES.
                                                         @note Only @ref BLE_GAP_SCAN_FP_ACCEPT_ALL and
                                                               @ref BLE_GAP_SCAN_FP_WHITELIST are valid when used with
                                                               @ref sd_ble_gap_connect */
  uint8_t               scan_phys;                  /**< Bitfield of PHYs to scan on. If set to @ref BLE_GAP_PHY_AUTO,
                                                         scan_phys will default to @ref BLE_GAP_PHY_1MBPS.
                                                         - If @ref ble_gap_scan_params_t::extended is set to 0, the only
                                                           supported PHY is @ref BLE_GAP_PHY_1MBPS.
                                                         - When used with @ref sd_ble_gap_scan_start,
                                                           the bitfield indicates the PHYs the scanner will use for scanning
                                                           on primary advertising channels. The scanner will accept
                                                           @ref BLE_GAP_PHYS_SUPPORTED as secondary advertising channel PHYs.
                                                         - When used with @ref sd_ble_gap_connect, the
                                                           bitfield indicates the PHYs on where a connection may be initiated.
                                                           If scan_phys contains @ref BLE_GAP_PHY_1MBPS and/or @ref BLE_GAP_PHY_2MBPS,
                                                           the primary scan PHY is @ref BLE_GAP_PHY_1MBPS.
                                                           If scan_phys also contains @ref BLE_GAP_PHY_CODED, the primary scan
                                                           PHY will also contain @ref BLE_GAP_PHY_CODED. If the only scan PHY is
                                                           @ref BLE_GAP_PHY_CODED, the primary scan PHY is
                                                           @ref BLE_GAP_PHY_CODED only. */
  uint16_t              interval;                   /**< Scan interval in 625 us units. @sa BLE_GAP_SCAN_INTERVALS. */
  uint16_t              window;                     /**< Scan window in 625 us units. @sa BLE_GAP_SCAN_WINDOW. */
  uint16_t              timeout;                    /**< Scan timeout in 10 ms units. @sa BLE_GAP_SCAN_TIMEOUT. */
  ble_gap_ch_mask_t     channel_mask;               /**< Channel mask for primary and secondary advertising channels.
                                                         At least one of the primary channels, that is channel index 37-39, must be
                                                         set to 0.
                                                         Masking away secondary channels is not supported. */
} ble_gap_scan_params_t;
/**@brief Data structure. */
typedef struct
{
  uint8_t     *p_data;  /**< Pointer to the data buffer provided to/from the application. */
  uint16_t     len;     /**< Length of the data buffer, in bytes. */
} ble_data_t;

//用户调用该函数,进行设置扫描参数,同时开始扫描
uint32_t sd_ble_gap_scan_start(ble_gap_scan_params_t const *p_scan_params, ble_data_t const * p_adv_report_buffer);

HCI层提供的接口:

  • 设置扫描参数
    设置扫描参数的指令格式:

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_16

  • HCI层提供的接口:ogf=0x08,ocf=0x00b
  • 开启扫描

    HCI层提供的接口:ogf=0x08,ocf=0x00c

二 连接


typedef struct
{
  uint8_t addr_id_peer : 1;       /**< Only valid for peer addresses.
                                       Reference to peer in device identities list (as set with @ref sd_ble_gap_device_identities_set) when peer is using privacy. */
  uint8_t addr_type    : 7;       /**< See @ref BLE_GAP_ADDR_TYPES. */
  uint8_t addr[BLE_GAP_ADDR_LEN]; /**< 48-bit address, LSB format. */
} ble_gap_addr_t;

typedef struct
{
  uint16_t min_conn_interval;         /**< Minimum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
  uint16_t max_conn_interval;         /**< Maximum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
  uint16_t slave_latency;             /**< Slave Latency in number of connection events, see @ref BLE_GAP_CP_LIMITS.*/
  uint16_t conn_sup_timeout;          /**< Connection Supervision Timeout in 10 ms units, see @ref BLE_GAP_CP_LIMITS.*/
} ble_gap_conn_params_t;
/*
p_peer_addr: 对端设备地址
p_scan_params:扫描参数
p_conn_params:连接参数
conn_cfg_tag:连接配置标识
*/
uint32_t sd_ble_gap_connect(ble_gap_addr_t const *p_peer_addr, ble_gap_scan_params_t const *p_scan_params, ble_gap_conn_params_t const *p_conn_params, uint8_t conn_cfg_tag)

HCI层连接命令的格式:

android 耳机数据传输 安卓蓝牙数据传输_ble_17

LL层连接请求格式:

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_18


HCI层接口: 

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_19


连接请求

在空中传播的数据:

aa d6 be 89 8e c5 22 81 1b f2 8e 55 68 d0 a3 1a d7 7b d0 cf c5 ca 6a e6 c7 ee 02 16 00 28 00 00 00 f4 01 ff ff ff ff 1f 26 2a de cc

android 耳机数据传输 安卓蓝牙数据传输_GAP_20

aa,前导码
d6 be 89 8e,0x8e89bed6接入地址
LL 报头 C5(1100 0101) 22
        0101b, 数据类型, CONNECT_IND,连接请求
        0b, RFU,未使用
        0b, ChSel, 通道选择,未使用
        1b,TxAdd,发起者的mac地址类型,1表示随机地址
        1b,RxAdd, 接收者的mac地址类型,1表示随机地址
        22, 0x22 Payload长度
Payload
        81 1b f2 8e 55 68, 发起者的Mac地址0x68558ef21b81
        d0 a3 1a d7 7b d0,接受者的mac地址0xd07bd71aa3d0
        cf c5 ca 6a,用于连接的计入地址0x6acac5cf
        e6 c7 ee,CRC初始值0xeec7e6
        02,传输窗口大小,单位1.25ms
        16 00,传输窗口偏移,单位1.25ms
        28 00, 连接间隔,0x0028,单位1.25ms
        00 00, 从设备延时,0x0000
        f4 01, 超时时间,0x01f4,单位10ms
        ff ff ff ff 1f, 通道图,表示哪些信道是好的 0x1f ff ff ff ff,最高的3位保留,其余位表示通信的信道,1表示可用,0表示不可用。
        26(001 00110), SCA & Hope
                001b, 高三位SCA,主设备时钟精度

android 耳机数据传输 安卓蓝牙数据传输_ble_21

00110b,低5位,hope值,6
2a de cc,CRC校验,0xccde2a

android 耳机数据传输 安卓蓝牙数据传输_android 耳机数据传输_22


HCI层会根据GAP传下来的参数,组合成LL层需要的数据格式,也就是黄色部分的数据。实际上GAP会按照HCI提供的接口所要求的参数格式传参。这里重点是突出空中传播的数据部分,因此统一使用LL层的格式。 

三 发现服务
3.1 发现分组服务
发现主服务的过程

android 耳机数据传输 安卓蓝牙数据传输_ble_23


用户调用sd_ble_gattc_primary_services_discover:

uint32_t sd_ble_gattc_primary_services_discover(uint16_t conn_handle, uint16_t start_handle, ble_uuid_t const *p_srvc_uuid)


协议栈的调用:

android 耳机数据传输 安卓蓝牙数据传输_GAP_24


发送的数据aa cc c5 ca 6a 0e 0b 07 00 04 00 10 01 00 ff ff 00 28 cf 2d 83

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_25


aa: 前导码

cc c5 ca 6a: 接入地址,0x6acac5cc

0e 0b,LL层报头

        10b,LLID,表示开始报文

        1b,NESE(预期序列号)

        1b,序列号(SN)

        0b,MD(更多数据)

        0b,CP(是否包含CTEinfo)

        最高两位,RFU保留

        0b,Payload长度

07 00 04 00,L2CAP层报头

        07 00,Length,Information Payload长度,0x0007

        04 00,通道号,0x0004

10 01 00 ff ff 00 28,ATT层数据

        10,请求类型,ATT_READ_BY_GROUP_TYPE_REQ

        01 00,开始Handle,0x0001

        ff ff,结束Handle,0xffff

        00 28,UUID类型,0x2800,表示主要服务

cf 2d 83,CRC校验,0x832dcf 

android 耳机数据传输 安卓蓝牙数据传输_ble_26

 

android 耳机数据传输 安卓蓝牙数据传输_android 耳机数据传输_27


3.2 发现分组服务响应

响应命令的数据格式:

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_28


android 耳机数据传输 安卓蓝牙数据传输_ble_29

空中传播的数据

aa cc c5 ca 6a 06 12 0e 00 04 00 11 06 01 00 09 00 00 18 0a 00 0a 00 01 18 a8 64 7c
  • 1

android 耳机数据传输 安卓蓝牙数据传输_ble_30

 

android 耳机数据传输 安卓蓝牙数据传输_GAP_31


四 数据交互

4.1 主设备写命令

ATT层数据格式:

android 耳机数据传输 安卓蓝牙数据传输_GAP_32


typedef struct
{
  uint8_t        write_op;             /**< Write Operation to be performed, see @ref BLE_GATT_WRITE_OPS. */
  uint8_t        flags;                /**< Flags, see @ref BLE_GATT_EXEC_WRITE_FLAGS. */
  uint16_t       handle;               /**< Handle to the attribute to be written. */
  uint16_t       offset;               /**< Offset in bytes. @note For WRITE_CMD and WRITE_REQ, offset must be 0. */
  uint16_t       len;                  /**< Length of data in bytes. */
  uint8_t const *p_value;              /**< Pointer to the value data. */
} ble_gattc_write_params_t;
uint32_t sd_ble_gattc_write(uint16_t conn_handle, ble_gattc_write_params_t const *p_write_params)

协议栈处理流程:

  • 用户层设置连接句柄,以及需要发送的数据信息
  • android 耳机数据传输 安卓蓝牙数据传输_bluetooth_33


 ATT层获得L2CAP层的buffer指针,并填充发送消息类型,需要写的属性句柄,以及真实的数据。消息类型和句柄必须要占用3个字节的空间,因此有效数据长度不能大于MTU_SIZE - 3。调用L2CAP层的装载函数,并指明数据长度和通道ID

android 耳机数据传输 安卓蓝牙数据传输_ble_34

 L2CAP层获取HCI层的数据buffer指针之后首先构建一个HCI的头,并把有效数据装载到buffer中

android 耳机数据传输 安卓蓝牙数据传输_android 耳机数据传输_35

 

android 耳机数据传输 安卓蓝牙数据传输_bluetooth_36

 

android 耳机数据传输 安卓蓝牙数据传输_蓝牙_37


4.2 从设备发出通知

android 耳机数据传输 安卓蓝牙数据传输_ble_38


android 耳机数据传输 安卓蓝牙数据传输_GAP_39