1. 前言
JetLinks 作为轻量级物联网开源平台,MQTT 是其设备与平台通信的核心协议。本文聚焦物联网设备侧核心场景 ------ 响应平台属性读取指令,基于 Python 结合 paho-mqtt 库实现 MQTT 3.1.1 协议适配,完成读取指令监听、指令解析、属性值回复全流程,适配教学实操与基础设备开发需求。
2. mqtt主题及报文格式
-
监听读取属性主题
-
主题格式
text/{productId:产品ID}/{deviceId:设备ID}/properties/read /2017982230047920128/2017982371131723776/properties/read -
报文格式
json{\"messageId\":\"消息ID,回复时需要一致.\",\"properties\":[\"属性ID\"]}json{ "headers": { "deviceName": "温度传感器", "productName": "温度传感器", "productId": "2017982230047920128", "_uid": "fJweyt5Yv0esfoVqkNct4Nm_A7JsRcSL", "traceparent": "00-3ed76ec4e72903d3f15b8ecc55f8c4eb-306e5f94318dd59f-01" }, "messageId": "2018333322761248768", "deviceId": "2017982371131723776", "timestamp": 1770043137623, "properties": [ "temperature" ], "messageType": "READ_PROPERTY", "replyType": "READ_PROPERTY_REPLY" } -
-
上报主题及报文
- 主题格式
text/{productId:产品ID}/{deviceId:设备ID}/properties/read/reply /2017982230047920128/2017982371131723776/read/reply- 报文格式
json{ "messageId": "消息ID,与读取指令中的ID一致", "properties": { "属性ID": "属性值" } }
3. jetlinks 配置
主要是物模型的设置,参考本人的这篇文章https://blog.csdn.net/weixin_43951955/article/details/157659141?spm=1001.2014.3001.5501
4. python实现
python
import json
import logging
import time
from paho.mqtt import client as mqtt_client
from paho.mqtt.client import MQTTv311
import random # 用于生成示例属性值(模拟真实设备)
# ===================== 配置项 ======================
MQTT_BROKER = "192.168.147.134"
MQTT_PORT = 1883
MQTT_USERNAME = "admin"
MQTT_PASSWORD = "admin"
CLIENT_ID = "2017982371131723776"
# 设备基础信息(用于构造监听/回复主题)
PRODUCT_ID = "2017982230047920128"
DEVICE_ID = "2017982371131723776"
# 监听/回复主题配置(重点)
READ_TOPIC = f"/{PRODUCT_ID}/{DEVICE_ID}/properties/read" # 监听读取指令的主题
REPLY_TOPIC = f"/{PRODUCT_ID}/{DEVICE_ID}/properties/read/reply" # 回复属性的主题
# 设备默认属性(模拟真实设备的属性值)
DEVICE_PROPERTIES = {
"temperature": 25.0, # 温度(示例)
"humidity": 60.0, # 湿度(可扩展)
}
# 日志配置
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def connect_mqtt() -> mqtt_client.Client:
"""优化后的连接逻辑:增加重连间隔、断开原因打印"""
reconnect_count = 0 # 重连计数(教学调试用)
def on_connect(client, userdata, flags, rc, properties=None):
nonlocal reconnect_count
reconnect_count = 0 # 连接成功重置计数
rc_msg = {
0: "连接成功",
1: "协议版本错误",
2: "客户端ID非法",
3: "服务器不可用",
4: "用户名/密码错误",
5: "未授权",
}
if rc == 0:
logger.info(f"✅ MQTT 3.1.1 连接成功({MQTT_BROKER}:{MQTT_PORT})")
else:
logger.error(f"❌ 连接失败(rc={rc}):{rc_msg.get(rc, '未知错误')}")
def on_disconnect(client, userdata, rc, properties=None):
nonlocal reconnect_count
reconnect_count += 1
if rc == 0:
logger.info("🔌 主动断开连接")
else:
# 打印断开原因(教学场景:帮助学生理解服务器反馈)
disconnect_msg = {
1: "协议错误",
2: "客户端ID重复",
3: "服务器不可用",
4: "用户名密码错误",
5: "未授权",
128: "订阅越权",
}
logger.warning(
f"⚠️ 被动断开(rc={rc}):{disconnect_msg.get(rc, '服务器主动断开')},第{reconnect_count}次重连..."
)
# 延迟重连(避免频繁请求触发服务器限流)
time.sleep(min(reconnect_count * 2, 10)) # 重连间隔:2s→4s→...→10s
# 创建客户端(MQTT 3.1.1 + 自动重连)
client = mqtt_client.Client(
client_id=CLIENT_ID,
callback_api_version=mqtt_client.CallbackAPIVersion.VERSION1,
protocol=MQTTv311,
)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.auto_reconnect = True # 启用自动重连(优化重连策略)
client.reconnect_delay_set(min_delay=2, max_delay=10) # 重连间隔控制
# 连接服务器
try:
client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
except Exception as e:
logger.error(f"❌ TCP连接失败:{str(e)}")
raise
return client
def parse_read_command(payload: str) -> tuple:
"""解析读取属性指令,返回(messageId, 要读取的属性列表)"""
try:
payload_data = json.loads(payload)
message_id = payload_data.get("messageId")
properties = payload_data.get("properties", [])
return message_id, properties
except json.JSONDecodeError:
logger.error(f"❌ 读取指令解析失败:{payload}")
return None, []
def generate_reply_payload(message_id: str, properties: list) -> str:
"""构造回复报文,严格匹配指定格式"""
# 构造属性值(模拟真实设备,给温度加随机波动)
reply_properties = {}
for prop in properties:
if prop == "temperature":
# 温度值随机波动±0.5℃,保留1位小数
reply_properties[prop] = round(
DEVICE_PROPERTIES[prop] + random.uniform(-0.5, 0.5), 1
)
elif prop in DEVICE_PROPERTIES:
# 其他属性返回默认值(可扩展波动逻辑)
reply_properties[prop] = DEVICE_PROPERTIES[prop]
else:
logger.warning(f"⚠️ 未知属性:{prop},跳过回复")
# 构造标准回复报文
reply_payload = json.dumps(
{"messageId": message_id, "properties": reply_properties}, ensure_ascii=False
)
return reply_payload
def on_message(client, userdata, msg):
"""监听读取指令,解析后回复属性值"""
topic = msg.topic
payload = msg.payload.decode("utf-8", errors="ignore")
# 只处理读取属性的指令(过滤其他无关消息)
if topic == READ_TOPIC:
logger.info(f"\n📩 收到属性读取指令:")
logger.info(f" 📌 指令Topic: {topic}")
logger.info(
f" 📝 指令报文: {json.dumps(json.loads(payload), ensure_ascii=False, indent=2)}"
)
# 1. 解析读取指令
message_id, properties = parse_read_command(payload)
if not message_id or not properties:
logger.error("❌ 读取指令解析失败,跳过回复")
return
# 2. 构造回复报文
reply_payload = generate_reply_payload(message_id, properties)
# 3. 发布回复报文
result = client.publish(REPLY_TOPIC, reply_payload, qos=0)
status = result[0]
if status == 0:
logger.info(f"✅ 属性回复成功:")
logger.info(f" 📌 回复Topic: {REPLY_TOPIC}")
logger.info(
f" 📝 回复报文: {json.dumps(json.loads(reply_payload), ensure_ascii=False, indent=2)}"
)
else:
logger.error(f"❌ 属性回复失败,状态码:{status}")
else:
# 非读取指令的消息,仅打印(可选关闭)
logger.info(f"\n📩 收到其他消息:")
logger.info(f" 📌 Topic: {topic}")
logger.info(f" 📝 消息体: {payload}")
def subscribe_read_topic(client: mqtt_client.Client):
"""仅订阅属性读取指令的Topic(精准监听,避免无关消息)"""
client.subscribe(READ_TOPIC, qos=0)
client.on_message = on_message
logger.info(f"📌 已订阅属性读取指令Topic:{READ_TOPIC},等待指令...")
def run():
"""启动MQTT监听器,监听读取指令并回复"""
logger.info("🚀 启动JetLinks MQTT属性读取响应服务(教学稳定版)")
# 1. 建立MQTT连接
client = connect_mqtt()
# 2. 订阅读取指令Topic
subscribe_read_topic(client)
# 3. 持续监听MQTT消息(阻塞式)
client.loop_forever()
if __name__ == "__main__":
try:
run()
except KeyboardInterrupt:
logger.info("\n🛑 退出监听")
except Exception as e:
logger.error(f"❌ 程序异常:{str(e)}", exc_info=True)
4.1. MQTT 3.1.1 协议适配
- 重要性:JetLinks 平台对 MQTT 协议版本兼容性要求严格,5.0 版本易出现通信异常,3.1.1 是嵌入式设备/教学场景的最优选择;
- 代码实现 :显式指定
protocol=MQTTv311,同时适配 paho-mqtt 2.0+ 的回调 API 版本,避免协议/API 不兼容导致连接失败; - 辅助保障:自动重连+分级重连间隔(2s→10s),解决网络波动导致的断开问题,保证长期在线。
4.2. 主题与报文格式
这是设备与 JetLinks 平台通信的"约定",错一处就会导致平台无法识别:
- 监听主题精准 :仅订阅
/productId/deviceId/properties/read,只处理平台下发的属性读取指令,过滤无关消息; - 回复主题合规 :严格使用
/productId/deviceId/properties/read/reply(修正了示例中缺失的properties层级,匹配平台预期); - messageId 一致性 :回复报文的
messageId必须和读取指令完全一致,这是平台关联"指令-回复"的唯一标识,代码中通过parse_read_command提取、generate_reply_payload复用,确保匹配。
4.3. 指令解析与回复
- 指令解析 :只提取
messageId和properties两个核心字段(忽略 headers/timestamp 等冗余信息),聚焦业务核心; - 属性值模拟:温度值加±0.5℃随机波动,模拟真实物理设备的属性变化(区别于固定值,更贴近实际场景);
- 回复发布校验 :发布后检查状态码(
result[0]),0 表示成功,非 0 打印错误,便于快速定位发布失败问题。
4.4. 异常容错
- 指令报文 JSON 解析失败时,打印错误并跳过回复,避免程序崩溃;
- 遇到未知属性(如平台下发不存在的
pressure),仅告警不中断流程,保证核心功能正常; - 断开连接时打印具体原因(如"订阅越权""客户端ID重复"),快速定位服务器/配置问题。
5. 测试

从图中可以看出,属性上传的数据能一一对应。
OK,大功告成!!!!!!!!!
6. 结论
本文完成了 JetLinks 平台 MQTT 3.1.1 协议下设备属性读取指令的响应实现,精准监听平台下发的读取指令,按指定格式解析并回复属性值,保障了设备与平台的正常通信。代码兼顾连接稳定性、格式合规性与异常容错能力,可直接用于教学演示,也可作为物联网设备端 MQTT 通信开发的基础参考。