奇妙小屏幕控制器(核桃派 ZeroW)
本目录是核桃派 ZeroW/H618 的独立服务源码,控制一块 64×64、1/32 扫描、ABCDE 行寻址的 HUB75 RGB 点阵屏。生产驱动是项目自带的 walnutpi-h618-hub75,不依赖其他开发板的 GPIO 库。
路径与运行方式
下列源码运行、编译和手工部署命令均在本文件所在的 核桃派软件源代码/ 目录执行;发布命令另行标明从工作区根目录执行。Windows 本机测试使用根目录下 测试相关资料/如何测试/本机测试环境/README.md 的命令。
- 程序:
/opt/matrix-screen-controller - 持久数据:
/var/lib/matrix-screen-controller - 运行数据:
/run/matrix-screen-controller - 服务:
matrix-screen-controller.service - 网页:
http://<设备通过 DHCP 获得的 IPv4>:8080/
本机 mock:
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
MATRIX_DRIVER=mock .venv/bin/python -m uvicorn app.main:app --host 127.0.0.1 --port 8080
核桃派不具备境外网络条件。生产安装必须使用项目根目录 发布更新相关/其他依赖/aarch64-py311/ 中已校验的 wheel,并强制 --no-index:
python3 -m venv .venv
.venv/bin/pip install --no-index --find-links ../发布更新相关/其他依赖/aarch64-py311 -r requirements.txt
make -C app/display/native clean all
原生驱动边界
- PIO 控制器:
allwinner,sun50i-h616-pinctrl - 映射基址:
0x0300B000 - PWM 控制器:
0x0300A000的 PWM4,OE 使用PI14/PWM4,LAT 使用PI15/GPIO - 硬件映射:
walnutpi-pi-bank-pwm-oe-v2,不得搭配旧版 LAT/OE 接线 - PI bank:偏移
0x120,数据寄存器再偏移0x10 - 图像:64×64 RGB888 输入、7-bit PWM、双缓冲帧边界交换
- 刷新档:15/20/30/45/60/80/100 Hz
- CPU3 绑定、实时优先级、内存锁定和 governor 会逐项报告实际结果,状态不得伪报成功
- 专用主机停用 lightdm、蓝牙、官方
gpioc-server和map_device;Wi-Fi、SSH、NetworkManager 不受影响。正式可刷镜像默认启用 SSH,使用配置账户密码登录并以同一密码执行完整 sudo,禁止 root 直接登录 - 退出或异常时先禁用 PWM4 并强制 OE 高、清屏,再恢复 GPIO/PWM 状态;systemd 前后都执行独立
hub75_safeoff
编译和纯逻辑测试:
make -C app/display/native clean all test
真实 /dev/mem 基准必须在 HUB75 和 ADC 都断开时、由 root 执行:
sudo app/display/native/hub75_benchmark --duration 60 --refresh-rate 100 --brightness 40 --dev-mem
接屏门槛与全部人工停顿顺序见项目上层 测试相关资料/如何测试/核桃派点阵屏控制服务测试流程.md。
闪烁修复状态
- 旧版 CPU 忙等 OE 已复现局部高亮块;当前版本使用 H618 PWM4 的硬件单脉冲模式(
PWM_MODE/PWM_PUL_START)自动结束低有效 OE 脉冲,并在点亮当前锁存画面的同时移入下一 bitplane,以 50% 扫描槽预算恢复1..100%全范围亮度。不得用PER反复启停连续 PWM 模拟单脉冲。该实现尚须依次通过板端、实屏与摄像头验收,在这些门槛完成前不得写成“闪烁已解决”。 - 任一门槛失败必须回滚旧
/opt、保持持久数据不变,并在断电后恢复原 LAT/OE 接线;不得把部分亮度通过写成全部通过。
I²C 与电压采样
Unit ADC 仅使用物理脚 3/5 的 /dev/i2c-1(PI8/PI7),默认地址 0x48。HUB75 驱动不会更改 PI7、PI8 和保留的 PI16。ADC 必须等屏幕基础画面全部通过后,在整机断电状态下连接;校准时万用表和 ADC 必须测量同一点。
数据安全
配置 schema 为 v8,包含设备共享的 workspace_order,并在 v7→v8 时把 media-import 插入文字与动图工作区之间。媒体源文件和严格 schema v1 任务记录位于 /var/lib/matrix-screen-controller/media-import/jobs;成功或取消立即清理,失败及等待设置保留 24 小时。只有数据文件不存在时才能创建默认值;损坏、未知更高版本或迁移失败时保留原文件并拒绝启动。更新程序必须先克隆 /var/lib/matrix-screen-controller,在副本上完成登记迁移并验证后再原子切换,不得把设备凭据、主机密钥、旧设备配置或测试数据打进部署载荷。
媒体转换依赖 Debian 的 ffmpeg、ffprobe、heif-convert(libheif-examples)以及带 zscale、tonemap 的 FFmpeg 构建。分析和转换由 matrix-screen-converter@.service 在 CPU0–1、150% CPU、384 MiB 内存、无交换区和私有网络限制下串行执行;主服务只负责流式上传、任务管理与结果展示。
现有设备使用原子更新脚本;它在版本化 staging 中离线安装、编译和测试,健康检查失败会自动恢复旧 /opt:
sudo sh scripts/update_walnutpi.sh
浏览器全量 OTA
系统设置显示 VERSION 中的正式软件版本和 FEATURE_UPDATED_AT 中固定的北京时间,并允许上传完整 .ota 文件。页面不展示最近 OTA 结果,但上传、安装、断线恢复和失败反馈保持可见;失败后会自动打开可滚动、可复制的完整诊断日志,关闭后仍可重新查看或复制。设备只在 /var/lib/matrix-screen-controller/ota/last-failure.log 原子保留最近一份不超过 1 MiB 的脱敏失败日志,下一次成功更新会将其删除;GET /api/ota/failure-log 仅在最近结果失败且日志存在时返回 text/plain,并禁止缓存。设备不连接更新服务器,不使用增量、加密或签名;包内 SHA-256 只检查文件损坏。同版和旧版在停服前拒绝,新版失败自动恢复更新前程序与用户数据。
从 1.0.3 首次升级到包含该能力的版本时,安装过程仍由 1.0.3 的旧 worker 控制,因此新日志页面只能在升级成功后使用。新版本测试入口兼容旧 worker 传入的隔离变量,并将 pytest、应用数据、运行数据和 Python 临时文件全部约束到 OTA 事务目录;它不会为安装日志功能而忽略测试失败或绕过原子回滚。
普通功能开发、修复和 SSH 开发部署都保持 VERSION 不变;每次可部署的功能新增或优化把 FEATURE_UPDATED_AT 写为精确到分钟的固定 +08:00 时间。只有用户明确要求导出 OTA 或可刷镜像时才推进共用正式版本;单纯导出、重复部署和文档修改不得刷新功能时间。普通更新成功导出默认将补丁位加一;新增或变更系统软件必须增加 minor 并归零 patch,使用 --version 指定已登记节点。失败不占号,全部操作登记在项目根 发布记录.json。
需要交付或测试 OTA 包时才从项目根执行:
python ".\核桃派软件源代码\scripts\export_release.py" ota --notes "本次更新说明"
OTA 输出为 发布更新相关/OTA数据包/<版本>/;镜像输出为 发布更新相关/导出包/<版本>/。已有版本目录不会覆盖。导出 IMG 可由用户删除以节省空间,缺失默认正常,不追补、不自动重建、不影响开发或常规测试。OTA 今后长期保留;1.0.1 缺失为已接受的历史例外,不再追补或提醒,不影响使用。后续构建不得引用历史产物,原发布记录保留。
可刷镜像导出与同版本修复
镜像导出只在 Linux root 构建主机上执行。入口使用标准库完成打包和镜像操作,不要求主机安装 Pillow、FontTools 或应用 venv;应用依赖由镜像中的 AArch64 离线 wheelhouse 在首次启动时安装。执行前先检查构建主机,而不是假设远端具备本机工具:
command -v python3
command -v bash
command -v losetup
command -v mount
command -v umount
command -v systemctl
command -v sha256sum
普通新版本镜像需要显式配置 JSON,并使用两阶段门禁。第一阶段只生成静态验证完成的候选,不修改正式版本或发布记录:
sudo python3 核桃派软件源代码/scripts/export_release.py image \
--version 1.1.1 --config /受保护临时目录/image-config.json \
--candidate-output /受保护临时目录/image-1.1.1-candidate \
--notes "完整可刷镜像 1.1.1"
同一字节候选完成真实 TF 卡首启、默认 Wi-Fi、SSH、sudo、内核、网页和再次重启验收后,使用不含秘密且与 IMG 摘要绑定的严格 JSON 晋升;晋升会重建期望 bundle 并重新只读验证镜像,成功后才推进版本:
sudo python3 核桃派软件源代码/scripts/export_release.py image \
--validated-candidate /受保护临时目录/image-1.1.1-candidate \
--validation-report /受保护临时目录/image-1.1.1-validation.json \
--notes "完整可刷镜像 1.1.1"
IMG 自带完整系统依赖,不应用 OTA 必经节点跳转规则。正式 IMG README 是公开默认账户和 Wi-Fi 值的唯一发布文档;建议用户刷写前用镜像编辑器修改。已经登记但经真实首启证明不可用的当前版本,只有获得明确授权后才使用同版本修复入口:
sudo python3 核桃派软件源代码/scripts/export_release.py image \
--repair-current \
--notes "同版本镜像修复说明"
修复入口从现有正式 IMG 复用完整双槽配置,从未修改官方基线重建,在隐藏目录完成全部验证后才替换正式目录和原发布记录;不追加重复版本、不修改 VERSION。失败会恢复旧目录和记录。旧的 refresh_sd_image_payload.py 原地刷新方式已停用,因为它不能安全处理 rootfs 应用载荷和 FAT 内核安装空间。
镜像格式 v3 在 FAT16 中只放配置、元数据和首启入口;应用 bundle 与候选内核分别预置到 /opt/matrix-image-bootstrap/app 和 /opt/matrix-image-bootstrap/axp313a。构建及设备安装均要求候选 boot 文件实际预算之外仍有 32 MiB 余量。首次启动失败保留 rootfs 压缩载荷供重试,全部成功后才统一删除。
测试
python -m pytest -q
node --test tests/*.mjs
真实硬件测试必须严格按上层测试文档逐项执行,上一项失败时不得跨过。任何接线、拔插、万用表测量或电压调整前都必须暂停并等待人工确认。
软件安装节点与 1.1.0
从 1.1.0 起,每个 minor 的 .0 是软件安装包(必经升级版本)。例如 1.0.6 → 1.1.0 → 1.2.0 → 1.2.3;不能跳过任何安装节点。同系列补丁可以跳过,例如 1.1.0 → 1.1.3。已预装 frp 的 1.0.6 调试设备也必须先完成 1.1.0,版本号与组件完整性分别检查。同版和降级仍不允许。
正式导出命令(项目根):
python "核桃派软件源代码/scripts/export_release.py" ota --version 1.1.0 --notes "软件安装包:离线安装或修复 frpc,建立必经升级节点"
UPGRADE_POLICY.json 登记连续安装节点及固定依赖摘要;已发布节点不得改写。1.1.0 使用旧更新器支持的 v1 外层协议,frpc 放在 software/system-dependencies/frpc/。以后使用 v2,正式旧更新器会拒绝该格式;新更新器会在安装前提示必须先安装的版本。普通补丁不携带系统二进制,仍包含应用及 Python wheelhouse;系统组件缺失时先修复再更新。首装镜像仍携带完整离线依赖。
frpc 完整匹配时不替换二进制或重启服务;否则离线修复,保留 UUID 配置、选择项和启停状态。初次默认关闭。OTA 从现有 frp 非 root 账户、镜像生成的 /etc/sudoers.d/90-matrix-screen-controller-account 或唯一 sudo 普通账户确定运行账户,无法确定则在修改前失败。
prepare_data_root 仅在根权限、真实 OTA 请求、候选源码和候选数据路径全部匹配时启动组件事务,普通迁移和测试不会安装系统软件。完整 pytest 必须先通过。事务记录及原组件备份镜像保存在原数据根和候选数据根的 ota/component-transaction/;系统启动恢复程序临时位于 /opt/matrix-screen-controller-component-recovery.py,恢复 unit 为 matrix-screen-component-recovery.service,运行期监护为 matrix-screen-component-watch.service。原 worker 成功记录与目标版本共同决定提交;失败或中断恢复原组件、权限、程序和数据。无法完成恢复时保留记录并拒绝下一次更新;成功恢复/提交后删除事务文件、临时恢复程序和 unit,成功提交还删除程序目录内的安装载荷。只包含运行状态的文件仍位于 /run/matrix-screen-controller。
OTA 停服运行目录保护(必须保留的防回归规则)
RuntimeDirectoryPreserve=restart 只保护重启,不保护分开的 systemctl stop / start。OTA 会显式停止主服务,因此主服务使用 RuntimeDirectoryPreserve=yes;该运行目录仍位于 /run,整机重启后清空。日志写入重新 mkdir 只能救回日志,不能救回已经删除的请求和进度,不能作为生命周期修复。
从旧版本安装时,候选迁移入口必须先创建 /run/systemd/system/matrix-screen-controller.service.d/90-matrix-ota-runtime.conf,执行 daemon-reload 并检查有效 RuntimeDirectoryPreserve=yes,再允许旧 worker 停服。保护一直保留到提交或回滚及清理结束;原有用户 drop-in 不覆盖。该标准 systemd 运行期目录是临时服务配置的明确例外;普通运行数据继续集中在 /run/matrix-screen-controller。
真实验收使用 sudo python3 scripts/test_runtime_directory_systemd.py,仅操作随机命名的专用测试 unit/目录,不停止生产服务。必须包含旧配置失败对照、显式 stop、restart、启动失败及保护清理,不能用 mock systemctl 代替。诊断存储故障不得中断回滚,有限内存缓冲可补写最近失败日志。
经用户明确授权修复当前 OTA 时,先构建到隔离候选目录并实机验收,再执行:
python "核桃派软件源代码/scripts/export_release.py" ota --repair-current --validated-candidate "<已验收候选包>" --validation-report "<实机验收JSON>" --notes "修复说明"
此入口校验原产物及记录、候选摘要、最终源码一致性和实机验收结果;完整归档原包与记录后,按原字节晋升候选包,不再构建、不推进 VERSION、不刷新功能时间。验证或提交失败保留原产物与记录。