You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

以下是一份基于代码实现整理的 WebSocket 通信协议文档,概述客户端(设备)与服务器之间如何通过 WebSocket 进行交互。该文档仅基于所提供的代码推断,实际部署时可能需要结合服务器端实现进行进一步确认或补充。


1. 总体流程概览

  1. 设备端初始化

    • 设备上电、初始化 Application
      • 初始化音频编解码器、显示屏、LED 等
      • 连接网络
      • 创建并初始化实现 Protocol 接口的 WebSocket 协议实例(WebsocketProtocol
    • 进入主循环等待事件(音频输入、音频输出、调度任务等)。
  2. 建立 WebSocket 连接

    • 当设备需要开始语音会话时(例如用户唤醒、手动按键触发等),调用 OpenAudioChannel()
      • 根据编译配置获取 WebSocket URLCONFIG_WEBSOCKET_URL
      • 设置若干请求头(Authorization, Protocol-Version, Device-Id, Client-Id
      • 调用 Connect() 与服务器建立 WebSocket 连接
  3. 发送客户端 “hello” 消息

    • 连接成功后,设备会发送一条 JSON 消息,示例结构如下:
    {
      "type": "hello",
      "version": 1,
      "transport": "websocket",
      "audio_params": {
        "format": "opus",
        "sample_rate": 16000,
        "channels": 1,
        "frame_duration": 60
      }
    }
    
    • 其中 "frame_duration" 的值对应 OPUS_FRAME_DURATION_MS(例如 60ms
  4. 服务器回复 “hello”

    • 设备等待服务器返回一条包含 "type": "hello" 的 JSON 消息,并检查 "transport": "websocket" 是否匹配。
    • 如果匹配,则认为服务器已就绪,标记音频通道打开成功。
    • 如果在超时时间(默认 10 秒)内未收到正确回复,认为连接失败并触发网络错误回调。
  5. 后续消息交互

    • 设备端和服务器端之间可发送两种主要类型的数据:

      1. 二进制音频数据Opus 编码)
      2. 文本 JSON 消息用于传输聊天状态、TTS/STT 事件、IoT 命令等)
    • 在代码里,接收回调主要分为:

      • OnData(...):
        • binarytrue 时,认为是音频帧;设备会将其当作 Opus 数据进行解码。
        • binaryfalse 时,认为是 JSON 文本,需要在设备端用 cJSON 进行解析并做相应业务逻辑处理(见下文消息结构)。
    • 当服务器或网络出现断连,回调 OnDisconnected() 被触发:

      • 设备会调用 on_audio_channel_closed_(),并最终回到空闲状态。
  6. 关闭 WebSocket 连接

    • 设备在需要结束语音会话时,会调用 CloseAudioChannel() 主动断开连接,并回到空闲状态。
    • 或者如果服务器端主动断开,也会引发同样的回调流程。

2. 通用请求头

在建立 WebSocket 连接时,代码示例中设置了以下请求头:

  • Authorization: 用于存放访问令牌,形如 "Bearer <token>"
  • Protocol-Version: 固定示例中为 "1"
  • Device-Id: 设备物理网卡 MAC 地址
  • Client-Id: 设备 UUID可在应用中唯一标识设备

这些头会随着 WebSocket 握手一起发送到服务器,服务器可根据需求进行校验、认证等。


3. JSON 消息结构

WebSocket 文本帧以 JSON 方式传输,以下为常见的 "type" 字段及其对应业务逻辑。若消息里包含未列出的字段,可能为可选或特定实现细节。

3.1 客户端→服务器

  1. Hello

    • 连接成功后,由客户端发送,告知服务器基本参数。
    • 例:
      {
        "type": "hello",
        "version": 1,
        "transport": "websocket",
        "audio_params": {
          "format": "opus",
          "sample_rate": 16000,
          "channels": 1,
          "frame_duration": 60
        }
      }
      
  2. Listen

    • 表示客户端开始或停止录音监听。
    • 常见字段:
      • "session_id":会话标识
      • "type": "listen"
      • "state""start", "stop", "detect"(唤醒检测已触发)
      • "mode""auto", "manual""realtime",表示识别模式。
    • 例:开始监听
      {
        "session_id": "xxx",
        "type": "listen",
        "state": "start",
        "mode": "manual"
      }
      
  3. Abort

    • 终止当前说话TTS 播放)或语音通道。
    • 例:
      {
        "session_id": "xxx",
        "type": "abort",
        "reason": "wake_word_detected"
      }
      
    • reason 值可为 "wake_word_detected" 或其他。
  4. Wake Word Detected

    • 用于客户端向服务器告知检测到唤醒词。
    • 例:
      {
        "session_id": "xxx",
        "type": "listen",
        "state": "detect",
        "text": "你好小明"
      }
      
  5. IoT

    • 发送当前设备的物联网相关信息:
      • Descriptors(描述设备功能、属性等)
      • States(设备状态的实时更新)
    • 例:
      {
        "session_id": "xxx",
        "type": "iot",
        "descriptors": { ... }
      }
      
      {
        "session_id": "xxx",
        "type": "iot",
        "states": { ... }
      }
      

3.2 服务器→客户端

  1. Hello

    • 服务器端返回的握手确认消息。
    • 必须包含 "type": "hello""transport": "websocket"
    • 可能会带有 audio_params,表示服务器期望的音频参数,或与客户端对齐的配置。
    • 成功接收后客户端会设置事件标志,表示 WebSocket 通道就绪。
  2. STT

    • {"type": "stt", "text": "..."}
    • 表示服务器端识别到了用户语音。(例如语音转文本结果)
    • 设备可能将此文本显示到屏幕上,后续再进入回答等流程。
  3. LLM

    • {"type": "llm", "emotion": "happy", "text": "😀"}
    • 服务器指示设备调整表情动画 / UI 表达。
  4. TTS

    • {"type": "tts", "state": "start"}:服务器准备下发 TTS 音频,客户端进入 “speaking” 播放状态。
    • {"type": "tts", "state": "stop"}:表示本次 TTS 结束。
    • {"type": "tts", "state": "sentence_start", "text": "..."}
      • 让设备在界面上显示当前要播放或朗读的文本片段(例如用于显示给用户)。
  5. IoT

    • {"type": "iot", "commands": [ ... ]}
    • 服务器向设备发送物联网的动作指令,设备解析并执行(如打开灯、设置温度等)。
  6. 音频数据:二进制帧

    • 当服务器发送音频二进制帧Opus 编码)时,客户端解码并播放。
    • 若客户端正在处于 “listening” (录音)状态,收到的音频帧会被忽略或清空以防冲突。

4. 音频编解码

  1. 客户端发送录音数据

    • 音频输入经过可能的回声消除、降噪或音量增益后,通过 Opus 编码打包为二进制帧发送给服务器。
    • 如果客户端每次编码生成的二进制帧大小为 N 字节,则会通过 WebSocket 的 binary 消息发送这块数据。
  2. 客户端播放收到的音频

    • 收到服务器的二进制帧时,同样认定是 Opus 数据。
    • 设备端会进行解码,然后交由音频输出接口播放。
    • 如果服务器的音频采样率与设备不一致,会在解码后再进行重采样。

5. 常见状态流转

以下简述设备端关键状态流转,与 WebSocket 消息对应:

  1. IdleConnecting

    • 用户触发或唤醒后,设备调用 OpenAudioChannel() → 建立 WebSocket 连接 → 发送 "type":"hello"
  2. ConnectingListening

    • 成功建立连接后,若继续执行 SendStartListening(...),则进入录音状态。此时设备会持续编码麦克风数据并发送到服务器。
  3. ListeningSpeaking

    • 收到服务器 TTS Start 消息 ({"type":"tts","state":"start"}) → 停止录音并播放接收到的音频。
  4. SpeakingIdle

    • 服务器 TTS Stop ({"type":"tts","state":"stop"}) → 音频播放结束。若未继续进入自动监听,则返回 Idle如果配置了自动循环则再度进入 Listening。
  5. Listening / SpeakingIdle(遇到异常或主动中断)

    • 调用 SendAbortSpeaking(...)CloseAudioChannel() → 中断会话 → 关闭 WebSocket → 状态回到 Idle。

6. 错误处理

  1. 连接失败

    • 如果 Connect(url) 返回失败或在等待服务器 “hello” 消息时超时,触发 on_network_error_() 回调。设备会提示“无法连接到服务”或类似错误信息。
  2. 服务器断开

    • 如果 WebSocket 异常断开,回调 OnDisconnected()
      • 设备回调 on_audio_channel_closed_()
      • 切换到 Idle 或其他重试逻辑。

7. 其它注意事项

  1. 鉴权

    • 设备通过设置 Authorization: Bearer <token> 提供鉴权,服务器端需验证是否有效。
    • 如果令牌过期或无效,服务器可拒绝握手或在后续断开。
  2. 会话控制

    • 代码中部分消息包含 session_id用于区分独立的对话或操作。服务端可根据需要对不同会话做分离处理WebSocket 协议为空。
  3. 音频负载

    • 代码里默认使用 Opus 格式,并设置 sample_rate = 16000,单声道。帧时长由 OPUS_FRAME_DURATION_MS 控制,一般为 60ms。可根据带宽或性能做适当调整。
  4. IoT 指令

    • "type":"iot" 的消息用户端代码对接 thing_manager 执行具体命令,因设备定制而不同。服务器端需确保下发格式与客户端保持一致。
  5. 错误或异常 JSON

    • 当 JSON 中缺少必要字段,例如 {"type": ...},客户端会记录错误日志(ESP_LOGE(TAG, "Missing message type, data: %s", data);),不会执行任何业务。

8. 消息示例

下面给出一个典型的双向消息示例(流程简化示意):

  1. 客户端 → 服务器(握手)

    {
      "type": "hello",
      "version": 1,
      "transport": "websocket",
      "audio_params": {
        "format": "opus",
        "sample_rate": 16000,
        "channels": 1,
        "frame_duration": 60
      }
    }
    
  2. 服务器 → 客户端(握手应答)

    {
      "type": "hello",
      "transport": "websocket",
      "audio_params": {
        "sample_rate": 16000
      }
    }
    
  3. 客户端 → 服务器(开始监听)

    {
      "session_id": "",
      "type": "listen",
      "state": "start",
      "mode": "auto"
    }
    

    同时客户端开始发送二进制帧Opus 数据)。

  4. 服务器 → 客户端ASR 结果)

    {
      "type": "stt",
      "text": "用户说的话"
    }
    
  5. 服务器 → 客户端TTS开始

    {
      "type": "tts",
      "state": "start"
    }
    

    接着服务器发送二进制音频帧给客户端播放。

  6. 服务器 → 客户端TTS结束

    {
      "type": "tts",
      "state": "stop"
    }
    

    客户端停止播放音频,若无更多指令,则回到空闲状态。


9. 总结

本协议通过在 WebSocket 上层传输 JSON 文本与二进制音频帧完成功能包括音频流上传、TTS 音频播放、语音识别与状态管理、IoT 指令下发等。其核心特征:

  • 握手阶段:发送 "type":"hello",等待服务器返回。
  • 音频通道:采用 Opus 编码的二进制帧双向传输语音流。
  • JSON 消息:使用 "type" 为核心字段标识不同业务逻辑,包括 TTS、STT、IoT、WakeWord 等。
  • 扩展性:可根据实际需求在 JSON 消息中添加字段,或在 headers 里进行额外鉴权。

服务器与客户端需提前约定各类消息的字段含义、时序逻辑以及错误处理规则,方能保证通信顺畅。上述信息可作为基础文档,便于后续对接、开发或扩展。