Files
matrix-screen-controller/核桃派软件源代码/README.md
T

16 KiB
Raw Blame History

奇妙小屏幕控制器(核桃派 ZeroW)

本目录是核桃派 ZeroW/H618 的独立服务源码,控制一块 64×64、1/32 扫描、ABCDE 行寻址的 HUB75 RGB 点阵屏。生产驱动是项目自带的 walnutpi-h618-hub75,不依赖其他开发板的 GPIO 库。

路径与运行方式

移动端控制器独立维护,入口见 ../移动端相关内容/README.md;设备侧新增契约见 ../整体开发需求/03_移动端接入需求.md。设备更新只评估并登记手机兼容影响,不自动迭代 App。BLE 与网页应复用公共业务层;不得让 App 依赖网页布局。当前蓝牙接入尚待实现,既有脚本仍执行原禁用策略,不能将需求文档当成设备已启用蓝牙。

普通前端/BLE 入口改动不新增实屏视觉验收。确需视觉检查时先停止、解释原因、询问用户如何启动并等待指示,不自动开摄像头、DroidCam 或视觉程序,不主动切换实屏测试图案。软件统计和逻辑帧检查按正常自动测试执行。

下列源码运行、编译和手工部署命令均在本文件所在的 核桃派软件源代码/ 目录执行;发布命令另行标明从工作区根目录执行。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

OTA 会记录更新前的候选内核、cpufreq 和性能模式状态。原本正常的设备若在更新中失去这些能力,应用更新按事务回滚;原本已回退到原内核的设备允许先更新应用。更新完成后,若已安装的候选 Image、两份 DTB、模块树、受管启动脚本和健康服务均与登记材料一致,独立任务仅安排一次候选重启;结果保存在持久根的 kernel-recovery.json,通过 /api/ota/status.kernel_recovery 查询。失败后不会在后续 OTA 自动重试。缺失或损坏候选材料时只提示需要专用内核修复包,普通 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,版本号与组件完整性分别检查。同版和降级仍不允许。

新 OTA 先导出到正式目录外的候选目录,完成真实设备试装及原版本恢复后,再按候选包 SHA-256 原字节晋升。以下命令从项目根执行;<受保护临时目录> 不得位于正式 OTA 发布目录内:

python "核桃派软件源代码/scripts/export_release.py" ota --candidate-output "<受保护临时目录>/ota-候选" --notes "更新说明"
python "核桃派软件源代码/scripts/export_release.py" ota --validated-candidate "<受保护临时目录>/ota-候选" --validation-report "<受保护临时目录>/实机验收.json" --notes "更新说明"

候选阶段不改 VERSION 或发布记录;正式目录包含候选原字节包、摘要、manifest 和 README。报告只含包摘要、目标/试装源/恢复版本、时间及离线安装、OTA 健康、运行目录、用户数据、FRP、蓝牙、事务清理、恢复原版本的布尔结果,不写凭据或设备标识。每个 minor 的 .0 仍需用 --version 明确指定登记的必经安装节点。直接打包脚本只用于非正式材料。

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、不刷新功能时间。验证或提交失败保留原产物与记录。