--- /dev/null
+2.sdk/release/**
\ No newline at end of file
--- /dev/null
+# 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` 是否已刷新。
+
+---
+
--- /dev/null
+# `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
# 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
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()
_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()
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):
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")
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
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
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]
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]
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
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
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:
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":
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:
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
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()
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()
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():
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():
# 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
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
@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
)\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
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
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
-/*
- *
- * 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
-#
-# 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
+try:\r
+ from qp_log import qp_print as print\r
+except Exception:\r
+ pass\r
+\r
import _thread\r
import usys\r
import versions\r
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) > $@
from common import Abstract, LogAdapter
+try:
+ from qp_log import qp_print as print
+except Exception:
+ pass
import EventMesh
import uwebsocket
import usys
#ai闹钟
+try:
+ from qp_log import qp_print as print
+except Exception:
+ pass
+
+
import EventMesh
import usys
import ujson
+try:\r
+ from qp_log import qp_print as print\r
+except Exception:\r
+ pass\r
+\r
+\r
from common import Abstract\r
import machine\r
import osTimer\r
+try:
+ from qp_log import qp_print as print
+except Exception:
+ pass
+
import sms
import atcmd
import net
+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
DEV_TYPE=7PIN
SRV_TYPE=beike_dev
-VERSION=V1.1
+VERSION=V1.2
+try:
+ from qp_log import qp_print as print
+except Exception:
+ pass
import ujson
import utime
import EventMesh
--- /dev/null
+"""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
-Version="V1.1" \r
-Build_Time="2026-05-08 10:22:58" \r
+Version="V1.2" \r
+Build_Time="2026-05-15 15:13:48" \r
*/\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
//#define CONFIG_JPEG 1\r
#define CONFIG_CAMERA_SAVEPHOTOS 1\r
#define CONFIG_VAD 1\r
-//菊风视频通话\r
+//鑿婇瑙嗛閫氳瘽\r
//#define CONFIG_juphoon 1\r
#CONFIG_HX3918=y\r
#CONFIG_SC7A20=y\r
CONFIG_VAD=y\r
+# CONFIG_QP_LOG is not set\r
#CONFIG_juphoon=y\r
--- /dev/null
+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)"
+}