feat: add Chinese 12x12 bitmap font (21075 glyphs) and fix boot gate

- Add ChineseFont12x12.h: U+4E00-U+9FFF CJK coverage, 535KB flash

- Add gen_chinese_font.mjs: @napi-rs/canvas based font generator tool

- Enable CJK rendering in MessageRenderer and CannedMessageModule

- Remove boot confirmation gate (required 2s button hold, caused shutdown loop)

- Update partition table: app 2.75MB, OTA 192KB, spiffs 1MB

- Update CHANGELOG
This commit is contained in:
2026-03-29 21:08:34 +08:00
parent 60caa476c0
commit 069630999e
12 changed files with 21639 additions and 96 deletions
+58
View File
@@ -136,3 +136,61 @@
4. `commitMultiTap()` 简化:不再有 index 0 是数字的特殊情况
5. `drawFrame` preview 逻辑同步简化 + 光标 +1
- 编译验证通过(SUCCESS 36s
## UTF8 常用中文字库
- **需求**ESP32-C3 + SH1106 OLED 上显示中文文本
- **方案**:12×12 点阵位图字库,XBM 格式(row-major, MSB first
- **生成工具**`tools/gen_chinese_font.mjs`Node.js + @napi-rs/canvas
- 字体源:Windows msyh.ttc(微软雅黑)
- 字符集:U+4E00-U+74FFCJK 常用汉字)+ 中文标点 + 全角数字/字母 = 10067 字形
- 每字形 2 bytes codepoint + 24 bytes bitmap = 26 bytes
- **输出文件**`src/graphics/fonts/ChineseFont12x12.h`
- 数据数组:`cfont12_data[]`PROGMEM,按 codepoint 升序排列)
- 查找函数:`cfont12_find(cp)` — 二分搜索,O(log n)
- 渲染函数:`cfont12_draw(display, x, y, cp)` — 用 setPixel 逐像素绘制
- UTF-8 解码:`cfont12_utf8(p, &len)` — 返回 codepoint 和字节长度
- 混合绘制:`cfont12_drawStr(display, x, y, s)` — ASCII 用 drawStringCJK 用 cfont12
- **集成点**`CannedMessageModule.cpp` drawFrame FREETEXT 部分
- text token 渲染:逐字符判断是否 CJKCJK 用 cfont12_drawASCII 用 drawString
- word wrap 宽度计算:CJK 字符宽度 = CFONT_W(12)ASCII 用 getStringWidth
- **Flash 占用**+256 KBdata 段),剩余 ~1.59 MB4MB flash
- 编译验证通过(SUCCESS 51s
## ⚠️ 分区表修复 — 固件超出 app 分区无法启动
- **现象**:烧录后 ESP32-C3 反复重启,卡在 ROM bootloader,无用户程序日志
- **根因**256KB 中文字库使固件从 ~2.31MB 增长到 ~2.57MB,超出原 app 分区 0x250000 (2.375MB)
- **修复**:调整 `partition-table.csv`
- app: 0x250000 → 0x2C0000 (2.75MB)
- flashApp (OTA): 0x0A0000 → 0x030000 (192KB)
- spiffs: 0x100000 不变 (1MB)
- 编译验证通过(SUCCESS 34s
## ⚠️ 中文字形像素错乱修复(完整历程)
- **现象**:中文字形隐约可见但局部像素缺失/多余 → 改 alpha 后变全白方块 → 修复后字体太粗
- **根因**:字库生成判断像素的逻辑有误
- 第一版用 R 通道 `< 128`(白色背景 + 黑色前景):抗锯齿边缘灰度 ≈128 在阈值边界,不稳定
- 第二版用 alpha `> 40`(透明背景 + 黑色前景):alpha 40 太低,抗锯齿边缘全被纳入 → 字体太粗
- **最终修复**:透明背景 + 黑色前景,alpha 阈值从 40 提高到 **128**
- 透明区域 alpha=0 → 不标记
- 黑色字形 alpha=255 → 标记
- 抗锯齿边缘 alpha<128 → 不标记(去掉毛边,字形更锐利)
- ⚠️ 关键:`imageSmoothingEnabled` 对 canvas fillText 无效,不能用它关抗锯齿
- **Unicode 覆盖扩展**U+4E00-U+74FF(10067字) → U+4E00-U+9FFF(21075字)
- 修复"鬼"(U+9B3C)、"界"(U+754C)等常用字缺失
- 覆盖 GB2312 全集 6763 字 + CJK 统一汉字扩展
- Flash: 535KB, 固件总大小 2.65MB / 2.75MB (92%)
- **问题**:收到的消息包含中文时无法正常显示
- **根因**`MessageRenderer.cpp``generateLines()` 逐字节处理 UTF-8,CJK 三字节字符被拆散;`drawStringWithEmotes()``drawString()` 渲染,不支持 CJK
- **修复**3处改动):
1. 添加辅助函数 `measureMixedWidth()`(混合宽度计算)和 `drawMixedString()`(混合渲染)
2. `generateLines()` 改为 UTF-8 感知的逐字符循环:CJK 字符作为独立 word 处理,宽度用 `measureMixedWidth()`
3. `drawStringWithEmotes()` 文本段渲染改用 `drawMixedString()`
4. `calculateLineHeights()` 检测 CJK 字符,行高取 max(FONT_HEIGHT_SMALL, CFONT_H)
- 编译通过,flash 仅增 ~1KB(固件 2.57MB < app 分区 2.75MB
## ⚠️ 开机确认逻辑导致无法启动
- **现象**`TCA9535: Boot not confirmed, shutting down` → deep sleep → 开不了机
- **根因**main.cpp 中 Wire.begin() 后的"开机确认窗口"要求用户在 3 秒内持续按住按键 2 秒,否则主动关机
- 这违背了物理开机设计:按下按键 → MOS 得电 → ESP32 启动 → 用户很快松手
- 3 秒窗口内几乎不可能持续按住 2 秒
- **修复**:删除开机确认窗口代码,只保留 `tca9535PowerEn(true)` 立即锁住供电
+11
View File
@@ -101,3 +101,14 @@
- 排除了大量 RadioLib 不用的协议(AX25/LoRaWAN/APRS 等)以节省 flash
- MAX_THREADS=40
- 默认 envtbeam(需要切换到 esp32c3_moonshine 系列时要指定 env
- **Flash 占用**esp32c3_moonshine_travelers):text≈1.92MB, data≈0.63MB, 总≈2.57MB / 4MB
- **分区表**partition-table.csv):app=0x2C0000(2.75MB), OTA=0x030000(192KB), spiffs=0x100000(1MB)
- **中文 12×12 字库**`src/graphics/fonts/ChineseFont12x12.h`21075 字形,535KB flash
- 生成工具:`tools/gen_chinese_font.mjs`(需 npm install @napi-rs/canvas
- 覆盖:U+4E00-U+9FFFCJK 统一汉字全集,含 GB2312 6763 字)
- 像素判定:透明背景 + 黑色前景,alpha > 128 阈值(去掉抗锯齿毛边,字形锐利)
- ⚠️ 阈值 40 太低→字体膨胀变粗;128 合适→清晰锐利
- ⚠️ `imageSmoothingEnabled` 对 canvas fillText 无效
- 渲染函数:`cfont12_find()` / `cfont12_draw()` / `cfont12_utf8()` / `cfont12_drawStr()`
- 集成点:CannedMessageModule drawFrame FREETEXT 部分(混合 ASCII+CJK 渲染)
- MessageRenderer(收到消息显示)也已支持:generateLines + drawStringWithEmotes + calculateLineHeights 均改为 CJK 感知