添加debug uart打印日志功能。方便调试电流
authorsnickersun <snickersun@163.com>
Fri, 15 May 2026 07:18:24 +0000 (15:18 +0800)
committersnickersun <snickersun@163.com>
Fri, 15 May 2026 07:18:24 +0000 (15:18 +0800)
22 files changed:
.gitignore [new file with mode: 0644]
1.doc/AI吊坠(双按键+三色指示灯)交互规范_new.md [new file with mode: 0644]
1.doc/VC_state迁移FSM子状态-改动清单.md [new file with mode: 0644]
2.sdk/Helios_SDK/services/microPython/ports/quectel/core/umqtt.py
2.sdk/new_clean.bat
2.sdk/project/7PIN/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/config/autoconf.h
2.sdk/project/7PIN/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/config/config.mk
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/EventMesh.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/Makefile
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/ai_VC.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/ai_alarm.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/btn_device_ec800m.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/main_init.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/motor.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/product_k8.ini
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/protocol.py
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/qp_log.py [new file with mode: 0644]
2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/versions.py
2.sdk/project/7PIN_1_5/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/config/autoconf.h
2.sdk/project/7PIN_1_5/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/config/config.mk
2.sdk/project/7PIN_1_5/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/images/firmware_base_release_pack.zip
2.sdk/tools/patch_qp_log.ps1 [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..85ee768
--- /dev/null
@@ -0,0 +1 @@
+2.sdk/release/**
\ No newline at end of file
diff --git a/1.doc/AI吊坠(双按键+三色指示灯)交互规范_new.md b/1.doc/AI吊坠(双按键+三色指示灯)交互规范_new.md
new file mode 100644 (file)
index 0000000..d6db6a8
--- /dev/null
@@ -0,0 +1,396 @@
+# AI吊坠(双按键+三色指示灯)交互规范
+
+核心原则:**操作有反馈、状态有提示、逻辑不冲突**,适配吊坠随身佩戴、无屏操作场景,按键+灯光+提示音三者联动,让用户无需记忆,凭直观反馈即可判断设备状态。
+
+基础约定:
+
+- 按键:Power键(开关键)、AI键(语音交互键)
+- 指示灯:红(R)、蓝(B)、绿(G)三色(遵循定义:绿=正常/电量/充满电;蓝=AI交互;红=低电/异常)
+- 提示音:采用短促、清晰的单音/双音,区分不同操作,音量适中(避免刺耳,适配贴身佩戴),支持后续可调节(可选)
+- 操作逻辑:长按=特殊功能(开关机、强制重启、连续对话);短按/双击=常用功能(唤醒、打断、复读),杜绝误触
+
+### LED 灯效与时间术语(与工程 `ai_VC.AI_led` 对齐)
+
+数字 GPIO 无 PWM 时,**闪 / 慢闪 / 交替 / 慢交替** 均由 `osTimer` 节拍 `**period_ms`** 驱动,含义如下。
+
+**单色亮灭(闪、慢闪)**:每隔 `period_ms` 在 **亮 / 灭** 之间翻转一次,故 **一整次「亮→灭→亮」约 `2 × period_ms`**。
+
+
+| 术语     | 时间含义          | 默认 `period_ms`(半拍) |
+| ------ | ------------- | ------------------ |
+| **闪**  | 约 **1 s** 一亮灭 | `500`              |
+| **慢闪** | 约 **2 s** 一亮灭 | `1000`             |
+
+
+**双色交替(交替、慢交替)**:每隔 `period_ms` **换一色**(蓝↔绿),故 **单色持续约 `period_ms`,蓝→绿→蓝一整圈约 `2 × period_ms`**。
+
+
+| 术语           | 时间含义            | 默认 `period_ms`(换色间隔) |
+| ------------ | --------------- | -------------------- |
+| **交替/交替闪**    | 约 **0.5 s 换一色** | `500`                |
+| **慢交替/交替慢闪** | 约 **1 s 换一色**   | `1000`               |
+
+
+**工程映射(默认)**:聆听蓝 **慢闪**、低电红 **慢闪**、待机绿 **慢闪** → `1000`;思考蓝绿 **交替** → `500`;弱网蓝绿 **慢交替** → `1000`。正文若写「快闪 / 2 次/秒」等,表示周期短于「闪」,工程可按场景单独设 `period_ms`(例如 `**250`** 约 0.5 s 一亮灭,便于对齐「2 次/秒」计数),与上表不冲突。
+
+## 备注
+
+休眠模式:无灯光,无提示音(最低电量消耗)
+
+聆听模式:蓝灯闪(1 分钟内无交互,自动进入待机模式)
+
+思考模式:蓝绿交替闪
+
+播放模式:绿灯闪
+
+待机模式:绿色慢闪(2 分钟内无交互,自动进入休眠模式)
+
+进入聆听模式:单声“滴”(短促)
+
+[请至钉钉文档查看附件《start.mp3》。](https://alidocs.dingtalk.com/i/nodes/KGZLxjv9VGj15aBlU1wKrXGzW6EDybno?iframeQuery=anchorId%3DX02moo4wrj78r6ita1utoy&utm_scene=person_space)
+
+接入拾音/进入思考:单声“层”(短促,有尾音)
+
+[请至钉钉文档查看附件《5c893cf044bde33914.mp3》。](https://alidocs.dingtalk.com/i/nodes/KGZLxjv9VGj15aBlU1wKrXGzW6EDybno?iframeQuery=anchorId%3DX02moo4y0r5romh54qr1l&utm_scene=person_space)
+
+开机提示音
+
+[请至钉钉文档查看附件《开机.mp3》。](https://alidocs.dingtalk.com/i/nodes/KGZLxjv9VGj15aBlU1wKrXGzW6EDybno?iframeQuery=anchorId%3DX02moo4z0qlkd1hwu42as&utm_scene=person_space)
+
+## 第一部分:基础操作(Power键)+ 灯光+提示音联动
+
+
+| 操作动作          | 设备当前状态           | 指示灯状态                   | 提示音                 | 执行结果                                          |
+| ------------- | ---------------- | ----------------------- | ------------------- | --------------------------------------------- |
+| Power键 长按3秒   | 关机状态             | 绿色灯常亮(持续3秒,同步长按时间),然后熄灭 | 现有设备开机提示音(后续视需求可修改) | 设备开机,进入正常待机状态                                 |
+| Power键 长按8秒   | 任何状态(死机/卡死/异常)   | 无变化                     | 无提示音                | 设备关机,切断所有电源                                   |
+| Power键 短按(单击) | 待机状态             | 无指示灯                    | 无提示音                | 进入休眠模式                                        |
+| Power键 短按(单击) | AI交互中(聆听/处理/TTS) | 绿色慢闪                    | 无提示音                | 停止对话,进入待机模式                                   |
+| Power键 短按(双击) | 待机状态(任意状态)       | 无变化                     | 播报:单对话模式/长对话模式      | 如果对话时停止对话,设置对话模式,进入待机模式, 非对话模式,设置对话模式直接进入待机模式 |
+
+
+## 第二部分:AI交互操作(AI键)+ 灯光+提示音联动
+
+
+| 操作动作                              | 对话模式  | 设备当前状态              | 指示灯状态               | 提示音                | 执行结果                              |
+| --------------------------------- | ----- | ------------------- | ------------------- | ------------------ | --------------------------------- |
+| AI键 短按1次                          | 长对话模式 | 待机状态                | 立即切换为蓝色慢闪(1次/秒)     | 单声“滴”(短促,清脆)       | 唤醒AI,进入语音聆听状态,开始收音(用户可说话)         |
+| AI键 短按1次                          | 长对话模式 | 语音聆听中(蓝色慢闪)         | 切换为蓝色慢闪             | 单声“滴”(短促)          | 重新进入语音聆听状态,开始收音(用户可说话)            |
+| AI键 短按1次                          | 长对话模式 | TTS播报中(绿色常亮灯)       | 切换为蓝色慢闪             | 单声“滴”(短促)          | 进入语音聆听状态,开始收音(用户可说话)              |
+| AI键 短按1次                          | 长对话模式 | 思考中(蓝绿交替闪)          | 切换为蓝色慢闪             | 单声“滴”(短促)          | 进入语音聆听状态,开始收音(用户可说话)              |
+| AI键 双击(两次短按,间隔≤1秒)-> 可选(根据可实现性而定) |       | 待机状态 & TTS播报结束后1分钟内 | 蓝色灯常亮(与正常TTS播报灯光一致) | “滴、滴”(2声短促,间隔0.5秒) | 复读上一次AI的TTS回复内容,复读结束后回到待机状态(蓝色灯灭) |
+| AI键 双击>>暂时无功能                     |       |                     |                     |                    |                                   |
+| AI键 长按(按下)                        | 单对话模式 | 待机状态/思考状态/播报状态      | 蓝色快闪(频率加快,2次/秒)     | 单声“滴”(短促,清脆)       | 进入收音状态                            |
+| AI键 长按(松开)                        | 单对话模式 | 聆听中                 | 蓝绿交替闪               | “曾——”(长音,持续1秒)     | 进入思考模式                            |
+
+
+## 第三部分:电量/充电状态 + 灯光+提示音联动
+
+
+| 设备状态              | 触发条件            | 指示灯状态                   | 提示音                 | 补充说明                     |
+| ----------------- | --------------- | ----------------------- | ------------------- | ------------------------ |
+| 正常待机(电量≥30%)      | 开机后无操作、交互结束后    | 指示灯灭(AI交互过程遵循如上第二部分的定义) | 无提示音                |                          |
+| 低电量提醒(10%≤电量<30%) | 电量降至阈值、每5分钟触发1次 | 红色慢闪(1次/1秒),每次闪3次       | “滴、滴”(2声短促,与灯光同步)   | 提醒用户尽快充电,不频繁打扰           |
+| 电量耗尽(电量<10%)      | 电量降至关机阈值        | 红色快闪3次(2次/秒)→ 熄灭        | “滴、滴、滴”(3声短促,与灯光同步) | 快闪结束后设备自动关机              |
+| 充电中(电量<100%)      | 连接充电器,接触正常      | 红色常亮                    | 单声“滴”(短促)           | 充电期间,按键操作仍有效(开关机、AI交互)   |
+| 充电满(电量=100%)      | 充电至满电阈值         | 红色常亮→切换为绿色常亮(延迟1秒)      | “滴——”(长音,持续1秒)      | 绿色常亮保持,提醒用户可拔下充电器(晚上不打扰) |
+
+
+## 第四部分:AI交互异常状态 + 灯光+提示音联动
+
+
+| 异常场景             | 指示灯状态                    | 提示音                 | 处理方式                 |
+| ---------------- | ------------------------ | ------------------- | -------------------- |
+| 语音识别失败(未听清/无法识别) | 红蓝快闪2次(2次/秒)→ 回到待机状态(灯灭) | “滴、滴”(2声短促,与灯光同步)   | 用户可再次短按AI键,重新唤醒聆听    |
+| 网络异常(无法请求AI)     | 蓝绿交替慢闪(1次/2秒),持续闪烁       | “网络异常信号弱”(每5秒1次,循环) | 检查网络连接,恢复网络后自动回到待机状态 |
+| AI处理超时           | 蓝色常亮→红蓝快闪2次→回到待机状态(灯灭)   | “滴、滴”(2声短促)         | 处理失败,用户可重新发起语音请求     |
+
+
+## 第五部分:补充规范(工程&用户体验)
+
+### 1. 提示音规范
+
+- 音量:默认中等(约50dB),避免贴身佩戴时刺耳,支持后续通过APP调节(可选)
+- 音色:统一采用清脆的电子音,避免复杂音效,确保不同场景的提示音可区分(短滴=操作确认,长滴=状态切换,多滴=提醒/异常)
+- 优先级:异常提示音(网络异常)>操作提示音(唤醒、打断)>状态提示音(充电满、低电)
+
+### 2. 灯光规范
+
+- **闪 / 慢闪 / 交替 / 慢交替** 的时间定义见文首 **「LED 灯效与时间术语」**;与工程 `period_ms` 一一对应,避免「1 次/秒」等口语与实现不一致。
+- 亮度:柔和不刺眼(≤100cd/m²),适配夜间、贴身佩戴场景,避免强光照射皮肤
+- 响应速度:按键操作后,灯光和提示音同步触发(延迟≤100ms),无卡顿
+- 冲突规避:同一时间仅显示一种状态灯光(如充电中触发AI交互,优先显示AI交互灯光,充电状态暂停显示,交互结束后恢复)
+
+### 3. 工程可实现性说明
+
+- 按键逻辑:长按时间(3秒/8秒/2秒)可通过MCU精准控制,避免误判(如长按2秒切换进入连续对话模式,需持续检测按键按压时间,不足3秒不执行)
+- 灯光控制:三色灯采用单灯三色设计,无需额外硬件,逻辑简单易实现
+- 提示音:喇叭播放,无需额外蜂鸣器
+
+### 4. 用户易用性补充
+
+- 所有操作均有“灯光+提示音”双重反馈,无屏状态下也能清晰判断设备状态
+- 避免复杂操作,核心功能(唤醒、开关机)仅需1种按键动作,老人、儿童可快速上手
+- 误触防护:短按Power键无操作、双击AI键仅在待机/播报后有效,减少误操作带来的困扰
+
+---
+
+## 附录 A:灯效编程对照表(`ai_VC.AI_led`)
+
+灯由 `EventMesh.publish("set_led_state", { ... })` 聚合驱动;**优先级自上而下**,先命中先显示,后面的输入仍写入内部状态但不生效,直到高优先级条件解除。
+
+
+| 优先级 | 输入字段       | 取值(示例)                | 内部 `_mode`    | 灯效(R/G/B) | `period_ms`(节拍)                      | 对应术语    | 说明                   |
+| --- | ---------- | --------------------- | ------------- | --------- | ------------------------------------ | ------- | -------------------- |
+| P1  | `ai_state` | `playing`             | `green_on`    | 绿常亮       | —                                    | —       | TTS/本地播报中            |
+| P2  | `ai_state` | `thinking`            | `bg_alt`      | 蓝↔绿交替     | `500`                                | **交替**  | 约 0.5 s 换一色          |
+| P3  | `ai_state` | `listening`           | `blue_blink`  | 蓝亮灭       | `1000`                               | **慢闪**  | 约 2 s 一亮灭;整颗 GPIO 亮灭 |
+| B1  | `charging` | `full`                | `green_on`    | 绿常亮       | —                                    | —       | USB 在位且电压达充满阈值       |
+| B2  | `charging` | `charging`            | `red_on`      | 红常亮       | —                                    | —       | USB 在位且未充满           |
+| B3  | `battery`  | `low` / `empty`       | `red_blink`   | 红亮灭       | `1000`                               | **慢闪**  | 非充电时按 SoC/OCV 判定     |
+| B4  | `signal`   | `weak`                | `bg_alt`      | 蓝↔绿交替     | `1000`                               | **慢交替** | CSQ 低于阈值             |
+| 默认  | (上述均未命中)   | `ai_state` 一般为 `idle` | `green_blink` | 绿亮灭       | `LED_IDLE_GREEN_BLINK_MS`(默认 `1000`) | **慢闪**  | 待机/空闲                |
+
+
+`**set_led_state` 消息字典键**
+
+
+| 键          | 类型  | 含义                                            |
+| ---------- | --- | --------------------------------------------- |
+| `ai_state` | str | `idle` / `listening` / `thinking` / `playing` |
+| `charging` | str | `none` / `charging` / `full`                  |
+| `battery`  | str | `normal` / `low` / `empty`                    |
+| `signal`   | str | `normal` / `weak`                             |
+
+
+**可配置项(`default_config.py` / `vc_config`)**
+
+
+| 配置名                                     | 作用                                      |
+| --------------------------------------- | --------------------------------------- |
+| `LED_GPIO`                              | (R, G, B) 引脚名                           |
+| `LED_ACTIVE_LOW` / `LED_ACTIVE_LOW_RGB` | 点亮电平反相                                  |
+| `LED_IDLE_GREEN_BLINK_MS`               | 待机绿灯**慢闪**半周期(ms),默认 `1000`,代码侧最小 `400` |
+
+
+**与正文规范的差异(便于联调对齐)**
+
+
+| 正文描述                    | 当前工程实现要点                                                                                                                                       |
+| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| 待机灯灭 / Power 短按待机无灯     | 代码待机为 **绿灯慢闪**(见上表默认行);与正文第三 part「正常待机灯灭」不一致时以代码为准或后续再改表                                                                                       |
+| 休眠无灯光                   | 休眠路径在 `ui.py`(`pm.autosleep` 等);LED 是否全灭取决于休眠前是否再发 `set_led_state`                                                                             |
+| Power 长按 8s 关机无灯 / 无提示音 | 正文第一 part 为「无变化、无提示音」;若需与关机动画/提示音一致,由电源键模块与产品再定稿                                                                                               |
+| 备注「蓝灯闪 / 绿灯闪」与附录节拍      | 工程侧聆听为 **慢闪**(`1000`)、播放为 **绿常亮**;与口语「闪」字面不完全相同时,以文首 **术语表** 与附录 A 为准                                                                          |
+| 闪 / 慢闪 / 交替 / 慢交替       | 与文首 **「LED 灯效与时间术语」** 及上表一致;`listening` / 低电 / 待机为 **慢闪**(`1000`),`thinking` 为 **交替**(`500`),弱网为 **慢交替**(`1000`);若正文写「1 次/秒」亮灭见术语 **闪**(`500`) |
+
+
+---
+
+## 附录 B:对话模式与状态机(`ai_VC.py`)
+
+> **源码对齐**:下列 FSM、`VC_TtsRxPhase`、`[FSM]`/`[KEY]` 日志字段等与 `2.sdk/Helios_SDK/services/microPython/ports/quectel/core/ai_VC.py` 一致。仓库内若另有 `project/*/sdk/.../ai_VC.py` 等副本,可能尚未合并同一套 `VCStateMachine`,**以当前固件实际编译进镜像的文件为准**。
+
+### B1 对话模式 `VC_mode`(存储键 `VC_mode`)
+
+
+| 值   | 常量名(代码)             | 含义       | 协议侧要点                                                   |
+| --- | ------------------- | -------- | ------------------------------------------------------- |
+| `0` | `VCMode.Realtime`   | 长对话(连续)  | VAD 开,一般 **无需** `audio.create` / `audio.completed` 包裹上行 |
+| `1` | `VCMode.PushToTalk` | 单对话(PTT) | VAD 关,**必须** `audio.create` → 帧 → `audio.completed`     |
+
+
+### B2 会话层 `VCSessionState`(`AI_VC.fsm.session`)
+
+
+| 值   | 名称             | 含义                   |
+| --- | -------------- | -------------------- |
+| `0` | `Disconnected` | 未连接 / 已断开            |
+| `1` | `Connecting`   | 连接中                  |
+| `2` | `Starting`     | 已发 Start,等 `started` |
+| `3` | `Started`      | 会话已建立,可上行/收事件        |
+
+
+### B3 轮次层 `VCTurnState`(`AI_VC.fsm.turn`)
+
+
+| 值   | 名称                | 含义(简述)                   | 当前 `VCStateMachine` 赋值情况                         |
+| --- | ----------------- | ------------------------ | ---------------------------------------------- |
+| `0` | `Idle`            | 本轮空闲                     | 会写入(会话开始/断开等)                                 |
+| `1` | `Listening`       | 聆听 / 等用户说话(长对话侧常见)       | 会写入(`started` / `canceled` / `audio.played` 后等)   |
+| `2` | `SpeechStreaming` | 语音上行中(流式)                | **枚举保留**;上行是否打开见 `fsm.speech_uplink_open`(`asr.started` 置 1) |
+| `3` | `SpeechEnded`     | 本轮说话结束(等 ASR/LLM)        | 会写入(`vad.silence` 等)                          |
+| `4` | `Thinking`        | LLM 思考中                  | 会写入(`llm.created`;`llm.completed` 兜底补位)        |
+| `5` | `TtsReceiving`    | 正在收 TTS 音频流              | 会写入(`tts.started` / `local.synthesis_sent`)   |
+| `6` | `Playing`         | 本地正在播 TTS                | 会写入(`local.play_start`)                       |
+| `7` | `AwaitPlayedAck`  | 已播完,等 `audio.played` 等门控 | **枚举保留**;当前以 `fsm.await_played` 布尔门控为主,`turn` 多在 `Playing`/`TtsReceiving` |
+
+
+### B3.1 TTS 接收相 `VC_TtsRxPhase`(`AI_VC.fsm.tts_rx_phase`)
+
+与 `tts.started` / `tts.completed` 对齐,用于区分服务端下发阶段(本地可能仍在播)。
+
+
+| 值   | 名称                 | 含义            |
+| --- | ------------------ | ------------- |
+| `0` | `Idle`             | 无 TTS 接收相活动   |
+| `1` | `Started`          | 已收到 `tts.started` |
+| `2` | `ServerCompleted` | 已收到 `tts.completed`(本地未必播完) |
+
+
+### B4 旧版 `VC_State_*` 整型(已移除,仅作历史对照)
+
+源码注释:**「原 `VC_State_*` 整型已移除;会话/轮次见 `VCTurnState` / `VCStateMachine`(含 `tts_rx_phase`、`speech_uplink_open`)。」** 下列宏名**不再出现在运行逻辑中**,仅便于对照旧日志或旧文档。
+
+
+| 宏名(历史)                                | 值    | 含义      |
+| --------------------------------------- | ---- | ------- |
+| `VC_State_None`                         | 0    | 无       |
+| `VC_State_Listening`                    | 1    | 听       |
+| `VC_State_SpeechStart`                  | 2    | 说开始     |
+| `VC_State_SpeechIng`                    | 3    | 正在说     |
+| `VC_State_SpeechEnd`                    | 4    | 说结束     |
+| `VC_State_RequestToRespond`             | 5    | 请求回复    |
+| `VC_State_Thinking`                     | 6    | 思考      |
+| `VC_State_Responding`                   | 7    | 响应/播放相关 |
+| `VC_State_TTSStart` … `VC_State_TTSEnd` | 8–11 | TTS 子阶段 |
+| `VC_State_TTSError`                     | 12   | TTS 异常  |
+| `VC_State_DialogError`                  | 13   | 对话异常    |
+| `VC_State_End`                          | 14   | 结束      |
+
+
+### B5 灯与 FSM 的对应关系(编程时)
+
+
+| `set_led_state` 的 `ai_state` | 典型触发场景(与 FSM/业务相关)                           |
+| ---------------------------- | -------------------------------------------- |
+| `listening`                  | 开麦聆听、长对话续聊等                                  |
+| `thinking`                   | `llm.created` / 思考阶段灯                        |
+| `playing`                    | `tts.started` 后本地 PCM 播放、`audio_cb` 播放起止等    |
+| `idle`                       | 交互结束、休眠前、或硬件态仅刷新 `charging/battery/signal` 时 |
+
+
+> **调试**:`EventMesh.publish("get_led_debug_state")` 返回当前聚合 `{ ai, charging, battery, signal, mode, period_ms }`,与串口 `[KEY]` / `[FSM]` 日志对照。
+
+### B6 状态机层级与典型跳转(实现速查)
+
+- **两层模型**:`session`(WebSocket 会话生命周期)与 `turn`(单轮「听→说→想→播」)。**`[FSM]` 单行日志**(`VCStateMachine.log`)形如:`[FSM] <reason> session=Started turn=Listening await_played=0 tts_rx=0 speech_up=0`(其中 `tts_rx` 为 `VC_TtsRxPhase` 数值,`speech_up` 为 `speech_uplink_open`)。
+- **`[KEY]` 行内片段**(`_vc_fsm_log_snip`):`sess=<int> turn=<int> tts_rx=<int> await=<0|1> sp_up=<0|1>`,与上式含义一致。
+- **协议约束**(代码注释摘要):对齐《金三服务-语音对话 V2.5》——收到 `started` 后才允许发音频帧;VAD 开时长对话无需 `audio.create`/`completed`;VAD 关时 PTT 必须 `audio.create`→帧→`audio.completed`;`audio.played` 为进入下一轮的门控之一。
+- **可选严格协议行为**:模块级开关 `VC_PROTO_GUARDS`(默认 `0`)控制 started 前 PCM 缓冲/丢弃、PTT 下 `audio.create` 前禁发 PCM、`llm.completed` 后久无 `tts.started` 的会话兜底等;与产品灯效无直接耦合,联调协议时可关注。
+- **详细边条件**:以 `ai_VC.py` 中 `VCStateMachine` 各 `on_server_*` / `on_local_*` 及事件适配 `on_server_event` 为准;下图为**阅读级**总览(与当前赋值路径对齐:`Listening` 可直接到 `SpeechEnded`),非穷举所有分支。
+
+```mermaid
+stateDiagram-v2
+    direction LR
+    state "会话 session" as S {
+        [*] --> Disconnected
+        Disconnected --> Connecting : websocket_connect
+        Connecting --> Starting : 发 Start
+        Starting --> Started : rx started
+        Started --> Disconnected : Close / 断链
+    }
+    state "轮次 turn(Started 内)" as T {
+        [*] --> Idle
+        Idle --> Listening : 开麦/续聊
+        Listening --> SpeechEnded : vad.silence 等
+        SpeechEnded --> Thinking : llm.created / completed 兜底
+        Thinking --> TtsReceiving : tts.started
+        TtsReceiving --> Playing : local.play_start
+        Playing --> Listening : audio.played 后 on_local_audio_played
+        Listening --> Idle : cancel / Close
+    }
+```
+
+
+
+`**VC_mode` 与 FSM 关系(简述)**
+
+
+| `VC_mode` | 对 `turn` 的影响要点                                                |
+| --------- | ------------------------------------------------------------- |
+| `0` 长对话   | 易在 `Listening` 与上行、`canceled` 自动续听等路径间切换;详见 PTT 与长对话分支        |
+| `1` 单对话   | 依赖 `audio.create` / `audio.completed`;长按 `ptt_begin` 打断后重开上行等 |
+
+
+---
+
+## 附录 C:充电 / 电量 / 信号(硬件态)与 LED 同步(`AI_hw_status` + `AI_led`)
+
+硬件侧由 `**AI_hw_status._update()`** 周期性(默认 `HW_STATUS_PERIOD_MS`,如 10000 ms)及 `**hw_usb_state` 事件** 刷新,并 **一次下发**:
+
+`EventMesh.publish("set_led_state", {"charging": chg, "battery": bat, "signal": sig})`
+
+仅更新这三个字段;`**ai_state` 仍由上一条 VC/业务逻辑保留**,最终灯效由 `**AI_led._apply_by_table` 优先级**决定:**任意非 `idle` 的 `ai_state`(listening/thinking/playing)都会盖住充电/电量/弱网灯**。
+
+### C1 采样输入 → 内部枚举(工程判定)
+
+
+| USB 在位 (`GET_USB_STATE` / 事件) | 电池电压 `vbatt_mv`(默认算法)                                   | `charging` | `battery`(仅 **USB 不在位** 时按 SoC 算)    |
+| ----------------------------- | ------------------------------------------------------- | ---------- | ------------------------------------ |
+| 否                             | SoC ≥ 5% 且非低电档                                          | `none`     | `normal`                             |
+| 否                             | SoC < 5%                                                | `none`     | `empty`                              |
+| 否                             | 5% ≤ SoC < 20%(工程档,非正文 10%/30%)                         | `none`     | `low`                                |
+| 是                             | < 充满阈值(默认 **≥4290 mV** 视为满,可 `VBATT_CHARGE_FULL_MV` 覆盖) | `charging` | 不按 SoC 刷低电(充电中不走路径 B3)               |
+| 是                             | ≥ 充满阈值                                                  | `full`     | 不参与 SoC 低电判定,`battery` 仍为默认 `normal` |
+
+
+**说明**:USB 在位时 `**battery` 不按电压刷新**(代码仅在 `usb_status != 1` 时根据 SoC 置 `low`/`empty`),灯效以 `**charging` 优先**(见附录 C3)。
+
+**SoC 来源**:默认 **OCV 电压表插值**(`_ocv_map`);若 `USE_SIMPLE_VBATT_THRESH=True`,则按 `VBATT_CHARGE_FULL_MV` / `VBATT_LOW_MV` / `VBATT_EMPTY_MV` 三档近似。
+
+### C2 信号弱网
+
+
+| 条件                                                                      | `signal` |
+| ----------------------------------------------------------------------- | -------- |
+| `net.csqQueryPoll()` 得到的 RSSI 存在且 **0 ≤ RSSI < SIGNAL_WEAK_CSQ**(默认 10) | `weak`   |
+| 否则                                                                      | `normal` |
+
+
+### C3 枚举 → `AI_led` 灯效(且 `**ai_state == idle`** 时生效)
+
+
+| `charging` | `battery`       | `signal` | 内部 `_mode`  | 灯效              | 节拍与术语                               |
+| ---------- | --------------- | -------- | ----------- | --------------- | ----------------------------------- |
+| `full`     | *               | *        | `green_on`  | 绿常亮             | —                                   |
+| `charging` | *               | *        | `red_on`    | 红常亮             | —                                   |
+| `none`     | `low` / `empty` | *        | `red_blink` | 红**慢闪**         | `period_ms=1000`(约 2 s 一亮灭)         |
+| `none`     | `normal`        | `weak`   | `bg_alt`    | 蓝绿**慢交替**       | `period_ms=1000`(约 1 s 换一色)         |
+| `none`     | `normal`        | `normal` | —           | 附录 A 默认:**绿慢闪** | `LED_IDLE_GREEN_BLINK_MS`,默认 `1000` |
+
+
+### C4 正文「第三部分」与工程灯效对照(含 AI 覆盖)
+
+
+| 正文设备状态      | 正文指示灯  | 工程基础态(`ai_state=idle`)                         | **AI 交互中**(`listening`/`thinking`/`playing`) |
+| ----------- | ------ | ---------------------------------------------- | -------------------------------------------- |
+| 正常待机(≥30%)  | 灯灭     | **绿灯慢闪**(见附录 A 差异说明)                           | 以 AI 灯为准(蓝慢闪 / 蓝绿交替 / 绿常亮)                   |
+| 低电量 10%~30% | 红慢闪等   | `battery=low` → `red_blink`(阈值实为 SoC<20%)      | AI 优先:例如 **播放中仍为绿常亮**,低电红慢闪不显示               |
+| 电量耗尽 <10%   | 红快闪→灭  | `battery=empty` → `red_blink`(SoC<5%);关机另由电源策略 | 同上,AI 可盖住                                    |
+| 充电中 <100%   | 红常亮    | `charging=charging` → `red_on`                 | AI 优先:如 **聆听蓝慢闪** 盖住充电红                      |
+| 充电满         | 红→绿常亮等 | `charging=full` → `green_on`                   | 与 **TTS `playing` 绿常亮** 同色,语义需靠场景区分          |
+
+
+---
+
+## 附录 D:异常状态(正文第四部分)与 LED 同步(工程现状)
+
+当前 `**AI_led*`* 无单独「红蓝快闪」「网络异常专用慢闪」等 `_mode`;异常分支多为 **TTS / `websocket_error` / `Close` + `ai_state: idle`**,灯效回到 **硬件态表(附录 C3)** 或 **待机绿灯慢闪**。
+
+
+| 正文异常场景        | 正文指示灯          | 工程侧典型处理                                                          | 灯效(概括)                                                                  |
+| ------------- | -------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------- |
+| 语音识别失败(未听清)   | 红蓝快闪 2 次 → 待机灭 | `asr.result` 空串时置 `AI_TTS_str="我没听清…"`,走后续 TTS/状态机;**未专设红蓝闪灯**   | 播提示 TTS 时多为 `**playing`→绿常亮**;结束变 `**idle`** 后接 **附录 C3**(弱电则红慢闪,否则绿慢闪) |
+| 网络异常(无法请求 AI) | 蓝绿慢交替 + 语音循环   | `websocket_error` 等:`Close`、`set_led_state({"ai_state":"idle"})` | `**idle` + 硬件态**:弱网则 `**bg_alt`(慢交替)**;否则 **绿慢闪**;无「持续网络异常」专用灯型         |
+| AI 处理超时       | 蓝常亮 → 红蓝快闪 → 灭 | 依赖超时/兜底分支,**无独立灯型表项**                                            | 过程中可能 `**thinking`(交替)** 或 `**idle`**;未实现规范中的红蓝快闪序列                     |
+
+
+### D1 与附录 A 的编程关系(异常联调)
+
+1. 需要 **「异常专用灯」**:应在业务点增加 `**set_led_state` 新键**(如 `alert`)并在 `**AI_led._apply_by_table`** 中插入优先级;当前未做。
+2. 仅依赖 **现有键**:异常后务必 `**ai_state: idle`** 才会重新露出 **充电/电量/信号** 灯;否则用户会一直看到 **AI 阶段灯**。
+3. `**websocket_error`** 内已 `**set_led_state({"ai_state":"idle"})`**,可与 `**get_led_debug_state**` 联调确认 `charging/battery/signal` 是否已刷新。
+
+---
+
diff --git a/1.doc/VC_state迁移FSM子状态-改动清单.md b/1.doc/VC_state迁移FSM子状态-改动清单.md
new file mode 100644 (file)
index 0000000..3e1e94d
--- /dev/null
@@ -0,0 +1,167 @@
+# `VC_state` 整型 → 扩展 FSM(子状态)迁移改动清单\r
+\r
+> **策略**:在 `VCStateMachine` 上增加**显式子状态**(尤其覆盖原 `VC_State_TTSStart`~`TTSEnd` 与「说」阶段),所有业务判断只读 `fsm`;**删除** `self.VC_state` 的赋值与比较;常量 `VC_State_*` 整表删除。  \r
+> **范围**:以 `2.sdk/project/7PIN_1_5/.../ai_VC.py` 为主;若产线启用 **Coze 分支**或 **`usr/test1.py`** 等拷贝,需**平行改**或删除未用拷贝(见文末)。\r
+\r
+---\r
+\r
+## 0. 前置结论(避免反复确认)\r
+\r
+| 问题 | 结论 |\r
+| --- | --- |\r
+| V2.5 协议是否要求 `VC_state`? | **否**。协议是 JSON 事件/负载;`VC_state` 仅为固件内部历史变量。 |\r
+| 与 `fsm.turn`(B3)关系 | **并行双源**;迁移后 **唯一真源** 为 `fsm` + 本子状态枚举。 |\r
+| `asr_state` | 同文件内另一套整型(`__init__` 附近);**本清单以 `VC_state` 为主**;建议第二阶段同样收敛到 FSM 或独立 `AsrPhase` 枚举。 |\r
+\r
+---\r
+\r
+## 1. FSM 扩展设计(必须先定稿再改分支)\r
+\r
+### 1.1 新增子状态(建议命名,可按实现微调)\r
+\r
+在 `VCStateMachine` 中新增字段,例如:\r
+\r
+- **`speech_phase`**(可选):`Idle | UplinkOpened | FirstFrame | Streaming | VadEnded` — 仅当需要替代 `VC_State_SpeechStart`~`SpeechEnd` 的细粒度时使用;若可用 `turn == SpeechStreaming/SpeechEnded` + `VC_record_state_flag` 表达,可**不增**此枚举以减负。\r
+- **`tts_rx_phase`**(推荐):`Idle | Started | Receiving | ServerCompleted | Error` — 对应原 **8~11** 区间在**收包线程**里表达的「服务端 TTS 流生命周期」,用于:\r
+  - `tts_in/out` 背压判断(原约 **5428~5431** 行);\r
+  - `>= VC_State_TTSStart` 类「已开始 TTS 则不再处理 ASR」逻辑(原约 **4949** 行等)。\r
+\r
+**转写规则(初稿,实现时以代码审查为准)**:\r
+\r
+| 原 `VC_state` / 区间 | 迁移后(读侧) |\r
+| --- | --- |\r
+| `>= TTSStart and <= TTSEnd` | `tts_rx_phase in (Started, Receiving, ServerCompleted)` **或** `turn in (TtsReceiving, Playing, AwaitPlayedAck)`(二者取并集需对照时序,避免漏判) |\r
+| `== Thinking` | `turn == Thinking`(原 **5622** `think.mp3` 门控) |\r
+| `>= SpeechIng` | `turn in (SpeechStreaming, SpeechEnded, Thinking, TtsReceiving, Playing, AwaitPlayedAck)` **或** `speech_phase` 达到 Streaming 之后(原 **6914** 长空闲门控) |\r
+| `>= SpeechStart`(且配合 `VC_record_state_flag`) | `VC_record_state_flag != 0` **且** `turn in (SpeechStreaming, SpeechEnded, …)` 或 `speech_phase != Idle` |\r
+| `VC_State_End` | `session != Started` **或** `turn == Idle` 且本轮显式结束标志(需与 `Close`/`completed` 语义对齐) |\r
+\r
+### 1.2 子状态**唯一写入点**\r
+\r
+- 在 **`VCStateMachine.on_server_event`**(及 `on_server_tts_*`、本地 `play_start` / `audio.played` 回调)内**集中**更新 `tts_rx_phase` / `speech_phase`。  \r
+- **禁止**在 `AI_client_VC_recv` 大 `if/elif event` 里散落 `self.VC_state = …`;改为调用 `fsm.on_tts_rx_started()` 等小方法。\r
+\r
+### 1.3 日志\r
+\r
+- `[KEY]` / `[VC][uplink]` 中 `VC_state=` 改为 **`turn=… tts_rx=… speech=…`**(或单行 `fsm.dump()`),便于与 `[FSM]` 对齐。\r
+\r
+---\r
+\r
+## 2. 按文件 / 代码块的删除与替换清单(`ai_VC.py`)\r
+\r
+以下行号以 **当前仓库 `ai_VC.py`** 为准,合并后若有漂移以 `rg "VC_state"` 为准。\r
+\r
+### 2.1 删除整表与成员\r
+\r
+| 序号 | 动作 | 位置(约) |\r
+| --- | --- | --- |\r
+| D1 | 删除 `VC_State_None`~`VC_State_End` 常量定义 | **2965–2979** |\r
+| D2 | 删除 `self.VC_state = …` 成员初始化 | **3859**(及所有赋值) |\r
+| D3 | 删除所有 `self.VC_state =` 赋值行 | 见 **2.3** |\r
+\r
+### 2.2 条件与表达式替换(读侧)\r
+\r
+| 序号 | 当前逻辑(约) | 替换方向 |\r
+| --- | --- | --- |\r
+| R1 | **4359** `playing or (VC_state in TTS..) or VC_Play_flag` | `playing or fsm_tts_active() or VC_Play_flag`;`fsm_tts_active` 用 `turn` + `tts_rx_phase` 封装 |\r
+| R2 | **4387** `VC_state in [TTSStart, TTSEnd]` | 同上或仅 `tts_rx_phase` |\r
+| R3 | **4949** `VC_state >= TTSStart` | `tts_rx_phase >= Started` 或 `turn in (TtsReceiving, Playing, AwaitPlayedAck)` |\r
+| R4 | **5428–5431** TTS 缓冲与 `< TTSStart` | 完全基于 `tts_rx_phase` + `turn` |\r
+| R5 | **5622** `VC_state == Thinking` | `getattr(self.fsm, "turn", 0) == VCTurnState.Thinking` |\r
+| R6 | **5672** `record and VC_state >= SpeechStart` | `record and in_speech_or_later()`(封装) |\r
+| R7 | **5785** `_need_audio_played_ack or VC_state >= TTSStart or VC_Play_flag` | `_need_audio_played_ack or fsm_tts_active() or VC_Play_flag` |\r
+| R8 | **6914** `VC_state >= SpeechIng` | `turn >= SpeechStreaming` 或 `speech_phase` 等价 |\r
+\r
+### 2.3 删除所有写侧赋值(逐条勾选)\r
+\r
+在替换读侧后,下列赋值应**整行删除**,改由 FSM 方法内更新子状态:\r
+\r
+| 约行号 | 原赋值 |\r
+| --- | --- |\r
+| 5581, 5583, 5587, 5679, 5703, 5742, 5780, 5791, 5885 | `VC_State_*` 多种 |\r
+| 6865, 6893, 6968 | `VC_State_None` |\r
+| 7037, 7249, 7279, 7313, 7387 | `VC_State_*` |\r
+| 4448, 4500 | `Close`/重置路径中的 `VC_State_None` |\r
+\r
+**事件分支内**(`AI_client_VC_recv` 一带)原 **5581–5791** 与 **5679/5703/5742** 等:每处先标「对应协议事件 + 本地动作」,再映射到 `fsm.on_server_*` / 新 `on_tts_rx_*`。\r
+\r
+### 2.4 仅日志\r
+\r
+| 约行号 | 动作 |\r
+| --- | --- |\r
+| 3465–3476, 4332–4333, 4543–4544, 4649–4650, 6910–6911, 7027, 7936–7937 | 去掉 `VC_state`,改为打印 `fsm.session/turn/tts_rx_phase/await_played` |\r
+\r
+### 2.5 注释 / 死代码\r
+\r
+| 约行号 | 动作 |\r
+| --- | --- |\r
+| 4705, 4863, 5014, 5842, 5919, 7345, 7912 | 删除或改写仍提及 `VC_state` 的注释块 |\r
+\r
+---\r
+\r
+## 3. `VCStateMachine` 本体改动(骨架)\r
+\r
+| 序号 | 任务 |\r
+| --- | --- |\r
+| F1 | 在 `VCStateMachine.__init__` 增加 `tts_rx_phase`、`speech_phase`(若采用)默认值 `Idle` |\r
+| F2 | 实现 `on_tts_stream_started/receiving/completed/error`(名称自定),由 **recv 线程**在解析 `tts.*` 事件时调用 |\r
+| F3 | `log()` 扩展一行子状态(可选字段) |\r
+| F4 | `on_server_started` / `Close` / `websocket_deconnect` 时**复位**所有子状态,避免跨会话泄漏 |\r
+| F5 | 审查 **`on_server_event`** 中已有 `tts.started` / `tts.completed` 与本地 `play_start` 的先后,保证 `tts_rx_phase` 与 `turn` 一致 |\r
+\r
+---\r
+\r
+## 4. 回归测试矩阵(必测)\r
+\r
+| 场景 | 验证点 |\r
+| --- | --- |\r
+| 长对话 VAD | `vad.silence` → 上行停;`llm.created` → 思考灯;`tts.started` → TTS 门控与 ASR 抑制 |\r
+| 单对话 PTT | `audio.create`~`completed`;松手进思考;无 `VC_state` 漂移 |\r
+| TTS 大缓冲 | 原 **5428** 条件:背压仍触发 |\r
+| `think.mp3` | 仅在 `turn==Thinking` 且未播放时响(原 **5622**) |\r
+| 长按 20s / `Close` | **6914**、**7936** 路径门控与清零 |\r
+| 按键打断 | **4359/4387** 与 `VC_Play_flag` 组合行为不变 |\r
+\r
+---\r
+\r
+## 5. 文档\r
+\r
+| 序号 | 文件 | 动作 |\r
+| --- | --- | --- |\r
+| Doc1 | `1.doc/AI吊坠(双按键+三色指示灯)交互规范_new.md` | **B4** 整节改为「已废弃」或删除,改为 **「FSM:`session` / `turn` / `tts_rx_phase`」** 一小节 + 与日志字段对照 |\r
+| Doc2 | `1.doc/AI吊坠LED状态优先级状态机.md` | 若文中引用 `VC_state=`,改为 `fsm` 字段 |\r
+\r
+---\r
+\r
+## 6. 并行代码路径(勿漏)\r
+\r
+| 路径 | 说明 |\r
+| --- | --- |\r
+| `ai_VC_coze.py` | 含完整 `VC_State_*` 与大量赋值;若构建仍引用,需**同策略迁移**或从构建剔除 |\r
+| `usr/test1.py` | 测试/样例拷贝;同步删改或标注「未维护」 |\r
+| `sleep_test.py` | 仅注释提及;清理即可 |\r
+| `2.sdk/Helios_SDK/.../ai_VC.py` | 若与 7PIN_1_5 双轨维护,合并策略二选一:**定期同步**或 **单源 + 文档说明** |\r
+\r
+---\r
+\r
+## 7. 建议实施顺序(降低一次性炸机风险)\r
+\r
+1. **F1–F5**:先加子状态 + 只读封装函数(`fsm_tts_active()` 等),内部**暂时**仍写 `VC_state`(镜像)— 可选,一般跳过。  \r
+2. **R1–R8**:替换所有读侧到 FSM。  \r
+3. **D3**:删所有写侧;跑全场景。  \r
+4. **D1–D2**:删常量与成员。  \r
+5. **Doc / 并行文件**。\r
+\r
+若团队接受更激进:**直接 D + R 一步完成**(不加镜像),则必须完整跑第 4 节矩阵。\r
+\r
+---\r
+\r
+## 8. 完成定义(DoD)\r
+\r
+- `rg "VC_state|VC_State_" ai_VC.py` 无匹配(除历史 `CHANGELOG`/注释若保留需写明)。  \r
+- 串口关键日志仍能通过 **`[FSM]` + 子状态** 还原问题。  \r
+- 交互规范 **B3** 为唯一「轮次状态」权威说明;**不再**出现「兼容旧版数值」表,除非作为 Git 历史附件归档。\r
+\r
+---\r
+\r
+*清单版本:与仓库 `ai_VC.py` 行号基于一次 `rg` 快照;合并分支后请重新 `rg` 校准。*\r
index 23bb0c5..041408a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
+try:
+    from qp_log import qp_print as print
+except Exception:
+    pass
 import uos
 name, platform = uos.uname()[1].split("=",1)
 
 import utime
-import log
 import _thread
 import usocket as socket
 import ustruct as struct
 import osTimer
 
+print("umqtt:{}".format(utime.localtime()))
 if platform != "FCM360W" and platform != "FCM362K":
     import net
     import dataCall
-
-log.basicConfig(level=log.INFO)
-mqtt_log = log.getLogger("MQTT")
+print("umqtt2:{}".format(utime.localtime()))
 # bob.xian 2023/9/20 [FCM360W may be a bit unstable when using the log module, so print is temporarily used] begin
-if platform == "FCM360W":
-    mqtt_log.info = print
-    mqtt_log.warning = print
+
 # bob.xian 2023/9/20 [FCM360W may be a bit unstable when using the log module, so print is temporarily used] end
 
 #ALIYDEVREGISTER = False
@@ -271,7 +270,7 @@ class BaseMqtt(object):
             self.sock.write(b"\xe0\0")
         # Pawn.zhou 2021/1/12 for JIRA STASR3601-2523 end
         except:
-            mqtt_log.warning("Error send mqtt_dis FAIL.")
+            print("Error send mqtt_dis FAIL.")
         try:
             if self.mqttsendlock.locked():
                 self.mqttsendlock.release()
@@ -289,12 +288,12 @@ class BaseMqtt(object):
                 _thread.delete_lock(self.mqttmsglock)
                 self.mqttmsglock = None
         except:
-            mqtt_log.warning("Error delete_lock FAIL.")
+            print("Error delete_lock FAIL.")
         utime.sleep_ms(500)
         try:
             self.sock.close()
         except:
-            mqtt_log.warning("Error socket close FAIL.")
+            print("Error socket close FAIL.")
 
     def close(self):
         self.sock.close()
@@ -339,14 +338,14 @@ class BaseMqtt(object):
             if self.wait_flag == 0:
                 self.wait_msg()
             if not self._await_pid(pid):
-                mqtt_log.warning("publish Pid[%s] QOS1 message was not received correctly"%pid)
+                print("publish Pid[%s] QOS1 message was not received correctly"%pid)
                 return False
             else:
                 # self.last_time = utime.mktime(utime.localtime())
                 self.last_time = utime.ticks_ms() // 1000
                 return True
         elif qos == 2:
-            mqtt_log.warning("publish QOS2 Not Support")
+            print("publish QOS2 Not Support")
             assert 0
 
     def __subscribe(self, topic, qos, sock):
@@ -386,7 +385,7 @@ class BaseMqtt(object):
 
     def unsubscribe(self, topic):
         if topic not in self.topic_list:
-            mqtt_log.warning("Topic Not Subscribed")
+            print("Topic Not Subscribed")
             return False
         else:
             pkt = bytearray(b"\xa2\0\0\0")
@@ -404,7 +403,7 @@ class BaseMqtt(object):
                     self.mqttsendlock.release()
                     return
                 except Exception as e:
-                    mqtt_log.warning("unsubscribe Pid[%s] ACK OSError[%s]." %(pid,  str(e)))
+                    print("unsubscribe Pid[%s] ACK OSError[%s]." %(pid,  str(e)))
                     if self.mqttsendlock.locked():
                         self.mqttsendlock.release()
                     return
@@ -442,7 +441,7 @@ class BaseMqtt(object):
                 self.mqttsendlock.release()
                 return
             except Exception as e:
-                mqtt_log.warning("Publish Pid[%s] ACK OSError[%s]." %(pid,  str(e)))
+                print("Publish Pid[%s] ACK OSError[%s]." %(pid,  str(e)))
                 if self.mqttsendlock.locked():
                     self.mqttsendlock.release()
                 return
@@ -475,7 +474,7 @@ class BaseMqtt(object):
         if res == b'\x40':  # PUBACK: save pid
             sz = self.sock.read(1)
             if sz != b"\x02":
-                mqtt_log.warning("Publish message does not return ACK correctly")
+                print("Publish message does not return ACK correctly")
                 return
             rcv_pid = self.sock.read(2)
             pid = rcv_pid[0] << 8 | rcv_pid[1]
@@ -488,7 +487,7 @@ class BaseMqtt(object):
         if res == b'\xb0':  # Unsubscribe Ack: save pid
             sz = self.sock.read(1)
             if sz != b"\x02":
-                mqtt_log.warning("Unsubscribe message does not return ACK correctly")
+                print("Unsubscribe message does not return ACK correctly")
                 return
             rcv_pid = self.sock.read(2)
             pid = rcv_pid[0] << 8 | rcv_pid[1]
@@ -505,7 +504,7 @@ class BaseMqtt(object):
             if pid in self.SERVERACK:
                 self.SERVERACK.discard(pid)
             if resp[3] == 0x80:
-                mqtt_log.warning("subscribe topic ACK Fail")
+                print("subscribe topic ACK Fail")
                 return 0x80
             else:
                 return 0x90
@@ -536,7 +535,7 @@ class BaseMqtt(object):
         try:
             self.cb(topic, msg)
         except Exception as e:
-            mqtt_log.warning("set_callback OSError[%s]." % str(e))
+            print("set_callback OSError[%s]." % str(e))
 
     # Checks whether a pending message from server is available.
     # If not, returns immediately with None. Otherwise, does
@@ -636,12 +635,12 @@ class MQTTClient(BaseMqtt):
                     self.sock.close()
                 #felix.hou 2024.5.11 add ,close() may cause reconnecting to the mqtt server to fail
                 time_info = self.logTime()
-                mqtt_log.info(
+                print(
                     "[%s] The network condition has been restored and an attempt will be made to reconnect" % time_info)
                 self.pingFlag = False
                 self.connSta = False
                 self.connect(self.clean_session)
-                mqtt_log.info("[%s] Reconnection successful!" % time_info)
+                print("[%s] Reconnection successful!" % time_info)
                 if self.manage:
                     self.subtimer.start(3000, 0, self.sub_timer_task)
                 else:
@@ -658,14 +657,14 @@ class MQTTClient(BaseMqtt):
                         try:
                             self.error_callback(e)#reconnect success
                         except Exception as e:
-                            mqtt_log.warning("error_callback OSError[%s]." % str(e))
+                            print("error_callback OSError[%s]." % str(e))
                 return
             except Exception as e:
                 if not self.timerFlag:
                     break
                 i += 1
                 time_info = self.logTime()
-                mqtt_log.warning(
+                print(
                     "[%s] The connection attempt failed [%s] and will be tried again after %d seconds." % (time_info, str(e), 5 + i))
                 utime.sleep(5 + i)
                 if platform != "FCM360W" and platform != "FCM362K":
@@ -673,11 +672,11 @@ class MQTTClient(BaseMqtt):
                     if net_sta != -1 and ((net_sta[1][0] == 1) or (net_sta[1][0] == 5)):
                         call_state = dataCall.getInfo(1, 0)
                         if call_state == -1:
-                            mqtt_log.info("LTE datacall Fail.")
+                            print("LTE datacall Fail.")
                         elif call_state[2][0] != 1:
-                            mqtt_log.info("LTE datacall IPV4 Fail.")
+                            print("LTE datacall IPV4 Fail.")
                     else:
-                        mqtt_log.info("LTE Net unregistered.")
+                        print("LTE Net unregistered.")
 
     def publish(self, topic, msg, retain=False, qos=0):
         while True:
@@ -690,7 +689,7 @@ class MQTTClient(BaseMqtt):
                 self.mqttsendlock.release()
                 return ret
             except Exception as e:
-                mqtt_log.warning("Publish Fail OSError[%s]." % str(e))
+                print("Publish Fail OSError[%s]." % str(e))
                 if self.mqttsendlock.locked():
                     self.mqttsendlock.release()
                 return False
@@ -721,9 +720,9 @@ class MQTTClient(BaseMqtt):
                 time_info = self.logTime()
                 self.wait_flag = 0
                 if self.ping == True:
-                    mqtt_log.warning("[%s] wait msg, send ping timeout. Trying to reconnect" % time_info)
+                    print("[%s] wait msg, send ping timeout. Trying to reconnect" % time_info)
                 else:
-                    mqtt_log.warning("[%s] wait msg OSError[%s] . Trying to reconnect" % (time_info, str(e)))
+                    print("[%s] wait msg OSError[%s] . Trying to reconnect" % (time_info, str(e)))
                 utime.sleep(1)
                 self.base_reconnect()
 
@@ -776,7 +775,7 @@ class MQTTClient(BaseMqtt):
                         utime.sleep(5)
                         continue
                     if self.PING == True:
-                        mqtt_log.warning("[%s] Send ping timeout 2. Trying to reconnect" % time)
+                        print("[%s] Send ping timeout 2. Trying to reconnect" % time)
                         if not self.reconn:
                             if self.mqttsendlock is not None and self.mqttsendlock.locked():
                                 self.mqttsendlock.release()
@@ -784,7 +783,7 @@ class MQTTClient(BaseMqtt):
                             raise Exception("Send ping timeout.")
                         else:
                             if self.pingnum <= 0:
-                                mqtt_log.warning("[%s] Send ping timeout 1. Trying to reconnect" % time)
+                                print("[%s] Send ping timeout 1. Trying to reconnect" % time)
                                 self.sock.settimeout(3)
                                 self.pingFlag = False
                                 if self.mqttsendlock is not None and self.mqttsendlock.locked():
@@ -792,7 +791,7 @@ class MQTTClient(BaseMqtt):
                                 self.looptask_id = None
                                 break
                             else:
-                                mqtt_log.warning("[%s] Trying to resend ping(%d)" % (time, self.pingnum))
+                                print("[%s] Trying to resend ping(%d)" % (time, self.pingnum))
                                 self.pingnum = self.pingnum - 1
                     while True:
                         if self.mqttsendlock is not None and self.mqttsendlock.locked():
@@ -826,9 +825,9 @@ class MQTTClient(BaseMqtt):
                 # Whether to use the built-in reconnect mechanism
                 time_info = self.logTime()
                 if self.PING == True:
-                    mqtt_log.warning("[%s] Send ping timeout 3. Trying to reconnect" % time_info)
+                    print("[%s] Send ping timeout 3. Trying to reconnect" % time_info)
                 else:
-                    mqtt_log.warning("[%s] Send ping, Network exception. Trying to reconnect" % time_info)
+                    print("[%s] Send ping, Network exception. Trying to reconnect" % time_info)
                 utime.sleep(2)
                 self.base_reconnect()
                 break
@@ -838,3 +837,5 @@ class MQTTClient(BaseMqtt):
         time_info = "%d-%d-%d %d:%d:%d" % (
             log_time[0], log_time[1], log_time[2], log_time[3], log_time[4], log_time[5],)
         return time_info
+
+print("umqtt3:{}".format(utime.localtime()))
\ No newline at end of file
index d182db1..7bfe1d2 100644 (file)
@@ -1,21 +1,47 @@
 @echo off\r
 setlocal EnableDelayedExpansion\r
 cd /d "%~dp0"\r
+@REM chcp 65001 >nul 2>&1\r
 \r
 set "PATH=D:\helios\bin;%PATH%"\r
 set "COMSPEC=%SystemRoot%\System32\cmd.exe"\r
 \r
+if /i "%~1"=="help" goto :show_help\r
+if /i "%~1"=="/?" goto :show_help\r
+if /i "%~1"=="-h" goto :show_help\r
+\r
 if "%~1"=="" (\r
   set "PRODUCT_NAME=k8"\r
 ) else (\r
   set "PRODUCT_NAME=%~1"\r
 )\r
 \r
-set "CORE_BASE=project\7PIN_1_5\sdk\services\microPython\ports\quectel\core"\r
+REM ¹¤³ÌÃû£¨¶ÔÓ¦ project\¹¤³ÌÃû\£©£¬Ä¬ÈÏ 7PIN_1_5£»µÚ3²ÎÊý¿É¸²¸Ç\r
+set "PROJ=7PIN_1_5"\r
+set "BOARD=EG810MCN_GA_VOLTE"\r
+if not "%~3"=="" set "PROJ=%~3"\r
+\r
+REM µÚ2²ÎÊý£ºqp_log  1=¶³½á qp_log.mpy ²¢Óàqp_print ´ò´®¿ÚÈÕÖ¾£»0=²»¶³½á£¬»ØÍË print\r
+set "QP_LOG_ARG=%~2"\r
+if "!QP_LOG_ARG!"=="" set "QP_LOG_ARG=1"\r
+set "QP_LOG_EN=1"\r
+if /i "!QP_LOG_ARG!"=="0" set "QP_LOG_EN=0"\r
+if /i "!QP_LOG_ARG!"=="off" set "QP_LOG_EN=0"\r
+if /i "!QP_LOG_ARG!"=="no" set "QP_LOG_EN=0"\r
+if /i "!QP_LOG_ARG!"=="n" set "QP_LOG_EN=0"\r
+if /i "!QP_LOG_ARG!"=="false" set "QP_LOG_EN=0"\r
+\r
+powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\patch_qp_log.ps1" -Enable:"!QP_LOG_EN!" -Root:"%CD%" -Project:"!PROJ!" -Board:"!BOARD!"\r
+if errorlevel 1 (\r
+  echo [´íÎó] ÉèÖàCONFIG_QP_LOG Ê§°Ü\r
+  exit /b 1\r
+)\r
+\r
+set "CORE_BASE=project\!PROJ!\sdk\services\microPython\ports\quectel\core"\r
 set "INI=%CORE_BASE%\products\product_!PRODUCT_NAME!.ini"\r
 \r
 if not exist "!INI!" (\r
-  echo [错误] 找不到 product.ini: !INI!\r
+  echo [´íÎó] ÕÒ²»µ½ product.ini: !INI!\r
   exit /b 1\r
 )\r
 \r
@@ -29,15 +55,15 @@ for /f "usebackq tokens=1,* delims==" %%a in ("!INI!") do (
 )\r
 \r
 if "!DEV_TYPE!"=="" (\r
-  echo [错误] product.ini 中未定义 DEV_TYPE\r
+  echo [´íÎó] product.ini ÖÐ䶨Òå DEV_TYPE\r
   exit /b 1\r
 )\r
 if "!SRV_TYPE!"=="" (\r
-  echo [错误] product.ini 中未定义 SRV_TYPE\r
+  echo [´íÎó] product.ini ÖÐ䶨Òå SRV_TYPE\r
   exit /b 1\r
 )\r
 if "!VERSION!"=="" (\r
-  echo [错误] product.ini 中未定义 VERSION\r
+  echo [´íÎó] product.ini ÖÐ䶨Òå VERSION\r
   exit /b 1\r
 )\r
 set "VERSION=!VERSION:"=!"\r
@@ -48,46 +74,46 @@ set "SRC_CFG=!CORE_BASE!\products\configs\!DEV_TYPE!\default_config.py"
 set "SRC_SRV=!CORE_BASE!\products\srv_configs\!SRV_TYPE!\default_srv.py"\r
 \r
 if not exist "!SRC_CFG!" (\r
-  echo [错误] 找不到设备配置: !SRC_CFG!\r
+  echo [´íÎó] ÕÒ²»µ½É豸ÅäÖÃ: !SRC_CFG!\r
   exit /b 1\r
 )\r
 if not exist "!SRC_SRV!" (\r
-  echo [错误] 找不到服务器配置: !SRC_SRV!\r
+  echo [´íÎó] ÕÒ²»µ½·þÎñÆ÷ÅäÖÃ: !SRC_SRV!\r
   exit /b 1\r
 )\r
 \r
-echo [信息] 覆盖 core\default_config.py ^<- products\configs\!DEV_TYPE!\r
+echo [ÐÅÏ¢] ¸²¸Ç core\default_config.py ^<- products\configs\!DEV_TYPE!\r
 copy /y "!SRC_CFG!" "!CORE_BASE!\default_config.py" >nul\r
 if errorlevel 1 (\r
-  echo [错误] copy default_config 失败\r
+  echo [´íÎó] copy default_config Ê§°Ü\r
   exit /b 1\r
 )\r
 \r
-echo [信息] 覆盖 core\default_srv.py ^<- products\srv_configs\!SRV_TYPE!\r
+echo [ÐÅÏ¢] ¸²¸Ç core\default_srv.py ^<- products\srv_configs\!SRV_TYPE!\r
 copy /y "!SRC_SRV!" "!CORE_BASE!\default_srv.py" >nul\r
 if errorlevel 1 (\r
-  echo [错误] copy default_srv 失败\r
+  echo [´íÎó] copy default_srv Ê§°Ü\r
   exit /b 1\r
 )\r
 \r
 cd Helios_SDK\r
 helios clean\r
 if errorlevel 1 (\r
-  echo [错误] helios clean 失败\r
+  echo [´íÎó] helios clean Ê§°Ü\r
   cd ..\r
   exit /b 1\r
 )\r
 cd ..\r
 \r
-call new.bat 7PIN_1_5 !PRODUCT_NAME!\r
+call new.bat !PROJ! !PRODUCT_NAME!\r
 if errorlevel 1 (\r
-  echo [错误] 编译失败 ^(new.bat^)\r
+  echo [´íÎó] ±àÒëʧ°Ü ^(new.bat^)\r
   exit /b 1\r
 )\r
 \r
-set "REL_DIR=Helios_SDK\opt\release\EG810MCN_GA_VOLTE"\r
+set "REL_DIR=Helios_SDK\opt\release\!BOARD!"\r
 if not exist "!REL_DIR!" (\r
-  echo [错误] 编译产物目录不存在: !REL_DIR!\r
+  echo [´íÎó] ±àÒë²úÎïĿ¼²»´æÔÚ: !REL_DIR!\r
   exit /b 1\r
 )\r
 \r
@@ -101,15 +127,33 @@ for /f "delims=" %%h in ('git -C "%~dp0.." rev-parse --short HEAD 2^>nul') do se
 if "!COMMIT!"=="" set "COMMIT=nogit"\r
 \r
 set "NEWNAME=!PRODUCT_NAME!_!DEV_TYPE!_!SRV_TYPE!_!VERSION!_!BUILD_TIME!_!COMMIT!"\r
+if "!QP_LOG_EN!"=="0" set "NEWNAME=!NEWNAME!_noqplog"\r
 \r
 if not exist "release" mkdir "release"\r
 \r
-echo [信息] 复制产物到 release\!NEWNAME!\r
+echo [ÐÅÏ¢] ¸´ÖƲúÎïµ½ release\!NEWNAME!\r
 xcopy /E /I /Y "!REL_DIR!" "release\!NEWNAME!\" >nul\r
 if errorlevel 1 (\r
-  echo [错误] xcopy 失败\r
+  echo [´íÎó] xcopy Ê§°Ü\r
   exit /b 1\r
 )\r
 \r
-echo [完成] release\!NEWNAME!\r
+echo [Íê³É] release\!NEWNAME!\r
+exit /b 0\r
+\r
+:show_help\r
+echo.\r
+echo Ó÷¨: new_clean.bat [²úÆ·Ãû] [qp_log] [¹¤³ÌÃû]\r
+echo   ²úÆ·Ãû  ¶ÔÓ¦ products\product_²úÆ·Ãû.ini£¬Ä¬ÈÏ k8\r
+echo   qp_log  ÊÇ·ñ±àÒë¶³½á qp_log ²¢Í¨¹ý qp_print ´ò´®¿ÚÈÕÖ¾\r
+echo           1 / on / y / yes   ¿ªÆô£¨Ä¬ÈÏ£©\r
+echo           0 / off / n / no   ¹Ø±Õ\r
+echo   ¹¤³ÌÃû  ¶ÔÓ¦ project\¹¤³ÌÃû\£¬Ä¬ÈÏ 7PIN_1_5\r
+echo.\r
+echo Ê¾Àý:\r
+echo   new_clean.bat k8\r
+echo   new_clean.bat k8 1\r
+echo   new_clean.bat k8 0\r
+echo   new_clean.bat k8 1 7PIN\r
+echo.\r
 exit /b 0\r
index c48d290..b055bb7 100644 (file)
@@ -1,50 +1,50 @@
-/*
- *
- * Automatically generated file; DO NOT EDIT.
- * Helios SDK Configuration
- *
- */
-
-
-#define PROJECT_NAME_G3_7PIN  1  //项目宏定义
-
-
-// #define CONFIG_LVGL_SPAN 1
-// #define CONFIG_LVGL_FS_HELIOS 1
-// #define CONFIG_LVGL_GIF_DECODE 1
-// #define CONFIG_LVGL_SPINNER 1
-//#define CONFIG_LCD 1
-// #define CONFIG_LVGL_ANIMIMG 1
-// #define CONFIG_LVGL_KEYBOARD 1
-// #define CONFIG_LVGL_CALENDAR 1
-// #define CONFIG_LVGL_LED 1
-#define CONFIG_VIOCE_CALL 1
-// #define CONFIG_LVGL_FS_EXTFLASH_HELIOS 1
-// #define CONFIG_LVGL_TABVIEW 1
-//#define CONFIG_LVGL_LINE 1
-//#define CONFIG_LVGL 1
-// #define CONFIG_LVGL_CHART 1
-// #define CONFIG_LVGL_LIST 1
-// #define CONFIG_LVGL_MSGBOX 1
-// #define CONFIG_LVGL_ARC 1
-// #define CONFIG_LVGL_METER 1
-//#define CONFIG_LVGL_JPG_DECODE 1
-// #define CONFIG_LVGL_PNG_DECODE 1
-// #define CONFIG_LVGL_IMGBTN 1
-//#define CONFIG_GH301x 1
-//#define CONFIG_HX3918 1
-#define CONFIG_MBEDTLS 1
-//#define CONFIG_SC7A20 1
-// #define CONFIG_LVGL_WIN 1
-// #define CONFIG_LVGL_COLORWHEEL 1
-// #define CONFIG_LVGL_CANVAS 1
-// #define CONFIG_LVGL_SPINBOX 1
-// #define CONFIG_LVGL_QRCODE 1
-// #define CONFIG_LVGL_TILEVIEW 1
-// #define CONFIG_LVGL_HANDLER_TASK_PRIORITY 80
-//#define CONFIG_CAMERA 1
-//#define CONFIG_JPEG 1
-#define CONFIG_CAMERA_SAVEPHOTOS 1
-#define CONFIG_VAD 1
-//菊风视频通话
-//#define CONFIG_juphoon 1
+/*\r
+ *\r
+ * Automatically generated file; DO NOT EDIT.\r
+ * Helios SDK Configuration\r
+ *\r
+ */\r
+\r
+\r
+#define PROJECT_NAME_G3_7PIN  1  //椤圭洰瀹忓畾涔?\r
+\r
+// #define CONFIG_LVGL_SPAN 1\r
+// #define CONFIG_LVGL_FS_HELIOS 1\r
+// #define CONFIG_LVGL_GIF_DECODE 1\r
+// #define CONFIG_LVGL_SPINNER 1\r
+//#define CONFIG_LCD 1\r
+// #define CONFIG_LVGL_ANIMIMG 1\r
+// #define CONFIG_LVGL_KEYBOARD 1\r
+// #define CONFIG_LVGL_CALENDAR 1\r
+// #define CONFIG_LVGL_LED 1\r
+#define CONFIG_VIOCE_CALL 1\r
+// #define CONFIG_LVGL_FS_EXTFLASH_HELIOS 1\r
+// #define CONFIG_LVGL_TABVIEW 1\r
+//#define CONFIG_LVGL_LINE 1\r
+//#define CONFIG_LVGL 1\r
+// #define CONFIG_LVGL_CHART 1\r
+// #define CONFIG_LVGL_LIST 1\r
+// #define CONFIG_LVGL_MSGBOX 1\r
+// #define CONFIG_LVGL_ARC 1\r
+// #define CONFIG_LVGL_METER 1\r
+//#define CONFIG_LVGL_JPG_DECODE 1\r
+// #define CONFIG_LVGL_PNG_DECODE 1\r
+// #define CONFIG_LVGL_IMGBTN 1\r
+//#define CONFIG_GH301x 1\r
+//#define CONFIG_HX3918 1\r
+#define CONFIG_MBEDTLS 1\r
+//#define CONFIG_SC7A20 1\r
+// #define CONFIG_LVGL_WIN 1\r
+// #define CONFIG_LVGL_COLORWHEEL 1\r
+// #define CONFIG_LVGL_CANVAS 1\r
+// #define CONFIG_LVGL_SPINBOX 1\r
+// #define CONFIG_LVGL_QRCODE 1\r
+// #define CONFIG_LVGL_TILEVIEW 1\r
+// #define CONFIG_LVGL_HANDLER_TASK_PRIORITY 80\r
+//#define CONFIG_CAMERA 1\r
+//#define CONFIG_JPEG 1\r
+#define CONFIG_CAMERA_SAVEPHOTOS 1\r
+#define CONFIG_VAD 1\r
+#define CONFIG_QP_LOG 1\r
+//鑿婇瑙嗛閫氳瘽\r
+//#define CONFIG_juphoon 1\r
index a1d5967..eb60e7a 100644 (file)
@@ -1,66 +1,67 @@
-#
-# Automatically generated file; DO NOT EDIT.
-# Helios SDK Configuration
-#
-PROJECT_NAME_G3_7PIN=y
-#
-# System Configuration
-#
-CONFIG_MBEDTLS=y
-
-#
-# EG810MCN_GA_VOLTE
-#
-# CONFIG_LVGL=y
-
-#
-# lvgl option
-#
-# CONFIG_LVGL_PNG_DECODE=y
-# CONFIG_LVGL_GIF_DECODE is not set
-# CONFIG_LVGL_JPG_DECODE=y
-#CONFIG_LVGL_FS_HELIOS=y
-# CONFIG_LVGL_FS_SD_HELIOS is not set
-# CONFIG_LVGL_FS_EXTFLASH_HELIOS=y
-# CONFIG_LVGL_CUSTOMER_FONT_SUPPORT is not set
-# CONFIG_LVGL_QRCODE=y
-# CONFIG_LVGL_FONT is not set
-# CONFIG_LVGL_ARC is not set
-#CONFIG_LVGL_IMGBTN=y
-#CONFIG_LVGL_LINE=y
-#CONFIG_LVGL_LIST=y
-# CONFIG_LVGL_CHART is not set
-# CONFIG_LVGL_LED is not set
-# CONFIG_LVGL_CANVAS=y
-# CONFIG_LVGL_WIN=y
-# CONFIG_LVGL_TABVIEW is not set
-# CONFIG_LVGL_TILEVIEW=y
-# CONFIG_LVGL_MSGBOX=y
-# CONFIG_LVGL_METER is not set
-# CONFIG_LVGL_CALENDAR is not set
-# CONFIG_LVGL_SPINNER is not set
-# CONFIG_LVGL_SPINBOX is not set
-# CONFIG_LVGL_ANIMIMG is not set
-# CONFIG_LVGL_COLORWHEEL is not set
-# CONFIG_LVGL_KEYBOARD=y
-# CONFIG_LVGL_SPAN=y
-# CONFIG_LVGL_HANDLER_TASK_PRIORITY=80
-# CONFIG_QRCODE is not set
-#CONFIG_CAMERA=y
-# CONFIG_CAMERA_DECODE is not set
-CONFIG_CAMERA_SAVEPHOTOS=y
-# CONFIG_QUECTHING is not set
-# CONFIG_SPINAND is not set
-# CONFIG_RTMP is not set
-#CONFIG_JPEG=y
-# CONFIG_SPI_SDCARD is not set
-CONFIG_VIOCE_CALL=y
-# CONFIG_TTS is not set
-#CONFIG_LCD=y
-# CONFIG_UART_SLIP is not set
-# CONFIG_POC is not set
-#CONFIG_GH301x=y
-#CONFIG_HX3918=y
-#CONFIG_SC7A20=y
-CONFIG_VAD=y
-#CONFIG_juphoon=y
+#\r
+# Automatically generated file; DO NOT EDIT.\r
+# Helios SDK Configuration\r
+#\r
+PROJECT_NAME_G3_7PIN=y\r
+#\r
+# System Configuration\r
+#\r
+CONFIG_MBEDTLS=y\r
+\r
+#\r
+# EG810MCN_GA_VOLTE\r
+#\r
+# CONFIG_LVGL=y\r
+\r
+#\r
+# lvgl option\r
+#\r
+# CONFIG_LVGL_PNG_DECODE=y\r
+# CONFIG_LVGL_GIF_DECODE is not set\r
+# CONFIG_LVGL_JPG_DECODE=y\r
+#CONFIG_LVGL_FS_HELIOS=y\r
+# CONFIG_LVGL_FS_SD_HELIOS is not set\r
+# CONFIG_LVGL_FS_EXTFLASH_HELIOS=y\r
+# CONFIG_LVGL_CUSTOMER_FONT_SUPPORT is not set\r
+# CONFIG_LVGL_QRCODE=y\r
+# CONFIG_LVGL_FONT is not set\r
+# CONFIG_LVGL_ARC is not set\r
+#CONFIG_LVGL_IMGBTN=y\r
+#CONFIG_LVGL_LINE=y\r
+#CONFIG_LVGL_LIST=y\r
+# CONFIG_LVGL_CHART is not set\r
+# CONFIG_LVGL_LED is not set\r
+# CONFIG_LVGL_CANVAS=y\r
+# CONFIG_LVGL_WIN=y\r
+# CONFIG_LVGL_TABVIEW is not set\r
+# CONFIG_LVGL_TILEVIEW=y\r
+# CONFIG_LVGL_MSGBOX=y\r
+# CONFIG_LVGL_METER is not set\r
+# CONFIG_LVGL_CALENDAR is not set\r
+# CONFIG_LVGL_SPINNER is not set\r
+# CONFIG_LVGL_SPINBOX is not set\r
+# CONFIG_LVGL_ANIMIMG is not set\r
+# CONFIG_LVGL_COLORWHEEL is not set\r
+# CONFIG_LVGL_KEYBOARD=y\r
+# CONFIG_LVGL_SPAN=y\r
+# CONFIG_LVGL_HANDLER_TASK_PRIORITY=80\r
+# CONFIG_QRCODE is not set\r
+#CONFIG_CAMERA=y\r
+# CONFIG_CAMERA_DECODE is not set\r
+CONFIG_CAMERA_SAVEPHOTOS=y\r
+# CONFIG_QUECTHING is not set\r
+# CONFIG_SPINAND is not set\r
+# CONFIG_RTMP is not set\r
+#CONFIG_JPEG=y\r
+# CONFIG_SPI_SDCARD is not set\r
+CONFIG_VIOCE_CALL=y\r
+# CONFIG_TTS is not set\r
+#CONFIG_LCD=y\r
+# CONFIG_UART_SLIP is not set\r
+# CONFIG_POC is not set\r
+#CONFIG_GH301x=y\r
+#CONFIG_HX3918=y\r
+#CONFIG_SC7A20=y\r
+CONFIG_VAD=y\r
+CONFIG_QP_LOG=y\r
+#CONFIG_juphoon=y\r
index 013850e..a935438 100644 (file)
@@ -1156,6 +1156,11 @@ frozen_mpy_list =        $(BUILD)/_boot.mpy \
 endif
 endif
 
+# CONFIG_QP_LOG:menuconfig / config.mk 控制是否冻结 qp_log(见 services/microPython/config/config.in)
+ifeq (y,$(CONFIG_QP_LOG))
+frozen_mpy_list += $(BUILD)/qp_log.mpy
+endif
+
 $(BUILD)/_frozen_mpy.c: $(frozen_mpy_list) $(HEADER_BUILD)/qstrdefs.generated.h
        @$(ECHO) "MISC freezing bytecode"
        $(Q)$(MPY_TOOL) -f -q $(HEADER_BUILD)/qstrdefs.preprocessed.h $(frozen_mpy_list) > $@
index dbc250e..5c5dffa 100644 (file)
@@ -1,4 +1,8 @@
 from common import Abstract, LogAdapter
+try:
+    from qp_log import qp_print as print
+except Exception:
+    pass
 import EventMesh
 import uwebsocket
 import usys
index d79939f..9f4c002 100644 (file)
@@ -1,3 +1,7 @@
+try:\r
+    from qp_log import qp_print as print\r
+except Exception:\r
+    pass\r
 from common import Abstract\r
 import osTimer\r
 import EventMesh\r
diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/qp_log.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/qp_log.py
new file mode 100644 (file)
index 0000000..d2c81cf
--- /dev/null
@@ -0,0 +1,65 @@
+"""QuecPython 标准 log 封装:默认 DEBUG,作为 print 的轻量替代。
+
+日志输出到 UART4(与 `log.set_output` 一致)。
+参见:https://developer.quectel.com/doc/quecpython/Application_guide/zh/system/log.html
+
+推荐在需要统一日志的模块顶部使用(不修改 builtins)::
+
+    from qp_log import qp_print as print
+"""
+
+_orig_print = print
+_logger = None
+_log_uart = None
+_log_ready = 0  # 0 未初始化,1 成功,-1 不可用
+_in_qp_print = 0
+
+# UART4 日志口波特率(与业务里其它 UART4 用法冲突时需自行统一)
+QP_LOG_UART_BAUD = 115200
+
+
+def _ensure():
+    global _logger, _log_ready, _log_uart
+    if _log_ready != 0:
+        return _logger
+    _log_ready = -1
+    try:
+        import log as _log
+        from machine import UART
+
+        _log.basicConfig(level=_log.DEBUG)
+        _logger = _log.getLogger("qp")
+        if _log_uart is None:
+            _log_uart = UART(UART.UART0, QP_LOG_UART_BAUD, 8, 0, 1, 0)
+        _log.set_output(_log_uart)
+        _log_ready = 1
+    except Exception:
+        _logger = None
+    return _logger
+
+
+def qp_print(*args, **kwargs):
+    global _in_qp_print
+    if _in_qp_print:
+        return _orig_print(*args, **kwargs)
+    sep = kwargs.get("sep", " ")
+    end = kwargs.get("end", "\n")
+    if kwargs.get("file") is not None or kwargs.get("flush", False):
+        return _orig_print(*args, **kwargs)
+    _in_qp_print = 1
+    try:
+        try:
+            msg = sep.join(str(a) for a in args) + str(end)
+            msg = msg.rstrip("\r\n")
+        except Exception:
+            msg = "<?qp_print?>"
+        lg = _ensure()
+        if lg is not None:
+            try:
+                lg.debug(msg)
+                return
+            except Exception:
+                pass
+        return _orig_print(*args, sep=sep, end=end)
+    finally:
+        _in_qp_print = 0
index 049ba1d..bde7682 100644 (file)
@@ -6,7 +6,7 @@
  */\r
 \r
 \r
-#define PROJECT_NAME_G3_7PIN  1  //项目宏定义\r
+#define PROJECT_NAME_G3_7PIN  1  //椤圭洰瀹忓畾涔?\r
 \r
 \r
 // #define CONFIG_LVGL_SPAN 1\r
@@ -46,5 +46,5 @@
 //#define CONFIG_JPEG 1\r
 #define CONFIG_CAMERA_SAVEPHOTOS 1\r
 #define CONFIG_VAD 1\r
-//菊风视频通话\r
+//鑿婇瑙嗛閫氳瘽\r
 //#define CONFIG_juphoon 1\r
index 33a3a68..54bf2e1 100644 (file)
Binary files a/2.sdk/project/7PIN_1_5/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/images/firmware_base_release_pack.zip and b/2.sdk/project/7PIN_1_5/sdk/system/platform/ASR_1606/boards/EG810MCN_GA_VOLTE/images/firmware_base_release_pack.zip differ
diff --git a/2.sdk/tools/patch_qp_log.ps1 b/2.sdk/tools/patch_qp_log.ps1
new file mode 100644 (file)
index 0000000..c01c5a6
--- /dev/null
@@ -0,0 +1,96 @@
+param(
+    [Parameter(Mandatory = $true)]
+    [string]$Enable,
+
+    [Parameter(Mandatory = $true)]
+    [string]$Root,
+
+    [Parameter(Mandatory = $true)]
+    [string]$Project,
+
+    [string]$Board = 'EG810MCN_GA_VOLTE'
+)
+
+$Enable = [string]$Enable
+if ($Enable -notin @('0', '1')) {
+    Write-Error "Enable must be 0 or 1, got: $Enable"
+    exit 1
+}
+$qpLogOn = ($Enable -eq '1')
+
+function Patch-Mk([string]$path) {
+    if (-not (Test-Path -LiteralPath $path)) {
+        Write-Error "missing $path"
+        exit 2
+    }
+    $lines = @(Get-Content -LiteralPath $path | Where-Object { $_ -notmatch 'CONFIG_QP_LOG' })
+    $out = New-Object System.Collections.Generic.List[string]
+    $done = $false
+    foreach ($line in $lines) {
+        [void]$out.Add($line)
+        if (-not $done -and $line -match '^CONFIG_VAD=y') {
+            if ($qpLogOn) {
+                [void]$out.Add('CONFIG_QP_LOG=y')
+            } else {
+                [void]$out.Add('# CONFIG_QP_LOG is not set')
+            }
+            $done = $true
+        }
+    }
+    if (-not $done) {
+        if ($qpLogOn) {
+            [void]$out.Add('CONFIG_QP_LOG=y')
+        } else {
+            [void]$out.Add('# CONFIG_QP_LOG is not set')
+        }
+    }
+    [System.IO.File]::WriteAllLines($path, $out.ToArray())
+}
+
+function Patch-H([string]$path) {
+    if (-not (Test-Path -LiteralPath $path)) {
+        Write-Error "missing $path"
+        exit 2
+    }
+    $lines = @(Get-Content -LiteralPath $path | Where-Object { $_ -notmatch 'CONFIG_QP_LOG' })
+    $out = New-Object System.Collections.Generic.List[string]
+    $done = $false
+    foreach ($line in $lines) {
+        [void]$out.Add($line)
+        if (-not $done -and $line -match '^#define CONFIG_VAD 1') {
+            if ($qpLogOn) {
+                [void]$out.Add('#define CONFIG_QP_LOG 1')
+            }
+            $done = $true
+        }
+    }
+    if ($qpLogOn -and -not $done) {
+        [void]$out.Add('#define CONFIG_QP_LOG 1')
+    }
+    [System.IO.File]::WriteAllLines($path, $out.ToArray())
+}
+
+$boardCfg = [System.IO.Path]::Combine(
+    $Root, 'project', $Project,
+    'sdk', 'system', 'platform', 'ASR_1606', 'boards', $Board, 'config'
+)
+
+$mk = @(
+    (Join-Path $Root 'Helios_SDK\config\config.mk'),
+    (Join-Path $boardCfg 'config.mk')
+)
+$hk = @(
+    (Join-Path $Root 'Helios_SDK\config\autoconf.h'),
+    (Join-Path $boardCfg 'autoconf.h')
+)
+
+Write-Host "Project=$Project Board=$Board"
+
+foreach ($p in $mk) { Patch-Mk $p }
+foreach ($p in $hk) { Patch-H $p }
+
+if ($qpLogOn) {
+    Write-Host "CONFIG_QP_LOG enabled (qp_log.mpy in frozen_mpy_list)"
+} else {
+    Write-Host "CONFIG_QP_LOG disabled (no qp_log in frozen_mpy_list)"
+}