使用 paho-mqtt 将夜灯接入 MQTT:Raspberry Pi 与虚拟 IoT 设备实战指南
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
导读
本文基于 Microsoft「IoT for Beginners」课程第 4 课(1-getting-started/lessons/4-connect-internet)的「连接互联网」部分,聚焦于为夜灯(nightlight)项目打通 MQTT 通信链路。你将学习在 Raspberry Pi 或虚拟 IoT 设备上安装paho-mqtt客户端库、配置唯一客户端 ID,并与公共测试代理test.mosquitto.org建立连接。读完本文后,你将掌握 MQTT 客户端连接、消息循环与主题(topic)命名的基本原理,并能在本地运行代码验证「MQTT connected!」的连接结果,为后续遥测上报与命令控制打下基础。
背景:为什么夜灯需要 MQTT
「IoT」中的I代表 Internet。在本课的前几部分,夜灯依靠设备本地代码直接读取光敏传感器并控制 LED;而本部分的目标是把控制逻辑「上移」到服务器端:设备只负责通过 MQTT 上报光照度遥测数据,服务器代码在收到数据后决策,再通过 MQTT 下发命令控制 LED 开关。
这对应的正是物联网最常见的发布/订阅(publish/subscribe)通信模型:设备连接到一个 MQTT 代理(broker),向其发布遥测、订阅命令;云服务同样连接该代理,订阅所有遥测、向指定设备发布命令。整体流程见课程总览文档 4-connect-internet/README.md 中的 pub-sub.png 示意图。
由于搭建私有 MQTT 代理较为复杂,本课选用由 Eclipse 基金会维护的开源代理 Mosquitto 提供的公共测试服务test.mosquitto.org——无需注册账号,即可用于测试 MQTT 客户端与服务器代码。
⚠️ 安全提示:该测试代理是公开且不安全的,任何人都有可能监听到你发布的消息,因此绝不能用它传输需要保密的数据。
安装 MQTT 客户端库
前置条件
- 已按课程前几部分完成夜灯项目,并在 VS Code 中打开。
- 虚拟 IoT 设备:终端必须处于 Python 虚拟环境(
.venv)中; - Raspberry Pi:直接在系统 Python 环境操作,不额外使用虚拟环境。
安装 paho-mqtt
在终端中执行:
pip3 install paho-mqttpaho-mqtt 是 Eclipse Paho 项目提供的 Python MQTT 客户端库,也是本课程设备端与服务器端统一使用的通信库。虚拟环境方案可参考课程总览中的「配置 Python 虚拟环境」一节(python3 -m venv .venv创建、source ./.venv/bin/activate激活),确保依赖只安装到当前项目目录,避免与系统其他 Python 包发生版本冲突。
编写设备端连接代码
设备端代码文件为 app.py,完整源码如下(虚拟设备版;Pi 版与之几乎一致,仅将counterfit_*导入替换为grove.*):
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 import paho.mqtt.client as mqtt 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)步骤一:引入 MQTT 库
在app.py顶部添加:
import paho.mqtt.client as mqttpaho.mqtt.client提供了Client、connect、publish、subscribe、loop_start等一系列 API,是应用与 MQTT 代理通信的核心入口。
步骤二:定义唯一客户端 ID 与客户端名
在光敏传感器与 LED 定义之后添加:
id = '<ID>' client_name = id + 'nightlight_client'务必将<ID>替换为唯一标识符,原因如下:
test.mosquitto.org是公共代理,全球许多用户(包括其他正在做本作业的学生)共用它;- 唯一的 MQTT 客户端名(
client_name)能避免不同客户端的连接互相冲突; - 该 ID 之后还会拼接到遥测主题与命令主题中(如
id + '/telemetry'、id + '/commands'),唯一性保证你发布/订阅的主题不与他人代码互相干扰; - 在后续编写服务器代码时,还需要使用完全相同的 ID 才能订阅到同一主题(课程特别警告:服务器端 ID 必须与设备端一致,否则无法收到遥测消息)。
💁 提示:可以使用 GUID 生成网站(如 GUIDGen)生成一个随机唯一 ID。
步骤三:创建客户端并连接代理
mqtt_client = mqtt.Client(client_name) mqtt_client.connect('test.mosquitto.org') mqtt_client.loop_start() print("MQTT connected!")这段代码做了三件事:
mqtt.Client(client_name):以唯一客户端名创建 MQTT 客户端对象;mqtt_client.connect('test.mosquitto.org'):与公共 MQTT 代理建立 TCP 连接(MQTT 默认使用 1883 端口);mqtt_client.loop_start():启动一个后台线程运行消息处理循环,持续监听所有已订阅主题上的消息——这正是后续遥测发送与命令接收能够「随时触发」的机制基础。
步骤四:运行验证
以与课程前几部分相同的方式运行代码(虚拟设备需先确保 CounterFit 应用已启动,并在正确引脚上创建了光敏传感器与 LED):
(.venv) ➜ nightlight python app.py MQTT connected! Light level: 0 Light level: 0看到MQTT connected!即表示设备已成功连上 MQTT 代理。此时主循环仍沿用本地控制逻辑:光照值小于 300 时点亮 LED,否则熄灭(阈值 300 是本课本地夜灯逻辑的判定标准)。
💁 完整可运行代码位于仓库 code-mqtt/virtual-device(虚拟设备)与 code-mqtt/pi(Raspberry Pi)目录。
深入理解 MQTT 的核心机制
结合课程总览文档 4-connect-internet/README.md 中的说明,理解以下几点能帮助你正确使用上面的连接代码:
主题(Topics)与通配符
MQTT 消息通过命名主题路由,而非直接发送给某个客户端。客户端可以向主题发布消息,订阅了该主题的所有客户端都会收到。主题支持层级结构,可用通配符订阅多个层级,例如把温度发到/telemetry/temperature、湿度发到/telemetry/humidity,云端只需订阅/telemetry/*即可同时接收两类遥测。
服务质量(QoS)
消息可以指定服务质量级别:
- 至多一次(At most once):消息只发送一次,收发双方不做任何确认(fire and forget);
- 至少一次(At least once):发送方反复重试直到收到确认(acknowledged delivery);
- 恰好一次(Exactly once):收发双方通过两级握手确保只收到一份拷贝(assured delivery)。
保留消息、心跳与安全
- 尽管名字里有「Message Queueing」,MQTT并不真正支持消息队列:客户端断线重连后收不到断线期间的消息;但可给消息设置retained(保留)标志,代理会保存某个主题上最后一条带该标志的消息,并在新客户端订阅时立刻下发,保证客户端总能拿到最新值;
- MQTT 还支持keep alive(心跳)机制,在消息间隔较长时检测连接是否仍然存活;
- MQTT 基于 TCP/IP 通信(默认端口 1883,与 HTTP 不同),也可以通过 WebSocket 与浏览器中的应用通信,或绕过防火墙限制;
- 连接既可以是公开开放的,也可以通过用户名/密码或证书进行加密与认证。
为什么本课使用loop_start()而非loop_forever()
从源码结构看,设备端后续(code-telemetry 与 code-commands 版本)会同时进行「主循环读取传感器并发布遥测」与「后台监听命令」两条并行任务:主线程负责传感器采样与发布,loop_start()启动的后台线程负责分发收到的订阅消息给回调函数。这正是连接阶段就调用loop_start()的用意——它为后续章节的「遥测上报 + 命令响应」并发模型提前搭好了运行框架。
面向单片机的对照实现
虽然本文聚焦于 Raspberry Pi / 虚拟设备(Python 版),但课程在 wio-terminal-mqtt.md 中为 Wio Terminal 提供了基于 PlatformIO 的 C++ 对照实现,位于 code-mqtt/wio-terminal/nightlight/src/main.cpp,核心逻辑可以印证上述 MQTT 连接流程:
#include <PubSubClient.h> #include <rpcWiFi.h> #include <SPI.h> #include "config.h" void createMQTTClient() { client.setServer(BROKER.c_str(), 1883); reconnectMQTTClient(); }- 使用
PubSubClient库,setServer(BROKER.c_str(), 1883)显式指定代理地址与1883 端口(Python 版connect('test.mosquitto.org')内部同样使用该默认端口); reconnectMQTTClient()循环尝试client.connect(CLIENT_NAME.c_str()),并打印client.state()返回值以输出连接失败原因,失败后每 5 秒重试;setup()中先connectWiFi()再createMQTTClient(),说明网络连通是 MQTT 连接的前提;loop()中每次都调用reconnectMQTTClient()与client.loop()来维持连接与处理消息。
这份 C++ 实现可作为理解「连接建立—断线重连—消息循环」三要素的补充示例,也与 Python 版connect()+loop_start()形成了良好的概念对照。
连接之后的下一步
完成 MQTT 连接只是本课的第一步,后续课程步骤会在此代码基础上逐步叠加:
- 发布遥测(single-board-computer-telemetry.md):定义
client_telemetry_topic = id + '/telemetry',将光照值打包为 JSON(如{"light": 0})通过mqtt_client.publish()发送,并以time.sleep(5)控制发送频率; - 接收命令(single-board-computer-commands.md):定义
server_command_topic = id + '/commands',通过mqtt_client.subscribe()订阅命令主题,并实现handle_command回调,根据消息中led_on字段的值调用led.on()/led.off(); - 服务器端:编写独立的 Python 服务器应用(见 code-server/server/app.py),以
id + 'nightlight_server'作为客户端名连接同一代理,订阅遥测主题,收到遥测后计算{'led_on': payload['light'] < 300}并通过client.publish(server_command_topic, json.dumps(command))回发命令。
这三个阶段的完整演进代码分别位于仓库 code-telemetry、code-commands 与 code-server 目录,可供对照学习。
常见问题与注意事项
- 虚拟环境未激活:在虚拟设备环境下如果直接运行
python app.py找不到paho模块,请确认终端提示符前有(.venv)前缀;Raspberry Pi 场景则无需虚拟环境; <ID>未替换:忘记替换占位符会导致客户端名与他人雷同,出现连接冲突或收到他人消息;服务器端与设备端的 ID 必须完全一致;- CounterFit 未运行:虚拟设备模式依赖 CounterFit 应用提供传感器模拟,需先启动它并在正确引脚创建光敏传感器(引脚 0)与 LED(引脚 5),对应代码中的
GroveLightSensor(0)与GroveLed(5); - 安全边界:
test.mosquitto.org仅用于学习与测试,不要在上面传输任何敏感数据。
总结
至此,你的 Raspberry Pi 或虚拟 IoT 设备已经具备与 MQTT 代理通信的能力:安装了paho-mqtt客户端库,配置了唯一客户端名,并通过connect()+loop_start()建立了连接、启动了后台消息循环。这一步是整个「联网夜灯」项目的地基——后续只需在此基础上添加发布遥测与订阅命令的代码,就能实现「设备上报光照度、服务器决策、LED 远程受控」的完整物联网应用闭环。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考