【2020-01-08】使用gatttool和bluepy来测试ESP32蓝牙实例

发布时间:2026/9/28 19:11:39

【2020-01-08】使用gatttool和bluepy来测试ESP32蓝牙实例
[历史归档]本文原发布于 cstriker1407.info 个人博客内容为历史存档仅供参考。发布时间2020-01-08 标题使用gatttool和bluepy来测试ESP32蓝牙实例分类编程 / python jython / 网络通讯 / Bluetooth 标签wireshark·bluetooth·hciconfig·gatttool·ESP32·bluepy·hcitool使用gatttool和bluepy来测试ESP32蓝牙实例参考链接ESP32代码分析命令行连接bluepy连接wireshark抓包bluepy本文笔记了如何使用Linux命令行工具和bluepy来测试ESP32蓝牙实例。参考链接【 https://cstriker1407.info/blog/esp32-simple-introduce-note/ 】【 https://cstriker1407.info/blog/bluetooth-hcitool-gatttool/ 】【 https://cstriker1407.info/blog/install-bt-stack-on-linux/ 】ESP32官方IDF中包含了很多蓝牙实例这里以【gatt_server_service_table】为例子笔记下在Linux下如何连接上该蓝牙Server。ESP32代码分析连接上ESP32开发板然后直接编译烧录【gatt_server_service_table】。esp/esp-idf/examples/bluetooth/bluedroid/ble/gatt_server_service_table$ idf.py flash esp/esp-idf/examples/bluetooth/bluedroid/ble/gatt_server_service_table$ idf.py monitor该实例中的gatt的设置代码如下/* Full Database Description - Used to add attributes into the database */staticconstesp_gatts_attr_db_tgatt_db[HRS_IDX_NB]{// Service Declaration[IDX_SVC]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)primary_service_uuid,ESP_GATT_PERM_READ,sizeof(uint16_t),sizeof(GATTS_SERVICE_UUID_TEST),(uint8_t*)GATTS_SERVICE_UUID_TEST}},/* Characteristic Declaration */[IDX_CHAR_A]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)character_declaration_uuid,ESP_GATT_PERM_READ,CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE,(uint8_t*)char_prop_read_write_notify}},/* Characteristic Value */[IDX_CHAR_VAL_A]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)GATTS_CHAR_UUID_TEST_A,ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},/* Client Characteristic Configuration Descriptor */[IDX_CHAR_CFG_A]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)character_client_config_uuid,ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,sizeof(uint16_t),sizeof(heart_measurement_ccc),(uint8_t*)heart_measurement_ccc}},/* Characteristic Declaration */[IDX_CHAR_B]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)character_declaration_uuid,ESP_GATT_PERM_READ,CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE,(uint8_t*)char_prop_read}},/* Characteristic Value */[IDX_CHAR_VAL_B]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)GATTS_CHAR_UUID_TEST_B,ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},/* Characteristic Declaration */[IDX_CHAR_C]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)character_declaration_uuid,ESP_GATT_PERM_READ,CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE,(uint8_t*)char_prop_write}},/* Characteristic Value */[IDX_CHAR_VAL_C]{{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)GATTS_CHAR_UUID_TEST_C,ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},};通过分析gatt的设置代码表可知GATT Server建立了一个primary serviceUUID为0xFF(GATTS_SERVICE_UUID_TEST)该service下面有3个Characteristic第一个0xFF01(GATTS_CHAR_UUID_TEST_A)可读可写可通知(0x1A ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY)第二个0xFF02(GATTS_CHAR_UUID_TEST_B)可读(0x02 ESP_GATT_CHAR_PROP_BIT_READ)第二个0xFF03(GATTS_CHAR_UUID_TEST_C)可写(0x08 ESP_GATT_CHAR_PROP_BIT_WRITE)继续分析其他代码可以发现当Notify设置发生变化时ESP32会主动发送一个Notify/Indicate通知由于第一个Characteristic功能最为丰富我们主要测试第一个Characteristic。命令行连接首先使用hcitool搜索该Server如下$sudohcitool lescan 。。。。。 C4:4F:33:16:F8:AF ESP_GATTS_DEMO 。。。。。可以看到该Server的Mac地址为【C4:4F:33:16:F8:AF】然后使用gatttool连接如下$ gatttool-I[][LE]helphelpShow thishelpexitExit interactive mode quit Exit interactive mode connect]Connect to a remote device disconnect Disconnect from a remote device primary[UUID]Primary Service Discovery included]Find Included Services characteristics]]Characteristics Discovery char-desc Characteristics Descriptor Discovery char-read-hndhandleCharacteristics Value/Descriptor Read by handle char-read-uuidUUIDCharacteristics Value/Descriptor Read by UUID char-write-reqhandlenew valueCharacteristic Value Write(Write Request)char-write-cmdhandlenew valueCharacteristic Value Write(No response)sec-level Set security level. Default: low mtuvalueExchange MTUforGATT/ATT[][LE]connect C4:4F:33:16:F8:AF#连接ESP32Attempting to connect to C4:4F:33:16:F8:AF Connection successful[C4:4F:33:16:F8:AF][LE]primary attr handle: 0x0001, end grp handle: 0x0005 uuid: 00001801-0000-1000-8000-00805f9b34fb attr handle: 0x0014, end grp handle: 0x001c uuid: 00001800-0000-1000-8000-00805f9b34fb attr handle: 0x0028, end grp handle: 0xffff uuid: 000000ff-0000-1000-8000-00805f9b34fb[C4:4F:33:16:F8:AF][LE]characteristics handle: 0x0002, char properties: 0x20, char value handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 00002a01-0000-1000-8000-00805f9b34fb handle: 0x0019, char properties: 0x02, char value handle: 0x001a, uuid: 00002aa6-0000-1000-8000-00805f9b34fb handle: 0x0029, char properties: 0x1a, char value handle: 0x002a, uuid: 0000ff01-0000-1000-8000-00805f9b34fb#操作handle为0x0029属性为可读可写可通知0x1Avalue的handle为0x2a,UUID为0xFF01handle: 0x002c, char properties: 0x02, char value handle: 0x002d, uuid: 0000ff02-0000-1000-8000-00805f9b34fb handle: 0x002e, char properties: 0x08, char value handle: 0x002f, uuid: 0000ff03-0000-1000-8000-00805f9b34fb[C4:4F:33:16:F8:AF][LE]char-desc handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb handle: 0x0004, uuid: 00002902-0000-1000-8000-00805f9b34fb handle: 0x0014, uuid: 00002800-0000-1000-8000-00805f9b34fb handle: 0x0015, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb handle: 0x0017, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x0018, uuid: 00002a01-0000-1000-8000-00805f9b34fb handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x001a, uuid: 00002aa6-0000-1000-8000-00805f9b34fb handle: 0x0028, uuid: 00002800-0000-1000-8000-00805f9b34fb handle: 0x0029, uuid: 00002803-0000-1000-8000-00805f9b34fb#操作handle为0x002b的UUID为0x2803(ESP_GATT_UUID_CHAR_DECLARE)表示characteristics的定义handle: 0x002a, uuid: 0000ff01-0000-1000-8000-00805f9b34fb#操作handle为0x002a的UUID为0xff01表示characteristics的值所在的uuidhandle: 0x002b, uuid: 00002902-0000-1000-8000-00805f9b34fb#操作handle为0x002b的UUID为0x2902(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)表示characteristics的Notify配置handle: 0x002c, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x002d, uuid: 0000ff02-0000-1000-8000-00805f9b34fb handle: 0x002e, uuid: 00002803-0000-1000-8000-00805f9b34fb handle: 0x002f, uuid: 0000ff03-0000-1000-8000-00805f9b34fb[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x0029#读一下0xFF01的定义Characteristic value/descriptor: 1a 2a 00 01 ff[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x002a#读一下0xFF01的值Characteristic value/descriptor:11223344[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x002b#读一下0xFF01的Notify配置Characteristic value/descriptor: 00 00[C4:4F:33:16:F8:AF][LE]char-write-cmd 0x002a aabbccdd#更改0xFF01的值[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x002a#读一下0xFF01的值Characteristic value/descriptor: aa bb ccdd[C4:4F:33:16:F8:AF][LE]char-write-req 0x002a12345678#更改0xFF01的值需要回复Characteristic value was written successfully[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x002a#读一下0xFF01的值Characteristic value/descriptor:12345678[C4:4F:33:16:F8:AF][LE]char-read-hnd 0x002b#读一下0xFF01的Notify配置Characteristic value/descriptor: 00 00[C4:4F:33:16:F8:AF][LE]char-write-cmd 0x002b 0100#打开NotifyNotification handle0x002a value: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e[C4:4F:33:16:F8:AF][LE]char-write-cmd 0x002b 0200#打开IndicateIndication handle0x002a value: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e[C4:4F:33:16:F8:AF][LE]char-write-cmd 0x002b 0000#关闭[C4:4F:33:16:F8:AF][LE]disconnect#解除连接bluepy连接bluepy的API都非常简洁明了这里就直接贴代码和日志输出通过对比日志和GATT表可以很清晰的了解API#!/usr/bin/env python# codingutf-8frombluepy.btleimport*importtimedefble_adv_scan(scan_time):ble_scannerScanner()print(Ble Scan Begin. scan_time: {} --.format(scan_time))ble_devicesble_scanner.scan(scan_time)fordevinble_devices:dev_macdev.addr dev_addr_typedev.addrType dev_rssidev.rssiprint( Find Dev:{}({}):{}.format(dev_mac,dev_addr_type,dev_rssi))for(adtype,desc,value)indev.getScanData():print(---- {}({}):{}.format(adtype,desc,value))print(Ble Scan Finish. scan_time: {} --.format(scan_time))defble_services_enum(mac_addr):remote_devPeripheral(mac_addr)print(Remote Services:)remote_servicesremote_dev.getServices()forsingle_serviceinremote_services:print( service uuid:{}.format(single_service.uuid))charssingle_service.getCharacteristics()print( handle property can read uuid)forsingle_charinchars:print( {} {}({}) {} {}.format(hex(single_char.getHandle()),single_char.propertiesToString(),hex(single_char.properties),single_char.supportsRead(),single_char.uuid))print(Remote Characteristics:)remote_charsremote_dev.getCharacteristics()print( handle can read property uuid)forsingle_charinremote_chars:print( {} {}({}) {} {}.format(hex(single_char.getHandle()),single_char.propertiesToString(),hex(single_char.properties),single_char.supportsRead(),single_char.uuid))print(Remote Descriptors:)remote_decsremote_dev.getDescriptors()forremote_decsinremote_decs:print( {}.format(remote_decs))remote_dev.disconnect()classNotifyDelegate(DefaultDelegate):def__init__(self):DefaultDelegate.__init__(self)defhandleNotification(self,cHandle,data):print(Rev Data{}:{}.format(hex(cHandle),bytes.hex(data)))defble_char_operate():remote_devPeripheral(C4:4F:33:16:F8:AF)main_serviceremote_dev.getServiceByUUID(0x000000ff)target_charmain_service.getCharacteristics(0x0000ff01)[0]target_char_handletarget_char.getHandle()print(-- Characteristics Operate --)print(ReadHex:{}.format(bytes.hex(target_char.read())))print(Write with No Response)target_char.write(bAABBCCDD)print(Write with Response)target_char.write(bytes.fromhex(223344),True)print(-- Peripheral Operate --)print(ReadHex:{}.format(bytes.hex(remote_dev.readCharacteristic(target_char_handle))))print(Write with No Response)remote_dev.writeCharacteristic(target_char_handle,b55667788)print(Write with Response)remote_dev.writeCharacteristic(target_char_handle,bytes.fromhex(44332211),True)print(-- Characteristics Notify/Indicate Operate --)remote_dev.withDelegate(NotifyDelegate())target_char_notify_handletarget_char_handle1print(Enable Notify)remote_dev.writeCharacteristic(target_char_notify_handle,bytes.fromhex(0100))remote_dev.waitForNotifications(10)print(Enable Indicate)remote_dev.writeCharacteristic(target_char_notify_handle,bytes.fromhex(0200))remote_dev.waitForNotifications(10)print(Disable All)remote_dev.writeCharacteristic(target_char_notify_handle,bytes.fromhex(0000))time.sleep(3)remote_dev.disconnect()if__name____main__:print(func ble_adv_scan begin)ble_adv_scan(5)print(func ble_services_enum begin)ble_services_enum(C4:4F:33:16:F8:AF)print(func ble_char_operate begin)ble_char_operate()输出如下$sudopython3 hello_bluepy.py func ble_adv_scan begin Ble Scan Begin. scan_time:5--。。。。。。 Find Dev:c4:4f:33:16:f8:af(public):-45 ----1(Flags):06 ----10(Tx Power):eb ----3(Complete 16b Services):000000ff-0000-1000-8000-00805f9b34fb ----9(Complete Local Name):ESP_GATTS_DEMO 。。。。。。 Ble Scan Finish. scan_time:5--funcble_services_enum beginRemote Services: service uuid:00001801-0000-1000-8000-00805f9b34fb handle property can read uuid 0x3 INDICATE (0x20) False 00002a05-0000-1000-8000-00805f9b34fb service uuid:00001800-0000-1000-8000-00805f9b34fb handle property can read uuid 0x16 READ (0x2) True 00002a00-0000-1000-8000-00805f9b34fb 0x18 READ (0x2) True 00002a01-0000-1000-8000-00805f9b34fb 0x1a READ (0x2) True 00002aa6-0000-1000-8000-00805f9b34fb service uuid:000000ff-0000-1000-8000-00805f9b34fb handle property can read uuid 0x2a READ WRITE NOTIFY (0x1a) True 0000ff01-0000-1000-8000-00805f9b34fb 0x2d READ (0x2) True 0000ff02-0000-1000-8000-00805f9b34fb 0x2f WRITE (0x8) False 0000ff03-0000-1000-8000-00805f9b34fb Remote Characteristics: handle can read property uuid 0x3 INDICATE (0x20) False 00002a05-0000-1000-8000-00805f9b34fb 0x16 READ (0x2) True 00002a00-0000-1000-8000-00805f9b34fb 0x18 READ (0x2) True 00002a01-0000-1000-8000-00805f9b34fb 0x1a READ (0x2) True 00002aa6-0000-1000-8000-00805f9b34fb 0x2a READ WRITE NOTIFY (0x1a) True 0000ff01-0000-1000-8000-00805f9b34fb 0x2d READ (0x2) True 0000ff02-0000-1000-8000-00805f9b34fb 0x2f WRITE (0x8) False 0000ff03-0000-1000-8000-00805f9b34fb Remote Descriptors: Descriptor Primary Service Declaration Descriptor Characteristic Declaration Descriptor Service Changed Descriptor Client Characteristic Configuration Descriptor Primary Service Declaration Descriptor Characteristic Declaration Descriptor Device Name Descriptor Characteristic Declaration Descriptor Appearance Descriptor Characteristic Declaration Descriptor Central Address Resolution Descriptor Primary Service Declaration Descriptor Characteristic Declaration Descriptor ff01 Descriptor Client Characteristic Configuration Descriptor Characteristic Declaration Descriptor ff02 Descriptor Characteristic Declaration Descriptor ff03 funcble_char_operate begin -- Characteristics Operate -- ReadHex:44332211 Write with No Response Write with Response -- Peripheral Operate -- ReadHex:223344 Write with No Response Write with Response -- Characteristics Notify/Indicate Operate -- Enable Notify Rev Data0x2a:000102030405060708090a0b0c0d0e Enable Indicate Rev Data0x2a:000102030405060708090a0b0c0d0e Disable Allwireshark抓包bluepy因为是本地蓝牙通讯因此可以通过wireshark抓取本地蓝牙模组的协议报文这里简单的贴一下。首先是抓取的adapter直接选取Bluetooth即可。ble_adv_scan()ble_services_enum(“C4:4F:33:16:F8:AF”)ble_char_operate()

相关新闻

【2020-01-02】linux strip readelf nm 简单应用和安全分析

【2020-01-02】linux strip readelf nm 简单应用和安全分析

2026/8/23 4:15:26

[历史归档] 本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。 发布时间: 2020-01-02 | 标题:linux strip readelf nm 简单应用和安全分析 | 分类: 操作系统 / linux / 安…

【2020-02-25】【转】IPv6邻居发现协议

【2020-02-25】【转】IPv6邻居发现协议

2026/8/23 4:15:33

[历史归档] 本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。 发布时间: 2020-02-25 | 标题:【转】IPv6邻居发现协议 | 分类: 网络通讯 / IPV6 | 标签&…

90后35岁时我决定从运维转行:找一门越老越吃香的职业!

90后35岁时我决定从运维转行:找一门越老越吃香的职业!

2026/9/26 9:25:38

90 后 35岁时我决定从运维转行:找一门越老越吃香的职业! 35岁之前,我总认为运维这个职位只要能熬得住、愿意加班的话,就一定能够熬出头来。但是真正做了几年之后才明白过来,巡检、处理报警、半夜救火等事情已经成为了…

CANN/GE ACL数据集缓冲区添加函数

CANN/GE ACL数据集缓冲区添加函数

2026/9/28 4:08:17

aclmdlAddDatasetBuffer 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、Te…

用ffmpeg高效批量调整图片尺寸的实战指南

用ffmpeg高效批量调整图片尺寸的实战指南

2026/9/28 16:01:49

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

2026/9/28 2:15:29

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱 【免费下载链接】transformers 🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and mu…

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

2026/9/28 3:14:54

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system sup…

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

2026/9/28 3:58:00

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

2026/9/28 3:47:14

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting mi…

远程协作的工作台整理

远程协作的工作台整理

2026/9/28 16:01:48

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/28 5:05:21

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/28 16:01:48

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…