11 KiB
lyfPrinter 上位机 APP 迁移到 ESP32(ESP-IDF) 实施文档
本文档基于当前 Android 上位机 APP 实现(MainActivity + ECBLE + BlueHandler + 各预览页),给出一套可落地的 ESP32/ESP-IDF 迁移方案。
适用目标:
- 让 ESP32 作为 BLE Central 连接
lyfPrinter热敏打印机 - 复用现有私有打印协议(
0x00~0x07、0xA0~0xA4) - 在 ESP32 上实现打印、状态监控、标签纸定位、OTA(打印机固件)能力
1. 现有 APP 能力基线(迁移输入)
Android 端关键行为:
- 扫描过滤设备名
lyfPrinter - 连接 BLE 外设并绑定特征:
- Notify:
0000fff1-0000-1000-8000-00805f9b34fb - Write:
0000fff2-0000-1000-8000-00805f9b34fb
- Notify:
- 请求
MTU=247 - 向写特征发送私有协议帧,接收 Notify 回包并解析状态
- 打印流程:
- 输入内容转 384 点宽位图
- 二值化
- 行取模(8 像素打包 1 字节)
- 按 240 字节数据体分包(整帧 244 字节)
- 按应答节奏发送完毕
- 打印前后控制:
- 开启/关闭 VH 电源
- 设置热参数(浓度映射)
- 结束后走纸留白
可参考:
APP_BLE打印控制实现说明.mdBLE_PAIRING_PARAMETERS.md
2. 迁移目标定义
建议按两级目标推进:
2.1 M1(必做,先跑通)
- 扫描/连接/重连
- 状态查询与解析(纸张、电量、温度)
- 基础打印(接收已编码点阵数据后发送)
- 标签纸 gap 定位与偏移读写
2.2 M2(增强)
- ESP32 端文本渲染与位图编码
- 图片/二维码本地生成与打印
- 打印机 OTA 完整流程
- UI 层(按键、串口命令、Web、LCD+LVGL 任一)
3. ESP-IDF 技术选型建议
推荐:
- ESP-IDF
v5.1+或v5.2+ - BLE Host:
NimBLE(内存占用更低,Central 场景稳定)
备选:
- Bluedroid 也可实现,但本项目建议优先 NimBLE。
编译配置建议(menuconfig):
- 启用 BLE(NimBLE)
- 增大 GATT MTU 到
247 - 提升 BT controller/host 内存预算
- 打开 NVS(存储设备地址、标签偏移、最近配置)
4. Android 到 ESP32 模块映射
| Android 类 | 迁移后模块 | 说明 |
|---|---|---|
MainActivity |
printer_ble_scan.c + app_cli.c |
扫描、设备选择、连接触发 |
ECBLE |
printer_ble_client.c |
GATT 连接、服务发现、通知订阅、写特征 |
BlueHandler |
printer_proto.c + printer_engine.c |
协议封装/解析、状态机、分包与流控 |
DataConvertTool |
image_raster.c |
二值化、384宽缩放、行取模 |
| 各 PreviewActivity | content_renderer_*.c |
文本/二维码/模板渲染 |
otaUpdateActivity |
printer_ota.c |
A0~A4 升级流程 |
建议目录:
components/
printer_ble/
include/printer_ble_client.h
printer_ble_client.c
printer_proto/
include/printer_proto.h
printer_proto.c
printer_engine/
include/printer_engine.h
printer_engine.c
image_raster/
include/image_raster.h
image_raster.c
printer_ota/
include/printer_ota.h
printer_ota.c
main/
app_main.c
app_cli.c
app_config.c
5. BLE 迁移要点(与 Android 行为对齐)
5.1 扫描与过滤
逻辑对齐 Android:
- 仅处理设备名
lyfPrinter - 保存 MAC、RSSI、最后发现时间
- 支持“按 MAC 直连”模式(量产更稳)
5.2 建链流程
scan -> connectdiscover service/characteristics- 找到
FFF1/FFF2 - 对
FFF1写 CCCD0x0001开启 Notify - 交换 MTU(目标 247)
5.3 写入策略
与 Android 保持一致:
- Write Without Response
- 发送内容使用十六进制帧字节序列
5.4 断线策略
建议状态机:
DISCONNECTEDSCANNINGCONNECTINGDISCOVERINGREADYPRINTING
断线后:
- 延时 300ms 重连
- 最多重试 4 次(与 Android 一致)
- 失败后回
SCANNING
6. 私有协议迁移(核心)
6.1 指令定义
// 打印
#define CMD_POWER 0x00
#define CMD_GET_STATUS 0x01
#define CMD_SET_DISTANCE 0x02
#define CMD_SET_PARAM 0x03
#define CMD_SEND_DATA 0x04
#define CMD_GAP_MOVE 0x05
#define CMD_GET_LABEL_OFFSET 0x06
#define CMD_SET_LABEL_OFFSET 0x07
// OTA
#define CMD_BOOT_JUMP_BOOT 0xA0
#define CMD_BOOT_ERASE_PAGE 0xA1
#define CMD_BOOT_WRITE_DATA 0xA2
#define CMD_BOOT_JUMP_APP 0xA3
#define CMD_BOOT_GET_VERSION 0xA4
6.2 帧结构
常规帧:
[addr:1][func:1][lenH:1][lenL:1][payload:len][checksum:optional]
校验和规则:
- 从
addr到 payload 最后一个字节累加,取低 8 位
与 Android 对齐点:
- 控制类命令一般带校验
CMD_SEND_DATA(0x04)数据帧可不带校验(当前 Android 逻辑)
6.3 回包解析
按 func 分发处理:
0x01:设备状态0x04:数据发送 ACK,用于释放“发送下一包”锁0x05:标签定位完成0x06:标签偏移读取结果0xA1~0xA4:OTA流程响应
7. 打印引擎迁移设计
7.1 打印参数映射
浓度映射(与 Android 对齐):
较淡 -> 1000中等 -> 1500较浓 -> 2000最深 -> 3000
打印前序列:
CMD_POWER开CMD_SET_PARAM(hot_mode, move_time, hot_time)- 分包发送
CMD_SEND_DATA
打印后序列:
CMD_POWER关CMD_SET_DISTANCE(12.5mm)留白走纸
7.2 分包规则
与 Android 同步:
- 单帧上限 244 bytes
- 协议头 4 bytes
- 数据体 240 bytes(384 点宽 -> 48 bytes/行 -> 每包 5 行)
流控策略:
- 发出数据包后等待
0x04ACK - 首包后可短延时一次
- 后续每包 ACK 驱动
- ACK 超时建议
2~5s,整任务超时30s
7.3 打印状态门限
建议保持一致:
- 缺纸:禁止打印
- 电量
<=40%:禁止打印 - 温度
>=60℃:禁止打印
8. 图像处理迁移策略(重点)
ESP32 内存有限,不建议一次性处理大图。推荐 3 种模式:
8.1 模式 A(推荐首版)
外部(PC/手机/云)先生成“384宽 + 二值化 + 行取模”数据,ESP32 仅负责协议发送。
优点:
- 开发最快
- ESP32 负载最低
8.2 模式 B(文本优先)
ESP32 只做文本/模板渲染,不做复杂图片解码。
优点:
- 资源可控
- 适合收据、标签、测试页
8.3 模式 C(全本地)
ESP32 本地做图片解码+缩放+二值化+取模。需外接 PSRAM,建议 ESP32-S3。
9. FreeRTOS 任务模型建议
建议拆成 4 个任务 + 2 个队列:
ble_task- 处理 GAP/GATT 事件
proto_task- 帧收发、ACK 管理、回包解析
print_task- 打印流程状态机(预热、分包、收尾)
cmd_task- 外部命令入口(串口/Web/UI)
队列:
q_cmd:业务命令(print/status/ota)q_evt:BLE/协议事件(connected/ack/timeout)
同步对象:
EventGroup:READY、ACK_04、GAP_OK、OTA_ACKMutex:写特征互斥
10. 关键 C 接口建议
// BLE
esp_err_t printer_ble_start_scan(void);
esp_err_t printer_ble_connect_by_name(const char *name);
esp_err_t printer_ble_write(const uint8_t *data, size_t len, bool no_rsp);
// 协议
size_t printer_proto_build_frame(uint8_t addr, uint8_t cmd,
const uint8_t *payload, uint16_t len,
bool with_checksum, uint8_t *out);
void printer_proto_handle_notify(const uint8_t *data, size_t len);
// 打印引擎
esp_err_t printer_engine_print_raw(const uint8_t *raw, size_t len, int hot_time);
esp_err_t printer_engine_get_status(void);
esp_err_t printer_engine_gap_move(void);
esp_err_t printer_engine_set_label_offset(uint8_t value);
// OTA
esp_err_t printer_ota_run(const uint8_t *fw, size_t len);
11. OTA 迁移(针对打印机固件升级)
流程对齐 Android:
A0跳 BootA1擦页(按 1024 bytes 估页数)A2写入(单包 236 bytes 数据)A3跳回 AppA4读版本确认
建议:
- 每步都有超时和重试(每步 3 次)
- 断电恢复策略:记录 last packet index
12. 分阶段实施计划
Phase 1(3~5天)
- BLE 扫描连接 + 订阅通知 + MTU
- 实现
GET_STATUS - 实现
POWER / SET_PARAM / SEND_DATA / SET_DISTANCE - 用固定测试页 raw 数据打印
验收:
- 可稳定打印 50 次无死锁
Phase 2(3~7天)
- 文本模板渲染
- 标签定位/偏移
- 设备参数保存(NVS)
验收:
- 标签纸对齐稳定
Phase 3(3~7天)
- OTA 全流程
- 回归测试与异常恢复
验收:
- 升级成功率 > 99%
13. 测试清单
基础通信:
- 扫描到
lyfPrinter - 连接后找到
FFF1/FFF2 - Notify 有数据
- MTU 协商到 247
协议一致性:
- 每条命令帧结构正确
- 校验和与 Android 一致
0x04ACK 能正确驱动分包
打印质量:
- 浓度 4 档效果一致
- 长图连续打印无丢行
- 结束留白距离稳定
异常场景:
- 打印中断线后可恢复
- 缺纸/低电/高温阻断生效
- OTA 超时重试有效
14. 常见问题与规避
-
write without response太快导致对端缓存溢出
规避:严格按0x04 ACK节奏发送。 -
连接成功但收不到 Notify
规避:确认 CCCD 写成功且未被后续重连覆盖。 -
打印偏移不稳定
规避:先GAP_MOVE,再按偏移走纸,偏移值写入 NVS。 -
ESP32 内存不足
规避:首版采用“外部预编码 raw 数据”,不在设备端做重图像处理。
15. 最小可用迁移路径(建议)
如果你希望最快落地,按下面最小路径:
- 先只做 BLE + 协议 + raw 发送
- 用串口命令输入
print_raw/status/gap/offset - 打通后再逐步补 UI 和本地渲染
这样可以最短时间复用 Android 的核心协议能力,并把主要风险收敛在 BLE 链路与分包流控上。
16. 附:协议发送伪代码
void do_print(const uint8_t *raw, size_t raw_len, int hot_time) {
send_cmd_power(true); // 0x00
send_cmd_set_param(1, 2, hot_time); // 0x03
const size_t chunk = 240;
size_t off = 0;
int idx = 0;
while (off < raw_len) {
size_t n = (raw_len - off > chunk) ? chunk : (raw_len - off);
send_cmd_data(raw + off, n); // 0x04
if (idx > 0) {
if (!wait_ack_04(3000)) {
// 超时处理:重发或中止
break;
}
} else {
vTaskDelay(pdMS_TO_TICKS(10));
}
off += n;
idx++;
}
send_cmd_power(false); // 0x00
send_cmd_set_distance(12.5f); // 0x02
}