外观
2026-08-14 Paperboard:联网七色电子纸信息板,从设备自己画图到多云生成整张画面
- 项目形态:800×480 七色电子纸信息板
- 显示硬件:Good Display
GDEY073D46+ 裸DESPI-C73 - 计算平台:ESP32、ESP8266、1 MiB ESP8285
- 固件主线:ESP32 render-only
0.7.2;ESP8266/ESP8285 RTOS SDK render-only0.2.0 - 云端主线:Cloudflare Workers + Durable Objects 状态参考实现;Tencent EdgeOne Cloud Functions Node/Sharp 组合入口
- 机械主线:GPT 参与设计的 RhinoCommon 参数化外壳与零五金 3D 打印桌面支架
我们实现了一台什么设备
我们把实时资讯、市场行情、天气、家庭温度和照片放到一块 7.3 英寸七色 ACeP 电子纸上。Paperboard 无需浏览器或手机常驻,接通电源后自行联网、取帧、校验、缓存并驱动屏幕;电子纸断电后仍保留最后画面。
当前系统已经形成完整的软硬件闭环:
| 能力 | 已实现的形态 |
|---|---|
| 信息布局 | layout1 展示未来 12 小时温度、降水与 UV、AQI、预警、天气详情、五条快讯、五个市场和两个房间温度;layout2 展示 500×416 动态图片与紧凑信息栏 |
| 照片模式 | photo 使用 800×480 fit-cover 全屏图片,不叠加标题、时间或署名 |
| 七色图形 | 天气场景、图标、文字和照片统一量化到 GDEY073D46 的黑、白、红、黄、蓝、绿、橙七色 |
| 本地交互 | Boot 短按立即刷新,长按在 layout1 → layout2 → photo 之间循环;每种布局独立设置刷新周期和安静时段,成功手动刷新后重新对齐周期 |
| 多平台固件 | ESP32、ESP8266 和 ESP8285 共用帧协议、双槽缓存、调度、配置与 C73 输出逻辑 |
| 多云兼容 | 布局、上游模型、字体、量化与 PBF1 由共享核心实现,Cloudflare 与 EdgeOne 只组合各自的 HTTP、图片处理和状态能力 |
| 可恢复显示 | 网络或上游异常时重放同布局最后有效帧,并在左下角叠加紧凑本地错误条;相同错误不重复全刷 |
| 结构件 | 前框、整体背板与无胶落座底座组成零五金开放式支架,保护裸玻璃、FPC 与背面电子区 |
一次完整电子纸刷新约需三十秒,因此 Paperboard 的设计围绕“完整帧”展开。画面在进入 C73 之前必须完成长度、色号和双 CRC 校验;传输失败时不发送全刷命令,屏幕继续保留上一张完整内容。
这一版更新了什么
8 月 7 日以来,Paperboard 的主线从“Cloudflare 上的一套全帧 Worker + 两类薄固件”继续收敛为可跨云组合的渲染核心和三平台帧终端。最近一版集中完成了四件事:
layout1把天气区改成行动面板:第一张图显示未来 12 小时分段温度曲线,第二张图在雨天叠加累计降水柱与 UV 曲线,无雨时单独显示 UV;AQI、体感、湿度、风、日出日落和当前预警进入同一固定区域。- 温度曲线按蓝、绿、橙、红绝对温区分段;连续高温全部超过 29℃ 时,只在橙/红色域内区分本轮局部高低。UV 使用固定等级色,不随当日最大值漂移。
- 地区名称、LocationID 和经纬度改由设备配置并随请求发送。新设备或不兼容旧 schema 会显示纯 ASCII 配置页,再通过无回显 UART 事务和
paperboard_config.py一次写入八个字段。 - ESP8266/ESP8285 从 Arduino/PlatformIO 迁到 ESP8266 RTOS SDK v3.4;ESP32
0.7.2默认开启 C73 输出,自动周期、安静时段、Light-sleep 与 Boot 手动刷新都进入共享策略层。
云端同时增加 EdgeOne Node/Sharp 组合入口。共享核心仍只生成标准 Request/Response 和 PBF1,平台 adapter 负责图片变换、状态和部署格式。这样可以在不改固件、不复制布局的前提下选择云端,但“可以构建”与“已经完成线上三布局和实屏验收”仍是两个状态。
最新 PBF1 实帧与三种布局
下面的 layout1 使用代码基线 bf231b7 先生成 144,000 字节 INDEX3 payload,再封装为 144,048 字节 PBF1。报告生成器随后调用 parseFrame() 校验布局、尺寸、header CRC32 与 payload CRC32,最后逐像素解码为网页图。网页图完整来自校验后的 PBF payload。展示城市固定为北京,资讯、房间温度和数值均使用不含个人位置与账户信息的固定 fixture。

layout1 最新 PBF1 实帧:800×480、144,048 字节,header CRC32 b8853d06,payload CRC32 178ddaa2。左侧是温度、累计降雨/UV、AQI 和天气详情,右侧保留五条快讯与五个市场。layout2 与 photo 继续使用同一套七色画布、图片量化和帧合同:
三种模式共用一套设备代码。Worker 在同一七色画布上切换排版,输出相同 PBF1 合同;ESP32 与 ESP8285 只按帧头中的布局编号完成缓存隔离和送屏。
技术路径总览
flowchart TB Core["端侧 core<br/>ESP32 采集、建模、排版与送屏"] ImageWorker["图片 Worker<br/>云端裁切、量化,端侧合成"] FullFrame["全帧 Render Worker<br/>云端生成完整 INDEX3 页面"] ThinFirmware["render-only 薄固件<br/>设备只负责可信帧播放"] MultiMCU["多 MCU 共用合同<br/>ESP32 / ESP8266 / ESP8285"] Core --> ImageWorker --> FullFrame --> ThinFirmware --> MultiMCU
flowchart TB Device["render-only 设备"] -->|"Bearer + location + layout + request_id"| CF["Cloudflare<br/>Router + Render DO + Images"] Device -->|"同一 POST /v1/render"| EO["EdgeOne Cloud Function<br/>Node.js + Sharp"] CF --> Core["共享 createWorker 核心<br/>上游、布局、字体、量化、PBF1"] EO --> Core Core --> Frame["144,048 B PBF1<br/>INDEX3 + header/payload CRC32"] Frame --> Verify["设备按 300 B/行流式校验"] Verify --> Flash["非活动 Flash 槽原子提交"] Flash --> C73["400 B/行转换并送入 C73"] C73 --> Panel["GDEY073D46 全屏刷新"]
我们在第一阶段把产品完整做进 ESP32,解决电子纸排版、状态合并和真实送屏;第二阶段先迁移图片,再把整张页面迁到 Worker;最终将设备收敛为低内存、可恢复的帧终端。渲染位置改变后,七色像素、PBF1、双 CRC、缓存提交顺序和 C73 输出保持为稳定合同。
第一代:设备自己采集数据、排版并驱动屏幕(core)
数据、排版和显示都在设备上完成
我们先让 core 方案运行在初代双核 ESP32 上:
text
Wi-Fi / SNTP
-> Jin10 + Lighter + QWeather + 本地传感器
-> dashboard_collect
-> dashboard_model
-> dashboard_runtime
-> dashboard_core 扫描线排版
-> C73 SPI adapter
-> GDEY073D46dashboard_runtime 管理采集周期、模式、刷新节奏、快照与健康状态,dashboard_core 接收纯模型并输出顺序色号行。渲染器不依赖 ESP-IDF、网络、NVS 或屏幕驱动,同一入口可以连接主机 PPM 预览、dry-run 摘要和真实 SPI sink。
| 层次 | 主要模块 | 实现内容 |
|---|---|---|
| 数据采集 | dashboard_collect、dashboard_jin10、dashboard_lighter、dashboard_weather、sensor_hub | 读取真实上游和传感器,维护各自最后有效状态 |
| 模型与调度 | dashboard_runtime、dashboard_control、dashboard_storage | 合并模型、持久化配置、控制刷新间隔与模式切换 |
| 排版与字体 | dashboard_core、dashboard_font | 中文换行、行情卡片、天气区、环境页、照片页和诊断图 |
| 图片协同 | worker/、dashboard_picture | 云端获取、裁切与量化图片,端侧以双槽 PBF1 缓存提供像素视图 |
| 屏幕输出 | epd_7in3f_dashboard、epd_7in3f | 将七色色号转换为 C73 4bpp 行,执行初始化、全刷、BUSY 等待与休眠 |
扫描线渲染绕开全帧内存
800×480 RGB888 画布约需 1.1 MiB,4bpp 全帧也需约 192 KiB。无 PSRAM 的经典 ESP32 无法长期为它们预留连续内存。core 每次只生成一行 800 个七色色号,sink 随即打包成 C73 所需的 400 字节 4bpp 行,页面复杂度不会转化为全帧 RAM 占用。
这个架构还带来一个实用能力:桌面预览与实物固件执行相同布局代码。字体裁片、换行、颜色顺序和图片覆盖位置都可以在主机端复现,屏幕适配器只处理字节打包与时序。
第一代方案确定了产品应该怎样工作
五条快讯作为完整候选提交,天气在整组字段合法后更新,单个行情失败时保留旧值,图片先写非活动槽再切换元数据。数据采集频率与电子纸全刷频率分开,内容没有变化时不驱动屏幕。
这些行为随后迁入 render-only:最后有效帧、双槽原子提交、失败不刷新半帧、相同错误去重,以及把“逻辑刷新”与“传输重试”分开。
为什么把画面生成搬到云端
端侧自治适合快速建立完整产品,却让每次视觉变化都进入固件:TLS、JSON schema、中文字体、天气插画、图片供应商和布局共同消耗 Flash、堆与维护时间。多字号字体和更复杂的图片策略继续增长后,1 MiB ESP8285 无法复用这套组件图。
因此,我们把变化快、计算重的内容层迁到边缘端,让 MCU 的资源集中用于 Wi-Fi、TLS 流读取、Flash 原子性与电子纸时序。
这次迁移还让我们解决了三个架构问题。第一,页面发布与固件发布解耦:修改字体、天气插画或快讯排版只部署 Worker。第二,不同 MCU 看到同一份最终像素,ESP32 与 ESP8285 无需分别实现业务页面。第三,失败边界从多个上游 JSON 收敛为一个固定长度帧,设备只判断 HTTP、PBF1、CRC、Flash 和 C73 五层状态。
| 架构问题 | core 的处理方式 | render-only 的处理方式 |
|---|---|---|
| 页面变化 | 布局、字体、图标和数据 adapter 随固件编译 | Worker 独立更新完整页面 |
| 资源峰值 | TLS、JSON、字体、图片视图和排版共用 MCU 资源 | MCU 只保留 TLS 流、行缓冲和 Flash 槽 |
| 多平台复用 | 业务组件依赖 ESP32 能力与 Flash 空间 | ESP32/ESP8285 消费同一 PBF1 |
| 上游失败 | 设备分别理解资讯、天气、行情和图片状态 | Worker 形成完整帧或安全错误,设备保留最后有效帧 |
| 视觉一致性 | 各平台需要复现排版实现 | 所有平台显示 Worker 生成的相同像素 |
我们保留 core 作为参考路线:它用最短路径验证了产品模型、七色排版和 C73 真实约束,并产出可复用的参考渲染器。render-only 建立在这些结果上,把已经稳定的设备语义与持续变化的内容实现分开。
第二代:共享渲染核心在不同云生成完整画面
createWorker() 固定应用入口
公网只暴露鉴权的 POST /v1/render。布局、字体、资讯与天气模型、图片目录、量化和 PBF1 编码都进入共享 createWorker();平台组合根只提供 HTTP 入口、图片变换、量化实现和状态存储。新增云端时不复制 layout1/layout2/photo,设备也不感知后端运行在 Cloudflare 还是 EdgeOne。
| Worker 层 | 实现 |
|---|---|
| HTTP 合同 | Bearer 鉴权、最大 4 KiB JSON、布局、地区名称/LocationID/经纬度校验和 32 位小写十六进制 request_id |
| 数据适配 | Jin10 快讯、Lighter 行情、QWeather、客厅美居温度、主卧只读快照、Wallhaven/Pixabay 图片目录 |
| 共享应用 | 请求规范化、并发采集、三种布局、图片选择、故障分类和安全响应 Header |
| 页面渲染 | 800×480 七色画布、三种布局、预栅格 Noto Sans CJK SC 16/20/24 px 字体 |
| 平台能力 | Cloudflare Images + Rust/Wasm;EdgeOne Sharp + 精确 JS Atkinson;平台状态能力分别注入 |
| 帧封装 | INDEX3 行打包、PBF1 头、header CRC32、payload CRC32 与响应元数据 |
一次请求内部发生什么
信息布局请求提交地区名称、LocationID、经纬度、布局、网络快照与 request_id;photo 不需要地区字段。Worker 先规范化请求并计算不含地区明文的 SHA-256 指纹。Cloudflare 同 ID、同合同返回持久缓存帧,同 ID 携带不同合同返回 409;共享核心的 inFlight Map 还会把同一实例内的并发重试合并为一次渲染。
layout1 并发启动信息模型、客厅美居温度和主卧只读快照;layout2 再并发图片链;photo 只执行图片链。等待时间由最慢的独立链路决定,不把资讯、天气、行情、家庭温度和图片阶段串行相加。
信息模型要求五条快讯和五个市场完整可用,天气候选按布局一次取得所需字段。两个房间温度是可选字段:客厅 adapter 维护美居登录会话,主卧通过同账户服务的只读 binding 获取快照;任一来源失败时对应位置显示 -- 和状态标记,不阻断其余信息板。设备不上传室内传感器值。
多云兼容的边界
| 维度 | Cloudflare Workers + Durable Objects | EdgeOne Cloud Functions |
|---|---|---|
| HTTP 入口 | Worker fetch,Router 转入固定 Render DO | Node.js onRequest(context) handler |
| 图片变换 | Cloudflare Images binding 执行 fit: cover | 同次函数调用读取原图,npm sharp 完成方向校正、裁切、白底合成与 PNG 输出 |
| 七色量化 | Rust/Wasm 精确 Atkinson | 共享 JS 精确 Atkinson;Node 20 bundle 把不支持的 Uint8Array.fromBase64() 局部改为 Buffer.from() |
| 状态语义 | DO SQLite 串行保存完整帧幂等、图片缓存、游标、最近 ID 和美居会话 | 当前只保留单实例并发去重,没有 DO 等价的跨实例持久状态 |
| 主卧温度 | 只读 Service Binding | 尚无等价 HTTPS adapter,保持缺失显示 |
| 当前证据 | 生产配置与真实供应商链路已有历史验证 | Node/Sharp 入口可构建并通过本地合同测试;新 Cloud Function 尚未完成大陆/全球三布局线上验收 |
Cloudflare 继续承担完整状态语义的参考实现。EdgeOne 提供 Node.js 运行时、Sharp 原生图片处理和可选大陆区域,当前状态一致性限定为单实例范围。bundle 只生成一个 /v1/render Node 函数,函数入口代码约 1.92 MB;包含 Sharp 的 Makers 最终包、冷/热时延、内存、GB-s、上游出站和成都设备链路仍要在线测量。AWS Lambda 当前只记录 event、Sharp 与 DynamoDB/S3 的组合 seam,尚无实现代码。
字体、天气行动面板与七色画布
Worker 的 renderer 直接维护 800×480 色号画布。Layout 1 左栏现在使用白底行动面板:温度曲线按绝对温区切分颜色;第二张图根据降雨状态显示累计降水柱与 UV,或单独显示 UV;AQI、体感、湿度、风、日出日落和预警压进下方固定卡片。Layout 2 把 500×416 图片放在左侧,快讯、天气、行情、两个房间温度和网络状态压缩到顶部与右栏;photo 使用整张图片画布。
中文字体在构建期从固定 Noto Sans CJK SC Medium 生成 16/20/24 px 的 1-bit 字形资产。Worker 运行时不读取 OTF,也不依赖文件系统;测量、换行、截断和字号选择都在 font.mjs 中完成。布局与字体可以随 Worker 发布更新,设备端保持同一帧播放器。
图片选择与跨云量化
Wallhaven 是主图片目录,Pixabay 处理快速失败或候选耗尽。photo 偏向 16:10,layout2 的 visual 变体偏向 4:3;候选需要满足最小尺寸、HTTPS 原图和裁切损失限制。DO 记录当前页与 offset,过滤最近 32 个图片 ID,当前页耗尽后继续下一页。
Cloudflare 组合把原图交给 Images binding 裁切和 PNG 解码,再用 Rust/Wasm Atkinson 输出 3bpp INDEX3。EdgeOne 组合在 Node 函数内用 Sharp 完成同样的 fit: cover 几何,再调用与 Wasm 基线逐字节对照的 JS Atkinson。构建期查找表吸收颜色空间换算,两条路径都不把平台差异带进最终 PBF1。
缓存与降级仍然返回完整画面
在 Cloudflare 参考实现中,完整帧缓存只服务 request_id 幂等,图片缓存只服务图像复用,两者的 TTL 与淘汰队列分开。图片上游短暂不可用时,Render DO 可以选择同变体的有效量化缓存;layout2 没有图片缓存时将图片区留白,信息区仍能组成完整页面。EdgeOne 当前没有跨实例持久图片缓存,图片缓存降级只属于 Cloudflare 参考实现。
所有成功路径最终调用 renderFramePayload() 生成 144,000 字节 payload,再由 encodeFrame() 写入 PBF1 头与双 CRC。响应携带 layout、frame CRC、图片 ID、图片供应商和降级状态等安全元数据,设备无需理解任何上游 JSON。
ACeP 七色量化的多轮迭代
ACeP 面板的像素只有七种物理颜料。普通 RGB 最近色、面向发光屏的调色板和通用照片抖动都无法直接给出稳定观感。我们的图片链经历了四次算法收敛,每次都保留可对照实现。
| 量化阶段 | 算法 | 解决的问题与结果 |
|---|---|---|
| 端侧参考路径 | 逐行 Bayer 有序抖动 | 只需约 2 KiB 工作集,适合 PPM 诊断;规则纹理明显,照片层次有限 |
| 面板感知 v1 | GDEY073D46 物理 L*a*b* 色心 + 浮点 Lab Floyd–Steinberg | 从逻辑 RGB 色板转向真实颜料中心,天空高光得到保留;山体和阴影容易混灰 |
| 快速试验分支 | 无状态蓝噪声查表 | 去掉误差缓冲并提升速度;实图出现更明显的噪点、色块和层次损失,随后删除 |
| 面板感知 v2 | 高光自适应 OKLab + 蛇形定点 Atkinson + 32³ LUT | 同时保住高光、暗部轮廓与局部对比,成为正式图片量化路径 |
| 边缘执行版 | 与 JS 基线逐字节一致的 Rust/Wasm | 把相同算法放入 Render DO 热路径,支持 RGB888 与 PNG8 快速入口并直接打包 3bpp |
同一张图上的量化差异

原图的连续天空、倒影和山体渐变最终只能由七种颜料密度表达。第二列的 Lab Floyd–Steinberg 已经摆脱逻辑 RGB 最近色,天空与山体轮廓完整,但全局误差扩散让中暗部混入较多中性色点。第三列的 OKLab Atkinson 以更小工作集建立更清晰的局部边界,白场比例提高,天空容易趋于平。最右侧加入高光自适应映射后,亮部继续使用白色作为主色,同时让天空、雪线和山脊重新获得蓝、橙与黑色的局部对比;水面倒影也保留了可辨认的层次。
算法差异在网页缩略图上首先表现为色块比例,在 7.3 英寸反射式面板上还会表现为观看距离下的颗粒融合。我们在当前版本中优先保证远看构图、亮暗关系和主体边缘,再利用七色点阵补充局部色感,没有把模拟 LCD 的连续色设为目标。
从逻辑色板转向物理色心
早期 Bayer 路径把七个逻辑 RGB 值当作显示目标,适合验证数据流和色号顺序。真实 GDEY073D46 的红、黄、蓝、绿、橙、黑、白在反射式介质上的亮度与色度中心并不等于屏幕 RGB 参考色。第二版开始使用规格书在 24°C、45°/0° 条件下给出的七个 L*a*b* 中心,颜色选择由“代码里的 RGB 距离”转为“目标颜料的感知距离”。
Lab Floyd–Steinberg 会把误差分配给四个后续像素,连续天空的高光层次得到改善;同样的全局扩散也容易让暗色山体混入过多中性颜料。无状态蓝噪声查表曾用于降低计算量,但实图中噪点和色块更加可见,速度优势没有抵消观感损失。
高光自适应 OKLab Atkinson
当前算法把 sRGB→OKLab、物理色心匹配和高光映射离线写入 32 × 32 × 32 的 5-bit RGB 查找表。输入的每个通道取高 5 位即可得到目标映射:
L* <= 65的山体和阴影保留原输入,让误差扩散继续表达轮廓与暗部差异;L* >= 85的天空、云层和水面映射到面板黑白点17.6…70.6;- 中间区间使用 smoothstep 平滑过渡,避免高光压缩形成硬边;
- 低色度像素只在黑白颜料中选择,减少灰阶区域的彩色伪影;
- 七个设备原色所在 LUT 单元固定回对应色号,纯色图标不会被邻近色吞掉。
蛇形 Atkinson 每行反向扫描,把量化误差的 6/8 分给六个未来像素,只需要三行 Int16 误差缓冲。八个像素直接打包为三个字节,不创建完整索引画布。500×416 日间风景的同机量化中位从 34.45–41.60 ms 降到 5.49–5.90 ms,约快 6.1–7.2 倍;速度来自 LUT、定点误差和边量化边打包的组合。
从 JavaScript 基线到 Rust/Wasm 热循环
全帧 Worker 继续沿用高光自适应 Atkinson 的像素语义。JS 版本既是清晰的参考实现,也是 EdgeOne Node 组合的执行路径;Cloudflare Render DO 注入 Rust/Wasm 版本。两者对同一输入逐字节一致。Wasm 内部预分配输入、误差行、调色板和输出存储,避免每次请求重建大对象。
RGB888 入口用于一般解码结果;PNG8 快速入口先对源调色板的每个颜色计算一次 5-bit key 与高光映射,再让像素索引复用该结果。量化器直接写 3bpp payload,PBF 封装可以复用同一 Wasm 线性内存视图。算法升级因此没有改变设备合同,ESP32 和 ESP8285 始终只接收七色色号与双 CRC。
设备只负责下载、校验和显示画面(render-only)
PBF1 固定了云端与设备的边界
| 字段 | 显示合同 |
|---|---|
| 分辨率 | 800×480 |
| 像素编码 | INDEX3,七种合法色号 |
| 每行数据 | 300 字节 |
| payload | 144,000 字节 |
| header | 48 字节 |
| 完整响应 | 144,048 字节 |
| 布局 | layout1、layout2、photo |
| 完整性 | header CRC32 + payload CRC32 |
Worker 与三种 MCU 只通过这份合同耦合。设备不包含 Jin10、Lighter、QWeather、Midea、图片供应商、中文字体或页面布局。新增天气插画、调整字号或更换图片目录时,只更新 Worker。
144 KiB 帧不进入 RAM
HTTP 响应先进入 48 字节头缓冲,再以每行 300 字节流过共享接收器。每行同时走两条路径:一条写入非活动 Flash 槽,另一条把 INDEX3 转成 C73 所需的 400 字节 4bpp 行并送入屏幕 RAM。
sequenceDiagram participant W as Render Worker participant R as 共享帧接收器 participant F as 非活动 Flash 槽 participant C as DESPI-C73 W->>R: 48 B PBF1 header loop 480 行 W->>R: 300 B INDEX3 R->>F: 写检查点数据 R->>C: 转换并写入 400 B 4bpp end R->>R: 校验长度、色号与 payload CRC R->>F: 完整回读并提交元数据 R->>C: 发送 0x12 全刷命令
接收中断、非法色号、长度错误、CRC 错误或 Flash 回读失败都会停止提交,C73 不收到 0x12。ESP32 使用两个 256 KiB 槽;1 MiB ESP8266/ESP8285 使用两个 144 KiB raw Flash 槽,并关闭 OTA 与文件系统,把空间留给应用、配置和两张完整画面。
三个平台共用行为,adapter 处理差异
共享 C 模块实现 PBF1、CRC、双槽元数据、重试状态机、配置事务、Boot 动作、错误提示和 C73 行协议。ESP-IDF adapter 提供 ESP32 的 NVS、HTTPS 和分区访问;ESP8266 RTOS SDK v3.4 adapter 提供 ESP8266/ESP8285 的 HTTP/TLS、NVS、分区和固定 HSPI。
低资源设备先通过 SNTP 建立 TLS 时间,使用 2,048 字节 HTTP 接收缓冲持续喂给 48 字节头和 300 字节行接收器。TLS 只保留客户端所需的 TLS 1.2、ECDHE-ECDSA、AES-GCM 与 P-256/P-384;设备只持有一条网络流、两条行缓冲和两个可恢复槽,无需构造完整页面对象。
Boot 短按生成新的逻辑刷新,长按把动作投递给主任务完成布局循环与持久化。成功手动刷新后,从 C73 完成时刻重新对齐当前布局的自动周期;失败保留原周期。中断/按键回调只传递事件,不执行 NVS 或网络操作,避免小回调栈承担阻塞任务。
ESP32 与 1 MiB ESP8266/ESP8285 两条设备路线
ESP32:从完整产品到参考实现
ESP32 是 Paperboard 的第一条路线。双核、4 MiB Flash、ESP-IDF、NVS 与 512 KiB image_cache 让它能够承载 core 的数据源、模型、字体、双布局、图片双槽和真实 C73 驱动。产品语义、面板时序和主机/实屏同源渲染都在这条路线上形成。
切换到 render-only 后,ESP32 不再编译业务采集和 dashboard_core。0.7.2 固件保留 STA、IPv4、SNTP、客户端 TLS、NVS、两个 256 KiB 完整帧槽和默认开启的 C73 adapter;SoftAP、DHCP server、mDNS、IPv6、企业 Wi-Fi、TLS server/self-test 等未使用能力从专用资源 profile 中裁掉。当前 C73 镜像为 901,648 字节,静态 DRAM 36,936 字节,IRAM 69,963 字节。该版本已经刷入目标 ESP32,串口确认保留配置联网与 C73 默认输出;一次网络失败走完本地错误画面全刷,干净在线 PBF、三布局和长期运行仍需继续实屏复验。
配置 schema v4 由设备持有 SSID、密码、Worker Token/endpoint、地区名称、LocationID 和经纬度。没有完整配置时,设备不联网,直接从固件本地生成纯 ASCII 设置页;paperboard_config.py 可以调用 QWeather 搜索,或使用 macOS Core Location 取得坐标后匹配地区,再通过无回显 UART 事务提交。位置与秘密不进入日志。
ESP8266/ESP8285:用 1 MiB 反推最小系统
ESP8266/ESP8285 把约束压到 1 MiB Flash。它无法复制 core 的 JSON、字体和页面合成,却可以运行 render-only:Bearer HTTPS 返回的 144,048 字节帧不进入 RAM,只流过 48 字节头、300 字节 INDEX3 行、2,048 字节 HTTP 缓冲和 400 字节 C73 行。
这条路线已从 Arduino/PlatformIO 迁到 Espressif ESP8266 RTOS SDK v3.4。ESP8266 与 ESP8285 共用一份 1 MiB DOUT 镜像,不启用 OTA、LittleFS、SPIFFS 或 EEPROM;配置进入普通 NVS,PBF1 进入自定义 frame_cache 分区。
| 1 MiB 区域 | 地址 | 大小 | 用途 |
|---|---|---|---|
| bootloader | 0x00000 起 | SDK 生成 | 启动代码 |
| partition table | 0x08000 | 4 KiB | 1 MiB 固定分区表 |
| NVS | 0x09000..0x0EFFF | 24 KiB | schema v4 配置与 CRC |
| PHY init | 0x0F000..0x0FFFF | 4 KiB | 射频初始化数据 |
| factory app | 0x10000..0xB0FFF | 644 KiB | 单应用 0.2.0 镜像,无 OTA |
| frame A | 0xB1000..0xD4FFF | 144 KiB | 第一张完整 PBF1 槽 |
| frame B | 0xD5000..0xF8FFF | 144 KiB | 第二张完整 PBF1 槽 |
| SDK 保留尾部 | 0xF9000..0xFFFFF | 28 KiB | SDK 系统保留区域 |
当前 0.2.0 应用为 557,488 字节,占 659,456 字节 factory 槽的 84.54%,余量 101,968 字节;静态 DRAM 12,148 字节,IRAM 27,404 字节。HSPI 使用 GPIO14 SCK、GPIO13 MOSI、GPIO16 CS、GPIO4 D/C、GPIO5 RESET 与 GPIO12 BUSY。构建和分区已经验证,目标板 TLS、峰值堆栈、C73、掉电恢复与 24 小时运行仍要分别验收。
两条路线共享什么
| 层次 | ESP32 路线 | 1 MiB ESP8266/ESP8285 路线 | 共享合同 |
|---|---|---|---|
| SDK | ESP-IDF 6.0.2 | ESP8266 RTOS SDK v3.4 | 共享业务组件,各自平台 adapter |
| 版本 | 0.7.2 | 0.2.0 | 独立版本线,共享协议与控制语义 |
| 配置 | NVS namespace | 普通 NVS | schema v4 事务提交,秘密临时缓冲清零 |
| 帧缓存 | 2 × 256 KiB 分区槽 | 2 × 144 KiB 分区槽 | 非活动槽写入、完整回读、元数据后提交 |
| 网络 | esp_http_client + mbedTLS | RTOS SDK HTTP client + mbedTLS | SNTP、Bearer、固定 /v1/render、流式接收 |
| 屏幕 | ESP-IDF GPIO/SPI adapter | ESP8266 HSPI adapter | 相同 C73 命令、4bpp 行、BUSY 与 abort 语义 |
| 调度与交互 | GPIO0 + 高有效 GPIO2 LED | GPIO0 + 低有效 GPIO2 LED | 安静时段、独立周期、Light-sleep、短按刷新、长按切布局和阶段灯码 |
ESP32 路线提供完整调试面、宽松的存储和成熟 C73 参考;1 MiB 路线证明内容层移出 MCU 后,同一产品可以落到极低成本、极小资源的 Wi-Fi 芯片。两条路线共用像素和恢复语义,平台差异被限制在网络、存储与 GPIO adapter。
GPT 参与设计的 3D 打印外壳
从机械约束生成参数化 CAD
我们让 GPT 参与约束整理、结构方案和 RhinoCommon 生成器设计,并输入一组可计算条件:GDEY073D46 裸屏外形 170.2 × 111.2 mm、玻璃厚度上界、FPC 宽度和伸出方向、C73 安装面、VHB 厚度、Bambu Lab X1 打印包络、PLA 多料、公差与无支撑方向。概念图只用于辅助观察,不承担参数化设计源的角色。
这些条件写入 paperboard_open_frame.py。脚本生成参数化 BRep、完整装配 3DM、打印布局、STL、参数 JSON、实体尺寸、装配交集与 STL 拓扑结果。修改屏幕净空、泡棉预压、背板厚度或底座角度时,关联零件和装配视图可以一起重建,避免手工 STL 成为唯一设计源。
零五金开放式结构
成品只包含三件 PLA 打印件:前框、整体背板和单件无胶落座底座。结构不使用螺钉、螺母、热熔螺母、金属铰链、弹性卡扣或紧配合销;前框与背板使用既有的 3M VHB 5608N,底座依靠宽松开放鞍座承托,整块屏幕总成可以向上直接取出。
组装后的成品观感

这张产品图用于展示组装后的比例、材质和桌面姿态。生成式渲染对底座梁件做了视觉化简;前框厚度、鞍座位置、三角桁架、横梁和电子板空间以参数化 Rhino 模型及下面两张 CAD 装配图为准。
flowchart TB Bezel["前框<br/>窗口、四角逃角、外圈硬止挡"] FrontPads["前侧低预载泡棉"] Panel["GDEY073D46 裸玻璃屏"] RearPads["后侧低预载泡棉"] Backplate["1.8 mm 整体背板<br/>0.8 mm 连续屏后皮层"] Electronics["C73 + ESP MCU<br/>背面开放电子区"] Base["68° 单件三角桁架底座<br/>双鞍座 + 轻量横梁"] Bezel --> FrontPads --> Panel --> RearPads --> Backplate Backplate --> Electronics Backplate -->|"从上方落座,0.8 mm 名义间隙"| Base
玻璃、FPC 与电子板分别处理
前框边缘、2.0 mm 四角逃角、前后成对泡棉和连续背板共同保护裸屏。有效显示区中央保持悬空,结构胶位于屏幕轮廓之外,装配压力沿 PLA 外圈硬止挡闭合,不通过胶带或背板翘曲夹紧玻璃。
背板底部使用贯通边缘的 36 mm 开放槽,给 25.5 mm FPC 留出自然下伸与弯曲空间。C73 是背面无器件的单面板,两片 20 × 5 mm 5608N 直接把它粘到背板平整电子侧;ESP32 放在右下开放区,杜邦线保留自然弯折和应力释放。
为 FDM 打印方向设计结构
桌面姿态固定为 68°。左右两组相同的 6 mm 宽三角桁架使用 2.4 mm 斜梁/底梁,配一根 146 × 2.4 × 2.4 mm 轻量横梁;闭合侧板从底边逐层收拢,底座以桌面接触面朝下即可无支撑打印。底座体积从厚底座方案的 12.755 cm³ 降到 8.253 cm³。
| 打印件 | 结构任务 | 当前几何 |
|---|---|---|
front_bezel.stl | 保护显示面边缘、建立窗口与四角逃角、承载前侧泡棉 | 23.497 cm³ |
backplate.stl | 保护玻璃背面、提供 FPC 通道、结构胶位与平整电子安装面 | 23.924 cm³ |
drop_in_cradle_base.stl | 68° 桌面姿态、双鞍座、三角桁架与横向稳定 | 8.253 cm³ |
| 三件合计 | 完整开放式桌面结构 | 55.674 cm³ |
切片基线为 Bambu Lab X1、0.4 mm 喷嘴、PLA、0.12 mm High Quality、0.20 mm 首层、4 道墙、15% Gyroid 和关闭支撑。生成器按每个相关打印表面向材料侧预留 0.20 mm 多料预算,打印件间配合同时扣除两侧误差;需要调整时修改独立参数并重新生成,不整体缩放 STL。
GPT 在这里承担了从机械语言到可执行几何规则的转译:屏幕尺寸、公差、玻璃受力、胶带拆卸、FPC 路径、重心和打印方向进入同一份代码模型,外壳因此能和电子方案一样被版本化、重建和审查。
架构迭代迁移了什么
| 技术阶段 | 迁移内容 | 保留下来的能力 |
|---|---|---|
| 端侧 core | ESP32 完成采集、模型、排版和送屏 | 扫描线渲染、七色顺序、最后有效状态、C73 时序 |
| 图片 Worker | 图片获取、裁切与七色量化移到云端 | PBF1 图片合同、双槽缓存、photo 像素基线 |
| 全帧 Worker | 字体、天气行动面板、信息布局和图片合成为完整页面 | 多字号中文、三种布局、云端快速视觉迭代 |
| 设备地区与调度 | 地区四元组、配置事务、安静时段和布局周期进入共享合同 | 设备自主位置配置、无配置 splash、成功手动刷新周期重对齐 |
| render-only | MCU 删除业务数据源和页面实现 | 低内存流播放、双 CRC、原子缓存、本地错误覆盖 |
| 多 MCU | 平台无关帧/缓存/C73 模块配 ESP-IDF 与 ESP8266 RTOS SDK adapter | 独立版本线复用同一 PBF1、配置与控制语义 |
| 多云 adapter | createWorker() 共享应用核心组合 Cloudflare DO/Images/Wasm 与 EdgeOne Node/Sharp/JS | 保持同一 HTTP/PBF1 合同,同时显式保留状态能力差异 |
| 参数化外壳 | GPT 把机械约束写入 RhinoCommon 生成器 | 前框、背板、底座、3DM/STL/JSON 同步重建 |
core 仍然保留参考价值。photo 的 C 渲染输出可以和 Worker 做逐像素比较,主机 sink 继续复现屏幕像素路径,最早形成的刷新与恢复语义也保护着 render-only。当前主线只维护一套页面实现:内容进入 Worker,可信播放进入共享固件,物理安装进入参数化外壳。
一套围绕稳定边界组织的软硬件系统
Paperboard 的稳定边界是 800×480 七色完整帧。边界上方,共享 Worker 核心可以持续更换字体、天气视觉、数据源和图片策略,再由 Cloudflare 或 EdgeOne adapter 组合平台能力;边界下方,三种 MCU 复用帧校验、双槽提交和 C73 输出;设备外部,GPT 参数化外壳把裸玻璃、FPC、电子板和打印方向纳入同一套结构约束。
这条路径让一块更新缓慢、内存敏感的电子纸获得了云端页面的迭代速度,同时保留嵌入式设备需要的确定性:收到完整帧才刷新,断网仍有画面,换 MCU 不重写业务页面,改外壳尺寸也不从网格模型重新开始。



