🍓 第十三章 IoT 实验范例:温湿度监控与 Node-RED
📄 一句话概括

DHT22 温湿度传感器接上 GPIO,Python 每半分钟读一次、经 HTTP 上传到 ThingSpeak 云端画成历史曲线——本章第一个完整 IoT 闭环:物理量→数据→云端图表;这一节也是全课【老教程死库】密度最高的一篇,四条老路全废,正路只有一条。

📝 本节和前面几节的关系

传感器接线是 7-1/7-3 的数字输入延伸(DHT 用一根数据线按时序协议传 40 位数据);读数后【往哪送】是 13-1 说的平台层——ThingSpeak 是最轻的入门云(开个通道就能用);上传用 13-2 见过的 HTTP 一问一答,只是这次树莓派反过来当【客户端】。

一、DHT22 接线:三条纪律

DHT11(蓝色、误差 ±2°C)与 DHT22(白色、更准)接线相同:

DHT22        树莓派
VCC   →      3.3V(物理 1 脚)
DATA  →      GPIO4(物理 7 脚)
GND   →      GND(物理 9 脚)
  1. 只用 3.3V 供电——DHT22 支持 5V,但 DATA 线的电平跟着供电走,5V 进 GPIO 会烧针(7-1 的铁律);
  2. DATA 要上拉电阻——DATA 与 3.3V 之间接 4.7kΩ~10kΩ;买【三脚模块版】通常已焊好,可省;
  3. DATA 选 GPIO4——物理 7 脚,离 3.3V 和 GND 都近,走线最短(线越短时序越稳)。

二、读数库的四条老路与一条正路(2026 现状)

DHT 是单总线时序协议(微秒级脉冲),读它全靠库拼时序——而这正是老库坟场:

⚠️ 老教程哪几处已失效(本章防坑段 ②)
  1. wiringPi——2019 年作者宣布停止维护,Pi 4 之后的新板全不认,别再装;
  2. Adafruit_DHT(老版)——已废弃,装都装不上;
  3. RPi.GPIO 直拼时序——Pi 5 上整个库失效(7 章防坑段主角),实测只能收到十几个边沿、凑不齐 40 位数据;
  4. gpiozero 的 DHT22 类——它把时序交给 pigpio 守护进程,而 pigpio 不认识 Pi 5 的 RP1 芯片(Trixie 仓库里 pigpiod 也已退场)——在 Pi 5 上此路不通;
  5. 正路=Adafruit CircuitPython 版 DHT 库:底层走 libgpiod 字符设备(7 章讲过的现代 GPIO 接口),Pi 4/Pi 5 通吃。

三、动手 Demo:读 DHT22 并上传 ThingSpeak

先备环境(在 11-1 的 --system-site-packages venv 里):

sudo apt install -y libgpiod2          # DHT 库的底层依赖
pip install adafruit-circuitpython-dht

再到 thingspeak.com 注册 → Channels → New Channel → 勾上 field1(温度)、field2(湿度)→ 建好后在 API Keys 页复制 16 位 Write API Key。

# demo13_3_thingspeak.py —— DHT22 读数上传 ThingSpeak
import time, urllib.request
import board, adafruit_dht

dht = adafruit_dht.DHT22(board.D4, use_pulseio=False)
KEY = "你的16位WriteKey"               # 换成自己的

def upload(t, h):
    url = ("https://api.thingspeak.com/update"
           f"?api_key={KEY}&field1={t}&field2={h}")
    with urllib.request.urlopen(url, timeout=10) as r:
        return r.read().decode()        # 成功返回条目编号,0 表示被拒

while True:
    try:
        t, h = dht.temperature, dht.humidity
        print(f"T={t:.1f}C  H={h:.1f}%")
        print("upload ->", upload(f"{t:.1f}", f"{h:.1f}"))
    except RuntimeError as e:
        print("read retry:", e)         # DHT 读失败是常态,不是故障
    time.sleep(30)                      # 免费层 15 秒限速,30 秒留余量

看到什么算成功:

T=24.8C  H=61.3%
upload -> 137

读结果:upload -> 137 的 137 是这个通道收到的第 137 条数据——返回 0 说明被拒(最常见原因:距上次上传不足 15 秒)。read retry 行不用担心,DHT 单总线没有应答机制,偶尔读空是协议常态,Adafruit 官方建议就是捕获 RuntimeError 后隔 2 秒再试——use_pulseio=False 也是必需的(树莓派上没有 CircuitPython 的 pulseio 底层,缺了它 import 都过不去)。打开 ThingSpeak 通道页,几分钟后 Start Private View 能看到温度湿度两条曲线在生长。

四、30 秒实验:亲测 15 秒限速

不接传感器也能做——用假数据连传两次,中间不睡觉:

import urllib.request, time
KEY = "你的16位WriteKey"
for i in range(2):
    url = f"https://api.thingspeak.com/update?api_key={KEY}&field1={20+i}"
    with urllib.request.urlopen(url, timeout=10) as r:
        print("try", i + 1, "->", r.read().decode())
    time.sleep(1)

看到什么算成功:第一次返回一个条目编号、第二次返回 0——这就是免费层 15 秒限速的现场证据。把 time.sleep(1) 改成 time.sleep(16) 再跑,两次都有编号。先亲手触发一次拒绝,比背文档记得牢;真实项目里【本地缓存、攒批上传】的设计就是被这个限速逼出来的。

五、常见误区

  1. 读到 None 就以为传感器坏了——dht.temperature 读空时返回 None,下一步格式化会抛错;正确姿势是 try/except 整段包住读数(协议常态),连续多分钟全失败才查接线。
  2. 把 Write Key 硬编码后传上网——Key 等于通道的钥匙,泄露即任何人可写;实验代码至少别提交到公开仓库,泄露就去 API Keys 页重新生成。
  3. 每秒上传刷曲线——免费层 15 秒一次(付费 1 秒),刷快了全变 0;温度本来变化就慢,30~60 秒一传完全够用。
  4. 以为 ThingSpeak 存的是原始数据——它适合存【特征化后的结果】(每分钟均值这类);毫秒级原始波形请存本地数据库。
自测三问(点开对答案) 1. DHT22 接线的三条纪律是什么?——只用 3.3V 供电(防 5V 进 GPIO)、DATA 加 4.7k~10k 上拉、DATA 选 GPIO4 走线最短。 2. 为什么 gpiozero 的 DHT 类在 Pi 5 上不能用?——它依赖 pigpio 守护进程拼时序,而 pigpio 不支持 Pi 5 的 RP1 芯片(Trixie 仓库也已移除);正路是 adafruit-circuitpython-dht+libgpiod。 3. `update` 接口返回 0 意味着什么?——写入被拒,最常见是距上次上传不足免费层 15 秒限速;成功时返回的是条目编号。

相关笔记