把喜欢的瞬间,放在身边。 基于 ESP32-C3 与华为拆机四色电子墨水屏(768×552 黑/白/黄/红)的电子照片 / 电子拍立得 / 挂件。
墨印 · MoInk 是一台可以随身携带的「电子拍立得」:手机或电脑连接设备热点后,在网页里完成选图 → 裁剪 → 风格化 → 文字排版,一键推送到 768×552 的华为拆机四色墨水屏(黑/白/黄/红)上。断电后画面永久保持,功耗极低,适合做成桌面相框、冰箱贴或随身挂件。
- 不用 App:设备自带 WiFi 热点与 captive portal,手机连上即自动弹出创作页面
- 不用服务器:所有图像合成在浏览器本地完成,设备只负责「接收 → 刷屏 → 睡觉」
- 长续航:传图完成即深度睡眠,GPIO5 按键唤醒,静态几乎零功耗
| 功能 | 说明 |
|---|---|
| 🖼️ 四色电子照片 | 黑/白/黄/红四色墨水屏(华为拆机 768×552 屏),支持拍立得白边、文字叠加 |
| ✂️ 浏览器内裁剪 | 内嵌 Cropper.js:捏合缩放、自由旋转、90° 贴合、EXIF 方向自动摆正 |
| 🎨 eink-warm 暖调算法 | 专为四色屏设计的色彩映射 + 8 种误差扩散抖动算法,肤色自然不偏红 |
| ✏️ 所见即所得文字编辑 | 多文字对象、拖动/缩放/旋转/描边/底色,画布实时渲染,四色文字直出 |
| 📱 响应式双模式 | 拍立得(图像为主)/ 便签(文字为主)双模式;手机单列、桌面双栏预览 |
| 🔁 一个入口搞定升级 | 固件与控制页走同一个「上传并升级」入口,上传后按内容自动识别类型:.bin = 固件升级(顺带更新控制页、重置设置、保留热点名与密码、自动重启),.html = 只热更控制页(CRC32 校验失败自动回退内嵌页) |
| ⚙️ 设备管理 | 热点名称/密码修改、电源管理(超时后休眠或关机断电)、残影清理、恢复出厂、电池电压检测 |
| 🔋 低功耗 | 传图后自动深度睡眠(Deep Sleep),按键唤醒,适配电池供电挂件形态 |
| 🖥️ 多批次屏适配 | A0 / A1 两种拆机屏批次驱动可切换;A1 提供两档写入模式,默认「768×552 顺序直写」(1:1 覆盖可见区,零重采样、无白边) |
- 画面合成 100% 在浏览器端:固件只做「热点 / 接收 / 刷图 / 休眠 / 唤醒 / 升级」,保持极小体积(Flash 占用约 61%),控制页通过热更通道独立迭代
- eink-warm 图像算法(逐行冻结):饱和度幂律映射 + 线性光抖动,解决四色屏「肤色发红」「画面发白」两大通病;有跨语言(Python/JS)逐像素一致性门禁
check_algo.py(12/12 MATCH) - 严谨的帧协议:16 字节帧头(魔数 / 版本 / 面板 / 尺寸 / CRC16-CCITT-FALSE)+ 2bpp 载荷(768×552 → 105,984B;A1 原生 800×600 → 120,000B),设备端逐字节校验并做几何自洽检查,坏帧直接拒绝
- 三重自愈:控制页 CRC32 校验失败自动回退内嵌副本;OTA 双分区 + 防降级;固件升级强制重置设置但保留热点名与密码,不会升完就失联
- 并发安全:刷屏 / 残影清理共用帧缓冲互斥锁;HTTP 上传全链喂电源看门狗,避免「传图中途深睡」
| 部件 | 规格 |
|---|---|
| 主控 | ESP32-C3 SUPERMINI(RISC-V 单核,无 PSRAM) |
| 屏幕 | 华为拆机 768×552 四色电子墨水屏(黑/白/黄/红,2bpp) |
| 唤醒按键 | GPIO5 → GND(低电平;单击唤醒、双击切图、长按 20s 恢复出厂) |
| 电池检测 | GPIO0(ADC1_CH0,1M:1M 分压) |
| 屏幕接线 | SCK=4 / MOSI=6 / CS=7 / DC=1 / RST=3 / BUSY=10 |
详细步骤请看 新手入门教程,这里只列关键三步:
# 1. 编译固件(需要 Python + PlatformIO)
python -m platformio run -e moink
# 2. USB 刷机(或直接用 flash_kit/ 一键刷机包)
python tools/flash_moink.py
# 3. 手机连接设备热点 MoInk-XXXX,浏览器自动弹出创作页也可以免编译直接使用 flash_kit/ 目录里的一键刷机包(含预编译固件;没装 Python 会引导自动安装),或到 GitHub Releases 下载刷机包。
- 连接:设备上电后创建热点
MoInk-XXXX(XXXX 为 MAC 后 2 字节),手机连接后自动弹出创作页(或浏览器访问http://192.168.4.1) - 拍立得模式:选图 → 裁剪调整 → 可选风格/留白/文字 → 「上传并刷新屏幕」
- 便签模式:直接输入文字(可加描边、底色、四色字体)→ 上传
- 设备设置:主页右上角 ⚙ 进入 —— 热点改名、休眠时间、残影清理、恢复出厂
- 更新:设置页「上传并升级」一个入口 —— 传
.bin= 固件升级(顺带更新控制页、重置设置、保留热点名与密码、自动重启),传.html= 只更新控制页(不重启)
moink/
├── src/ # 固件源码(ESP-IDF 6.1 + PlatformIO)
│ ├── main.c # 主入口:初始化 / 循环 / 睡眠与唤醒
│ ├── epd_drv.c # 墨水屏驱动(A0 隔行映射 / A1 顺序直写两档模式)
│ ├── frame.c # 帧协议接收与校验(CRC16)
│ ├── ota_web.c # HTTP 服务 / 统一升级入口(内容嗅探分流)/ 控制页热更
│ ├── netif_ap.c # 纯 SoftAP 网络接口
│ ├── captive.c # captive portal DNS
│ ├── settings.c # NVS 设置存储
│ └── power.c # 电源管理 / 电池 ADC / 深睡
├── page/ # 创作页面(单文件 HTML,可独立热更)
│ ├── index.html # 当前工作版本
│ └── vendor/ # 内嵌第三方库(Cropper.js v1.6.2)
├── tools/ # 开发与门禁工具
│ ├── check_algo.py # 算法冻结比对(逐像素一致性门禁)
│ ├── smoke_page.js # 页面结构冒烟测试
│ ├── make_index_header.py # 页面 → C 头文件(固件内嵌)
│ ├── flash_moink.py # USB 刷机
│ └── package_bins.py # 打包发布 bin
├── flash_kit/ # 一键刷机包(含预编译固件,免编译体验)
├── docs/ # 文档(入门教程 / 契约 / 变更日志)
├── legacy_ref/ # 旧版参考实现与 eink-warm 算法真源(只读,不迁移)
├── partitions.csv # 分区表(nvs/ota_0/ota_1/web)
├── platformio.ini # PlatformIO 配置(env=moink)
└── CHANGELOG → docs/CHANGELOG.md
| 文档 | 内容 |
|---|---|
| docs/入门教程.md | 新手图文教程:从安装到显示第一张照片 |
| docs/CONTRACT.md | 锁定契约:帧格式 / 色码 / 几何 / 引脚 / API / 版本判定 |
| docs/CHANGELOG.md | 版本更新记录(R1.2.0 起固件与控制页共用单一版本号) |
- 色码固定:
0x00=黑 0x01=白 0x02=黄 0x03=红(实测标定,与部分参考实现相反,改动前必读docs/CONTRACT.md) - 几何固定:帧缓冲为逻辑图 180° 旋转存储,
packIdx与驱动严格对应 - 固件不做任何图像处理——所有像素由页面在浏览器端合成完毕后才发送
- 请勿在固件中添加蓝牙(ESP32-C3 无 PSRAM,蓝牙会挤占 WiFi 缓冲)
私有项目,版权保留。如需开源请先补充 LICENSE 并检查 legacy_ref/ 中第三方素材的授权情况。