ESP32 BLE学习(1) — GAP和GATT回调函数作用

前言

(1)学习本文之前,需要先了解的基础知识:ESP32 BLE学习(0) — 基础架构
(2)该部分只是对GAPGATT回调函数进行简单的介绍,因为这两个回调函数中参数太多,个人认为没必要全部进行讲解。因此只有当我认为需要使用的时候,才会讲解对应的内容。

回调函数作用介绍

(1)在学习蓝牙基础架构的时候,我们知道GAP主要用来进行广播,扫描和发起连接等。而GATT用来规范属性(Attribute)中的数据内容,并运用分组(group)的概念对属性(Attribute)进行分类管理。
(2)在ESP32中,GAPGATT的所有操作将分别在esp_ble_gatts_register_callback()esp_ble_gap_register_callback()这两个函数注册的回调函数中进行。

    // 注册 GATT 回调函数,处理所有的 GATT 事件
    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
        return;
    }

    // 注册 GAP 回调函数,
    ret = esp_ble_gap_register_callback(gap_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
        return;
    }

(3)在这两个回调函数中的类型如下:

// GATT 回调函数
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);
// GAP 回调函数
typedef void (* esp_gap_ble_cb_t)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);

回调函数参数具体分析

GATT回调函数

esp_gatts_cb_event_t结构体

(1)esp_gatts_cb_event_t就是一个enum枚举参数。当出现对应的GATT事件的时候,底层协议栈就会触发GATT回调函数,并将对应事件以esp_gatts_cb_event_t枚举形式进行通知。
(2)接下来我将会讲解常见的几种GATT事件及其如何进行触发。

  • ESP_GATTS_REG_EVT : 当调用 esp_ble_gatts_app_register() 函数注册应用ID触发该事件。
  • ESP_GATTS_READ_EVT :当客户端发送读命令,ESP32作为服务端将会触发该事件。
  • ESP_GATTS_WRITE_EVT : 当客户端发送一个写请求并且写的数据<=(MTU-3)ESP32作为从设备将会触发该事件。
  • ESP_GATTS_EXEC_WRITE_EVT : 当客户端发送一个写请求并且写的数据>(MTU-3)ESP32作为从设备将会触发该事件。
  • ESP_GATTS_MTU_EVT :当客户端发送设置MTU请求时,ESP32作为服务端触发该事件。
  • ESP_GATTS_CONF_EVT : 当ESP32作为服务端收到客户端对之前发送的指示(Indication)或通知(Notification)的确认时,就会触发该事件。
  • ESP_GATTS_START_EVT : 当ESP32作为服务端成功启动,该事件触发。
  • ESP_GATTS_CONNECT_EVT : 当客户端发起连接事件,ESP32作为服务端,该事件触发。
  • ESP_GATTS_DISCONNECT_EVT : 当客户端断开连接,ESP32作为服务端,该事件触发。
  • ESP_GATTS_CREAT_ATTR_TAB_EVTesp_ble_gatts_create_attr_tab() 创建 GATT 完成时触发该事件。

esp_gatt_if_t

(1)这其实就是一个无符号的8bit数据,它用于标识不同的GATT客户端,该参数与esp_ble_gatts_app_register()函数注册的app_id成对应关系。
(2)当我们调用esp_ble_gatts_app_register()函数注册的app_id时候,协议栈会为其分配一个对应的 gatts_if,用于标识一个 GATT 服务。同时会触发esp_ble_gatts_register_callback() 注册的回调函数中的 ESP_GATTS_REG_EVT 事件。

typedef uint8_t    esp_gatt_if_t;   

esp_ble_gatts_cb_param_t结构体

(1)esp_ble_gatts_cb_param_t是一个联合体,该联合体和esp_gatts_cb_event_t这个enum枚举参数是存在对应关系的。
(2)如下为对应图:

/* --- ESP_GATTS_REG_EVT --- */
param->reg.status // 注册应用ID的操作状态
param->reg.app_id // esp_ble_gatts_app_register()注册函数中输入的app_id
/* --- ESP_GATTS_READ_EVT --- */
param->read.conn_id // GATT读事件的连接ID,用于标识当前读取操作所涉及的连接的唯一标识符
param->read.trans_id // 确保了每个读操作的事务一致性,防止响应被错误地关联到其他读请求
param->read.bda // 写入的蓝牙设备地址
param->read.handle // GATT 句柄
param->read.offset // 如果需要读取的值太长,该值为偏移量
param->read.is_long // 需要读取的值是否过长,与.offset进行配合
param->read.need_rsp // 该写操是否需要回复
/* --- ESP_GATTS_WRITE_EVT --- */
param->write.conn_id // GATT写事件的连接ID,用于标识当前写操作所涉及的连接的唯一标识符
param->write.trans_id // 确保了每个写操作的事务一致性,防止响应被错误地关联到其他读请求
param->write.bda // 要被写入的蓝牙设备地址
param->write.handle // GATT 句柄
param->write.offset // 如果需要写的值太长,该值为偏移量
param->write.need_rsp // 该写操是否需要回复
param->write.is_prep // GATT(通用属性配置文件)服务器的写入操作是否为预备写入(Prepare Write)
param->write.len // 该写操作的值长度
param->write.value // 该写操作的值
/* --- ESP_GATTS_EXEC_WRITE_EVT --- */
param->exec_write.conn_id // 连接ID
param->exec_write.trans_id // 传递ID
param->exec_write.bda // 要被写入的蓝牙设备地址
param->exec_write.exec_write_flag // 执行写标志
/* --- ESP_GATTS_MTU_EVT --- */
param->mtu.conn_id // 连接ID
param->mtu.mtu // MTU大小
/* --- ESP_GATTS_CONF_EVT --- */
param->conf.status  // 客户端的确认状态
param->conf.conn_id // 连接ID
param->conf.handle  // GATT句柄
param->conf.len     //  指示(Indication)或通知(Notification)的值长度,在发送指示(Indication)或通知(Notification)失败的时候,该值有效
param->conf.value   // 指示(Indication)或通知(Notification)的值,在发送指示(Indication)或通知(Notification)失败的时候,该值有效
/* --- ESP_GATTS_START_EVT --- */
param->start.status         // GATT服务启动状态
param->start.service_handle // GATT服务句柄
/* --- ESP_GATTS_CONNECT_EVT --- */
param->connect.conn_id       // 连接ID
param->connect.link_role     // 链路层角色,0标识主设备,1标识从设备
param->connect.remote_bda    // 对端蓝牙地址
param->connect.conn_params   // 当前的连接参数
param->connect.ble_addr_type // 对端蓝牙设备地址类型
param->connect.conn_handle   // HCI层连接句柄
/* --- ESP_GATTS_DISCONNECT_EVT --- */
param->disconnect.conn_id    // 连接ID
param->disconnect.remote_bda // 对端蓝牙设备地址
param->disconnect.reason     // 蓝牙断连原因
/* --- ESP_GATTS_CREAT_ATTR_TAB_EVT --- */
param->add_attr_tab.status      // GATT服务创建状态
param->add_attr_tab.svc_uuid    // 创建的GATT服务UUID类型,有16bit、32bit、128bit
param->add_attr_tab.svc_inst_id // 创建的GATT服务UUID ID号
param->add_attr_tab.num_handle  // 要添加到GATT数据库的属性句柄编号
param->add_attr_tab.handles     // GATT句柄编号

GAP回调函数

esp_gap_ble_cb_event_t

(1)esp_gap_ble_cb_event_t就是一个enum枚举参数。当出现对应的GAP事件的时候,底层协议栈就会触发GAP回调函数,并将对应事件以esp_gap_ble_cb_event_t枚举形式进行通知。
(2)接下来我将会讲解常见的几种GAP事件及其如何进行触发。

  • ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT : 当调用 esp_ble_gap_config_adv_data_raw() 函数设置了广播包成功后触发该事件。
  • ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT : 当调用 esp_ble_gap_config_scan_rsp_data_raw() 函数设置了广播扫描响应包成功后触发该事件。
  • ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT : 当调用 esp_ble_gap_config_adv_data() 函数,其中.set_scan_rsp = false设置广播包成功后触发该事件。
  • ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT : 当调用 esp_ble_gap_config_adv_data() 函数,其中.set_scan_rsp = true设置广播扫描响应包成功后触发该事件。
  • ESP_GAP_BLE_ADV_START_COMPLETE_EVT : 当调用esp_ble_gap_start_advertising()函数成功启动广播之后触发该事件。
  • ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT : 当调用esp_ble_gap_stop_advertising()函数成功启动广播之后触发该事件。不过在BLE5.0之后的广播包在连接时有ESP_GAP_BLE_ADV_TERMINATED_EVT事件报上来。
  • ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT : 很多应用场景中,从设备主设备刚建立连接时,会使用“快速”的连接参数以达到迅速交换信息的目的,但是,从设备主设备机之间并不是一直需要快速的交换信息,因为这样会加快从机电池能量的消耗,这时候,从设备就希望能降低连接事件的频度(连接间隔),从而降低功耗,延长电池工作时间。因此,这里从设备就可以向主设备发起链路层连接参数更新控制(Connection Parameter Update Control)。连接参数更新成功,那么该事件就会被触发。

esp_ble_gap_cb_param_t

(1)esp_ble_gap_cb_param_t是一个联合体,该联合体和esp_gap_ble_cb_event_t这个enum枚举参数是存在对应关系的。
(2)如下为对应图:

/* --- ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT --- */
param->adv_data_raw_cmpl.status // 设置广播包操作成功状态
/* --- ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT --- */
param->scan_rsp_data_raw_cmpl.status // 设置扫描回应包操作成功状态
/* --- ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT --- */
param->adv_data_cmpl.status // 设置广播包操作成功状态
/* --- ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT --- */
param->scan_rsp_data_cmpl.status // 设置扫描回应包操作成功状态
/* --- ESP_GAP_BLE_ADV_START_COMPLETE_EVT --- */
param->adv_start_cmpl.status // 设置广播开始操作成功状态
/* --- ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT --- */
param->adv_stop_cmpl.status // 设置广播停止操作成功状态
/* --- ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT --- */
param->update_conn_params.status   // 设置连接更新参数操作成功状态
param->update_conn_params.bda      // 蓝牙设备地址
param->update_conn_params.min_int  // 最小连接间隔
param->update_conn_params.max_int  // 最大连接间隔
param->update_conn_params.latency  // 从机连接延迟时间(以连接事件数为单位)。范围:0x0000 ~ 0x01F3
param->update_conn_params.conn_int // 当前连接间隔
param->update_conn_params.timeout  // LE Link的监督超时。取值范围:0x000A ~ 0x0C80。Time = N * 10 msec

参考

(1)乐鑫官方文档
(2)乐鑫官方论坛:Write a string to characteristic

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是示例代码: ```python from machine import Pin from time import ticks_ms from ubluetooth import BLE, UUID, FLAG_NOTIFY, FLAG_READ, FLAG_WRITE, FLAG_WRITE_NO_RESPONSE, BLEException # 设定UUID MY_SERVICE_UUID = UUID(0x1811) MY_CHARACTERISTIC_UUID = UUID(0x2A46) MY_DESCRIPTOR_UUID = UUID(0x2901) # 初始化BLE ble = BLE() # 设定广告数据 adv_data = bytearray(b'\x02\x01\x06\x03\x02\x11\x15') + ble.config('mac')[::-1] + bytearray(b'\x0f\x09Micropython ESP32') adv_data += bytearray(b'\x03\x03\x11\x15\xE0\xFF') # 设定扫描响应数据 scan_resp_data = bytearray(b'\x04\x09ESP32\x02\x0a\x00\x02\x0a\x06\xDE\xAD\xBE\xEF') # 设定GATT服务、特征值和描述符 my_service = ble.gatts_service( uuid=MY_SERVICE_UUID, isprimary=True ) my_characteristic = my_service.add_characteristic( uuid=MY_CHARACTERISTIC_UUID, properties=FLAG_READ | FLAG_WRITE, value=b'Initial value', permissions=0x0A, ) my_descriptor = my_characteristic.add_descriptor( uuid=MY_DESCRIPTOR_UUID, value=b'ESP32', ) # 扫描结果处理函数 def scan_result(adt, addrType, addr): print('Scanned device:', addr, 'type:', addrType, 'advertising data:', adt) # 广告事件处理函数 def adv_event(adv): print('Advertising:', adv) # 设定扫描回调函数 ble.gap_scan(2000, 30000, interval_us=128000, window_us=112500, callback=scan_result) # 设定广告事件回调函数 ble.irq(ble.IRQ_ADVERTISING_START, adv_event) # 循环处理BLE事件 while True: try: events = ble.events() for event, data in events: if event == BLE.EVENT_SCAN_TIMEOUT: print('Scan timeout') elif event == BLE.EVENT_SCAN_RESULT: # 处理扫描结果 addr, addrType, advtype, rssi, advdata = data if addrType == 0: # 仅处理公共地址设备 print('Scanned device with public address:', addr) if b'Micropython ESP32' in advdata: print('Device', addr, 'is an ESP32') # 连接设备 try: dev = ble.gap_connect(addr) print('Connected to device:', addr) # 发现服务 services = dev.list_services() for service in services: if service.uuid() == MY_SERVICE_UUID: # 发现特征值 characteristics = service.list_characteristics() for characteristic in characteristics: if characteristic.uuid() == MY_CHARACTERISTIC_UUID: print('Found characteristic:', characteristic.uuid()) # 读取特征值 value = characteristic.read() print('Current value:', value) # 写入特征值 characteristic.write(b'New value') print('Value written') break # 断开连接 dev.disconnect() print('Disconnected from device:', addr) except BLEException as e: print('Could not connect to device:', addr) print(str(e)) elif event == BLE.EVENT_GATTS_WRITE: # 处理特征值写入事件 conn_handle, attr_handle = data value = my_characteristic.read() print('Characteristic value written:', value) except Exception as e: print(str(e)) # 等待100ms wait_ms = ticks_ms() + 100 while ticks_ms() < wait_ms: pass ``` 希望我的回答对你有所帮助,如有其他问题,不要犹豫地继续提问哦!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风正豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值