nimble 蓝牙开发二: API 使用文档

文章目录

  • nimble 蓝牙开发二:API 使用文档
    • 设置蓝牙设备地址 BD_ADDR
    • GAP 应用
      • 广播类型
      • GAP 事件
      • GAP 事件回调函数
      • GAP 事件结构体:struct ble_gap_event
      • 发送蓝牙广播 - boardcaster
      • boardcaster 流程:
        • 发送蓝牙广播相关的 API
          • 设置广播数据的 API
          • 设置扫描响应数据的 API
          • 启动广播发送 API
          • 广播类型的确定
        • 发送广播产生的事件及 GAP 事件回调函数
        • boardcaster 流程分析
      • 扫描蓝牙广播 - observer
        • 主动扫描和被动扫描
        • 扫描流程
        • 扫描蓝牙广播相关的 API
          • 启动广播接收 API
          • 解析广播负载 API
        • 扫描蓝牙广播产生的事件及 GAP 回调函数
        • observer流程分析
      • 连接状态从机 - peripheral
        • 从机连接流程抽象逻辑图
        • 发送可连接广播 API
        • peripheral 产生的事件及 GAP 回调函数
        • peripheral 流程分析
      • 连接状态主机 - Central
        • 主机连接抽象逻辑图
        • 首先执行扫描蓝牙广播流程
        • 再启动建立连接流程 API
        • 连接建立流程产生的 GAP 事件
    • GATT 应用
      • GATT 服务器应用以及相关 API
        • 服务(service)抽象数据结构 - struct ble_gatt_svc_def
        • 特征(Characteristic)数据结构 - struct ble_gatt_chr_def
        • 特征描述符数据结构 - struct ble_gatt_dsc_def
        • GATT 服务注册相关 API
          • GATT 服务资源计数 API
          • GATT 服务注册 API
          • 使用上述两个 API 的注意事项
        • GATT 服务器流程
        • GATT 服务器产生的事件
      • GATT 客户端应用及其相关 API
        • GATT 客户端侧的服务(service)抽象数据结构
        • GATT 客户端侧的属性的抽象结构
        • GATT 客户端对特征定义的抽象数据结构
        • GATT 客户端对特征描述符的抽象数据结构
        • GATT 客户端的数据交换抽象逻辑图
        • GATT 客户端使用的相关 API
          • 发现所有的主服务 API
          • 在指定的主服务内查找所有的特征定义 API
          • 在指定的服务内查找所有的特征描述符 API
          • 读指定属性句柄的属性值 API
          • 写指定属性句柄的属性值 API
          • GATT 客户端需要处理的 GAP 事件
  • 正在更新。。。

nimble 蓝牙开发二:API 使用文档

设置蓝牙设备地址 BD_ADDR

蓝牙设备地址存在两种类型:公共地址和随机地址。蓝牙地址都是 48 位,6字节长。

其中,随机地址由细分为3种类型:不可解析随机地址,可解析随机地址,静态随机地址。

在开始任意数据传输之前,都必须设置蓝牙设备地址,或公共地址,或随机地址,或者公共地址和随机地址都设置。

蓝牙公共设备地址通过系统配置宏 MYNEWT_VAL_BLE_PUBLIC_DEV_ADDR 设置,在 host 和 controller 同步后会用宏定义中设置的公共地址配置 host 的公共标识地址。

蓝牙随机设备地址的设置可以使用如下接口:

int ble_hs_id_set_rnd(const uint8_t *rnd_addr);

参数说明:

  • rnd_addr : 6 字节随机设备地址,注意设备地址的字节序。

返回值:随机地址设置成功返回0,失败返回错误码。

在启动任何蓝牙流程之前,可以使用下面的函数来检查当前最合适使用的蓝牙地址,优先检查随机地址(如果随机设备地址已经设置的话)。

int ble_hs_id_infer_auto(int privacy, uint8_t *out_addr_type);

参数说明:

  • privacy : 0/1,表示是否使用一个解析地址。

  • out_addr_type : 输出合适的地址类型,供蓝牙 GAP 流程使用。

返回值:成功返回0,失败返回错误码。

如果函数执行成功,那么 out_addr_type 可能的返回值如下:

nimble 蓝牙开发二: API 使用文档_第1张图片

GAP 应用

GAP 应用用于 BLE 设备发送广播、扫描广播与建立连接。

注意,GAP 应用不包含连接建立之后的数据交换。

其中,BLE 在 GAP 应用中可以扮演 4 种角色:

角色 应用特性
Broadcaster 用于发送不可链接广播,并对 Observer 发送的扫描请求做出响应,不能与 Observer 建立连接
Observer 接收 Broadcaster 发送的广播,可以选择向 Broadcaster 发送扫描请求,并接收对应的扫描响应
Peripheral 用于发送可连接广播,并根据接收到连接请求与 Central 建立连接
Central 接收可连接广播,并向 Peripheral 发送连接请求,并建立连接

广播类型

对于常用的传统广播,广播类型共有 4 种:

广播类型 描述
ADV_IND 可连接、可扫描、不定向广播
ADV_DIRECT_IND 可连接、不可扫描、定向广播
ADV_SCAN_IND 不可连接、可扫描、不定向广播
ADV_NONCONN_IND 不可连接、不可扫描、不定向广播

在下面的《广播类型的确定》一节会讲解,广播类型的确定方法,广播类型由 struct ble_gap_adv_params 结构体的 conn_mode 参数和 disc_mode 参数共同确定。

GAP 事件

GAP 事件包含了 BLE 设备在数据交换过程中所有可能产生的异步事件,换句话说,即使是 GATT 应用也会产生 GAP 事件。
nimble Host 中所有可能的 GAP 事件如下:

GAP 事件枚举值 GAP 事件宏 GAP 事件描述
0 BLE_GAP_EVENT_CONNECT 连接尝试事件,可能连接成功,也可能连接失败
1 BLE_GAP_EVENT_DISCONNECT 连接终止事件
2 RFU RFU
3 BLE_GAP_EVENT_CONN_UPDATE 更新连接参数完成事件
4 BLE_GAP_EVENT_CONN_UPDATE_REQ 表示连接的对端设备尝试更新连接参数事件
5 BLE_GAP_EVENT_L2CAP_UPDATE_REQ 表示连接的对端设备尝试更新连接参数事件
6 BLE_GAP_EVENT_TERM_FAILURE 连接终止失败事件
7 BLE_GAP_EVENT_DISC 广播报告事件,表示在发现流程中扫描到广播报文
8 BLE_GAP_EVENT_DISC_COMPLETE 发现流程结束事件
9 BLE_GAP_EVENT_ADV_COMPLETE 广播流程结束事件
10 BLE_GAP_EVENT_ENC_CHANGE 连接加密状态更改事件
11 BLE_GAP_EVENT_PASSKEY_ACTION 配对过程中的密钥查询完成事件
12 BLE_GAP_EVENT_NOTIFY_RX 接收一个属性的 notification / indication 事件
13 BLE_GAP_EVENT_NOTIFY_TX 发送一个属性的 notifycation / indication 完成事件
14 BLE_GAP_EVENT_SUBSCRIBE 订阅事件,表示对端设备(GATT 客户端)修改了 CCCD (客户端特征配置描述符)的值,使 CCCD 中的 notify / indicate 的标志位发生改变
15 BLE_GAP_EVENT_MTU 表示 MTU 交换流程完成事件
16 BLE_GAP_EVENT_IDENTITY_RESOLVED 配对成功后产生的事件,标识地址成功解析事件?
17 BLE_GAP_EVENT_REPEAT_PAIRING 重复配对事件
18 BLE_GAP_EVENT_PHY_UPDATE_COMPLETE PHY 层更新事件
19 BLE_GAP_EVENT_EXT_DISC 在发现流程中收到一个扩展广播报告事件
20 BLE_GAP_EVENT_PERIODIC_SYNC 发现流程中建立周期同步广播链路事件
21 BLE_GAP_EVENT_PERIODIC_REPORT 周期广播记录事件
22 BLE_GAP_EVENT_PERIODIC_SYNC_LOST 周期同步广播链路丢失事件
23 BLE_GAP_EVENT_SCAN_REQ_RCVD 扩展广播下的扫描请求事件
24 BLE_GAP_EVENT_PERIODIC_TRANSFER 收到周期同步广播传输事件

GAP 事件回调函数

GAP 事件回调函数类型如下:

typedef int ble_gap_event_fn(struct ble_gap_event *event, void *arg);

参数说明:

  • event, struct ble_gap_event 类型参数,唯一的代表了当前产生的 GAP 事件,包含事件类型以及事件相关信息。
  • arg, 这是自定义的 GAP 回调函数形参,启动 GAP 流程时传入。

返回值:处理成功返回 0,处理失败返回非 0 错误码。

GAP 事件结构体:struct ble_gap_event

struct ble_gap_event 结构体为 GAP 事件结构体,可以唯一的代表一个已发生的 GAP 事件,其数据结构如下所示:

struct ble_gap_event {
    /**
     * Indicates the type of GAP event that occurred.  This is one of the
     * BLE_GAP_EVENT codes.
     * 事件类型
     */
    uint8_t type;

    /**
     * A discriminated union containing additional details concerning the GAP
     * event.  The 'type' field indicates which member of the union is valid.
     * 联合体内的参数类型取决于 Type 位域的值
     */
    union {
    /*
  	 * 存储各类事件的事件信息结构,太长了,省略不写
  	 */
    };
    };

struct ble_gap_event 事件结构体主要由两个参数组合而成:

  • type, 表示 GAP 事件类型。
  • union 联合体,存储各类 GAP 事件的事件相关信息数据结构参数,具体参数由 type 决定。

GAP 事件类型 type 和 GAP 事件数据结构对象的映射关系如下:

GAP 事件类型 type type 对应的 GAP 事件数据结构参数
BLE_GAP_EVENT_CONNECT connect
BLE_GAP_EVENT_DISCONNECT disconnect
BLE_GAP_EVENT_DISC disc
BLE_GAP_EVENT_EXT_DISC ext_disc
BLE_GAP_EVENT_DISC_COMPLETE disc_complete
BLE_GAP_EVENT_ADV_COMPLETE adv_complete
BLE_GAP_EVENT_CONN_UPDATE conn_update
BLE_GAP_EVENT_L2CAP_UPDATE_REQ / BLE_GAP_EVENT_CONN_UPDATE_REQ conn_update_req
BLE_GAP_EVENT_TERM_FAILURE term_failure
BLE_GAP_EVENT_ENC_CHANGE enc_change
BLE_GAP_EVENT_PASSKEY_ACTION passkey
BLE_GAP_EVENT_NOTIFY_RX notify_rx
BLE_GAP_EVENT_NOTIFY_TX notify_tx
BLE_GAP_EVENT_SUBSCRIBE subscribe
BLE_GAP_EVENT_MTU mtu
BLE_GAP_EVENT_IDENTITY_RESOLVED identity_resolved
BLE_GAP_EVENT_REPEAT_PAIRING repeat_pairing
BLE_GAP_EVENT_PHY_UPDATE_COMPLETE phy_updated
BLE_GAP_EVENT_PERIODIC_SYNC periodic_sync
BLE_GAP_EVENT_PERIODIC_REPORT periodic_report
BLE_GAP_EVENT_PERIODIC_SYNC_LOST periodic_sync_lost
BLE_GAP_EVENT_SCAN_REQ_RCVD scan_req_rcvd
BLE_GAP_EVENT_PERIODIC_TRANSFER periodic_transfer

发送蓝牙广播 - boardcaster

boardcaster 流程:

广告流程抽象逻辑图如下:
nimble 蓝牙开发二: API 使用文档_第2张图片

API 执行流程图如下:

nimble 蓝牙开发二: API 使用文档_第3张图片

发送蓝牙广播相关的 API

这些 API 原型在 nimble 协议栈目录下的 /nimble/host/include/host/ble_gap.h 文件中。

设置广播数据的 API

下面的函数用于设置标准的、符合规范的广播数据 AD structure:

int ble_gap_adv_set_fields(const struct ble_hs_adv_fields *adv_fields);

参数说明:

  • adv_fields :这个结构体包含所有 AD structure 数据类型抽象,因为蓝牙5.2规范(这是我阅读的)对广播数据提出了格式和类型要求,所以我们只能在广播数据中设置指定的参数类型,传输有限的数据。注意:蓝牙 5.2 规范要求广播数据不能超过 31 个字节。不使用的参数请设置为 NULL。

返回值 : 成功返回 0, 失败返回非 0 错误码。

struct ble_hs_adv_fields 结构体对标准蓝牙广播数据的抽象如下:

struct ble_hs_adv_fields {
    /*** 0x01 - Flags. */
    uint8_t flags;

    /*** 0x02,0x03 - 16-bit service class UUIDs. */
    const ble_uuid16_t *uuids16;
    uint8_t num_uuids16;
    unsigned uuids16_is_complete:1;

    /*** 0x04,0x05 - 32-bit service class UUIDs. */
    const ble_uuid32_t *uuids32;
    uint8_t num_uuids32;
    unsigned uuids32_is_complete:1;

    /*** 0x06,0x07 - 128-bit service class UUIDs. */
    const ble_uuid128_t *uuids128;
    uint8_t num_uuids128;
    unsigned uuids128_is_complete:1;

    /*** 0x08,0x09 - Local name. */
    const uint8_t *name;
    uint8_t name_len;
    unsigned name_is_complete:1;

    /*** 0x0a - Tx power level. */
    int8_t tx_pwr_lvl;
    unsigned tx_pwr_lvl_is_present:1;

    /*** 0x0d - Slave connection interval range. */
    const uint8_t *slave_itvl_range;

    /*** 0x16 - Service data - 16-bit UUID. 包含*/
    const uint8_t *svc_data_uuid16;
    uint8_t svc_data_uuid16_len;

    /*** 0x17 - Public target address. */
    const uint8_t *public_tgt_addr;
    uint8_t num_public_tgt_addrs;

    /*** 0x19 - Appearance. */
    uint16_t appearance;
    unsigned appearance_is_present:1;

    /*** 0x1a - Advertising interval. */
    uint16_t adv_itvl;
    unsigned adv_itvl_is_present:1;

    /*** 0x20 - Service data - 32-bit UUID. */
    const uint8_t *svc_data_uuid32;
    uint8_t svc_data_uuid32_len;

    /*** 0x21 - Service data - 128-bit UUID. */
    const uint8_t *svc_data_uuid128;
    uint8_t svc_data_uuid128_len;

    /*** 0x24 - URI. */
    const uint8_t *uri;
    uint8_t uri_len;

    /*** 0xff - Manufacturer specific data. 制造商特定数据,前两个字节是制造商 ID */
    const uint8_t *mfg_data;
    uint8_t mfg_data_len;
};

该结构体参数说明如下:
nimble 蓝牙开发二: API 使用文档_第4张图片

标准蓝牙广播数据格式定义在蓝牙 5.2 规范的 卷3,Part C,第 11 节;标准广播数据类型格式定义在 CSS 的 Part A,第 1 节。

如果按照标准格式,那么我们只能设置有限类型的数据。

同样的,nimble 也提供了一个接口用于设置自由格式的广播数据:

int ble_gap_adv_set_data(const uint8_t *data, int data_len);

参数说明:

  • data : 这是自定义数据的地址。

  • data_len : 自定义数据的长度,同样需要注意的是,广播数据长度不能超过 31 字节。

返回值 :成功返回 0,失败返回非 0 错误码。

这个接口允许我们自由设置广播负载数据,也就是说可以不符合规范。

广播 PDU (Protocol Data Unit , 可以理解为蓝牙协议帧) 负载数据应在广播启动之前设置。

设置扫描响应数据的 API

扫描响应负载数据的格式规范跟上面的广播负载数据的格式规范是一样的。而且扫描响应也是广播的一种。所以扫描响应数据和广播数据的类型都是 AD Structure。

下面的函数用于设置标准的规范的扫描响应数据:

int ble_gap_adv_rsp_set_fields(const struct ble_hs_adv_fields *rsp_fields);

参数说明:

  • rsp_fields : 扫描响应数据对象,长度不超过 31 字节,数据类型跟广播数据一样

返回值:成功返回 0,失败返回非 0 错误码。

同样的,nimble 也提供了一个可以自由设置响应扫描负载的 API:

int ble_gap_adv_rsp_set_data(const uint8_t *data, int data_len);

参数说明:

  • data:扫描响应负载存储地址;

  • data_len:扫描响应负载长度,注意,长度不能超过 31 个字节。

扫描响应 PDU 负载数据应该广播启动之前设置。

启动广播发送 API

下面的函数用于启动广播发送,并注册 GAP 事件回调函数

int ble_gap_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
                      int32_t duration_ms,
                      const struct ble_gap_adv_params *adv_params,
                      ble_gap_event_fn *cb, void *cb_arg);

参数说明:

  • own_addr_type : 本机蓝牙设备地址类型,可以由ble_hs_id_infer_auto() 函数确认。设置广播数据包中使用的蓝牙设备地址类型,如果使用了公共地址,则设置为公共地址类型,否则请设置为随机地址类型

  • direct_addr : 如果使用定向广播,则设置为目的设备地址,否则为 NULL。

  • duration_ms : 广播流程时间, 如果设置为宏 BLE_HS_FOREVER 则永远不会超时,否则会产生一个 BLE_GAP_EVENT_ADV_COMPLETE 广播完成事件。

  • adv_params : struct ble_gap_adv_params 对象,广播参数,设置广播的类型,广播间隔等参数。

  • cb :GAP 事件回调函数,由应用层注册的回调函数,用于处理广播事件的事件,比如 BLE_GAP_EVENT_ADV_COMPLETE 事件。

  • cb_arg : 事件回调函数参数。

返回值 :成功返回 0,失败返回非 0 错误码

struct ble_gap_adv_params 结构体为广播流程抽象,用于设置广播流程参数:

struct ble_gap_adv_params {
    /** Advertising mode. Can be one of following constants:
     *  - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2).
     *  - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3).
     *  - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4).
     *  连接模式
     */
    uint8_t conn_mode;
    /** Discoverable mode. Can be one of following constants:
     *  - BLE_GAP_DISC_MODE_NON  (non-discoverable; 3.C.9.2.2).
     *  - BLE_GAP_DISC_MODE_LTD (limited-discoverable; 3.C.9.2.3).
     *  - BLE_GAP_DISC_MODE_GEN (general-discoverable; 3.C.9.2.4).
     *  发现模式
     */
    uint8_t disc_mode;

    /** Minimum advertising interval, if 0 stack use sane defaults,最小广告间隔,0 表示使用默认值,单位:0.625ms */
    uint16_t itvl_min;
    /** Maximum advertising interval, if 0 stack use sane defaults,最大广告间隔,0 表示使用默认值,单位:0.625ms */
    uint16_t itvl_max;
    /** Advertising channel map , if 0 stack use sane defaults,主广告信道掩码,0 表示使用默认值 */
    uint8_t channel_map;

    /** Advertising  Filter policy,广告屏蔽策略 */
    uint8_t filter_policy;

    /** If do High Duty cycle for Directed Advertising,是否在定向广告时使用高占空比 */
    uint8_t high_duty_cycle:1;
};

struct ble_gap_adv_params 结构体参数说明如下:

nimble 蓝牙开发二: API 使用文档_第5张图片

广播类型的确定

广播类型的确定方法如下:

广播类型 conn_mode 参数 disc_mode 参数
ADV_IND BLE_GAP_CONN_MODE_UND BLE_GAP_DISC_MODE_GEN
ADV_DIRECT_IND BLE_GAP_CONN_MODE_DIR BLE_GAP_CONN_MODE_NON
ADV_NONCONN_IND BLE_GAP_CONN_MODE_NON BLE_GAP_CONN_MODE_NON
ADV_SCAN_IND BLE_GAP_CONN_MODE_NON BLE_GAP_DISC_MODE_GEN

发送广播产生的事件及 GAP 事件回调函数

因为 boardcaster 发送的是不可连接广播,在设置了广播周期之后,则会触发广播完成事件

注意,设备发送扫描响应 PDU 不会触发事件,直接发送广播启动之前设置的扫描响应数据,如果应用层没有设置扫描响应数据,那么扫描响应 PDU 的负载位域长度为0。

所以,boardcaster 广播启动时注册的回调函数只需要处理广播周期结束之后的广播完成事件即可。

boardcaster 流程分析

此部分需要结合 nimble 应用代码。

所有的 nimble 蓝牙操作之只能在 host 与 controller 同步之后使用。

ble_gap_adv_set_fields()中,host 通过 HCI 发送设置广告 PDU 负载数据的命令(OpCode = 0x2008), 来设置广告 PDU 负载数据。

ble_gap_adv_start()中,host 协议栈会根据自定义的参数来发送相对应的广告流程参数设置 HCI 命令,并通过广告流程启动 HCI 命令来启动广告流程。

最后,host 协议栈会启动 host 的软件定时器来对广告周期进行定时。如果超时则会在 host 软件定时器的回调函数中发送停止广告流程 HCI 命令,并调用应用层定义的回调函数来向应用层报告因为广告周期超时而产生的广告完成事件

扫描蓝牙广播 - observer

主动扫描和被动扫描

observer存在两个模式,主动扫描被动扫描,这两种模式的区别在于:主动扫描模式下,observer的 controller 在收到一个可扫描的广播帧之后会发送扫描请求帧 SCAN_REQ,然后尝试接收 advertiser 发送的扫描响应 SCAN_RSP 。

扫描流程

扫描流程抽象逻辑图:
nimble 蓝牙开发二: API 使用文档_第6张图片

API 流程图:
nimble 蓝牙开发二: API 使用文档_第7张图片

扫描蓝牙广播相关的 API

这些 API 原型在 nimble 协议栈目录下的 /nimble/host/include/host/ble_gap.h 文件中。

启动广播接收 API

下面的 API 用于启动蓝牙的发现流程(即扫描):

int ble_gap_disc(uint8_t own_addr_type, int32_t duration_ms,
                 const struct ble_gap_disc_params *disc_params,
                 ble_gap_event_fn *cb, void *cb_arg);

参数说明:

  • own_addr_typr : 发送 SCAN_REQ 使用的地址类型,如果是被动扫描,则不使用,因为被动扫描不会发送 SCAN_REQ。

  • duration_ms : host 侧设置的发现周期(扫描周期),单位 ms。

  • disc_params : 发现流程参数,即扫描参数,比如扫描窗口、扫描间隔与扫描屏蔽策略等。

  • cb : GAP 事件回调函数。发现流程中会产生两种事件:接收到广播后产生广告报告事件;发现周期结束后的发现完成事件。

  • cd_arg : GAP 事件回调函数参数。

返回值:0或其他非 0 错误码。

struct ble_gap_disc_params 为发现流程参数结构体,其数据结构如下:

struct ble_gap_disc_params {
    /** Scan interval in 0.625ms units */
    uint16_t itvl;

    /** Scan window in 0.625ms units */
    uint16_t window;

    /** Scan filter policy */
    uint8_t filter_policy;

    /** If limited discovery procedure should be used */
    uint8_t limited:1;

    /** If passive scan should be used */
    uint8_t passive:1;

    /** If enable duplicates filtering */
    uint8_t filter_duplicates:1;
};

发现流程参数说明:
nimble 蓝牙开发二: API 使用文档_第8张图片

解析广播负载 API

如下函数用于解析广播 PDU 负载:

int ble_hs_adv_parse_fields(struct ble_hs_adv_fields *adv_fields,
                            const uint8_t *src, uint8_t src_len);

参数说明:

  • adv_fields : 广播负载解析后填充的标准广播数据对象。

  • src:广播 PDU 负载源数据。

  • src_len : 广播 PDU 负载长度。

返回值: 0 或者其他非 0 错误码。

我们可以根据解析出来的 adv_fields ,访问我们感兴趣的内容。

扫描蓝牙广播产生的事件及 GAP 回调函数

扫描流程中会产生两种事件:

1.扫描到广播报文之后会产生广告报告事件,事件码为宏 BLE_GAP_EVENT_DISC = 0x07,此时我们可以在 GAP 回调函数中解析扫描到的广播负载数据。

2.自定义的发现周期到期之后会产生发现流程完成事件,事件码为宏 BLE_GAP_EVENT_DISC_COMPLETE = 0x08,我们可以在 GAP 回调函数中重新开始扫描或其他操作。

observer流程分析

此部分需要结合 nimble 代码。

所有的 nimble 蓝牙操作之只能在 host 与 controller 同步之后调用。

通过 ble_gap_disc()开启扫描流程,host 通过 HCI 命令设置 controller 的扫描参数,然后再通过 HCI 命令使能 controller 的扫描。

在自定义的 GAP 回调函数中处理扫描产生的事件。产生的事件有两类:

  1. controller 发送广告报告 HCI 事件数据包给 host,产生了广告报告事件。

  2. 设置的发现周期超时后,由 host 软件定时器引起的发现完成事件(扫描完成事件)。

连接状态从机 - peripheral

如果要进入连接状态下的从机,那么必须发送可连接的广播。

从机连接流程抽象逻辑图

nimble 蓝牙开发二: API 使用文档_第9张图片

在连接建立之后,BLE 设备只允许在连接事件中进行数据交换,Peripheral 会做为连接状态下的从机,与主机在连接事件中进行点对点通信。

发送可连接广播 API

我们使用发送广播的 API 来发送可连接广播,即:

int ble_gap_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
                      int32_t duration_ms,
                      const struct ble_gap_adv_params *adv_params,
                      ble_gap_event_fn *cb, void *cb_arg);

只在 API 的使用上存在一定的区别,我们要在 adv_params 参数中设置广播 PDU 类型为可连接广播,如下:

    /*
     * 选择广告 PDU 类型,ADV_IND
     */
    adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;   /* 不定向可连接模式 */
    adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;   /* 通用发现模式 */

其他操作同发送广播一致。

peripheral 产生的事件及 GAP 回调函数

由于发送的是可连接的广播,所以我们可以与其他 BLE 设备建立连接。

所以,产生的事件类型较多:

  1. 由于广播周期结束产生的广播完成事件;
  2. 由于连接建立而导致的连接事件,此时 controller 会分配连接句柄,host 会分配连接对象;
  3. 由于连接状态主机发送连接更新 PDU 而导致的连接更新事件;
  4. 由于断开连接流程而导致的断开连接事件;
  5. 其他未处理事件等。

peripheral 流程分析

我们只需启动可连接广播 PDU 发送流程即可实现 peripheral。

此部分分析需要查看 nimble 协议栈的代码。

连接状态主机 - Central

连接状态主机首先扫描广播,并与可连接广播 建立连接。

通常使用两个步骤来建立连接:

  1. 扫描流程,发现想要连接的可连接设备;
  2. 建立连接流程,与指定的可连接设备建立连接(指定对端地址)。

主机连接抽象逻辑图

nimble 蓝牙开发二: API 使用文档_第10张图片

在启动连接流程时,我们需要指定连接的从机地址,连接建立后的连接事件参数等。

API 运行逻辑图如下:
nimble 蓝牙开发二: API 使用文档_第11张图片

首先执行扫描蓝牙广播流程

该部分同 observer 一致,使用下面的 API 来启动扫描:

int ble_gap_disc(uint8_t own_addr_type, int32_t duration_ms,
                 const struct ble_gap_disc_params *disc_params,
                 ble_gap_event_fn *cb, void *cb_arg);

需要注意的是,此时的 GAP 事件回调函数可以只处理扫描流程产生的事件,即:

  1. 广告报告事件;
  2. 扫描完成事件。

我们需要在广播报告事件中,处理广播报文,查找想要连接的 BLE 设备。

再启动建立连接流程 API

在上面的广告报告事件中,我们可以根据解析出来的广告 PDU 负载来选择我们想要连接的设备(比如服务 UUID 参数,设备命参数等)。

我们可以使用如下的 API 来建立连接:

int ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr,
                    int32_t duration_ms,
                    const struct ble_gap_conn_params *params,
                    ble_gap_event_fn *cb, void *cb_arg);

参数说明:

  • own_addr_type : 连接使用的本地设备地址类型;

  • peer_addr : 想要连接的对端设备地址,如果为 NULL,则与白名单上的设备建立连接;

  • duration_ms : 建立连接的周期时间,如果在这个时间内没有建立连接成功,则产生连接建立失败事件;

  • param : 连接建立参数,如果为 NULL,则使用默认值;

  • cb : 连接建立流程 GAP 回调函数;

  • cb_arg : GAP 回调函数参数。

返回值 : 成功返回 0,失败返回错误码。

我们通过 struct ble_gap_conn_params 结构体来设置连接流程参数,该结构体数据结构如下:

struct ble_gap_conn_params {
    /** Scan interval in 0.625ms units */
    uint16_t scan_itvl;

    /** Scan window in 0.625ms units */
    uint16_t scan_window;

    /** Minimum value for connection interval in 1.25ms units */
    uint16_t itvl_min;

    /** Maximum value for connection interval in 1.25ms units */
    uint16_t itvl_max;

    /** Connection latency */
    uint16_t latency;

    /** Supervision timeout in 10ms units */
    uint16_t supervision_timeout;

    /** Minimum length of connection event in 0.625ms units */
    uint16_t min_ce_len;

    /** Maximum length of connection event in 0.625ms units */
    uint16_t max_ce_len;
};

连接参数结构体参数说明如下:
nimble 蓝牙开发二: API 使用文档_第12张图片

连接建立流程产生的 GAP 事件

产生的事件如下:

  1. 连接事件,标识连接建立情况,可能成功,也可能失败;
  2. 广告报告事件,在广告报告事件中,如果发现了想要连接的设备,那么我们需要先失能 GAP 的扫描流程,才能重新启动 GAP 的建立连接流程,如下图:
            /*
             * 失能当前正在执行的发现流程
             */
            ble_gap_disc_cancel();
            /*
             * 启动 GAP master 的连接流程,定向连接
             */
            ble_gap_connect(g_own_addr_type, &(event->disc.addr), 10000,
                            NULL, conn_event, NULL);
  1. 断开连接事件;
  2. 连接更新事件;
  3. 其他事件。

GATT 应用

GATT 应用用于在已连接设备之间进行数据交换。

GATT 存在两种角色,分别对应已连接的两个设备:
nimble 蓝牙开发二: API 使用文档_第13张图片

GATT 服务器应用以及相关 API

GATT 服务器负责定义一些列的服务(service)供 GATT 客户端访问,服务代表了 GATT 服务器端实现的一种功能。

服务(service)抽象数据结构 - struct ble_gatt_svc_def

struct ble_gatt_svc_def 结构体是对服务定义的抽象,可以唯一的用来表示一个 GATT 服务,其数据结构抽象如下:

struct ble_gatt_svc_def {
    /**
     * One of the following:
     *     o BLE_GATT_SVC_TYPE_PRIMARY - primary service
     *     o BLE_GATT_SVC_TYPE_SECONDARY - secondary service
     *     o 0 - No more services in this array.
     * 这是服务声明的属性类型,主服务还是次级服务
     */
    uint8_t type;

    /**
     * Pointer to service UUID; use BLE_UUIDxx_DECLARE macros to declare
     * proper UUID; NULL if there are no more characteristics in the service.
     * 这是服务声明属性的属性值,即服务 UUID
     * 存储服务 UUID 的地址,这是每一个 UUID 结构对象的第一个参数
     */
    const ble_uuid_t *uuid;

    /**
     * Array of pointers to other service definitions.  These services are
     * reported as "included services" during service discovery.  Terminate the
     * array with NULL.
     * 服务包含的引用服务对象数组
     */
    const struct ble_gatt_svc_def **includes;

    /**
     * Array of characteristic definitions corresponding to characteristics
     * belonging to this service.
     * 这是服务包含的特征定义
     */
    const struct ble_gatt_chr_def *characteristics;
};

struct ble_gatt_svc_def 的参数说明:
nimble 蓝牙开发二: API 使用文档_第14张图片

特征(Characteristic)数据结构 - struct ble_gatt_chr_def

struct ble_gatt_chr_def 是对特征定义的抽象,可以唯一的用来表示一个特征定义,其数据结构如下:

struct ble_gatt_chr_def {
    /**
     * Pointer to characteristic UUID; use BLE_UUIDxx_DECLARE macros to declare
     * proper UUID; NULL if there are no more characteristics in the service.
     * 特征 UUID,即特征值的 UUID,这是特征值 UUID 的存储地址
     */
    const ble_uuid_t *uuid;

    /**
     * Callback that gets executed when this characteristic is read or
     * written.
     * 当这个特征被读或写时调用的回调函数,特征定义时,这个回调函数必须存在
     */
    ble_gatt_access_fn *access_cb;

    /**
     * Optional argument for callback.
     * 回调函数参数
     */
    void *arg;

    /**
     * Array of this characteristic's descriptors.  NULL if no descriptors.
     * Do not include CCCD; it gets added automatically if this
     * characteristic's notify or indicate flag is set.
     * 特征定义中的特征描述符定义数组
     * 不要包含 CCCD(客户端特征配置描述符),如果特征值属性的 notify 位和 indication 位置位,则 CCCD 自动包含
     */
    struct ble_gatt_dsc_def *descriptors;

    /**
     * Specifies the set of permitted operations for this characteristic.
     * 特征值属性的属性权限
     */
    ble_gatt_chr_flags flags;

    /** Specifies minimum required key size to access this characteristic. */
    uint8_t min_key_size;

    /**
     * At registration time, this is filled in with the characteristic's value
     * attribute handle.
     * 应用层提供的用于保存特征值声明属性的属性句柄的地址
     */
    uint16_t *val_handle;
};

特征定义结构体 struct ble_gatt_chr_def 参数说明如下:
nimble 蓝牙开发二: API 使用文档_第15张图片
其中 access_cb 的函数原型如下:

/*
 * GATT 访问函数类,特征值或除 CCCD 外的其他特征描述符属性访问回调函数
 */
typedef int ble_gatt_access_fn(uint16_t conn_handle, uint16_t attr_handle,
                               struct ble_gatt_access_ctxt *ctxt, void *arg);

参数说明:

  • conn_handle, 连接句柄,指示当前访问的属性属于哪一个 GATT 服务器对象;
  • attr_handle, 属性句柄,表示当前访问的是哪一个属性;
  • ctxt, struct ble_gatt_access_ctxt 类型对象,表示属性访问的上下文抽象数据结构;
  • arg, 特征定义结构体中自定义的属性访问回调函数参数

返回值: 成功返回 0,错误返回非 0 错误码

struct ble_gatt_access_ctxt 属性访问上下文抽象数据结构,该结构体如下:

struct ble_gatt_access_ctxt {
    uint8_t op;
    struct os_mbuf *om;
    union {
        const struct ble_gatt_chr_def *chr;
        const struct ble_gatt_dsc_def *dsc;
    };
};

属性访问结构体参数说明:
nimble 蓝牙开发二: API 使用文档_第16张图片

特征描述符数据结构 - struct ble_gatt_dsc_def

结构体 struct ble_gatt_dsc_def 是特征描述符的抽线结构,结构体构成如下:

struct ble_gatt_dsc_def {
    /**
     * Pointer to descriptor UUID; use BLE_UUIDxx_DECLARE macros to declare
     * proper UUID; NULL if there are no more characteristics in the service.
     * 指向特征描述符 UUID 存储地址
     */
    const ble_uuid_t *uuid;

    /** Specifies the set of permitted operations for this descriptor. 描述符属性权限*/
    uint8_t att_flags;

    /** Specifies minimum required key size to access this descriptor. */
    uint8_t min_key_size;

    /** Callback that gets executed when the descriptor is read or written.
     *  特征描述符被访问时的回调函数,回调函数必须存在
     */
    ble_gatt_access_fn *access_cb;

    /** Optional argument for callback. */
    void *arg;
};

由于 CCCD 特征描述符不需要应用开发人员指定,只需要在特征定义中的 Flag 标志位置位 notify 或 indicate 即可,而其他的特征描述符用得很少,所以我们在这里就不详细介绍特征描述符数据结构了,

GATT 服务注册相关 API

GATT 服务注册需要两步:

  1. 使用资源计数 API - int ble_gatts_count_cfg(const struct ble_gatt_svc_def *defs),进行资源计数,更新全局计数器变量;
  2. 使用注册 API - int ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs),添加自定义的服务定义到全局服务定义指针数组中;
GATT 服务资源计数 API

资源计数 API 原型如下:

/**
 * Adjusts a host configuration object's settings to accommodate the specified
 * service definition array.  This function adds the counts to the appropriate
 * fields in the supplied configuration object without clearing them first, so
 * it can be called repeatedly with different inputs to calculate totals.  Be
 * sure to zero the GATT server settings prior to the first call to this
 * function.
 * 调整主机协议栈的配置对象的设置以适应指定的服务定义数组,它会增加合合适的位域参数,这个函数可以重复调用
 *
 * @param defs                  The service array containing the resource
 *                                  definitions to be counted.
 *
 * @return                      0 on success;
 *                              BLE_HS_EINVAL if the svcs array contains an
 *                                  invalid resource definition.
 */
int ble_gatts_count_cfg(const struct ble_gatt_svc_def *defs);

参数说明:

  • defs,struct ble_gatt_svc_def 类型的结构体指针,表示应用程序自定义的服务定义数组。

返回值:成功返回 0,错误返回非 0 错误码。

GATT 服务注册 API

服务注册 API 原型如下:

/**
 * Queues a set of service definitions for registration.  All services queued
 * in this manner get registered when ble_gatts_start() is called.
 * 所有以这种方式入队的服务会在 ble_gatts_start() 调用时注册,
 * 调用这个函数注册的服务必须在 host 和 controller 同步之前进行
 *
 * @param svcs                  An array of service definitions to queue for
 *                                  registration.  This array must be
 *                                  terminated with an entry whose 'type'
 *                                  equals 0.
 *
 * @return                      0 on success;
 *                              BLE_HS_ENOMEM on heap exhaustion.
 */
int ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs);

参数说明:

  • svcs,struct ble_gatt_svc_def 类型的结构体指针,表示应用程序自定义的服务定义数组。

返回值:成功返回0,失败返回非 0 错误码。

使用上述两个 API 的注意事项

注意:

  1. 使用上述两个 API 注册 GATT 服务时必须保证协议栈还没有同步,即 Host 协议栈还没有启动;
  2. 上述两个 API 使用之后并没有将 GATT 服务真正的注册进 Host 中,只更新了一些全局变量用来记录自定义的 GATT 服务,真正的 GATT 服务注册会在 Host 协议栈真正启动时调用的 ble_gatts_start() 函数中注册。

GATT 服务器流程

GATT 服务器数据交换逻辑流程与 Peripheral 一致:
nimble 蓝牙开发二: API 使用文档_第17张图片
不同的是,在连接状态中, GATT 客户端会使用 GATT 功能来访问 GATT 服务器上定义的 GATT 服务,然后触发自定义的回调函数,或 GAP 事件,比如:
nimble 蓝牙开发二: API 使用文档_第18张图片
API 执行流程如下:
nimble 蓝牙开发二: API 使用文档_第19张图片

GATT 服务器产生的事件

  1. 可能产生的广播流程完成事件;
  2. 连接产生的连接完成事件;
  3. 各种原因导致的连接断开事件;
  4. 由自己启动的连接参数更新事件;
  5. 由 GATT 客户端访问 CCCD 产生的订阅事件;
  6. 由 GATT 的 MTU 交换功能产生的 MTU 事件;
  7. 其他的事件,不是很常用,就不例举了。

·
·
·

GATT 客户端应用及其相关 API

GATT 客户端用来访问 GATT 服务器定义的服务(service)与特征(Characteristic)。

由于 GATT 客户端只能属性类型、属性句柄、和属性值感兴趣,所以对于 GATT 客户端,重新抽象了服务(service)、特征(Characteristic)、特征描述符的数据结构。

GATT 客户端侧的服务(service)抽象数据结构

当 GATT 客户端查找 GATT 服务器上定义的服务时,GATT 客户端侧服务的抽象数据结构如下:

struct ble_gatt_svc {
    uint16_t start_handle;  /* 服务起始句柄,也是服务声明属性的句柄 */
    uint16_t end_handle;    /* 服务结束句柄 */
    ble_uuid_any_t uuid;    /* 服务 UUID */
};

参数说明:

nimble 蓝牙开发二: API 使用文档_第20张图片

GATT 客户端侧的属性的抽象结构

GATT 客户端由于需要访问属性值,所以存在对属性的抽象,其数据结构如下:

struct ble_gatt_attr {
    uint16_t handle;    /* 属性句柄 */
    uint16_t offset;
    struct os_mbuf *om; /* 属性值 */
};

参数说明:
nimble 蓝牙开发二: API 使用文档_第21张图片

GATT 客户端对特征定义的抽象数据结构

当 GATT 客户端访问 GATT 服务器上的特征定义时,获得的 GATT 特征定义抽象数据结构如下:

struct ble_gatt_chr {
    uint16_t def_handle;    /* 特征值声明属性的属性句柄 */
    uint16_t val_handle;    /* 特征值句柄 */
    uint8_t properties;     /* 特征值属性 */
    ble_uuid_any_t uuid;    /* 特征值 UUID */
};

参数说明:
nimble 蓝牙开发二: API 使用文档_第22张图片

GATT 客户端对特征描述符的抽象数据结构

当 GATT 客户端查找 GATT 服务器定义的特征描述符时,获得的特征描述符抽象数据结构如下:

struct ble_gatt_dsc {
    uint16_t handle;    /* 特征描述符声明属性的属性句柄 */
    ble_uuid_any_t uuid;    /* 特征描述符的 UUID */
};

参数说明:

nimble 蓝牙开发二: API 使用文档_第23张图片

GATT 客户端的数据交换抽象逻辑图

GATT 客户端的数据交换流程与 Central 的数据交换流程基本上是一致的:
nimble 蓝牙开发二: API 使用文档_第24张图片
不同的是,在进入连接状态之后,GATT 客户端会使用服务发现 GATT 功能来查找 GATT 服务器定义的服务与特征,其抽象的查找流程如下:
nimble 蓝牙开发二: API 使用文档_第25张图片

GATT 客户端使用的相关 API

发现所有的主服务 API
/**
 * Initiates GATT procedure: Discover All Primary Services.
 * 该流程不会产生 GAP 事件,当收到 ATT_READ_BY_GROUP_TYPE_RSP 时,通过 cb 向应用层报告发现的服务定义
 * 只有在解析出一个主服务定义后,和主服务发现流程执行结束后才会调用自定义的回调函数
 *
 * @param conn_handle           The connection over which to execute the
 *                                  procedure.
 * @param cb                    The function to call to report procedure status
 *                                  updates; null for no callback.
 * @param cb_arg                The optional argument to pass to the callback
 *                                  function.
 */
int ble_gattc_disc_all_svcs(uint16_t conn_handle,
                            ble_gatt_disc_svc_fn *cb, void *cb_arg);

参数说明:

  • conn_handle, 连接句柄,用于确定访问哪一个 GATT 服务器;
  • cb,自定义的收到响应时调用回调函数,主服务发现回调函数;
  • cb_arg, 自定义的回调函数参数。

返回值:函数执行成功返回0,失败返回错误码。

主服务发现回调函数类型如下:

typedef int ble_gatt_disc_svc_fn(uint16_t conn_handle,
                                 const struct ble_gatt_error *error,
                                 const struct ble_gatt_svc *service,
                                 void *arg);

参数说明:

  • conn_handle, 连接句柄,执行该回调的连接对象;
  • error, 错误状态指示结构体;
  • service, 查找到的主服务抽象数据结构;
  • arg, 自定义的回调函数参数。

返回值: 0 或其他非0值;

当查找到一个主服务定义,或者主服务发现流程结束时,都会调用该回调函数。

在指定的主服务内查找所有的特征定义 API
/**
 * Initiates GATT procedure: Discover All Characteristics of a Service.
 * 对于指定的主服务,启动的特征声明发现流程
 *
 * @param conn_handle           The connection over which to execute the
 *                                  procedure.
 * @param start_handle          The handle to begin the search at (generally
 *                                  the service definition handle).
 * @param end_handle            The handle to end the search at (generally the
 *                                  last handle in the service).
 * @param cb                    The function to call to report procedure status
 *                                  updates; null for no callback.
 * @param cb_arg                The optional argument to pass to the callback
 *                                  function.
 *
 * @return                      0 on success; nonzero on failure.
 */
int ble_gattc_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
                            uint16_t end_handle, ble_gatt_chr_fn *cb,
                            void *cb_arg);

参数说明:

  • conn_handle, 连接句柄,表示当前通信对象;
  • start_handle, 服务定义起始句柄,也是服务声明的句柄;
  • end-handle, 服务定义结束句柄;
  • cb, 自定义的响应回调函数,查找到特征定义时调用;
  • cd_arg, 自定义的回调函数参数。

返回值: 0 或错误码。

查找特征定义的回调函数类型如下:

typedef int ble_gatt_chr_fn(uint16_t conn_handle,
                            const struct ble_gatt_error *error,
                            const struct ble_gatt_chr *chr, void *arg);

参数说明:

  • conn_handle, 连接句柄,指示连接对象;
  • error, 状态指示结构体;
  • chr, 查找到的特征定义;
  • arg, 自定义的回调函数参数。

返回值:0或错误码。

注意:在一个特征定义查找流程中,该函数可以被调用多次,因为一个服务中可能包含多个特征定义。

在指定的服务内查找所有的特征描述符 API

当服务范围内的特征定义查找完毕后,我们需要查找服务内的所有特征描述符。

/**
 * Initiates GATT procedure: Discover All Characteristic Descriptors.
 *
 * @param conn_handle           The connection over which to execute the
 *                                  procedure.
 * @param chr_val_handle        The handle of the characteristic value
 *                                  attribute.
 * @param chr_end_handle        The last handle in the characteristic
 *                                  definition.
 * @param cb                    The function to call to report procedure status
 *                                  updates; null for no callback.
 * @param cb_arg                The optional argument to pass to the callback
 *                                  function.
 *
 * @return                      0 on success; nonzero on failure.
 */
int ble_gattc_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
                            uint16_t end_handle,
                            ble_gatt_dsc_fn *cb, void *cb_arg);

参数说明:

  • conn_handle, 表示连接对象;
  • start_handle, 服务的起始句柄,即服务声明属性句柄;
  • end_handle, 服务结束句柄;
  • cb, 自定义的响应回调函数;
  • cb_arg, 自定义的回调函数参数。

返回值:0或错误码。

响应回调函数如下:

typedef int ble_gatt_dsc_fn(uint16_t conn_handle,
                            const struct ble_gatt_error *error,
                            uint16_t chr_val_handle,
                            const struct ble_gatt_dsc *dsc,
                            void *arg);

参数说明:

  • conn_handle, 连接句柄,指示连接对象;
  • error, 错误状态指示对象;
  • char_val_handle, 特征值句柄,表示查找的特征描述符所属的特征值;
  • dsc, 表示查找的特征描述符对象;
  • arg, 自定义的回调函数参数。

返回值:0或错误码。

读指定属性句柄的属性值 API
int ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle,
                   ble_gatt_attr_fn *cb, void *cb_arg);

参数说明:

  • conn_handle, 连接句柄,表示连接对象;
  • attr_handle, 要读的属性句柄;
  • cb, 收到读响应时的自定义回调函数;
  • cb_arg, 回调函数参数。

返回值:0 或错误码。

属性访问回调函数如下:

/**
 * The host will free the attribute mbuf automatically after the callback is
 * executed.  The application can take ownership of the mbuf and prevent it
 * from being freed by assigning NULL to attr->om.
 */
typedef int ble_gatt_attr_fn(uint16_t conn_handle,
                             const struct ble_gatt_error *error,
                             struct ble_gatt_attr *attr,
                             void *arg);

参数说明:

  • conn_handle, 连接句柄,指示连接对象;
  • error, 错误状态指示对象;
  • attr, 属性对象,存储访问的属性值;
  • arg, 自定义的回调函数;

返回值:0 或错误码。

如果没有出现错误的话,即 error 中的 status == 0,那么 attr 中存储访问的属性值。

写指定属性句柄的属性值 API
/**
 * Initiates GATT procedure: Write Characteristic Value (flat buffer version).
 *
 * @param conn_handle           The connection over which to execute the
 *                                  procedure.
 * @param attr_handle           The handle of the characteristic value to write
 *                                  to.
 * @param value                 The value to write to the characteristic.
 * @param value_len             The number of bytes to write.
 * @param cb                    The function to call to report procedure status
 *                                  updates; null for no callback.
 * @param cb_arg                The optional argument to pass to the callback
 *                                  function.
 *
 * @return                      0 on success; nonzero on failure.
 */
int ble_gattc_write_flat(uint16_t conn_handle, uint16_t attr_handle,
                         const void *data, uint16_t data_len,
                         ble_gatt_attr_fn *cb, void *cb_arg);

参数说明:

  • conn_handle, 连接句柄,指示连接对象;
  • attr_handle, 准备写的属性句柄;
  • data, 等待写的数据缓存区;
  • data_len, 等待写的数据长度;
  • cb,收到写响应时的回调函数;
  • cb_arg, 回调函数参数。

返回值: 0或错误码

写属性值的回调函数类型与读属性值的回调函数类型是一致的,在写响应回调函数中,我们可以通过 error 对象来判断是否写属性成功。

GATT 客户端需要处理的 GAP 事件

对比 Central, GAP 客户端需要处理更多的事件,如下:

  1. 扫描广告期间的广告报告事件,查找想要连接的 BLE 设备,并启动连接;
  2. 连接事件,连接成功后,启动 GATT 服务发现功能;
  3. 接收 GATT 服务器发送的 notification / indication 事件,如果 GATT 客户端写 CCCD 属性的 notify / indicate 位为 1,那么可能会收到属性的 notification / indication,并触发该事件;
  4. MTU 交换事件,如果 GATT 启动了 MTU 交换流程,则会产生该事件;
  5. 其他事件。

正在更新。。。

你可能感兴趣的:(蓝牙,nimble,蓝牙)