嵌入式操作系统 | 项目启动与环境准备(PyQt5 / Flask 两个"最小可跑")
本课程开源地址(Gitee) :https://gitee.com/fujianxinxi/qianrushixitongyingyongkaifa.git
课件、示例代码与验收脚本都在该仓库,可直接
git clone或下载 ZIP 使用。
模块七 · 2 学时 · 环境:VMware + Ubuntu 20.04
一句话概述 :先用 20 分钟把项目讲清楚(三个程序、三层架构、一套帧协议),然后动手把环境跑起来 :一条 apt 命令装好 PyQt5/pyserial/Flask,跑出一个 Qt 窗口和一个网页,把虚拟串口准备好,把《协议契约》填好------这节课只要"能跑",不写业务代码。
本节目标
- 能画出项目三层架构,说清三个程序各自干什么、之间怎么传数据
- 会用一条 apt 命令 装好 Python 三件套,并用
check_env.py自检通过 - 能跑出最小 PyQt5 窗口 和最小 Flask 页面 ,并知道它们分别在哪里看(这是本课重点)
- 会复习课时 22 的三条命令把虚拟串口准备好,并把《协议契约》填好(组内人人一份)
一、讲解内容
1.1 知识讲解
我们要做的项目。 一句话:用界面控制一个"模拟设备",而设备是用串口连着的。
┌──────────────────────┐ TCP:控制命令(同一套帧) ┌────────────────────────────┐
│ 上位机界面 │ ─────────────────────────────▶ │ C 网关(uloop 事件循环) │
│ ① Flask 网页(浏览器) │ ◀───────────────────────────── │ · 串口侧:发命令 / 收状态 │
│ ② 或 PyQt5 窗口 │ UDP:状态上报(每 2 秒) │ · TCP 命令通道(uloop_fd) │
└──────────────────────┘ │ · ubus:device.status │
│ · uci:串口/端口/间隔 │
└──────────────┬─────────────┘
│ socat 虚拟串口
│ 55 AA 帧 + CRC16
┌──────────────▼─────────────┐
│ 设备模拟器(PyQt5 面板) │
│ LED 灯 / 风扇档位 / 温度 │
└────────────────────────────┘
三个程序(正好三个人一台戏):
| 程序 | 用什么写 | 干什么 | 跑在哪 |
|---|---|---|---|
| 设备模拟器 | Python + PyQt5 | 显示并执行"设备"的动作:灯亮灭、风扇档位、温度 | 虚拟机桌面 |
| C 网关 | C + uloop | 中间人:串口 ⇄ 网络互相转发;维护状态;uci 读参数、ubus 暴露状态 | 终端 |
| 上位机界面 | Python + Flask(网页)或 PyQt5(窗口) | 看状态、点按钮下命令 | 浏览器 / 桌面 |
三条数据流(后面每节课都会用到,先记住名字):
- 控制下行:点按钮 → TCP 命令帧 → 网关 → 串口帧 → 设备动作(灯亮、风扇转)
- 状态上行:设备 → 串口状态帧 → 网关更新"最新值" → 每 2 秒 UDP 上报 → 界面刷新
- 旁路查询 :
ubus call device status(课时 20)看状态;改/etc/config/device(课时 19)改参数
❗ 本课最重要的一条提示:两种界面"在哪里看"完全不同
| 界面 | 能在哪看 | 不能在哪看 |
|---|---|---|
| PyQt5 窗口 | ① 虚拟机桌面 里开的终端(VMware/VirtualBox 窗口里)② 带 X11 转发的 SSH(ssh -X,Windows 上还要装 X 服务器,麻烦) |
❌ 普通 VS Code Remote-SSH 终端 → 会报 could not connect to display |
| Flask 网页 | ① 虚拟机里的浏览器 ② Windows 宿主机 浏览器:http://<虚拟机IP>:5000 ③ VS Code Remote-SSH:端口会自动转发,点一下就能在宿主机浏览器打开 |
几乎没有限制(这就是我们建议"上位机做成网页"的原因) |
结论:做 PyQt5 的实验(课时 25、26)请到虚拟机自己的桌面里去敲 ;网页部分随便在哪都行。
如果确实没有桌面(比如只在 SSH 里),可以装
xvfb用"虚拟显示器"自检:xvfb-run -a python3 xxx.py --shot /tmp/a.png------程序会渲染并存一张图给你看。
为什么这么分工(选型说明) :设备面板和上位机要"画面",用 Python 生态最省事;网关是长期运行、接串口和网络的核心,用 C + uloop(课时 15--23 学的那套)又省资源又能复习前面全部知识。真去工厂做项目也是这么分工的。
Python 三件套(都用 apt 装,因为虚拟机里没装 pip):
| 库 | 干什么 | 本课以后用在哪 |
|---|---|---|
python3-pyqt5 |
画窗口、控件、按钮 | 设备模拟器(课时 25/26) |
python3-serial(pyserial) |
Python 读写串口 | 设备模拟器接串口(课时 26) |
python3-flask |
起一个网页服务 | 上位机界面(课时 30) |
Qt 事件循环和 uloop 是一回事(这个类比能省很多口水):
| C(uloop) | Python(Qt) |
|---|---|
uloop_init() |
app = QApplication(...) |
uloop_fd_add() / uloop_timeout_set() |
btn.clicked.connect(槽) / QTimer |
uloop_run() 进循环等事件 |
app.exec_() 进循环等事件 |
| 回调函数 | 槽函数(slot) |
所以 GUI 程序里绝不能写
while True:那样事件循环就转不起来了,界面会白屏卡死------要定时干活就用QTimer(课时 25 会用到)。
1.2 操作步骤(一条命令一步,看现象)
本节前两步只是"装东西和检查",不写代码 。学生什么都不会也没关系:一条一条照着敲,每条都有"应该看到什么"。哪条不对就先解决它,再往下走。
第 1 步:把环境装好(照着敲,别一口气全贴)
1-1 先刷新软件列表(第一次装东西一定要做;连不上网的先解决网络/换源)
shell
sudo apt update
现象:最后一行出现 Reading state information... Done(中间有几个 Ign 或 Hit 都不用管)。
如果这里就报错(
Could not resolve、Failed to fetch、Temporary failure resolving):说明虚拟机没网或源不可用------先看
ping -c 2 www.aliyun.com通不通;不通就检查 VMware 的"网络适配器"是不是选在 NAT(课时 01 讲过)。换源的办法在课时 06:
sudo sed -i 's|archive.ubuntu.com|mirrors.aliyun.com|' /etc/apt/sources.list && sudo apt update
1-2 装窗口界面库 PyQt5(课时 25、26 用)
shell
sudo apt install -y python3-pyqt5
现象:会刷很多行,最后出现 Setting up python3-pyqt5 (...),然后回到提示符。中途若问 Do you want to continue? [Y/n] 直接回车。
(本机实测:这一步连同依赖要下载十几 MB、装完占磁盘几十 MB。)
1-3 装串口库 pyserial(课时 26 用)
shell
sudo apt install -y python3-serial
现象:最后出现 Setting up python3-serial (...)。
1-4 装网页库 Flask(课时 30 用)
shell
sudo apt install -y python3-flask
现象:最后出现 Setting up python3-flask (...)。
三件套合起来:下载 17.7 MB、占磁盘 75.3 MB (本机实测,
df -h /能看出来)。想省事也可以一条命令装完:
sudo apt install -y python3-pyqt5 python3-serial python3-flask------但分开装更好排查:哪条失败一眼就看到。
1-5(可选)装"虚拟显示器" xvfb:没有桌面的同学用它把 Qt 窗口渲染成图片
shell
sudo apt install -y xvfb
现象:装完后 xvfb-run --help 能打出帮助(而不是 command not found)。
1-6 确认课时 15 编译的三个 C 库还在(课时 27 起网关要用 libubox/ubus/uci)
shell
ls /usr/local/include/libubox/uloop.h
ldconfig -p | grep -E 'libubox|libubus|libuci'
现象(本机实测,三条 => /usr/local/lib/... 都要有):
text
/usr/local/include/libubox/uloop.h
libuci.so (libc6,x86-64) => /usr/local/lib/libuci.so
libubus.so (libc6,x86-64) => /usr/local/lib/libubus.so
libubox.so (libc6,x86-64) => /usr/local/lib/libubox.so
缺了怎么办:回课时 15 ,按 libubox → ubus → uci 的顺序编译安装一遍(源码包在
代码/依赖源码包/);装完记得
sudo ldconfig,再回到这条命令确认。
1-7 确认其它要用的工具都在(一条命令全查)
shell
which socat git gcc make cmake pkg-config
ls /usr/include/json-c/json.h
现象(本机实测;socat 是课时 22 起一直在用的"虚拟串口"工具):
text
/usr/bin/socat
/usr/bin/git
/usr/bin/gcc
/usr/bin/make
/usr/bin/cmake
/usr/bin/pkg-config
/usr/include/json-c/json.h
缺哪个就装哪个(都是一条命令):
shell
sudo apt install -y socat # 虚拟串口
sudo apt install -y build-essential # gcc / make
sudo apt install -y cmake pkg-config # 课时 15 编译库要用的
sudo apt install -y libjson-c-dev # 课时 21 JSON 用
sudo apt install -y git # 拉代码/交作业用
这一整套(1-1 到 1-7)就是本课程的环境安装清单,我在本节附录 F 里又整理了一份"从零开始照抄版",可以直接照着走一遍。
第 2 步:一条命令自检环境
shell
cd ~/lab/课时24-项目启动与协议
python3 check_env.py
现象(实测):
text
Python 版本:3.8.10
PyQt5(图形界面) OK 5.14.1
pyserial(串口) OK 3.4
Flask(网页界面) OK 1.1.1
结论:环境就绪,可以开始做项目。
第 3 步:最小 Qt 窗口------必须在虚拟机桌面里跑
shell
python3 hello_qt.py
现象:弹出一个 480×200 的窗口,标题是"课时24:我的第一个 Qt 窗口",中间两行绿色文字"图形环境 OK!/ 下一节课:用 Qt 搭一个设备面板"(下图是本机实测截图):

如果你在 VS Code 的 SSH 终端里跑(很常见),会看到这样的报错(本机实测):
text
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized.
这不是代码错,是"这台机器没有屏幕可画"。两个办法:
| 办法 | 怎么做 | 适合谁 |
|---|---|---|
| ① 到虚拟机桌面里跑(推荐) | 在 VMware 窗口里的 Ubuntu 桌面打开终端,cd 过去再 python3 hello_qt.py |
所有人 |
| ② 用虚拟显示器自检 | xvfb-run -a python3 hello_qt.py --shot /tmp/qt.png |
只有 SSH 的同学 |
第 ② 种办法的实测输出(它不显示窗口,而是渲染成一张图片):
text
窗口截图已保存:/tmp/qt.png(成功)
用图片查看器打开 /tmp/qt.png,看到的就是那个窗口的样子------能出图,说明代码和环境都没问题。
第 4 步:最小 Flask 页面------哪里都看得到
shell
python3 hello_flask.py
现象(实测日志):
text
* Serving Flask app "hello_flask" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
这时三种看法(这就是"网页界面"的好处):
| 在哪里看 | 地址 | 说明 |
|---|---|---|
| 虚拟机里的浏览器 | http://127.0.0.1:5000 |
最直接 |
| Windows 宿主机浏览器 | http://<虚拟机IP>:5000 |
本机实测虚拟机 IP 是 192.168.31.129,你的用 ip a 查;这说明界面不用挤在虚拟机里看 |
| 只用了 VS Code Remote-SSH | VS Code 下方"端口 / Ports"面板 → 找到 5000 → 点"在浏览器中打开" | VS Code 自动转发,最省事 |
不想开浏览器也能验证(SSH 里很方便):
shell
wget -q -O - http://127.0.0.1:5000/ | head -6 # 抓页面
wget -q -O - http://127.0.0.1:5000/ | grep -o '第 <b>[0-9]*</b> 次刷新' # 看计数器
ss -ltnp | grep 5000 # 谁在监听
现象(实测):
text
<h2>设备网关在线</h2>
<p>这是 Flask 渲染出来的页面,第 <b>1</b> 次刷新。</p>
...
第 <b>2</b> 次刷新
LISTEN 0 128 0.0.0.0:5000 0.0.0.0:* users:(("python3",pid=16111,fd=3))
第 5 步:把虚拟串口准备好(复习课时 22 的三条命令)
shell
socat -d -d pty,raw,echo=0,link=/tmp/ttyV0 pty,raw,echo=0,link=/tmp/ttyV1 # 终端 A,保持运行
ls -l /tmp/ttyV0 /tmp/ttyV1
现象:两个软链接出现(/tmp/ttyV0 -> /dev/pts/x、/tmp/ttyV1 -> /dev/pts/y)。
它们是后面几节课的"设备线":网关用 V0,设备面板用 V1。
第 6 步:填《协议契约》并用命令行核对 CRC(本课动手产出)
打开 协议契约.md,把"第三节 TYPE 表"和"第五节 扩展表"填好(组内人人一份)。然后用课时 22 的工具核对 CRC:
shell
python3 ../课时22-串口命令行调试/crc_check.py --crc 01 01 01
现象(实测):
text
输入字节:01 01 01
CRC = 0x50E0(低字节在前:E0 50)
完整帧:01 01 01 E0 50
加上帧头 55 AA,就是"点亮 LED "的完整帧:55 AA 01 01 01 E0 50。
其余四条命令的实测结果(直接抄进你的契约表,也用来检查自己算得对不对):
| 命令 | 完整帧 |
|---|---|
| 关灯 | 55 AA 01 01 00 21 90 |
| 风扇 3 档 | 55 AA 02 01 03 91 91 |
| 查状态 | 55 AA 03 00 01 40 |
| 复位 | 55 AA 04 00 03 70 |
| 状态上报(灯亮/风扇2档/25.0℃) | 55 AA 80 04 01 02 00 FA CE 64 |
第 7 步:收工。 Flask 窗口按 Ctrl+C 停;终端 A 的 socat 也 Ctrl+C(虚拟串口随即消失:ls -l /tmp/ttyV0 会报"没有那个文件")。
1.3 常见错误与排查
| 现象 | 原因 | 解决 |
|---|---|---|
could not connect to display / no Qt platform plugin could be initialized |
在没有屏幕的地方跑 GUI(SSH) | 到虚拟机桌面里跑;或 xvfb-run -a python3 xxx.py --shot /tmp/a.png |
xvfb-run: command not found |
没装 xvfb | sudo apt install -y xvfb |
Flask 启动报 Address already in use |
5000 端口被上一次没退干净的程序占着 | `ss -ltnp |
Windows 宿主机打不开 http://IP:5000 |
① 程序只监听了 127.0.0.1 ② IP 拿错 ③ 虚拟机网络模式是"仅主机"且宿主机不同网段 |
确认代码里 app.run(host="0.0.0.0");ip a 查 IP;VMware 用 NAT/桥接,或在虚拟网络编辑器里加端口转发 |
sudo apt install 装不上 |
没网 / 源不可用 | 课时 06 讲过换源;也可用老师给的离线包 |
import PyQt5 报错,但 apt 说装了 |
用错了 Python(conda/venv 里的 python) | 一律用系统 python3 |
| 界面里中文变成方块 | 虚拟机缺中文字体 | sudo apt install -y fonts-wqy-zenhei(本机实测自带,正常显示) |
| 端口/进程乱 | 忘记之前的程序还在跑 | `ps -ef |
二、学生练习
本节命令速查表:
| 命令 | 作用 | 示例 |
|---|---|---|
sudo apt install -y python3-pyqt5 python3-serial python3-flask |
装 Python 三件套 | 见第 1 步 |
python3 check_env.py |
一条命令自检环境 | --- |
python3 hello_qt.py |
跑最小 Qt 窗口(要在桌面里) | 见第 3 步 |
xvfb-run -a python3 hello_qt.py --shot /tmp/qt.png |
没有桌面时渲染成图片自检 | --- |
python3 hello_flask.py |
起最小网页服务(5000 端口) | 见第 4 步 |
wget -q -O - http://127.0.0.1:5000/ |
不开浏览器抓页面 | --- |
| `ss -ltnp | grep 5000` | 看谁在监听端口 |
ip a |
查虚拟机 IP(宿主机访问用) | --- |
socat -d -d pty,... |
造一对虚拟串口(课时 22 的三条命令) | 见第 5 步 |
python3 ../课时22-串口命令行调试/crc_check.py --crc 01 01 01 |
算协议帧的 CRC | 见第 6 步 |
| 编号 | 任务 | 提示 | 预期结果 |
|---|---|---|---|
| L24.1 | 装三件套并自检 | 第 1、2 步 | check_env.py 三行全 OK |
| L24.2 | 在虚拟机桌面里跑出 Qt 窗口 | 第 3 步 | 窗口 + 两行绿字(截图) |
| L24.3 | 在 SSH 里故意跑一次 Qt,看报错;再用 xvfb 自检 | 第 3 步两个办法 | 看到 could not connect to display,然后 窗口截图已保存...(成功) |
| L24.4 | 三种方式看 Flask 页面 | 第 4 步 | 虚拟机浏览器 + Windows 宿主机浏览器各截一张 |
| L24.5 | 不开浏览器验证 Flask | wget + ss -ltnp |
看到页面内容和"第 N 次刷新" |
| L24.6 | 改 Flask 端口为 8080 再跑 | 改 app.run(port=8080) |
浏览器 http://IP:8080 能开,5000 不再监听 |
| L24.7 | 算出 5 条命令的完整帧填进契约表 | 第 6 步 | 与课件表格里的实测值一致 |
| L24.8 | 造好虚拟串口并确认 | 第 5 步 | ls -l 两个软链接 |
三、作业
- 必做:交 4 张截图(
check_env.py输出 / Qt 窗口 / 虚拟机里的 Flask 页面 / Windows 宿主机 上的 Flask 页面),并回答:① 为什么 PyQt5 的窗口在 SSH 里打不开,而 Flask 的页面在 Windows 上也能看?② 三个程序(设备模拟器、C 网关、上位机界面)各自负责什么?③ 如果把模拟设备换成真实硬件(比如带继电器的开发板),三层里哪一层要改、怎么改?④ 协议帧里为什么要加 CRC16? - 选做:① 把
hello_flask.py的页面改成自己的名字 + 学号 + 一句"这是我的项目";② 给 Qt 窗口加一个"你好"按钮,点了在窗口里显示一行字(提示:先看课时 25 的第 4 步写法) - 提交:截图 + 文字回答,命名
课时24-作业截图.png
四、本节小结与下节预告
本节把项目"立起来"了:三层架构、三条数据流、一套帧协议(连带 CRC 测试向量都算好了),还亲手把环境跑通了------Qt 窗口要在桌面看(或用 xvfb 存图),Flask 页面在 Windows 宿主机也能看,这个对照以后每节课都要用到。
下节课开始写第一个程序:用 Qt 控件把"设备面板"搭出来 (灯、风扇档位、温度、日志四块),并学会"信号 → 槽":点按钮 → 灯变绿 + 日志写一行。这节课不接串口,人人能过。
附录:怎么运行 + 完整代码
三个文件都在
代码/课时24-项目启动与协议/里,行尾请用 LF。
附录 A|怎么运行
shell
# ① 装三件套(一次就够)+ 可选装 xvfb(没有桌面的同学)
sudo apt install -y python3-pyqt5 python3-serial python3-flask
sudo apt install -y xvfb # 可选
# ② 自检
cd ~/lab/课时24-项目启动与协议
python3 check_env.py
# ③ Qt 窗口:在虚拟机桌面里跑(SSH 里会报 could not connect to display)
python3 hello_qt.py
xvfb-run -a python3 hello_qt.py --shot /tmp/qt.png # 没桌面时的替代办法
# ④ Flask 页面:虚拟机里跑,浏览器/宿主机都能看(Ctrl+C 停)
python3 hello_flask.py
ip a | grep inet # 查虚拟机 IP,宿主机浏览器开 http://IP:5000
wget -q -O - http://127.0.0.1:5000/ | head -6
# ⑤ 虚拟串口(终端 A,保持运行)
socat -d -d pty,raw,echo=0,link=/tmp/ttyV0 pty,raw,echo=0,link=/tmp/ttyV1
ls -l /tmp/ttyV0 /tmp/ttyV1
# ⑥ 算协议帧的 CRC(核对《协议契约》里的测试向量)
python3 ../课时22-串口命令行调试/crc_check.py --crc 01 01 01
附录 B|文件 check_env.py(一条命令自检环境)
python
#!/usr/bin/env python3
"""课时 24 ------ 一条命令检查 Python 环境是否装好
用法:
python3 check_env.py
现象(都装好了):
Python 版本:3.8.10
PyQt5(图形界面) OK 5.14.1
pyserial(串口) OK 3.4
Flask(网页界面) OK 1.1.1
结论:环境就绪,可以开始做项目。
如果缺包,按提示在虚拟机里执行(需要联网):
sudo apt install -y python3-pyqt5 python3-serial python3-flask
"""
import importlib
import sys
# (显示名(已按中文宽度补好空格), 模块名, 取版本的方式)
CHECKS = [
("PyQt5(图形界面) ", "PyQt5.QtCore", lambda m: m.PYQT_VERSION_STR),
("pyserial(串口) ", "serial", lambda m: m.__version__),
("Flask(网页界面) ", "flask", lambda m: m.__version__),
]
def main():
print("Python 版本:%s" % sys.version.split()[0])
all_ok = True
for label, mod_name, get_ver in CHECKS:
try:
mod = importlib.import_module(mod_name)
except ImportError as exc:
print("%s 未装(%s)" % (label, exc))
all_ok = False
continue
try:
ver = get_ver(mod)
except AttributeError:
ver = "版本未知"
print("%s OK %s" % (label, ver))
print()
if all_ok:
print("结论:环境就绪,可以开始做项目。")
return 0
print("结论:有缺的包,请在虚拟机里执行(需要联网):")
print(" sudo apt install -y python3-pyqt5 python3-serial python3-flask")
return 1
if __name__ == "__main__":
sys.exit(main())
附录 C|文件 hello_qt.py(最小 Qt 窗口)
python
#!/usr/bin/env python3
"""课时 24 ------ 最小 PyQt5 窗口:先确认"图形界面这条路"能走通
【重要:PyQt5 窗口必须在有桌面的环境里看】
能用:① VMware/VirtualBox 的 Ubuntu 桌面窗口里开的终端
② 带 X11 转发的 SSH(ssh -X,Windows 上要装 X 服务器,比较麻烦)
不能用:普通的 VS Code Remote-SSH 终端(会报 could not connect to display)。
→ 老师提示:做 PyQt5 相关实验(课时 25、26)都到虚拟机自己的桌面里去敲。
用法(三选一):
① 有桌面: python3 hello_qt.py # 弹出一个窗口
② 只有 SSH:xvfb-run -a python3 hello_qt.py --shot /tmp/qt.png
(xvfb 是"虚拟显示器",让程序在没有屏幕的地方也能渲染,
渲染完存一张 png------本文件专门留了 --shot 这个口子)
③ 只有 SSH 又不想装任何东西:跳过本文件,直接做 hello_flask.py
现象:窗口标题"课时24:我的第一个 Qt 窗口",中间一行绿色文字。
"""
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget
class HelloWindow(QMainWindow):
"""Qt 程序的三件套:QApplication(事件循环)+ 窗口 + 控件(用布局摆)"""
def __init__(self):
super().__init__()
self.setWindowTitle("课时24:我的第一个 Qt 窗口")
self.resize(480, 200)
label = QLabel("图形环境 OK!\n下一节课:用 Qt 搭一个设备面板")
label.setStyleSheet("font-size: 20px; color: #0a7a3d;")
central = QWidget() # 窗口中间那块"身体"
layout = QVBoxLayout(central) # 垂直布局:控件从上往下摆
layout.addStretch() # 上面留白
layout.addWidget(label, alignment=Qt.AlignCenter)
layout.addStretch() # 下面留白
self.setCentralWidget(central)
def main():
app = QApplication(sys.argv) # ① 创建"事件循环"(和 C 的 uloop_init 一个意思)
win = HelloWindow() # ② 创建窗口
win.show() # ③ 显示它
if "--shot" in sys.argv: # 没有显示器时的自检:渲染并存图
path = sys.argv[sys.argv.index("--shot") + 1]
QApplication.processEvents() # 先把界面画出来
ok = win.grab().save(path)
print("窗口截图已保存:%s(%s)" % (path, "成功" if ok else "失败"))
return 0
return app.exec_() # ④ 进入事件循环,等鼠标键盘事件(对应 uloop_run())
if __name__ == "__main__":
sys.exit(main())
附录 D|文件 hello_flask.py(最小网页)
python
#!/usr/bin/env python3
"""课时 24 ------ 最小 Flask 页面:确认"浏览器界面这条路"能走通
【好消息:Flask 的页面在哪里看都行】
· 虚拟机里:浏览器打开 http://127.0.0.1:5000
· Windows 宿主机:浏览器打开 http://<虚拟机IP>:5000 (虚拟机里用 `ip a` 查 IP)
· 只用了 VS Code Remote-SSH:VS Code 会自动转发端口,
在"端口/Ports"面板点一下"在浏览器中打开"就行
(对比:PyQt5 的窗口就只能在虚拟机桌面里看,见 hello_qt.py 的说明)
运行(在虚拟机里):
python3 hello_flask.py
停止:Ctrl+C
现象:浏览器里出现一个页面:"设备网关在线" + 第 N 次刷新。
"""
from flask import Flask
app = Flask(__name__)
HITS = {"n": 0} # 页面被刷新了几次(下一节课会换成"真实设备状态")
@app.route("/")
def index():
HITS["n"] += 1
return """<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>课时24:最小网页</title></head>
<body style="font-family: sans-serif; margin: 40px;">
<h2>设备网关在线</h2>
<p>这是 Flask 渲染出来的页面,第 <b>%d</b> 次刷新。</p>
<p>下一节课(课时 25)先在虚拟机桌面里做一个 Qt 设备面板,<br>
再下一节(课时 26)让那个面板能收串口命令。</p>
</body>
</html>""" % HITS["n"]
if __name__ == "__main__":
# host="0.0.0.0":这样 Windows 宿主机也能访问;5000 是 Flask 的默认端口
app.run(host="0.0.0.0", port=5000, debug=False)
附录 E|文件 协议契约.md(组内人人一份)
完整内容在 代码/课时24-项目启动与协议/协议契约.md,本节要填的是:
- 第三节 TYPE 表 :5 个 TYPE(
0x01灯、0x02风扇、0x03查状态、0x04复位、0x80状态上报) - 第五节扩展表:自己想加的功能(比如第二路灯、蜂鸣器),先定 TYPE 再写代码
- 第四节测试向量 :用
crc_check.py --crc算出完整帧,抄进表格
记不住也没关系:把附录里这张"实测帧"表抄到自己的契约文档上就够用了------
55 AA 01 01 01 E0 50(点灯)、55 AA 01 01 00 21 90(关灯)、55 AA 02 01 03 91 91(风扇3档)、
55 AA 03 00 01 40(查状态)、55 AA 04 00 03 70(复位)、55 AA 80 04 01 02 00 FA CE 64(状态上报)
附录 F|环境安装清单(从零开始"照抄版")
用途:刚装好 Ubuntu 虚拟机 / 换了台机器 / 做到一半发现缺东西 ,就从这里照抄一遍。
做法:一条一条敲,敲一条看一条的结果(每条下面都写了"应该看到什么")。全部做完约 10~20 分钟(看网速)。
F-0 前提检查(两条命令)
shell
whoami # 应该输出你的用户名(本课程都用普通用户 + sudo)
ping -c 2 www.aliyun.com # 能通就说明有网(不通先修网络,别急着装东西)
F-1 刷新软件列表(每次要装东西前都可以先做)
shell
sudo apt update
应看到:Reading state information... Done
F-2 装 C 侧基础工具 + 虚拟串口 + JSON 库 (一条命令;先 apt update)
shell
sudo apt install -y build-essential cmake pkg-config git socat libjson-c-dev
应看到:若干 Setting up ...,最后回到提示符。检查:
shell
which gcc make cmake pkg-config git socat
应看到:/usr/bin/gcc、/usr/bin/make、/usr/bin/cmake、/usr/bin/pkg-config、/usr/bin/git、/usr/bin/socat
F-3 装 Python 侧三件套(+ 可选的虚拟显示器)
shell
sudo apt install -y python3-pyqt5 python3-serial python3-flask
sudo apt install -y xvfb # 可选:没有桌面时用它把 Qt 窗口渲染成图
应看到:Setting up python3-pyqt5 / python3-serial / python3-flask。自检:
shell
python3 -c "import PyQt5, serial, flask; print('Python 三件套 OK')"
应看到:Python 三件套 OK
F-4 编译安装 C 侧三个库(libubox → ubus → uci)
这三个库 Ubuntu 源里没有,必须自己编译(课时 15 已经完整讲过一遍,这里只给"照抄顺序"):
shell
# ① 解压课程给的源码包(在 代码/依赖源码包/ 里)
cd ~/lab && mkdir -p build && cd build
tar xzf ~/lab/依赖源码包/libubox-2023-05-23-75a3b870.tar.gz
tar xzf ~/lab/依赖源码包/ubus-2023-06-05-f787c97b.tar.gz
tar xzf ~/lab/依赖源码包/uci-2023-08-10-5781664d.tar.gz
# ② 依次编译安装(顺序不能变:ubus 和 uci 都依赖 libubox)
cd libubox-2023-05-23-75a3b870 && cmake . && make && sudo make install && cd ..
cd ubus-2023-06-05-f787c97b && cmake -DBUILD_LUA=OFF . && make && sudo make install && cd ..
cd uci-2023-08-10-5781664d && cmake -DBUILD_LUA=OFF . && make && sudo make install && cd ..
sudo ldconfig
自检(三条 => /usr/local/lib/... 都要有):
shell
ls /usr/local/include/libubox/uloop.h
ldconfig -p | grep -E 'libubox|libubus|libuci'
更详细的每一步(每条命令的现象、常见报错、
-Wno-error之类)见课时 15 ;源码包与版本校验见代码/依赖源码包/VERSIONS.txt。
F-5 一键总自检(做完上面四项,用这一段确认)
shell
# C 侧
gcc --version | head -1
ls /usr/local/include/libubox/uloop.h
ldconfig -p | grep -c -E 'libubox|libubus|libuci' # 应该是 3
# Python 侧
python3 --version
python3 -c "import PyQt5, serial, flask; print('PyQt5/pyserial/Flask OK')"
# 虚拟串口工具
which socat
# 有没有可用的显示(桌面里应输出 :0 之类;SSH 里是空的)
echo $DISPLAY
F-6 装不上怎么办(常见四种)
| 现象 | 原因 | 处理 |
|---|---|---|
Could not resolve / Failed to fetch |
虚拟机没网 / DNS | 检查 VMware 网络适配器选 NAT;ping -c2 www.aliyun.com;不行就换源(课时 06) |
Could not get lock /var/lib/dpkg/lock |
有另一个 apt 正在跑(比如后台在更新) | 等 1~2 分钟;或 `ps -ef |
No space left on device |
磁盘满 | df -h / 看;清 ~/.cache、旧内核、sudo apt clean |
装完还是 import PyQt5 失败 |
用错了 Python(conda/venv) | 一律用系统 python3;which python3 应是 /usr/bin/python3 |
| 界面中文是方块 | 缺中文字体 | sudo apt install -y fonts-wqy-zenhei |
给老师的提示 :F-1~F-4 可以提前在一台"母机"上做好,再把虚拟机克隆/导出成模板 发给学生;
学生只需跑 F-5 自检------但仍然建议第一节课让学生自己敲一遍 F-1~F-3,这是课时 06(软件管理)的落地练习。