ESP32与Python的BLE通信订阅通知报错问题排查
问题:ESP32 BLE通知订阅失败(WinError -2140864509)
问题现象
PC端Python代码能成功连接ESP32 BLE服务器,但订阅通知时抛出以下错误:
Exception in thread Thread-1 (start_ble_loop): Traceback (most recent call last): File "C:\Program Files\Python312\Lib\threading.py", line 1073, in _bootstrap_inner self.run() File "C:\Program Files\Python312\Lib\threading.py", line 1010, in run self._target(*self._args, **self._kwargs) File "E:\AC_Work\Task_9_Radar implementation\BLE GUI\gui_code.py", line 42, in start_ble_loop loop.run_until_complete(connect_and_receive()) File "C:\Program Files\Python312\Lib\asyncio\base_events.py", line 684, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "E:\AC_Work\Task_9_Radar implementation\BLE GUI\gui_code.py", line 28, in connect_and_receive await client.start_notify(BLE_CHARACTERISTIC_UUID, notification_handler) File "D:\Users\26101179\AppData\Roaming\Python\Python312\site-packages\bleak\__init__.py", line 844, in start_notify await self._backend.start_notify(characteristic, wrapped_callback, **kwargs) File "D:\Users\26101179\AppData\Roaming\Python\Python312\site-packages\bleak\backends\winrt\client.py", line 981, in start_notify await winrt_char.write_client_characteristic_configuration_descriptor_async( OSError: [WinError -2140864509] The attribute cannot be written
ESP32 Arduino代码
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> #include "esp_bt_device.h" #define SERVICE_UUID "12345678-1234-5678-1234-56789abcdef0" #define CHARACTERISTIC_UUID "87654321-4321-6789-4321-67890abcdef0" BLECharacteristic *pCharacteristic; bool deviceConnected = false; void printBLEMacAddress() { const uint8_t* mac = esp_bt_dev_get_address(); Serial.print("ESP32 BLE MAC Address: "); for (int i = 0; i < 6; i++) { Serial.printf("%02X", mac[i]); if (i < 5) Serial.print(":"); } Serial.println(); } class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; } void onDisconnect(BLEServer* pServer) { deviceConnected = false; pServer->getAdvertising()->start(); // Restart advertising } }; void setup() { Serial.begin(115200); BLEDevice::init("ESP32_BLE"); // Print BLE MAC Address printBLEMacAddress(); BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY ); pService->start(); pServer->getAdvertising()->start(); Serial.println("BLE Server Started"); } void loop() { if (deviceConnected) { String millisStr = String(millis()); // Get millis() value pCharacteristic->setValue(millisStr.c_str()); pCharacteristic->notify(); // Send the value Serial.println("Sent: " + millisStr); } delay(1000); }
Python客户端代码
import tkinter as tk from bleak import BleakClient, BleakError import asyncio import time import threading # Values determined through nrfConnect ESP32_BLE_MAC = "74:4D:BD:61:D2:6D" BLE_CHARACTERISTIC_UUID = "87654321-4321-6789-4321-67890abcdef0" async def connect_and_receive(): while True: # Keep retrying connection try: print("Attempting to connect to ESP32 BLE...") label.config(text="Connecting to ESP32...") async with BleakClient(ESP32_BLE_MAC) as client: if client.is_connected: print("Connected to ESP32 BLE") label.config(text="Connected to ESP32") def notification_handler(sender, data): """Callback function when new data is received.""" millis_value = int.from_bytes(data, byteorder="little") label.config(text=f"Millis: {millis_value}") # Subscribe to notifications await client.start_notify(BLE_CHARACTERISTIC_UUID, notification_handler) while True: await asyncio.sleep(1) # Keep listening for data except BleakError as e: print(f"Connection failed: {e}") label.config(text="ESP32 not found! Retrying...") time.sleep(5) # Wait before retrying # Function to run asyncio loop in a separate thread def start_ble_loop(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(connect_and_receive()) # Tkinter GUI Setup root = tk.Tk() root.title("ESP32 BLE Monitor") label = tk.Label(root, text="Waiting for connection...", font=("Arial", 16)) label.pack(pady=20) # Start BLE communication in a separate thread threading.Thread(target=start_ble_loop, daemon=True).start() # Run the GUI root.mainloop()
原因及解决方法
核心原因
错误WinError -2140864509本质是BLE特征未配置可写的客户端特征配置描述符(CCCD)。根据BLE协议,客户端订阅通知时,必须向特征的CCCD写入启用指令,但你的ESP32代码仅声明了PROPERTY_NOTIFY,未允许CCCD被写入,导致Windows端无法完成订阅操作。
另外,Python代码的解析逻辑存在错误:ESP32发送的是ASCII字符串,而Python用int.from_bytes解析会得到错误数值。
解决步骤
修改ESP32代码,添加CCCD权限
创建特征时,需添加BLECharacteristic::PROPERTY_WRITE权限,或显式添加标准CCCD描述符(BLE2902),二选一即可:// 方法1:添加WRITE权限 pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE ); // 方法2:显式添加标准CCCD描述符(更规范) pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY ); pCharacteristic->addDescriptor(new BLE2902());修正Python数据解析逻辑
将字节数据解码为字符串后再转整数:def notification_handler(sender, data): """Callback function when new data is received.""" millis_value = int(data.decode('utf-8')) label.config(text=f"Millis: {millis_value}")可选优化:仅在客户端订阅时发送通知
避免无效的通知发送操作:void loop() { if (deviceConnected && pCharacteristic->getSubscribedCount() > 0) { String millisStr = String(millis()); pCharacteristic->setValue(millisStr.c_str()); pCharacteristic->notify(); Serial.println("Sent: " + millisStr); } delay(1000); }
内容的提问来源于stack exchange,提问作者Mobi Zaman
相关产品推荐
相关产品推荐

