news 2026/9/15 11:47:43

使用 paho-mqtt 将夜灯接入 MQTT:Raspberry Pi 与虚拟 IoT 设备实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 paho-mqtt 将夜灯接入 MQTT:Raspberry Pi 与虚拟 IoT 设备实战指南

使用 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-mqtt

paho-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 mqtt

paho.mqtt.client提供了Clientconnectpublishsubscribeloop_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!")

这段代码做了三件事:

  1. mqtt.Client(client_name):以唯一客户端名创建 MQTT 客户端对象;
  2. mqtt_client.connect('test.mosquitto.org'):与公共 MQTT 代理建立 TCP 连接(MQTT 默认使用 1883 端口);
  3. 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 连接只是本课的第一步,后续课程步骤会在此代码基础上逐步叠加:

  1. 发布遥测(single-board-computer-telemetry.md):定义client_telemetry_topic = id + '/telemetry',将光照值打包为 JSON(如{"light": 0})通过mqtt_client.publish()发送,并以time.sleep(5)控制发送频率;
  2. 接收命令(single-board-computer-commands.md):定义server_command_topic = id + '/commands',通过mqtt_client.subscribe()订阅命令主题,并实现handle_command回调,根据消息中led_on字段的值调用led.on()/led.off()
  3. 服务器端:编写独立的 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 11:44:39

Semantica合规实战:FDA 21 CFR Part 11与ICH E6 GCP落地的7步指南

Semantica合规实战&#xff1a;FDA 21 CFR Part 11与ICH E6 GCP落地的7步指南 【免费下载链接】semantica Graph-Native Infrastructure for Context and Accountable AI Systems 项目地址: https://gitcode.com/GitHub_Trending/sema/semantica Semantica 是一个面向受…

作者头像 李华
网站建设 2026/9/15 11:44:34

dede如何手机网站和电脑网站的数据同步更新新手入门避坑指南

dede如何手机网站和电脑网站的数据同步更新新手入门避坑指南 很多新手刚接手企业官网,发现用现成的模板网站太丑不够用,改代码又容易把后台搞崩,这种“想改改不动,不改又难看”的焦虑感简直让人抓狂。特别是涉及到 DedeCMS 这种老牌的开源系统,很多新手在尝试让手机端(M 站)和电脑端(PC…

作者头像 李华
网站建设 2026/9/15 11:43:49

MATLAB实现Elman神经网络:时间序列预测原理与实战全解析

简介&#xff1a;基于Elman神经网络的时间序列预测MATLAB实现&#xff0c;面向需要处理序列数据、开展预测建模的工程师与科研人员。该代码包完整演示了从数据预处理、网络构建、训练到测试预测的全流程&#xff0c;适用于气象预报、股票走势、语音识别等存在序列依赖的场景&am…

作者头像 李华