Протокол связи WebSocket
Следующий документ протокола связи WebSocket организован на основе реализации кода, описывающий, как клиенты (устройства) взаимодействуют с серверами через WebSocket. Данный документ выведен исключительно из предоставленного кода и может потребовать дальнейшего подтверждения или дополнения при сочетании с серверной реализацией во время фактического развертывания.
1. Обзор общего процесса
Инициализация на стороне устройства
- Включение устройства, инициализация
Application:- Инициализация аудиокодека, дисплея, LED и т.д.
- Подключение к сети
- Создание и инициализация экземпляра протокола WebSocket (
WebsocketProtocol), реализующего интерфейсProtocol
- Переход в основной цикл ожидания событий (аудиовход, аудиовыход, запланированные задачи и т.д.).
- Включение устройства, инициализация
Установка соединения WebSocket
- Когда устройство должно начать голосовую сессию (например, пробуждение пользователя, ручной триггер кнопки), вызывается
OpenAudioChannel():- Получение URL WebSocket из конфигурации компиляции (
CONFIG_WEBSOCKET_URL) - Установка нескольких заголовков запроса (
Authorization,Protocol-Version,Device-Id,Client-Id) - Вызов
Connect()для установки соединения WebSocket с сервером
- Получение URL WebSocket из конфигурации компиляции (
- Когда устройство должно начать голосовую сессию (например, пробуждение пользователя, ручной триггер кнопки), вызывается
Отправка клиентского сообщения “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(например, 60мс).
Ответ сервера “hello”
- Устройство ждет, пока сервер вернет JSON-сообщение, содержащее
"type": "hello", и проверяет соответствие"transport": "websocket". - Если соответствует, сервер считается готовым, и открытие аудиоканала помечается как успешное.
- Если правильный ответ не получен в течение времени ожидания (по умолчанию 10 секунд), соединение считается неудачным и запускается обратный вызов сетевой ошибки.
- Устройство ждет, пока сервер вернет JSON-сообщение, содержащее
Последующее взаимодействие сообщений
Устройство и сервер могут отправлять два основных типа данных:
- Бинарные аудиоданные (кодированные Opus)
- Текстовые JSON-сообщения (для передачи состояния чата, событий TTS/STT, команд IoT и т.д.)
В коде обратные вызовы приема в основном делятся на:
OnData(...):- Когда
binaryравноtrue, считается аудиокадром; устройство обрабатывает его как данные Opus для декодирования. - Когда
binaryравноfalse, считается JSON-текстом, требующим анализа cJSON на стороне устройства для соответствующей бизнес-логики (см. структуру сообщений ниже).
- Когда
Когда сервер или сеть отключаются, запускается обратный вызов
OnDisconnected():- Устройство вызывает
on_audio_channel_closed_()и в конечном итоге возвращается в состояние ожидания.
- Устройство вызывает
Закрытие соединения WebSocket
- Устройство активно отключается путем вызова
CloseAudioChannel()при завершении голосовой сессии и возвращается в состояние ожидания. - Или если сервер активно отключается, также запускается тот же поток обратного вызова.
- Устройство активно отключается путем вызова
2. Общие заголовки запроса
При установке соединения WebSocket пример кода устанавливает следующие заголовки запроса:
Authorization: Хранит токен доступа, форматированный как"Bearer <token>"Protocol-Version: Фиксированный как"1"в примереDevice-Id: MAC-адрес физической сетевой карты устройстваClient-Id: UUID устройства (может уникально идентифицировать устройство в приложении)
Эти заголовки отправляются на сервер вместе с рукопожатием WebSocket, и сервер может выполнять проверку, аутентификацию и т.д. по мере необходимости.
3. Структура JSON-сообщений
Текстовые кадры WebSocket передаются в формате JSON. Ниже приведены общие поля "type" и их соответствующая бизнес-логика. Поля, не указанные в списке, могут быть необязательными или специфичными для реализации деталями.
3.1 Клиент→Сервер
Hello
- Отправляется клиентом после успешного соединения для информирования сервера об основных параметрах.
- Пример:
{ "type": "hello", "version": 1, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } }
Listen
- Указывает, что клиент начинает или останавливает прослушивание записи.
- Общие поля:
"session_id": Идентификатор сессии"type": "listen""state":"start","stop","detect"(обнаружение пробуждения запущено)"mode":"auto","manual"или"realtime", указывающие режим распознавания.
- Пример: Начать прослушивание
{ "session_id": "xxx", "type": "listen", "state": "start", "mode": "manual" }
Abort
- Прервать текущую речь (воспроизведение TTS) или голосовой канал.
- Пример:
{ "session_id": "xxx", "type": "abort", "reason": "wake_word_detected" } - Значение
reasonможет быть"wake_word_detected"или другим.
Wake Word Detected
- Используется клиентом для уведомления сервера об обнаружении слова пробуждения.
- Пример:
{ "session_id": "xxx", "type": "listen", "state": "detect", "text": "привет сяомин" }
IoT
- Отправка IoT-связанной информации текущего устройства:
- Descriptors (описание функций устройства, атрибутов и т.д.)
- States (обновления состояния устройства в реальном времени)
- Пример:или
{ "session_id": "xxx", "type": "iot", "descriptors": { ... } }{ "session_id": "xxx", "type": "iot", "states": { ... } }
- Отправка IoT-связанной информации текущего устройства:
3.2 Сервер→Клиент
Hello
- Сообщение подтверждения рукопожатия, возвращаемое сервером.
- Должно содержать
"type": "hello"и"transport": "websocket". - Может включать
audio_params, указывающие ожидаемые сервером аудиопараметры или конфигурацию, согласованную с клиентом. - После успешного получения клиент устанавливает флаг события, указывающий, что канал WebSocket готов.
STT
{"type": "stt", "text": "..."}- Указывает, что сервер распознал пользовательскую речь (например, результат преобразования речи в текст)
- Устройство может отобразить этот текст на экране, затем перейти к потоку ответа.
LLM
{"type": "llm", "emotion": "happy", "text": "😀"}- Сервер инструктирует устройство настроить анимацию эмоций / выражение UI.
TTS
{"type": "tts", "state": "start"}: Сервер готовится отправить аудио TTS, клиент входит в состояние воспроизведения “speaking”.{"type": "tts", "state": "stop"}: Указывает, что текущая сессия TTS завершена.{"type": "tts", "state": "sentence_start", "text": "..."}- Заставляет устройство отображать текущий сегмент текста для воспроизведения или чтения в интерфейсе (например, для отображения пользователю).
IoT
{"type": "iot", "commands": [ ... ]}- Сервер отправляет команды действий IoT на устройство, устройство анализирует и выполняет (например, включение света, установка температуры и т.д.).
Аудиоданные: Бинарные кадры
- Когда сервер отправляет бинарные аудиокадры (кодированные Opus), клиент декодирует и воспроизводит.
- Если клиент находится в состоянии “listening” (запись), полученные аудиокадры игнорируются или очищаются для предотвращения конфликтов.
4. Кодирование/декодирование аудио
Клиент отправляет данные записи
- Аудиовход, после возможной отмены эха, шумоподавления или усиления громкости, упаковывается через кодирование Opus в бинарные кадры и отправляется на сервер.
- Если кодирование клиента генерирует бинарные кадры по N байт каждый раз, эти данные отправляются через бинарные сообщения WebSocket.
Клиент воспроизводит полученное аудио
- При получении бинарных кадров от сервера они также считаются данными Opus.
- Устройство выполняет декодирование, затем передает в интерфейс аудиовыхода для воспроизведения.
- Если частота дискретизации аудио сервера отличается от устройства, ресемплинг выполняется после декодирования.
5. Общие переходы состояний
Ниже кратко описаны ключевые переходы состояний на стороне устройства, соответствующие сообщениям WebSocket:
Idle → Connecting
- После триггера пользователя или пробуждения устройство вызывает
OpenAudioChannel()→ устанавливает соединение WebSocket → отправляет"type":"hello".
- После триггера пользователя или пробуждения устройство вызывает
Connecting → Listening
- После успешного установления соединения, если продолжается выполнение
SendStartListening(...), переходит в состояние записи. В это время устройство непрерывно кодирует данные микрофона и отправляет на сервер.
- После успешного установления соединения, если продолжается выполнение
Listening → Speaking
- Получение сообщения TTS Start от сервера (
{"type":"tts","state":"start"}) → остановка записи и воспроизведение полученного аудио.
- Получение сообщения TTS Start от сервера (
Speaking → Idle
- TTS Stop от сервера (
{"type":"tts","state":"stop"}) → воспроизведение аудио заканчивается. Если не продолжается автопрослушивание, возврат к Idle; если настроен автоцикл, повторный вход в Listening.
- TTS Stop от сервера (
Listening / Speaking → Idle (встреча исключений или активное прерывание)
- Вызов
SendAbortSpeaking(...)илиCloseAudioChannel()→ прерывание сессии → закрытие WebSocket → состояние возвращается к Idle.
- Вызов
6. Обработка ошибок
Неудача соединения
- Если
Connect(url)возвращает неудачу или истекает время ожидания при ожидании сообщения “hello” от сервера, запускается обратный вызовon_network_error_(). Устройство подсказывает “Невозможно подключиться к службе” или аналогичное сообщение об ошибке.
- Если
Отключение сервера
- Если WebSocket аномально отключается, обратный вызов
OnDisconnected():- Устройство выполняет обратные вызовы
on_audio_channel_closed_() - Переключается на Idle или другую логику повтора.
- Устройство выполняет обратные вызовы
- Если WebSocket аномально отключается, обратный вызов
7. Другие примечания
Аутентификация
- Устройство предоставляет аутентификацию, устанавливая
Authorization: Bearer <token>, сервер должен проверить действительность. - Если токен истек или недействителен, сервер может отказать в рукопожатии или отключиться впоследствии.
- Устройство предоставляет аутентификацию, устанавливая
Управление сессией
- Некоторые сообщения в коде содержат
session_id, используемый для различения независимых разговоров или операций. Сервер может выполнять отдельную обработку для разных сессий по мере необходимости, протокол WebSocket пуст.
- Некоторые сообщения в коде содержат
Аудиополезная нагрузка
- Код по умолчанию использует формат Opus с
sample_rate = 16000, моно. Продолжительность кадра контролируетсяOPUS_FRAME_DURATION_MS, обычно 60мс. Может быть соответственно отрегулирована в зависимости от пропускной способности или производительности.
- Код по умолчанию использует формат Opus с
Команды IoT
- Сообщения
"type":"iot"пользовательского кода интерфейса сthing_managerдля выполнения конкретных команд, различающихся по настройке устройства. Сервер должен обеспечить, чтобы отправляемый формат оставался согласованным с клиентом.
- Сообщения
Ошибка или исключение JSON
- Когда в JSON отсутствуют необходимые поля, например,
{"type": ...}, клиент регистрирует ошибку (ESP_LOGE(TAG, "Missing message type, data: %s", data);), без выполнения какой-либо бизнес-логики.
- Когда в JSON отсутствуют необходимые поля, например,
8. Примеры сообщений
Ниже приведен типичный пример двунаправленных сообщений (упрощенная иллюстрация потока):
Клиент → Сервер (рукопожатие)
{ "type": "hello", "version": 1, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } }Сервер → Клиент (ответ рукопожатия)
{ "type": "hello", "transport": "websocket", "audio_params": { "sample_rate": 16000 } }Клиент → Сервер (начать прослушивание)
{ "session_id": "", "type": "listen", "state": "start", "mode": "auto" }Клиент одновременно начинает отправку бинарных кадров (данные Opus).
Сервер → Клиент (результат ASR)
{ "type": "stt", "text": "что сказал пользователь" }Сервер → Клиент (начало TTS)
{ "type": "tts", "state": "start" }Затем сервер отправляет бинарные аудиокадры клиенту для воспроизведения.
Сервер → Клиент (конец TTS)
{ "type": "tts", "state": "stop" }Клиент останавливает воспроизведение аудио, возвращается в состояние ожидания, если нет дополнительных команд.
9. Резюме
Этот протокол передает JSON-текст и бинарные аудиокадры поверх WebSocket, завершая функции, включая загрузку аудиопотока, воспроизведение аудио TTS, распознавание речи и управление состоянием, доставку команд IoT и т.д. Основные характеристики:
- Фаза рукопожатия: Отправка
"type":"hello", ожидание возврата сервера. - Аудиоканал: Использование бинарных кадров, кодированных Opus, для двунаправленной передачи голосового потока.
- JSON-сообщения: Использование
"type"как основного поля для идентификации различной бизнес-логики, включая TTS, STT, IoT, WakeWord и т.д. - Расширяемость: Поля могут быть добавлены в JSON-сообщения или дополнительная аутентификация в заголовках на основе фактических потребностей.
Сервер и клиент должны заранее договориться о значениях полей, логике времени и правилах обработки ошибок для различных типов сообщений, чтобы обеспечить плавную связь. Приведенная выше информация может служить базовой документацией для последующего интерфейсинга, разработки или расширения.