前几天翻到一个仓库,papermono-shopping-list,作者 id 是 seamusc,把 M5Stack 的 PaperMono 做成贴在冰箱门上的家庭购物清单:设备挂在冰箱门上,手机浏览器里加东西,两边同步。作者自己写明了这是个人项目,只在他家一台设备上跑,能工作,但不是产品。
它做了什么我不从头讲,这种事看一眼 README 比听人转述快。我想拆开看的只有两处:刷屏策略和联网策略。电子纸加电池的设备,真正稀缺的动作就两个——刷一次屏、开一次 Wi-Fi,剩下的架构说到底都在回答「这两笔预算什么时候花」。这个项目让我愿意记一笔的地方在于,它把这两笔预算都写成了显式规则,而不是散在代码里凭手感调。
硬件底子是 ESP32-S3,配一块 3.97 英寸 480×800 的电子纸触摸屏,固件用 C++/Arduino 写,大概 2400 行。这个量级我觉得挺关键:大到能覆盖触摸、键盘、同步、电源几块,又小到能整个读完,不是那种「跑是跑起来了但没人看得懂」的规模。
刷屏这块,点击、滚动、输入全走局部刷新,不闪屏,但每做 10 次局部刷新就强制来一次全屏。逻辑大意就是一个计数器:
partial_refresh_count++
if (partial_refresh_count >= 10) {
full_refresh();
partial_refresh_count = 0;
}
真正的实现要跟触摸驱动的状态机对起来,核心就是这个。局部刷新快、不闪,代价是残影慢慢积累;周期性的全屏刷新把残影抹掉,顺带也算护屏。10 这个数字是拍出来的还是试出来的我没去问,但它显然是个可调量,不是写死的物理常数。
有个细节我一开始没看懂:它的灰度是拿 1 位点阵图案画出来的。翻了一会儿文档才想明白——走面板自带的灰阶模式的话,局部刷新和全屏刷新对同一块灰的呈现不一样,强制全屏那一瞬间颜色会跳一下。用空间上的抖动图案替掉灰阶模式,两种刷新下的观感就一致了。这个坑我估计自己动手写的时候还得再踩一次才记得住,先记下来。
联网那块的克制程度更高:Wi-Fi 只在同步的时候开。触发有三条路——网页里设的日程(比如夜里不同步、周末更勤)、距上次同步超过 5 分钟之后的随便一点、以及编辑完立刻同步。离线也不影响用,编辑排队写进闪存,等下次同步补上。所以清单、建议、勾选、添加这些在没信号的超市里照样能用,比「实时同步」实在得多。
想复现的话,顺序大概是这样:
- 先把服务器起起来。在
server/下建虚拟环境、装项目、把归类器关掉,再让 uvicorn 在 8000 端口监听:
python -m venv .venv
source .venv/bin/activate
pip install -e .
SHOPPING_LIST_CLASSIFIER=none uvicorn ... --port 8000
具体的模块路径去看 docs/server.md,我不在这儿抄一遍。归类器设成 none,是因为默认那条路会把新条目发给 Claude 归类,想先不接外部服务就把它关掉。
-
手机浏览器打开服务器的 8000 端口,进 Aisles,按你平时逛超市的走路顺序把过道加一遍。这个顺序在手机上设一次就够,设备屏幕和手机屏幕都会跟着它排。
-
轮到固件之前,先把设备闪存备份下来。出厂校准数据在里面,烧没了补不回来,
docs/firmware.md里写了备份、进下载模式和从浏览器烧录的整套流程。 -
装 PlatformIO,用 USB-C 连上 PaperMono。
firmware/是个正常的 PlatformIO 工程,编译烧录照常规走。
验证也不复杂:点一下屏幕 → 如果距上次同步已经超过 5 分钟,应该能看到它开始同步;在手机上加一条,设备等一会儿应该刷出来;把 Wi-Fi 关掉再加两条,重新联网后这两条应该补上去。勾选和键盘建议这几样,断网状态下应该照常工作。这几步自己跑一遍,比读一遍代码更能确认那两笔预算到底花在哪。
顺带说一句,这个仓库的 docs 分得挺细:硬件、固件、架构、服务器、HTTP API、设计笔记各一份。硬件那份里有引脚图和 I²C 地址,但我觉得最值钱的是最后那节「遇到的怪问题」——这种内容教程里没人写,只有真踩过的人才写得出来。
几个我已经看到的坑,提前记一下:固件没有设备端的 Wi-Fi 设置界面,凭据是编译时写进去的,换个路由器就得重烧一次;服务器没有认证,只能放局域网,或者塞在带认证的反向代理后面;作者只在 PaperMono C153 这一个型号上测过。另外固件的板级支持、电子纸和触摸驱动、键盘布局和控件风格都是从 andrecolz 的 MonoMesh 改过来的,所以整个仓库跟着用 GPLv3,想改完再发的话这条得注意,docs/design-notes.md 里列了具体复用了哪些部分——这种「抄了哪几块写清楚」的做法本身也值得学一手。
过道归类那里还有个处理我想单独说一句:服务器第一次见到某个条目名,可以选地把名字发给 Claude 归到某个过道,但如果你后来手动把它挪到别的过道,这条修正会被保留下来。手动改的结果压过自动结果,看起来不起眼,做小工具的时候最容易漏——漏了之后每次同步都会把你的修改冲掉,用两天你就不想用了。
划重点:一是电子纸加电池的设备,先想清楚刷屏和联网这两笔预算什么时候花,再动手写界面;二是灰度用点阵图案画、局部刷新配周期性的全屏刷新,这两条可以原样搬走;三是改编别人的固件时,把「哪几块是别人的」写进文档里,比事后补授权省事得多。你可以拿手上那块电子纸试一下局部刷新加周期性全屏的组合,顺手把 10 改成 5 或 20,感受一下残影到底多久开始碍眼。