IoT-For-Beginners 实操:使用 paho-mqtt 将夜灯设备接入 test.mosquitto.org 公共 MQTT Broker
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
本文是 IoT-For-Beginners 课程第 4 课《将设备连接到互联网》中「树莓派 / 虚拟 IoT 设备」分支的实战指南,讲解如何用 Python 的paho-mqtt库让夜灯设备通过 MQTT 协议与公共 Brokertest.mosquitto.org建立连接。读完本文,你将掌握 MQTT 客户端的三步核心流程——安装依赖、创建客户端并连接 Broker、启动后台消息循环,并理解设备为后续遥测上报与远程命令控制所做的主题与命名准备。
任务背景:让夜灯从"本地控制"走向"联网控制"
本课的前一阶段,夜灯设备(树莓派 + Grove 传感器,或基于 CounterFit 的虚拟 IoT 设备)已经在本地实现了「光照传感器读数 → 阈值判断 → 点亮/熄灭 LED」的完整逻辑。本阶段的目标是引入互联网控制:设备需要与test.mosquitto.org通信,一方面发布携带光照读数的遥测数据(telemetry),另一方面准备订阅来自云端的 LED 控制命令。整节课的最终架构是:设备发布遥测 → 服务端代码接收并判断光照阈值 → 服务端向命令主题发布led_on指令 → 设备订阅命令主题并驱动 LED。
与其自建 MQTT Broker,本课直接使用 Eclipse Mosquitto 开源 Broker 的公共测试实例test.mosquitto.org。它无需注册账号即可连接,非常适合验证 MQTT 客户端与服务器代码。但要注意:这是一个公开且不安全的 Broker,任何人都可能监听你发布的消息,切勿传输任何需要保密的数据。
下图为本次作业的整体数据流,光照读数被读取、判断后,最终通过命令控制 LED:
MQTT 快速认知:Broker、主题与发布/订阅
在动手编码前,先建立 MQTT 的核心模型(详细讲解见第 4 课 README):
- Broker 与客户端:MQTT 采用"单一 Broker + 多个客户端"的星型结构。所有客户端都连接到 Broker,由 Broker 负责把消息路由给相关客户端。
- 主题路由:消息通过命名主题(topic)路由,而非直接发给某个客户端。客户端可以发布(publish)消息到主题,任何订阅(subscribe)该主题的客户端都会收到它。
- 主题层级与通配符:主题可以分层,例如温度发到
/telemetry/temperature、湿度发到/telemetry/humidity,云端应用订阅/telemetry/*即可同时收到两类遥测。 - QoS 服务质量:
至多一次(At most once,即发即弃)、至少一次(At least once,发送方重试直到收到确认)、恰好一次(Exactly once,通过两级握手确保只收一份)。MQTT 名字里虽有"队列",但它并不真正支持消息队列:客户端断线重连后不会收到断线期间的消息,除非消息设置了 retained(保留)标志——Broker 会保存主题上最后一条带该标志的消息,供后续订阅者获取。 - Keep Alive:MQTT 还内置心跳保活机制,在消息间隔较长时检查连接是否仍然存活。
准备工作:VS Code 与运行环境
开始前,请先打开夜灯项目(nightlight project)的 VS Code 工作区,并根据设备形态确认运行环境:
- 虚拟 IoT 设备:使用 CounterFit 模拟硬件,需要确保终端处于 Python 虚拟环境中(例如已激活的
.venv),并保证 CounterFit 应用正在运行、光照传感器与 LED 已创建在正确的引脚上。 - 树莓派:直接在板子上运行,不使用虚拟环境。
第一步:安装 MQTT 客户端包paho-mqtt
与 Broker 通信需要 MQTT 客户端库。在终端中执行:
pip3 install paho-mqtt- 虚拟 IoT 设备场景:该包会安装到当前激活的虚拟环境内,仅对当前项目生效;
- 树莓派场景:直接安装到系统的 Python 环境中。
paho-mqtt是 Python 生态中最流行的 MQTT 库之一,本课程的服务端代码(见 code-server/server/app.py)同样依赖它。
第二步:编写设备连接代码
在app.py顶部追加 MQTT 库的导入:
import paho.mqtt.client as mqttpaho.mqtt.client库让应用具备通过 MQTT 通信的能力。
接着,在光照传感器与 LED 的定义之后,加入设备 ID 与客户端名:
id = '<ID>' client_name = id + 'nightlight_client'务必用唯一 ID 替换<ID>。test.mosquitto.org是公共 Broker,许多学习者都在使用,唯一的 MQTT 客户端名与后续的主题名可以保证你的代码不会与其他人的相互干扰。这个 ID 在后续编写服务端代码时还要再次使用(服务端用id + '/telemetry'订阅遥测、用id + '/commands'发布命令),必须保持一致。你可以通过在线 GUID 生成工具获取一个随机唯一 ID。
client_name是当前客户端在 Broker 上的唯一标识符。
随后创建客户端对象并连接 Broker:
mqtt_client = mqtt.Client(client_name) mqtt_client.connect('test.mosquitto.org') mqtt_client.loop_start() print("MQTT connected!")这段代码做了三件事:
mqtt.Client(client_name):以唯一名称创建客户端对象;connect('test.mosquitto.org'):连接到公共 MQTT Broker(默认 1883 端口);loop_start():启动一个运行在后台线程中的处理循环,持续监听所有已订阅主题上的消息——这是后续接收命令消息的基础。
第三步:运行并验证连接
按上一阶段的运行方式执行代码。虚拟 IoT 设备需确保 CounterFit 应用与传感器/执行器就绪:
(.venv) ➜ nightlight python app.py MQTT connected! Light level: 0 Light level: 0看到MQTT connected!即表示设备已成功连接 Broker。当前while True:主循环仍保留着本地的阈值控制逻辑(light < 300时点亮 LED),后续步骤会将它替换为「发布遥测」与「订阅命令」的实现。
仓库源码对照:完整的连接实现
课程将本阶段完整代码放在仓库中,可直接对照学习:
- 虚拟设备版:code-mqtt/virtual-device/nightlight/app.py
- 树莓派版:code-mqtt/pi/nightlight/app.py
两个版本除传感器/执行器的导入来源不同(虚拟设备使用counterfit_shims_grove模拟库并通过CounterFitConnection.init('127.0.0.1', 5000)连接 CounterFit,树莓派直接使用grove硬件库)外,MQTT 连接部分完全一致:
import time # 虚拟设备版: from counterfit_connection import CounterFitConnection from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor from counterfit_shims_grove.grove_led import GroveLed # 树莓派版: # from grove.grove_light_sensor_v1_2 import GroveLightSensor # from grove.grove_led import GroveLed import paho.mqtt.client as mqtt # 虚拟设备版需初始化 CounterFit 连接: CounterFitConnection.init('127.0.0.1', 5000) light_sensor = GroveLightSensor(0) led = GroveLed(5) id = '<ID>' client_name = id + 'nightlight_client' mqtt_client = mqtt.Client(client_name) mqtt_client.connect('test.mosquitto.org') mqtt_client.loop_start() print("MQTT connected!") while True: light = light_sensor.light print('Light level:', light) if light < 300: led.on() else: led.off() time.sleep(1)可以观察到几个关键点:
- 光照传感器接在 Grove 端口 0,LED 接在端口 5;
- 本地阈值逻辑以 300 为界,与后续服务端判断阈值(
payload['light'] < 300)保持一致; - 主循环每 1 秒读取一次光照并驱动 LED,同时
loop_start()的后台线程已就绪,等待后续订阅命令。
其他硬件平台对照
本课还提供了 Wio Terminal(Arduino)分支,MQTT 连接思路相同、但 API 不同:它使用PubSubClient库,通过 main.cpp 中的client.setServer(BROKER.c_str(), 1883)指定 Broker 与端口,并在loop()中反复调用client.loop()维持消息处理。感兴趣可对比阅读 wio-terminal-mqtt.md。
连接之后:遥测主题与命令主题
本阶段只完成了"连接",但它为接下来的两步做好了命名铺垫:
发布遥测(见 single-board-computer-telemetry.md 与 code-telemetry/virtual-device/nightlight/app.py):
client_telemetry_topic = id + '/telemetry' # ... telemetry = json.dumps({'light' : light}) mqtt_client.publish(client_telemetry_topic, telemetry) time.sleep(5)设备将光照读数编码为 JSON(如{"light": 0}),发布到id + '/telemetry'主题,并让主循环休眠 5 秒以降低发送频率。服务端代码(code-server/server/app.py)用相同的id订阅该主题,打印收到的遥测。
处理命令(见 single-board-computer-commands.md):
server_command_topic = id + '/commands' def handle_command(client, userdata, message): payload = json.loads(message.payload.decode()) print("Message received:", payload) if payload['led_on']: led.on() else: led.off() mqtt_client.subscribe(server_command_topic) mqtt_client.on_message = handle_command设备订阅id + '/commands'主题,服务端据此判断光照阈值并发布{'led_on': true/false}指令,on_message回调驱动 LED 亮灭。这正是本阶段loop_start()后台循环的意义所在——连接建立后,Broker 推送的命令消息将由该循环分发到回调函数。
关键注意事项与最佳实践
- 唯一 ID 必须前后一致:设备端、服务端的
id必须完全相同,否则服务端无法订阅到正确的主题。建议生成一个 GUID 后集中记录,供设备代码与服务端代码共用。 - 公共 Broker 的隐私边界:
test.mosquitto.org面向所有人开放,任何人可订阅任意主题,仅适合课程练习与开发测试,严禁发布敏感数据。 - 遥测频率要平衡:频率越高响应越快,但功耗、带宽、数据量与云端处理成本也随之上升。本课设备取 5 秒间隔、LED 本地循环取 1 秒,属于教学场景的合理折中;真实产品需结合场景权衡(例如恒温器每分钟一次即可,而工厂关键设备可能需要每秒多次采样)。
- 断线处理:MQTT 本身不保证断线期间的消息送达。若业务要求不丢消息,需要在设备与服务端自行实现确认/重发机制。
延伸阅读
- 第 4 课完整讲义(含通信协议对比、QoS 详解、挑战与测验):README.md
- 本阶段的作业要求:assignment.md
- 中文译文对应的英文原文:single-board-computer-mqtt.md
- 课后延伸:用 Mosquitto 自行搭建 Broker 并连接体验。默认配置不允许匿名连接与外网访问,可在
mosquitto.conf中加入listener 1883 0.0.0.0与allow_anonymous true放开限制(仅限本地实验)。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考