# 奇妙小屏幕控制器(核桃派 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: ```bash 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`: ```bash 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` 编译和纯逻辑测试: ```bash make -C app/display/native clean all test ``` 真实 `/dev/mem` 基准必须在 HUB75 和 ADC 都断开时、由 root 执行: ```bash 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 为 v11,包含设备共享的 `workspace_order`,v10→v11仅移除文字导航,其余顺序不变;旧文字入口跳转画图页。媒体源文件和严格 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`: ```bash 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 包时才从项目根执行: ```powershell python ".\核桃派软件源代码\scripts\export_release.py" ota --notes "本次更新说明" ``` OTA 输出为 `发布更新相关/OTA数据包/<版本>/`;镜像输出为 `发布更新相关/导出包/<版本>/`。已有版本目录不会覆盖。导出 IMG 可由用户删除以节省空间,缺失默认正常,不追补、不自动重建、不影响开发或常规测试。OTA 今后长期保留;1.0.1 缺失为已接受的历史例外,不再追补或提醒,不影响使用。后续构建不得引用历史产物,原发布记录保留。 ## 可刷镜像导出与同版本修复 镜像导出只在 Linux root 构建主机上执行。入口使用标准库完成打包和镜像操作,不要求主机安装 Pillow、FontTools 或应用 venv;应用依赖由镜像中的 AArch64 离线 wheelhouse 在首次启动时安装。执行前先检查构建主机,而不是假设远端具备本机工具: ```bash command -v python3 command -v bash command -v losetup command -v mount command -v umount command -v systemctl command -v sha256sum ``` 普通新版本镜像需要显式配置 JSON,并使用两阶段门禁。第一阶段只生成静态验证完成的候选,不修改正式版本或发布记录: ```bash 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 并重新只读验证镜像,成功后才推进版本: ```bash 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 值的唯一发布文档;建议用户刷写前用镜像编辑器修改。已经登记但经真实首启证明不可用的当前版本,只有获得明确授权后才使用同版本修复入口: ```bash 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 压缩载荷供重试,全部成功后才统一删除。 ## 测试 ```bash 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 发布目录内: ```powershell 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 时,先构建到隔离候选目录并实机验收,再执行: ```powershell python "核桃派软件源代码/scripts/export_release.py" ota --repair-current --validated-candidate "<已验收候选包>" --validation-report "<实机验收JSON>" --notes "修复说明" ``` 此入口校验原产物及记录、候选摘要、最终源码一致性和实机验收结果;完整归档原包与记录后,按原字节晋升候选包,不再构建、不推进 VERSION、不刷新功能时间。验证或提交失败保留原产物与记录。 ## 多图层及扩展编辑 画图页清空按钮下管理画笔/文字层;至少保留一个画笔层。每个文字层可编辑多段文字。小圆球菜单切换192×192扩展区、移动当前层及视线模式;实际输出始终中央64×64。移动越界确认后永久裁切,可通过会话撤销恢复。 scene v2使用有序图层、±64偏移、192 RGBA压缩PNG和文字相对裁切矩形。模板schema v2、动图schema v3;迁移先校验旧摘要,在数据副本上生成新场景/缩略图后原子切换。损坏/未知/未来版本保留原件并拒绝启动。无需新板端依赖;PNG编解码复用Pillow,浏览器使用仓库内fflate(MIT)。RGB888及手机内容库/播放协议保持不变。