[历史归档]本文原发布于 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()