ESP32学习笔记十九之BLE协议GAP&GATT

本文深入解析蓝牙GATT(属性协议)及其在低功耗蓝牙(BLE)中的角色,涵盖GAP与GATT的区别,GATT服务框架,以及BLE设备间的交互流程。详细介绍了GATT的属性、服务、特征和描述符的结构,以及GATT回调函数和状态机的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GAP

GAP全名是Generic Access Profile,通用访问配置文件,它定义了蓝牙设备的角色,中心和外设,并且控制他们的连接和广播数据。广播数据有两种方式:广播数据和扫描回复数据,数据包大小最长为31字节,其中广播数据方式是必需的。广播数据就是蓝牙设备自己定时广播数据出来,让周围环境的其它设备可以扫描到它,知道它的存在,是外设主动完成的。而扫描回复数据是可选的,它会根据需要响应请求,比如收到中心设备连接请求,会响应这个请求。这个协议决定了蓝牙设备的交互方式,比如iBeacon只能向外广播消息,而小米手环可以与中心设备双向通信。

GAP的工作流程如下图:

大部分情况外设广播自己来让中心设备发现自己并建立连接,然后GATT(后面讲到)就可以用来进行更多的数据交换,这种方式是独占的,就是建立连接了以后外设就不再广播了,只跟连接上的中心通信。但也有一种是不需要连接的,比如上面说的Beancon设备,它只需要向外广播数据,只要在一定的范围内,所有的中心设备都可以收到数据,不需要指定跟具体某个中心建立连接,他们的网络拓扑图如下:

GATT

GATT的介绍

GATT是用Attribute Protocol(属性协议)定义的一个Service服务框架。这个框架定义了Service以及他们的Characteristics的格式和规程。规程就是定义了包括发现、读、写、通知、指示以及配置广播的characteristics。

 一个attribute由三个元素组成:

  1. 16 bit的句柄(唯一性,用于区分和查找不同的attribute)
  2. UUID(ATT本身不定义,留给GATT来定义)
  3. 一个定长的值value(配合UUID使用,由GATT来决定这个UUID的意义和数据)

GATT是建立在GAP基础之上发挥作用的,就是两个BLE设备只有通过GAP建立连接之后才能用GATT进行通信。上面说了扫描回复数据交互方式是独占的,所以GATT通信只允许是一个外设和一个中心连接。如果两个外设想要通信,唯一的方式就是建立GATT连接,通过中心来中转。

一个外设只能跟一个中心建立连接(独占的),而一个中心可以同时连接多个外设(一个手机可以同时连接多个BLE设备)

GATT连接网络拓扑图

 GATT通信的双方是C/S关系,外设作为GATT的服务器(Server),中心设备是GATT的客户端(Client),它向Server发起请求。注意,所有的通信都是由主设备(客户端Client)发起,服务器Server响应数据反馈给Client。一旦建立了通信连接,外设会建议中心设备做定时连接(connection interval),这样中心设备就会在每个连接间隔尝试重新连接,检查有没有新的数据。这个只是一个建议,中心设备可能不会严格按照这个时间间隔来执行,例如中心设备正忙于连接其它外设或者资源太忙。

外设与中心数据交换图:

GATT结构由嵌套的Profile、Service、Characteristics组成,如下图:

Service就是一个独立的逻辑项,它包含一个或多个Characteristic,每个Service都由唯一的UUID标识,UUID有16位的有128位的,16位的UUID是官方通过认证的,需要花钱购买,128位的可以随便自己定义。 

Characteristic是GATT中最小的逻辑数据单元,当然它可能包含一组关联的数据;中心可以通过读取service、再进一步读取characteristic来获得具体的数值。

与Service类似,每个 Characteristic 用 16 bit 或者 128 bit 的 UUID 唯一标识。所有数据交互必须通过明确的UUID确定到service和characteristic。

实际上,和 BLE 外设打交道,主要是通过 Characteristic。你可以从 Characteristic 读取数据,也可以往 Characteristic 写数据。这样就实现了双向的通信。所以你可以自己实现一个类似串口(UART)的 Sevice,这个 Service 中包含两个 Characteristic,一个被配置只读的通道(RX),另一个配置为只写的通道(TX)。

在GATT的Profile的定义11个features,映射了程序Procedure。

  1. 配置交换(exchanging configuration) 
  2. 发现一个设备上的服务s和特征s
  3. 读取一个特征值(characteristic value)
  4. 写入一个特征值
  5. 通知一个特征值
  6. 指示一个特征值 

ESP32例程分析

GATT连接是独占的,即一个BLE周边设备同时只能与一个中心设备连接。

profile 可以理解为一种规范,一个标准的通信协议中,存于从机(server)中。蓝牙组织规定了一些标准的profile。每个profile中包含多个service,每个service代表从机的一种能力。

GATT服务器的架构组织

一个GATT 服务器应用程序架构(由Application Profiles组织起来)如下: 

每个Profile定义为一个结构体,结构体成员依赖于该Application Profile 实现的services服务和characteristic特征。结构体成员还包括GATT interface(GATT 接口)、Application ID(应用程序ID)和处理profile事件的回调函数。 

如果Characteristic 支持通知(notifications)或指示(indicatons),它就必须是实现CCCD(Client Characteristic  Configuration Descriptor)----这是额外的ATT。描述符有一个句柄和UUID。

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    dd_uint16_t gatts_if;
    dd_uint16_t app_id;
    dd_uint16_t conn_id;
    dd_uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    dd_uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    dd_uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

Application Profile存储在数组中,并分配相应的回调函数gatts_profile_a_event_handler() 和 gatts_profile_b_event_handler()。 

在GATT客户机上的不同的应用程序使用不同的接口,用gatts_if参数来表示。在初始化时,gatts-if参数初始化为ESP_GATT_IF_NONE,这意味着Application Profile还没有连接任何客户端。

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
    [PROFILE_A_APP_ID] = {
        .gatts_cb = gatts_profile_a_event_handler,
        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
    [PROFILE_B_APP_ID] = {
        .gatts_cb = gatts_profile_b_event_handler,/* This demo does not implement, similar as profile A */
        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
};

这是两个元素的数组。可以用Application ID来注册Application Profiles,Application ID是由应用程序分配的用来标识每个Profile。 通过这种方法,可以在一个Server中run多个Application Profile。 

esp_ble_gatts_app_register(PROFILE_A_APP_ID);

GATT回调函数的注册以及分析

 下面是GATT回调函数的注册函数esp_ble_gatts_register_callback:

esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback);

对于GATT server回调函数类型分析

typedef void (* esp_gatts_cb_t)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

 

event: esp_gatts_cb_event_t  这是一个枚举类型,表示调用该回调函数时的事件(或蓝牙的状态)

/// GAP BLE callback event type
typedef enum {
    ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT        = 0,       /*!< When advertising data set complete, the event comes */
    ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT,             /*!< When scan response data set complete, the event comes */
    ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT,                /*!< When scan parameters set complete, the event comes */
    ESP_GAP_BLE_SCAN_RESULT_EVT,                            /*!< When one scan result ready, the event comes each time */
    ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT,              /*!< When raw advertising data set complete, the event comes */
    ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT,         /*!< When raw advertising data set complete, the event comes */
    ESP_GAP_BLE_ADV_START_COMPLETE_EVT,                     /*!< When start advertising complete, the event comes */
    ESP_GAP_BLE_SCAN_START_COMPLETE_EVT,                    /*!< When start scan complete, the event comes */
    ESP_GAP_BLE_AUTH_CMPL_EVT,                              /* Authentication complete indication. */
    ESP_GAP_BLE_KEY_EVT,                                    /* BLE  key event for peer device keys */
    ESP_GAP_BLE_SEC_REQ_EVT,                                /* BLE  security request */
    ESP_GAP_BLE_PASSKEY_NOTIF_EVT,                          /* passkey notification event */
    ESP_GAP_BLE_PASSKEY_REQ_EVT,                            /* passkey request event */
    ESP_GAP_BLE_OOB_REQ_EVT,                                /* OOB request event */
    ESP_GAP_BLE_LOCAL_IR_EVT,                               /* BLE local IR event */
    ESP_GAP_BLE_LOCAL_ER_EVT,                               /* BLE local ER event */
    ESP_GAP_BLE_NC_REQ_EVT,                                 /* Numeric Comparison request event */
    ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT,                      /*!< When stop adv complete, the event comes */
    ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT,                     /*!< When stop scan complete, the event comes */
    ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT,                   /*!< When set the static rand address complete, the event comes */
    ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT,                     /*!< When update connection parameters complete, the event comes */
    ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT,                /*!< When set pkt length complete, the event comes */
    ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT,             /*!< When  Enable/disable privacy on the local device complete, the event comes */
    ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT,               /*!< When remove the bond device complete, the event comes */
    ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT,                /*!< When clear the bond device clear complete, the event comes */
    ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT,                  /*!< When get the bond device list complete, the event comes */
    ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT,                     /*!< When read the rssi complete, the event comes */
    ESP_GAP_BLE_UPDATE_WHITELIST_COMPLETE_EVT,              /*!< When add or remove whitelist complete, the event comes */
    ESP_GAP_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_COMPLETE_EVT,  /*!< When update duplicate exceptional list complete, the event comes */
    ESP_GAP_BLE_EVT_MAX,
} esp_gap_ble_cb_event_t;

ga

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值