把一张图片转换成 .film 文件,通过 USB 虚拟串口直接上传到 FrameFilm Dock,上传完成后设备会自动刷新墨水屏显示该图片。
位置说明:本 skill 与配套脚本一起放在项目
tools/framefilm-dock-upload/下。
.film 文件列表不适用:给冰箱贴(FrameFilm / Pro / Max)传图——那些机型没有 USB 串口,只有蓝牙,请用 tools/ForFilm 网页工具。
pip install pyserial Pillow
server/filmhub/backend/app/services/film_convert.py,脚本会从自身位置和当前目录向上自动查找仓库根。若在仓库外运行,用环境变量指定:
FRAMEFILM_REPO=<仓库根> python tools/framefilm-dock-upload/scripts/dock_upload.py ...
脚本路径:tools/framefilm-dock-upload/scripts/dock_upload.py
# 上传一张图片(自动查找串口、自动转换、自动校验)
python tools/framefilm-dock-upload/scripts/dock_upload.py photo.jpg
# 指定串口 / 指定设备上的文件名 / 换抖动算法
python tools/framefilm-dock-upload/scripts/dock_upload.py photo.jpg --port COM7 --name holiday.film --dither atkinson
# Atkinson 增强(与 ForFilm / 小程序默认算法一致,观感最好)
python tools/framefilm-dock-upload/scripts/dock_upload.py photo.jpg --dither atkinson_enhanced
# 只列出设备上已有的文件
python tools/framefilm-dock-upload/scripts/dock_upload.py --list
# 上传现成的 .film(跳过转换)
python tools/framefilm-dock-upload/scripts/dock_upload.py --film out.film --name demo.film
# 调整观感
python tools/framefilm-dock-upload/scripts/dock_upload.py photo.jpg --strength 120 --contrast 110 --brightness 5
python tools/framefilm-dock-upload/scripts/dock_upload.py photo.jpg --bw # 黑白模式
| 参数 | 说明 | 默认 |
|---|---|---|
image |
待上传图片(jpg/png/bmp/webp),自动按竖版画布 568×760裁剪填充,再逆时针旋转 90° 送屏(Dock 是竖屏使用) | — |
--film |
直接上传现成 .film,跳过转换(不做旋转) |
— |
--port |
串口号;不填则按 VID=0x303A / PID=0x8000 自动查找 | 自动 |
--name |
设备上的文件名,自动补 .film 后缀 |
图片名 |
--dither |
抖动算法:floyd_steinberg/atkinson/atkinson_enhanced/stucki/jarvis/bayer/gamma_floyd_steinberg/adaptive/smart_adaptive/none |
floyd_steinberg |
--strength |
抖动强度 0-200 | 80 |
--contrast / --brightness / --saturation |
画面调整 | 100 / 0 / 100 |
--bw |
黑白(2 色)模式 | 关 |
--rotate |
内容旋转角度:90=Dock 竖屏(默认)/ 0=面板原生横屏 / 180 / 270 |
90 |
--delay |
每 192 字节块的间隔(秒),用于限速 | 0.002 |
--save-film |
把转换结果另存一份 .film |
— |
--list |
只列设备文件后退出 | 关 |
--no-verify |
上传后不回头校验 | 关 |
pip install pyserial Pillow)。--save-film 留一份 .film 便于排查(可选)。| 现象 | 原因与处理 |
|---|---|
未找到 FrameFilm Dock 串口 |
dock 没插好/没枚举出 COM 口;或该口是 ESP32 内置 USB-Serial-JTAG(PID 0x1001)。确认固件里 SYS_FUNC_USB_CDC_EN=1,必要时用 --port 手工指定 |
错误: 文件大小 ... Dock 要求 215872 字节 |
用了非 Dock 的转换参数;Dock 固定 760×568、6 色、4bpp(32+760×568/2=215872) |
找不到仓库中的转换实现 |
在仓库外运行了;设置 FRAMEFILM_REPO 或回到仓库内运行 |
| 上传后屏幕没变化 | 检查文件列表输出;确认名字带 .film 后缀(设备只把 .film 计入列表);EPD 刷新本身要几秒 |
| 文件传上去但花屏/错位 | 图片转换参数不匹配该机型;确认握手时收到的是 panel=0x06 |
| 上屏内容横着/倒着 | 旋转角不对:默认 --rotate 90 适配 Dock 竖屏;设备改成横放时试 0/180/270 |
| 大文件传输容易失败 | 加大 --delay(如 0.005)降速重试,避免设备接收队列溢出丢数据 |
HEAD(0x55) | CH | LEN | DATA | SUM,SUM 为前面所有字节之和;与 BLE 链路完全一致。FILE_START(0x03) → FILE_NAME(0x00) → FILE_LEN(0x01,大端 4 字节) → FILE_DATA(0x02,分块,≤255 字节/帧) → FILE_STOP(0x04)。0x42 的应答为 panelId(1) + width(2, 大端) + height(2, 大端),用于确认对端是 Dock。--rotate,对齐小程序 film-utils.js 的 isPortraitPanel 处理)。0x06 会按设备上每个文件回一帧:id(1) + nameLen(1) + name(nameLen),帧之间有约 50ms 间隔。scripts/ae_dither.py(不依赖服务端算法实现),与 ForFilm convert.js 的 atkinsonEnhancedQuantize / 小程序 dither-advanced.js 逐像素一致——CIELAB 加权选色 + 蓝/青区双 LUT 查表 + 墨水屏校准色残差 + Atkinson 六邻域 1/8 扩散;LUT 数据(786432 B 修正表 + 262144 B 选色表)直接复用仓库里的 tools/ForFilm/js/atkinson_enhanced_lut.js,取不到时退化到纯 CIELAB 选色。六色专用,与 --bw 互斥,忽略 --strength。