TXW81x 微信消息模块总体架构开发文档
项目: TXW81x 可视对讲 UI
模块: 微信式语音与表情消息
阶段: UI、业务服务、UDP 与 Opus 链路整合
提交范围:e9a2fb5至2440a8e
分析基线:2440a8e(2026-07-17)
1. 文档目标
本文给后续开发者建立模块级心智模型,重点说明以下问题:
- 微信入口从哪里创建,页面由谁管理;
- 按键事件如何转换为录音、发送、播放等业务动作;
- UDP 收发线程、服务线程和音频线程如何协作;
- Opus 在本项目中承担什么职责;
- 哪些路径已经实现,哪些仍是预留能力;
- 维护时需要优先关注哪些并发、队列和协议风险。
配套文档:
TXW81x微信消息UDP-v2协议开发文档.md:UDP v2 包格式与语音文件传输协议;TXW81x微信语音Opus与WCA音频链路开发文档.md:Opus 参数、WCA 文件和录放音任务;TXW81x微信UI控制层与业务服务开发文档.md:LVGL 页面、控制层、服务状态与消息队列。
2. 代码范围
| 层级 | 主要文件 | 职责 |
|---|---|---|
| 应用入口 | sdk/app/ui/main_ui.c |
调用 wechat_service_create_ui() 创建微信入口 |
| UI 展示 | sdk/lib/gui/intercom_ui/wechat_ui.c |
创建聊天页面、气泡、表情面板和 30 ms 消息轮询定时器 |
| 输入控制 | sdk/lib/gui/intercom_ui/wechat_ui_control.c |
将 LVGL 键值转换为语音按下/松开、表情选择等动作 |
| 业务服务 | sdk/lib/gui/intercom_ui/wechat_service.c |
初始化资源、串行化业务事件、调度录音/播放、桥接 UI 与网络 |
| 音频适配 | sdk/lib/gui/intercom_ui/wechat_app/wechat_audio.c |
音频流、Opus 编解码、WCA 文件和异步作业状态机 |
| UDP 传输 | sdk/app/wechat_udp/wechat_udp.c |
UDP socket、协议封包、接收解析、文件分片发送 |
| Opus 库 | sdk/lib/opus/ |
Opus 1.5.1 编解码实现,固定点配置 |
sdk/app/intercom/intercom_opus.c 是项目原有的实时对讲链路。微信语音模块没有直接复用它的实时网络抖动缓冲和重传逻辑,而是复用了底层音频 stream 与 Opus API,形成“先录制为文件,再通过 UDP 发送”的异步语音消息链路。
基线版本的关键入口:
sdk/app/ui/main_ui.c:85:创建微信服务 UI;sdk/lib/gui/intercom_ui/wechat_ui.c:818:通用 UI 创建接口;sdk/lib/gui/intercom_ui/wechat_service.c:642:业务服务初始化;sdk/app/wechat_udp/wechat_udp.c:365:UDP 初始化;sdk/lib/gui/intercom_ui/wechat_app/wechat_audio.c:925:音频与 Opus 初始化。
3. 总体分层
1 | main_ui.c |
设计上的核心边界如下:
- UI 层不直接操作 socket、文件或 Opus。
- UDP 接收任务不操作 LVGL 对象,只回调服务层。
- 音频工作任务不操作 LVGL,只向服务队列投递完成事件。
- 服务任务负责把跨线程事件串行化,并保证录音和播放互斥。
- UI 通过独立 UI 队列获取可显示的消息,不直接读取服务内部状态。
4. 运行实体与资源
| 运行实体 | 创建位置 | 优先级 | 栈/队列 | 主要工作 |
|---|---|---|---|---|
| LVGL 线程/定时器 | GUI 框架 | GUI 上下文 | 30 ms timer | 页面操作、消息气泡渲染 |
wechat_service |
wechat_service_init() |
Normal | 栈 2048 B;事件队列 16 | 业务状态编排 |
wechat_udp_tx |
wechat_udp_init() |
Normal | 栈 1024 B;发送队列 16 | 异步发送表情 |
wechat_udp_rx |
wechat_udp_init() |
Normal | 栈 1024 B | 收包、校验、回调服务层 |
wechat_audio |
wechat_audio_init() |
Normal | PSRAM 栈 30 KiB;作业队列 4 | 录音、编码、解码、播放 |
重要静态/动态资源:
- UDP 上下文包含 1209 B 接收缓冲区和 1208 B 发送缓冲区;
- 音频播放时从 PSRAM 申请
4 × 1024 B节点数据区; - 音频任务栈从 PSRAM 申请 30 KiB;
- Opus encoder、decoder 在服务打开时创建,在页面关闭时释放;
- 语音文件统一保存在
FLASH:/OPUS。
5. 初始化与退出顺序
5.1 打开页面
main_ui.c创建微信菜单入口。- 用户短按入口,
wechat_ui_open()调用服务层open回调。 wechat_service_init()依次创建互斥锁、停止信号量、UI 队列和服务队列。- 初始化
wechat_audio,创建 Opus encoder、decoder 和音频工作任务。 - 初始化
wechat_udp,绑定本地 UDP 端口5010,创建收发任务。 - 根据 Wi-Fi 模式配置固定对端:
- AP 模式:
192.168.1.100:5010; - STA 模式:
192.168.1.1:5010。
- AP 模式:
- 创建
wechat_service任务。 - 服务初始化成功后,UI 才隐藏主页面并创建聊天页面、焦点组和消息 timer。
5.2 关闭页面
- 删除 UI 消息 timer,停用输入控制。
- 调用服务层
close回调。 - 服务任务收到 STOP 消息后发出停止信号。
- 停止 UDP 收发任务。
- 中止并销毁音频任务,释放 Opus 和 PSRAM 资源。
- 清理未完成的接收文件、队列、锁和信号量。
- 删除聊天页面,恢复原 LVGL group 和主页面。
6. 本地语音消息链路
1 | 按住语音键 |
注意:UI 在松键时计算的秒数被放入 VOICE_STOP 事件,但当前服务逻辑只把它当作附带数据,并不用于最终发送。最终语音时长来自 WCA 文件的实际 Opus 帧数,即 wechat_audio_result_t.duration_sec。
7. 远端语音消息链路
- UDP RX 任务收到
WECHAT_UDP_MSG_VOICE。 - 服务回调按
BEGIN / DATA / END子类型处理。 - BEGIN 创建
FLASH:/OPUS/RX<transfer_id>.TMP。 - DATA 要求
offset严格等于当前已接收长度,并边写文件边计算 CRC32。 - END 校验 transfer ID、文件长度和 CRC32。
- 校验通过后将临时文件重命名为
.WCA。 - 服务任务把它转为待播放语音并通知 UI 添加远端气泡。
- 若当前没有录音或播放任务,立即创建播放 job。
- 音频任务逐帧 Opus 解码,将 PCM 节点送往 speaker stream。
录音优先级高于远端播放:用户在播放期间按下语音键时,服务先停止当前播放,待播放完成事件返回后再开始录音。
8. 表情消息链路
8.1 本地发送
- 用户打开表情面板并选择索引
0..7。 - UI 调用
emoji_selected回调。 - 服务把 SEND_EMOJI 投递到服务队列。
- UI 在投递成功后立即显示本地表情气泡。
- 服务任务调用
wechat_udp_send_emoji()。 - UDP TX 任务异步发送一个 1 B payload 的表情包。
这里采用乐观 UI:本地气泡表示业务事件成功入队,不表示 UDP 已确认送达。
8.2 远端接收
- UDP RX 校验协议头和表情索引。
- 服务回调投递 REMOTE_EMOJI 事件。
- 服务任务写入 UI 队列。
- LVGL timer 拉取消息并显示远端表情气泡。
9. 当前功能边界
已实现:
- 8 个固定表情的双向 UDP 消息;
- 8 kHz、单声道、20 ms 帧的 Opus 语音录制;
- WCA 文件保存、UDP 分片发送、CRC32 校验;
- 远端语音自动播放;
- 录音优先于播放的半双工业务调度;
- UI 与业务服务解耦;
- 语音、表情编译期开关。
尚未实现或仅预留:
- 拍照按钮动作为空;
- UDP 的 PHOTO_BEGIN、PHOTO_DATA、PHOTO_END 只有消息类型定义;
- ACK 类型尚未形成确认与重传机制;
- UI 语音气泡可点击但没有绑定重播事件;
- 聊天记录未持久化,重开页面不会恢复历史消息;
- 对端 IP 为固定配置,不支持发现、配对或动态切换。
10. 维护风险摘要
高优先级
- 音频完成回调使用非阻塞方式投递服务队列,且当前忽略投递失败。若 16 深度的服务队列已满,服务层可能收不到完成事件,导致
record_job_id或play_job_id无法清零。 - UDP 协议没有 ACK、重传或乱序重组。任意一个语音 DATA 包丢失或乱序都会使接收端中止整个文件。
中优先级
- UDP RX 回调内直接执行 Flash 文件创建、写入、重命名和 CRC 计算。Flash 延迟过高时可能拖慢后续收包。
- 语音接收超时只在收到下一次 BEGIN 时检查。单次传输中途停止后,临时文件可能一直保持打开,直到新 BEGIN 或模块退出。
- UI 队列深度为 8,写入采用非阻塞方式;突发远端消息可能被丢弃。
- 接收端只校验来源 IP,没有校验来源 UDP 端口。
兼容性注意
- UDP 多字节字段使用大端序,跨平台可解析。
- WCA 文件头和每帧
packet_len使用目标机本地字节序。当前文件以整体二进制形式传输并在同类设备上播放没有问题,但不应直接假定其跨端序兼容。 - Opus 库配置为 fixed-point,且启用了 C99 VLA;音频工作任务的大栈需求需要持续关注。
11. 建议的后续演进顺序
- 为服务队列完成事件增加可靠投递或兜底状态恢复。
- 给语音协议增加 ACK、超时清理和重传策略。
- 将 UDP RX 的文件写入移到专用接收任务或缓存队列。
- 为消息增加 message ID、发送状态和失败提示,替代完全乐观的 UI。
- 实现语音气泡重播与聊天记录索引。
- 再扩展照片分片协议,复用 transfer ID、offset、size、CRC 的通用文件传输模型。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 ZHG2XU!