Chapter 11 · 树莓派与嵌入式 Linux

物联网:MQTT 与云

单机采集只是起点。让树莓派把传感器数据实时推给手机、云平台、其他设备,靠的是物联网界的通用语言——MQTT。轻量、省电、天生为不稳定网络设计。

课程进度92%

关键概念

MQTT 协议
Message Queuing Telemetry Transport,专为物联网设计的轻量级消息协议。报文头极小、省带宽省电,专门应对不稳定的无线网络。它是发布/订阅模型,是智能家居、传感器网络、车联网的事实标准。默认端口 1883(明文)/ 8883(TLS 加密)。
发布/订阅模型(Pub/Sub)
MQTT 的核心。设备不直接互相通信,而是通过中间人 Broker 中转:发布者把消息发到某个主题订阅者订阅该主题就能收到。发布订阅双方解耦、互不知道对方存在。一个传感器发布温度,手机、云、显示屏可同时订阅接收。
Broker(消息代理)
MQTT 网络的中枢服务器,负责接收所有发布的消息、按主题转发给订阅者。Mosquitto 是最流行的开源 Broker,轻到能跑在树莓派上。整个物联网系统的消息都过 Broker,它是心脏。
主题(Topic)与通配符
消息的分类地址,用 / 分层,如 home/livingroom/temperature。订阅时可用通配符:+ 匹配单层(home/+/temperature 匹配所有房间温度),# 匹配多层(home/# 匹配 home 下所有)。良好的主题设计让系统清晰可扩展。
QoS(服务质量等级)
MQTT 的消息投递保证,三档:QoS 0 最多一次(发了不管,可能丢);QoS 1 至少一次(保证收到,可能重复);QoS 2 恰好一次(不丢不重,开销最大)。传感器读数用 QoS 0/1,关键控制指令用 QoS 1/2,权衡可靠性与开销。
保留消息(Retained)
发布时标记 retain=True,Broker 会保存该主题的最后一条消息。新订阅者一连上就立即收到这条"最新状态",而非傻等下一次发布。适合状态类数据(如"灯当前开着"),保证新客户端上线即知当前值。
遗嘱消息(LWT, Last Will)
Last Will and Testament。客户端连接时预先告诉 Broker:"如果我意外掉线,替我发这条消息"。设备断电/断网时,Broker 自动向指定主题发遗嘱(如 status: offline),让系统立即知道设备离线。这是判断设备存活的优雅机制。
边缘计算 vs 云端
边缘计算:数据在树莓派本地处理(过滤、聚合、决策),只把结果/异常上云。优点:低延迟、省带宽、离线可用、隐私好。云端:原始数据全上传云平台处理。实践中常混合——本地实时响应 + 云端存储分析。树莓派算力足以承担边缘节点角色。

MQTT 发布/订阅架构

理解 MQTT,关键是抓住"设备不直连、全走 Broker 中转"这一点:

MQTT 发布/订阅模型(以智能家居为例) ═══════════════════════════════════════════════════════════════ 发布者(Publishers) Broker 订阅者(Subscribers) ┌──────────────┐ ┌──────────┐ ┌──────────────┐ │ 树莓派温度传感器│──publish──►│ │──►订阅──│ 手机 App │ │ topic: │ home/ │ Mosquitto│ home/ │ (看实时温度) │ │ home/temp │ temp │ │ temp └──────────────┘ └──────────────┘ │ 按主题 │ ┌──────────────┐ │ 转发消息 │ ┌──────────────┐ │ 门磁传感器 │──publish──► │ │──►订阅──│ Home Assistant│ │ home/door │ home/door │ │ home/# │ (自动化规则) │ └──────────────┘ └────┬─────┘ └──────────────┘ │ ┌────────────▼────────────┐ │ 发布订阅解耦:发布者不知 │ │ 道谁在收,订阅者不知道谁 │ │ 在发,全靠主题匹配 │ └──────────────────────────┘

搭建 Mosquitto Broker

先在树莓派上装 Broker(也可用第 10 章 Docker 版)。这里用 apt 直装:

# 安装 Mosquitto broker 和命令行客户端工具
sudo apt install -y mosquitto mosquitto-clients

# 设为开机自启并启动
sudo systemctl enable --now mosquitto

# 查看状态
systemctl status mosquitto

默认只允许本机连接。要让局域网其他设备也能连,需配置监听与认证:

# 编辑配置
sudo nano /etc/mosquitto/conf.d/local.conf

# 写入以下内容:
#   listener 1883 0.0.0.0     ← 监听所有网卡(局域网可访问)
#   allow_anonymous false     ← 禁止匿名,强制认证
#   password_file /etc/mosquitto/passwd

# 创建用户名密码(会提示输入密码)
sudo mosquitto_passwd -c /etc/mosquitto/passwd piuser

# 重启生效
sudo systemctl restart mosquitto

用命令行工具快速测试收发(开两个终端):

# 终端1:订阅 home/# 下所有主题
mosquitto_sub -h localhost -u piuser -P 你的密码 -t "home/#" -v

# 终端2:发布一条温度消息
mosquitto_pub -h localhost -u piuser -P 你的密码 \
    -t "home/livingroom/temp" -m "26.5"

# 终端1 立即收到:home/livingroom/temp 26.5

Python 发布:上报传感器数据

paho-mqtt 库把第 5 章的传感器数据发布出去:

# 虚拟环境中安装 paho-mqtt
pip install paho-mqtt
# publisher.py — 定时发布温度数据
import paho.mqtt.client as mqtt
import json, time, random

BROKER = "localhost"       # Broker 地址
TOPIC = "home/livingroom/temp"

# 创建客户端(新版 paho 需指定 CallbackAPIVersion)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.username_pw_set("piuser", "你的密码")

# 设置遗嘱:意外掉线时 Broker 替发 offline
client.will_set("home/livingroom/status", "offline", retain=True)

client.connect(BROKER, 1883, 60)
client.loop_start()          # 后台网络线程

# 上线,发布在线状态(保留消息)
client.publish("home/livingroom/status", "online", retain=True)

try:
    while True:
        temp = round(20 + random.random() * 10, 1)  # 模拟读数
        payload = json.dumps({"temp": temp, "ts": int(time.time())})
        # QoS 1 保证送达,retain 让新订阅者立即拿到最新值
        client.publish(TOPIC, payload, qos=1, retain=True)
        print(f"已发布: {payload}")
        time.sleep(5)
except KeyboardInterrupt:
    client.publish("home/livingroom/status", "offline", retain=True)
    client.loop_stop()
    client.disconnect()

Python 订阅:接收并响应

另一端订阅消息,收到后做处理(如超温报警、控制 GPIO):

# subscriber.py — 订阅温度,超阈值报警
import paho.mqtt.client as mqtt
import json

# 连接成功回调:订阅主题
def on_connect(client, userdata, flags, reason_code, properties):
    print(f"已连接 (code={reason_code})")
    client.subscribe("home/+/temp", qos=1)  # + 匹配所有房间

# 收到消息回调
def on_message(client, userdata, msg):
    data = json.loads(msg.payload)
    temp = data["temp"]
    print(f"[{msg.topic}] 温度 {temp}°C")
    if temp > 28:
        print("⚠️ 高温报警!可在此触发风扇/推送通知")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.username_pw_set("piuser", "你的密码")
client.on_connect = on_connect
client.on_message = on_message

client.connect("localhost", 1883, 60)
client.loop_forever()      # 阻塞循环,持续接收
主题命名规范让系统可扩展

好的主题层级设计事半功倍。推荐结构 {位置}/{设备}/{数据类型},如 home/livingroom/temphome/kitchen/humidityhome/door/status。这样订阅 home/livingroom/+ 拿客厅全部数据、home/+/temp 拿所有温度、home/# 拿全屋。避免用中文、空格、开头斜杠。规划好主题树,后期加设备零成本。

连接云平台上报数据

数据除了本地流转,还可上云做长期存储、远程访问、图表分析。多数云 IoT 平台/物联网服务都兼容 MQTT,只需改 Broker 地址和凭证:

# cloud_report.py — 上报到云端 MQTT(以通用云 broker 为例)
import paho.mqtt.client as mqtt
import ssl, json, time

# 云平台提供的接入信息(各家不同,通常在控制台获取)
CLOUD_BROKER = "your-endpoint.iot.example.com"
CLOUD_PORT = 8883       # TLS 加密端口

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="pi-livingroom")
client.username_pw_set("设备用户名", "设备密钥")

# 启用 TLS(云端必须加密,绝不能明文上公网)
client.tls_set(tls_version=ssl.PROTOCOL_TLS_CLIENT)

client.connect(CLOUD_BROKER, CLOUD_PORT, 60)
client.loop_start()

# 边缘计算:本地聚合后再上报(省流量)
readings = []
while True:
    readings.append(read_sensor())      # 每秒采一次
    if len(readings) >= 60:          # 攒够 60 条
        avg = sum(readings) / len(readings)
        # 只上报每分钟均值,而非每秒原始值 → 边缘计算
        client.publish("devices/pi/telemetry",
                       json.dumps({"avg_temp": round(avg, 2)}), qos=1)
        readings.clear()
    time.sleep(1)
QoS 等级投递保证开销典型用途
QoS 0最多一次(可能丢)最小高频传感器读数,丢几个无所谓
QoS 1至少一次(可能重复)大多数场景,状态上报
QoS 2恰好一次最大关键控制指令、计费数据
上公网必须加密与认证

本地局域网 MQTT 用 1883 明文尚可,但一旦数据出家门上云/公网,必须走 TLS(8883 端口)+ 强认证。明文 MQTT 会被中间人窃听、伪造指令控制你的设备。云平台一律要求 TLS + 设备密钥/证书。同时启用 allow_anonymous false,别让 Broker 裸奔——公网上的开放 Mosquitto 会被扫描滥用。

本章小结

MQTT 是物联网通用语言:轻量、省电、为不稳定网络设计,采用发布/订阅模型——设备不直连,全经 Broker(Mosquitto)按主题中转,发布订阅双方解耦。主题用 / 分层、+/# 通配符订阅,推荐 {位置}/{设备}/{类型} 命名。QoS 三档权衡可靠性(0 可能丢/1 至少一次/2 恰好一次);保留消息让新订阅者立即拿到最新状态;遗嘱消息在设备掉线时自动通知。Python 用 paho-mqtt 库:publish() 发布、回调 on_message 接收。数据可本地流转,也可上云(改 Broker 地址+TLS 8883+设备密钥),并用边缘计算在本地聚合后再上报省流量。铁律:出公网必须 TLS 加密 + 认证,绝不裸奔明文 Broker。下一章我们把前 11 章串起来,做一个完整的家庭自动化中枢。