BlueZ GATT服务器Characteristic无法读取值及通知实现求助
GATT服务器Characteristic读取失败及通知功能问题排查
问题描述
在树莓派3B上搭建GATT服务器时,遇到两个核心问题:
- TxCharacteristic类的ReadValue()方法完全未被调用,即便设置测试返回值也无任何响应,且确认几乎未修改之前可用的代码。
- BLE通知功能实现存在问题,无法正常向客户端推送数据。
相关代码片段
class TxCharacteristic(Characteristic): """ Transmit characteristic, sends data to the client """ TX_CHRC_UUID = '0x0002' def __init__(self, bus, index, service): Characteristic.__init__( self, bus, index, self.TX_CHRC_UUID, ['read', 'notify'], service) self.notifying = False self.value = "" def StartNotify(self): if self.notifying: print('Already notifying, nothing to do') return self.notifying = True def StopNotify(self): if not self.notifying: print('Not notifying, nothing to do') return self.notifying = False def ReadValue(self): print("Tx read: " + bytes(self.value).decode) self.value = Comms.TxRead(Comms) return self.value
读取失败的原因及修复方法
1. UUID格式不符合规范
BLE特征的UUID格式要求严格,'0x0002'这种带0x前缀的写法可能无法被BlueZ等BLE栈正确识别,导致客户端无法发现该特征,自然不会发起读请求。
修复:
使用完整的128位UUID(推荐,避免冲突),或者正确的16位UUID格式:
# 完整128位UUID(基于蓝牙BASE UUID) TX_CHRC_UUID = '00000002-0000-1000-8000-00805f9b34fb' # 或简化的16位写法(不带0x前缀) # TX_CHRC_UUID = '0002'
2. ReadValue方法签名不匹配
多数BLE框架(如BlueZ的gatt-python实现)中,ReadValue方法需要接收options参数,若签名缺失,父类方法不会被正确覆盖,导致自定义逻辑不执行。
修复:
修正方法签名,并确保返回值为字节数组:
def ReadValue(self, options): # 修复打印语句的语法错误(decode需加括号,字符串转字节用encode) print(f"Tx read: {self.value}") # 修正Comms.TxRead调用(若TxRead是类方法,无需传Comms类作为参数) self.value = Comms.TxRead() # 返回字节数组,符合BLE数据格式要求 return bytes(self.value, 'utf-8')
3. 附加检查项
- 用树莓派的
bluetoothctl工具验证GATT服务和特征是否正确注册:- 执行
bluetoothctl进入交互模式 - 输入
menu gatt进入GATT菜单 - 输入
list-attributes查看已注册的特征,确认目标特征的UUID和属性(read/notify)存在
- 执行
- 检查LightBlue客户端是否正确连接设备,尝试读取特征时查看客户端的错误提示。
BLE通知功能优化建议
当前的StartNotify和StopNotify仅修改了notifying标志,未实现实际的推送逻辑。通知功能需要主动向客户端发送数据,可通过线程循环实现:
import threading import time def StartNotify(self): if self.notifying: print('Already notifying, nothing to do') return self.notifying = True # 启动后台线程循环发送通知 def notify_loop(): while self.notifying: # 获取待推送的数据 data = Comms.TxRead() if data: # 调用SendValue向客户端推送字节数据 self.SendValue(bytes(data, 'utf-8'), {}) # 控制推送间隔,可根据需求调整 time.sleep(1) # 设置守护线程,避免程序退出时残留线程 threading.Thread(target=notify_loop, daemon=True).start() def StopNotify(self): if not self.notifying: print('Not notifying, nothing to do') return self.notifying = False
内容的提问来源于stack exchange,提问作者Auri M
相关产品推荐
相关产品推荐

