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

用 Flask 起一个本地网站,把 LED 的开关搬进浏览器——手机连上同一 Wi-Fi 打开网页就能点;这是本课第一个【浏览器↔服务器↔GPIO】三段式 IoT 应用,也是后面所有实验的骨架。

📝 本节和前面几节的关系

5-1 的 Apache 会发静态网页,但它不认识 GPIO——本节换 Flask(Python 写的小型 Web 框架),让网页和 7-2 的 GPIO 代码住在同一个进程里,网页一点、针脚就动。11-1 的 venv 与 --system-site-packages 在此第二次发挥作用(Flask 走 apt、gpiozero 也在系统层,venv 必须看得见它们)。

一、为什么是 Flask 而不是 5-1 的 Apache

Apache(5-1) Flask(本节)
本质 通用 Web 服务器 Python 的 Web 框架(一个库)
网页逻辑 PHP 等外部解释器 就写在 Python 里,import gpiozero 直接用
适合 静态站、传统网站 设备控制页、API、小工具

判据:网页要【摸硬件】时用 Flask——同一个 Python 进程里 LED(17).on() 一行就完事;Apache 做这事要绕 PHP→shell→Python 三层,又慢又容易出权限问题。

二、环境:一条命令装 Flask(PEP 668 老朋友)

sudo apt install python3-flask

apt 装(系统层),不在 venv 里 pip 装——因为它要和系统层的 gpiozero(7 章装的)住在同一片屋檐下。如果你在 venv 里跑本节 Demo,记得是 11-1 说的 --system-site-packages 版 venv,否则 import 不到系统层的包。

⚠️ 老教程哪几处已失效(本章防坑段 ①)
  1. 老教程用 RPi.GPIO 写网页后端——它在 Pi 5 上已失效(7 章防坑段的主角);本节一律 gpiozero。
  2. 老教程让 Flask 监听公网/改路由器端口映射——绝对不要。app.run(host="0.0.0.0") 只该在局域网实验;这个开发服务器没有认证、没有 HTTPS,暴露公网等于把 GPIO 控制权交给所有人。
  3. 老教程的 @app.route('/led/on') 里直接拼字符串 SQL/命令——本节用最简路由;要接收用户输入时永远走表单/JSON,别拼命令。

三、动手 Demo:浏览器开关 LED

接线照 7-3:LED 正极经 330Ω 电阻接 GPIO17(物理 11 脚),负极回 GND。

# demo13_2_webled.py —— 浏览器开关 LED
from flask import Flask
from gpiozero import LED

app = Flask(__name__)
led = LED(17)                       # BCM 17(7-2 的老朋友)

@app.route("/")
def home():
    state = "ON" if led.is_lit else "OFF"
    return f"""<h1>Raspberry Pi LED: {state}</h1>
      <a href="/on">[ ON ]</a>  <a href="/off">[ OFF ]</a>"""

@app.route("/on")
def on():
    led.on()
    return home()                   # 开完顺手回主页,状态立刻刷新

@app.route("/off")
def off():
    led.off()
    return home()

app.run(host="0.0.0.0", port=5000)
python3 demo13_2_webled.py
# 另一台设备浏览器打开 http://<树莓派IP>:5000

看到什么算成功:页面显示 Raspberry Pi LED: OFF 和两个链接;点 [ ON ] 页面刷新成 ON,面包板上的 LED 同时亮;手机连同一 Wi-Fi 访问同样能点。查树莓派 IP 用 hostname -I。

读结果:三个路由就是三个函数——浏览器每次点链接都在发一次 HTTP GET(2-5 的请求/响应模型),Flask 按路径分发到函数,函数顺手摸了一下 GPIO。【网页】与【硬件】之间没有任何魔法,只隔一个函数调用。

四、30 秒实验:状态是谁的真相?

开两个浏览器窗口(电脑+手机)都指向这一页;在窗口 A 点 [ ON ],然后只刷新窗口 B(不点任何链接)。

看到什么算成功:窗口 B 刷新后显示 ON——因为 LED 状态存在树莓派上的 led 对象里,两个窗口只是它的两个【取景器】。反过来说:窗口 A 不刷新就永远显示旧状态——这正是 13-5 仪表板要解决的问题(让服务器主动推、而不是等人刷新)。

五、常见误区

  1. 以为需要 root 才能跑——gpiozero 走 gpio 组(7 章的 usermod -aG gpio),普通用户就能跑;用 sudo 跑 Flask 反而会把配置和缓存写乱。
  2. 把端口 5000 当固定——Flask 默认 5000,app.run(port=8080) 随便改;被占用时报 Address already in use,换一个就行。
  3. 以为关掉终端网站还在——开发服务器随进程生死;要常驻用 nohup python3 ... & 或以后交给 systemd——但实验阶段随手 Ctrl+C 停掉更安全。
  4. 在路由函数里写 while True——一个路由卡住整个网站(Flask 默认单线程处理);硬件轮询交给独立线程或 Node-RED,路由函数只做【快进快出】。
自测三问(点开对答案) 1. 为什么本节选 Flask 而不用 5-1 的 Apache?——网页逻辑要直接摸 GPIO,Flask 里 import gpiozero 同进程一行调用;Apache 要绕外部解释器多层。 2. `app.run(host="0.0.0.0")` 意味着什么?有什么风险?——监听所有网卡、同网段设备都能访问;无认证无加密,绝不能暴露公网。 3. 两个浏览器窗口看到的状态为什么不一致?——状态真相在服务端的 led 对象;窗口只是取景器,不刷新就显示旧值(本节引出 13-5 的推送问题)。

相关笔记