用 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 不到系统层的包。
- 老教程用
RPi.GPIO写网页后端——它在 Pi 5 上已失效(7 章防坑段的主角);本节一律 gpiozero。 - 老教程让 Flask 监听公网/改路由器端口映射——绝对不要。
app.run(host="0.0.0.0")只该在局域网实验;这个开发服务器没有认证、没有 HTTPS,暴露公网等于把 GPIO 控制权交给所有人。 - 老教程的
@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 仪表板要解决的问题(让服务器主动推、而不是等人刷新)。
五、常见误区
- 以为需要 root 才能跑——gpiozero 走 gpio 组(7 章的
usermod -aG gpio),普通用户就能跑;用 sudo 跑 Flask 反而会把配置和缓存写乱。 - 把端口 5000 当固定——Flask 默认 5000,
app.run(port=8080)随便改;被占用时报Address already in use,换一个就行。 - 以为关掉终端网站还在——开发服务器随进程生死;要常驻用
nohup python3 ... &或以后交给 systemd——但实验阶段随手 Ctrl+C 停掉更安全。 - 在路由函数里写
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 的推送问题)。- ☐ 下一节(13-3 物联网实验范例:温湿度监控与 ThingSpeak)
相关笔记
- 数字输出与输入 —— LED 接线与 GPIO 输出的出处
- 架设 Web 服务器 —— HTTP/请求响应模型的先修
- 建立与管理 Python 虚拟环境 —— venv 与 --system-site-packages 的出处
- 🍓 树莓派 · 索引 —— 全课路线图