使用AdafruitIO时单台蓝牙设备MQTT数据无法发布的问题排查
背景
修改了同事的树莓派Python代码,新增向AdafruitIO实时发布蓝牙传感器数据的功能。原代码仅每日采集数据发邮箱,现在逻辑是连接指定蓝牙设备,采集7个传感器数据,写入本地后推送到MQTT broker,推送主题为user_name/groups/macaddress/json,JSON格式示例如下:
client.publish("user_name/groups/macaddress/json", j) # 用于创建或发布到broker上名为设备MAC地址的分组 # macaddress是设备的MAC地址 # j为如下格式的JSON # { # "feeds": { # "ETH": 1, # "Hum": 54, # "temp-1": 18, # "temp-2": 85 # } # }
问题
无论扫描顺序如何,总有一台特定MAC地址的设备数据无法发布。新增其他设备数据能正常推送,移除故障设备后其余设备也正常。MQTT相关代码如下:
def uploadMQTT(macs, mqttList): # Imports import paho.mqtt.client as mqtt import ssl import random import sys import time import json mac = macs feedValues = mqttList username = "user_name" key = "password" feeds = ["temp-2-vs", "temp-2-vm", "temp-1-vs", "temp-1-vm", "hum-vs", "hum-vm", "eth-vm"] broker = "io.adafruit.com" port = 8883 # 创建feed名称字典 feedDict = dict((feed,0) for feed in feeds) # 创建客户端对象 client = mqtt.Client() # 设置用户名密码 client.username_pw_set(username, key) # 加密连接 client.tls_set(certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED) # 连接成功回调 def connected(client, userdata, message, tmp=None): print("connected") # 断开连接回调 def disconnected(client, userdata, message, tmp=None): print("disconnected") # 接收消息回调 def message(client, userdata, message, tmp=None): print("Received message " + str(message.payload) + " on topic '" + message.topic + "' with QoS " + str(message.qos)) # 设置回调函数 client.on_connect = connected client.on_disconnect = disconnected client.on_message = message # 连接broker client.connect(broker,port) client.loop_start() # 遍历feedValues的计数器 count = 0 # 构造发布主题前缀 currfeed = (username + "/groups/" + mac) for y in feeds: # 将feedValues赋值给feedDict value = str(feedValues[count]) # 给feed值加上MAC地址(AdafruitIO下载文件名不会包含分组名) feedDict[y] = value + "," + macs count += 1 # 构造JSON结构 jsonDict = {'feeds':feedDict} j = json.dumps(jsonDict, indent = 4) # 发布消息 client.publish(currfeed + "/json",j)
已确认故障设备的feedValues、MAC地址、JSON格式均与正常设备一致,且使用AdafruitIO高级版,不会触发数据点限制(单设备采集发布耗时约45秒)。
已尝试操作
添加大量打印语句,验证推送的JSON数据、目标分组主题均无错误,未发现值或MAC地址存在异常字符。正常设备与故障设备的JSON数据对比:
{ "feeds": { "temp-1-vm": "22792,CB:B1:03:56:69:C1", "temp-2-vs": "40950,CB:B1:03:56:69:C1", "hum-vm": "4470,CB:B1:03:56:69:C1", "eth-vm": "409486,CB:B1:03:56:69:C1", "hum-vs": "40950,CB:B1:03:56:69:C1", "temp-2-vm": "831,CB:B1:03:56:69:C1", "temp-1-vs": "40950,CB:B1:03:56:69:C1" } } { "feeds": { "temp-1-vm": "16667,EB:10:08:5E:22:91", "temp-2-vs": "38336,EB:10:08:5E:22:91", "hum-vm": "17069,EB:10:08:5E:22:91", "eth-vm": "385375,EB:10:08:5E:22:91", "hum-vs": "38803,EB:10:08:5E:22:91", "temp-2-vm": "32165,EB:10:08:5E:22:91", "temp-1-vs": "37314,EB:10:08:5E:22:91" } }
排查方向及解决建议
修复回调函数参数错误:你目前的回调函数参数不符合paho-mqtt的规范,会导致回调无法正常执行,无法确认连接状态。修正后的回调应该是:
def connected(client, userdata, flags, rc): print(f"Connected with result code {rc}") # rc=0表示连接成功 def disconnected(client, userdata, rc): print(f"Disconnected with result code {rc}") def message(client, userdata, msg): print(f"Received message {str(msg.payload)} on topic '{msg.topic}' with QoS {msg.qos}")这样能直观看到连接是否成功,避免因连接失败导致消息发布失败。
等待消息发送完成:
client.publish是异步操作,函数执行完毕后客户端可能被销毁,导致消息还没发送就中断。在client.publish后添加等待时间,或者确保消息发送完成再停止loop:client.publish(currfeed + "/json", j, qos=1) # 用QoS=1确保消息送达 time.sleep(2) # 等待消息发送完成 client.loop_stop() client.disconnect()检查AdafruitIO后台状态:登录AdafruitIO后台,确认故障设备MAC对应的分组是否存在,所有Feed是否都已创建,有没有被禁用或权限异常。可以尝试删除该分组重新创建,再推送数据测试。
测试MAC地址主题兼容性:虽然AdafruitIO支持带冒号的主题,但可以临时把MAC地址的冒号换成下划线(比如
EB_10_08_5E_22_91),创建新分组并修改代码中的主题,测试是否能正常发布,排除字符兼容性问题。启用MQTT日志:在代码中添加日志输出,查看paho-mqtt的内部状态,比如:
import logging logging.basicConfig(level=logging.INFO)这样能看到更多连接和发布过程中的细节,定位问题。
内容的提问来源于stack exchange,提问作者Matthew Jennison

