1. 前言
JetLinks 是轻量级物联网开源平台,MQTT 协议作为其核心设备通信协议,是物联网教学与实操的重点内容。设备属性主动上报是物联网设备与平台交互的基础场景,本文聚焦教学场景,基于 Python 语言结合 paho-mqtt 库,实现适配 MQTT 3.1.1 协议的 JetLinks 设备属性周期性上报功能。通过明确上报主题、报文格式规范,以及 JetLinks 物模型配置要点,帮助学习者理解物联网设备侧 MQTT 通信的核心逻辑,同时解决协议版本兼容、连接稳定性、属性解析等实操中的常见问题,为后续物联网设备开发打下基础。
2. mqtt主题及报文格式
-
主题格式
text/{productId:产品ID}/{deviceId:设备ID}/properties/report /2017982230047920128/2017982371131723776/properties/report -
报文格式
json{"properties": {"temperature": 25.5}}
3. jetlinks 配置
主要是配置产品的物模型

务必将 读 写 上报 都选上,否则会有问题
4. python实现
务必指明是3.11版本,5.0版本应该会有问题,3.0应该也没问题
python
import json
import logging
import time
import threading
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"
# 主动上报配置(重点)
REPORT_TOPIC = (
"/2017982230047920128/2017982371131723776/properties/report" # 指定上报主题
)
REPORT_INTERVAL = 5 # 上报间隔:5秒
# 示例属性(可根据教学需求修改,格式:{"属性ID":"属性值"})
DEFAULT_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_property_message(topic: str, payload: str) -> dict:
"""解析MQTT消息中的设备属性"""
property_keywords = ["properties", "report", "upload", "attr"]
if not any(keyword in topic.lower() for keyword in property_keywords):
return {}
try:
payload_data = json.loads(payload)
return payload_data.get("properties", payload_data)
except json.JSONDecodeError:
try:
properties = {}
for item in payload.split(","):
k, v = item.split(":", 1)
if v.replace(".", "").isdigit():
v = float(v) if "." in v else int(v)
properties[k.strip()] = v
return properties
except:
logger.error(f"❌ 消息体解析失败:{payload}")
return {}
def on_message(client, userdata, msg):
"""监听MQTT消息并解析属性"""
topic = msg.topic
payload = msg.payload.decode("utf-8", errors="ignore")
logger.info(f"\n📩 收到消息:")
logger.info(f" 📌 Topic: {topic}")
logger.info(f" 📝 消息体: {payload}")
properties = parse_property_message(topic, payload)
if properties:
logger.info(
f"🔧 解析属性:{json.dumps(properties, ensure_ascii=False, indent=2)}"
)
with open("jetlinks_device_properties.log", "a", encoding="utf-8") as f:
log_time = logging.Formatter("%(asctime)s").format(
logging.LogRecord("", 0, "", 0, "", (), None)
)
f.write(
f"{log_time} | Topic: {topic} | 属性: {json.dumps(properties, ensure_ascii=False)}\n"
)
else:
logger.info("ℹ️ 非属性消息,忽略")
def subscribe_all_topics(client: mqtt_client.Client):
"""订阅所有MQTT Topic"""
client.subscribe("#", qos=0)
client.on_message = on_message
logger.info(f"📌 订阅所有Topic(协议3.1.1),开始监听...")
def publish_properties_periodically(client: mqtt_client.Client):
"""
周期性主动上报属性(5秒一次)
:param client: MQTT客户端实例
"""
logger.info(
f"🚀 启动属性主动上报(间隔{REPORT_INTERVAL}秒),上报Topic:{REPORT_TOPIC}"
)
while True:
try:
# 构造上报报文:严格按照 "{\"properties\":{\"属性ID\":\"属性值\"}}" 格式
# 示例:给温度值加随机波动(模拟真实设备,教学更贴近实际)
dynamic_properties = {
"temperature": round(
DEFAULT_PROPERTIES["temperature"] + random.uniform(-0.5, 0.5), 1
)
}
# 构造标准上报报文
report_payload = json.dumps(
{"properties": dynamic_properties}, ensure_ascii=False
)
# 发布消息(QoS=0,适配轻量级设备)
result = client.publish(REPORT_TOPIC, report_payload, qos=0)
# 检查发布结果
status = result[0]
if status == 0:
logger.info(f"\n📤 主动上报属性成功:")
logger.info(f" 📌 上报Topic: {REPORT_TOPIC}")
logger.info(f" 📝 上报报文: {report_payload}")
else:
logger.error(f"❌ 主动上报属性失败,状态码:{status}")
except Exception as e:
logger.error(f"❌ 主动上报属性异常:{str(e)}")
# 间隔指定时间后继续上报
time.sleep(REPORT_INTERVAL)
def run():
"""启动MQTT监听器 + 主动上报线程"""
logger.info("🚀 启动JetLinks MQTT监听器(教学稳定版)")
# 1. 建立MQTT连接
client = connect_mqtt()
# 2. 订阅所有Topic
subscribe_all_topics(client)
# 3. 启动主动上报线程(与监听逻辑分离,避免阻塞)
publish_thread = threading.Thread(
target=publish_properties_periodically,
args=(client,),
daemon=True, # 主线程退出时,上报线程自动退出
)
publish_thread.start()
# 4. 持续监听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 稳定连接:适配 JetLinks 服务器认证(admin/admin),带自动重连、断开原因提示,避免频繁连接刷屏;
- 全 Topic 监听:订阅所有 MQTT 消息,解析设备属性(兼容 JSON/简化键值对格式),并将属性写入本地日志文件;
- 周期性主动上报:5 秒一次向指定 Topic 上报温度属性(模拟真实设备),严格遵循指定报文格式。
4.2 关键模块说明
| 模块/函数 | 核心作用 |
|---|---|
| 配置项 | 定义 MQTT 连接信息(IP/端口/账号)、上报 Topic、5 秒上报间隔、示例属性; |
connect_mqtt() |
创建 MQTT 3.1.1 客户端,处理连接/断开回调,优化重连策略(间隔 2-10 秒); |
parse_property_message |
解析收到的 MQTT 消息,提取设备属性(兼容多格式); |
on_message() |
监听并打印收到的消息,解析属性后写入日志; |
publish_properties_periodically |
独立线程运行,5 秒一次上报温度属性(带随机波动模拟真实设备); |
run() |
整合逻辑:建立连接 → 订阅 Topic → 启动上报线程 → 持续监听; |
4.3 核心特点
- 协议适配:显式指定 MQTT 3.1.1,兼容 paho-mqtt 2.0+ 版本,避免回调 API 版本错误;
- 线程安全:上报和监听分离(独立线程),互不阻塞,保证功能稳定性;
- 教学友好 :
- 日志清晰标注连接/上报/监听状态,便于调试;
- 温度值带随机小波动,模拟真实设备的属性变化;
- 异常捕获完善,上报失败/解析失败均有明确提示;
- 格式严格 :主动上报报文严格遵循
{"properties":{"属性ID":"属性值"}}格式,上报 Topic 与你指定的完全一致。
5. 测试

在windows环境中,运行python程序,客户端上传报文,服务端接收到消息。
OK,大功告成!!!!!!!!!
6. 结论
本文完成了基于 Python 的 JetLinks MQTT 3.1.1 协议设备属性主动上报功能实现,核心成果与要点如下:
- 功能层面:成功实现 5 秒周期的温度属性主动上报,报文格式、上报主题严格匹配 JetLinks 平台要求,同时兼顾 MQTT 消息监听与属性解析,本地日志可留存上报/接收的属性数据,满足教学演示与调试需求;
- 适配层面:通过显式指定 MQTT 3.1.1 协议版本、优化重连策略,解决了协议兼容、连接不稳定等问题,JetLinks 物模型「读/写/上报」权限的配置保障了属性上报的正常交互;
- 拓展层面:本实现具备良好的灵活性,可直接修改配置项适配湿度、光照等不同属性的上报,也可调整上报间隔适配不同业务场景;同时该逻辑可无缝迁移至嵌入式硬件设备(如单片机),实现真实物理设备的数据上报,为物联网全链路实操提供了基础参考。
整体实现兼顾了教学易懂性与实操实用性,完整覆盖了 MQTT 协议适配、平台配置、代码实现、功能验证全流程,可作为 JetLinks 物联网平台 MQTT 通信实操的入门参考。