四博 AI 智能音箱 4G S3架构方案

四博 AI 智能音箱 4G S3 版本技术方案

1. 产品定义

本方案面向一款 支持蓝牙、Wi-Fi、4G 三模联网的四博 AI 智能音箱

核心能力包括:

复制代码
1. AI 对话:
   支持小智、豆包、ChatGPT、客户私有大模型。

2. 三模联网:
   Wi-Fi 上网、4G 上网、蓝牙 PAN / BLE 配网。

3. 远场拾音:
   支持 5 米范围远距离唤醒、打断和对话。

4. 高噪响应:
   在空气炸锅、油烟机、电视背景声等高噪环境下稳定响应。

5. 实时打断:
   AI 说话、TTS 播报、蓝牙音乐播放过程中可唤醒词打断。

6. 可改唤醒词:
   支持 VB6824 唤醒词升级和客户定制唤醒词。

7. S3 二次开发:
   基于 ESP32-S3,支持 ESP-IDF、DOIT_AI、AT+MCP、HTTP、MQTT、WebSocket。

8. 客户系统接入:
   可对接客户 App、小程序、私有云、业务后台、智能家居平台。

推荐硬件配置:

复制代码
主控:ESP32-S3R8 / ESPS3-32-N16R8
Flash:16MB
PSRAM:8MB
语音前端:VB6824
联网:Wi-Fi + BLE + 4G + 可选 7014 蓝牙 PAN
音频:麦克风 + AEC 回采 + I2S 功放 + 喇叭
显示:1.3 / 1.54 / 2.0 寸 LCD,可选
电源:Type-C + 电池 + 充电管理
系统:ESP-IDF + DOIT_AI + 小智协议 / 私有后端
扩展:UART / GPIO / I2C / SPI / MCP / MQTT / HTTP

AI 硬件选型资料中提到,ESP32-C2 / C3 / S3 加 VB6824 语音方案已经成熟应用于电子吧唧、S3 双目、S3 拍学机、地球仪、拍拍灯等产品;VB6824 完成音频编解码、AEC、语音唤醒、改唤醒词等功能,让主控芯片专注通信及 UI。


2. 整机系统架构

复制代码
┌──────────────────────────────────────────────────────────────┐
│                  四博 AI 智能音箱 4G S3 版本                  │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────────────┐        UART / I2C / GPIO           │
│  │ VB6824 语音前端       │◄──────────────────────────────┐    │
│  │ - 离线唤醒             │                               │    │
│  │ - 远距离拾音           │                               │    │
│  │ - AEC 回声消除         │                               │    │
│  │ - 高噪降噪             │                               │    │
│  │ - 唤醒词打断           │                               │    │
│  │ - 实时打断             │                               │    │
│  │ - 改唤醒词             │                               │    │
│  └─────────┬────────────┘                               │    │
│            │ MIC / 回采音频                              │    │
│                                                           ▼    │
│  ┌────────────────────────────────────────────────────────┐   │
│  │ ESP32-S3R8 + 16M Flash + 8M PSRAM                     │   │
│  │ - Wi-Fi STA / SoftAP / BluFi                          │   │
│  │ - 4G PPP / ECM / RNDIS                                │   │
│  │ - 蓝牙 PAN / 蓝牙音箱,可选 7014                      │   │
│  │ - WebSocket AI 对话                                   │   │
│  │ - OTA / NVS / 设备激活                                │   │
│  │ - LCD / LED / 按键 / UI                               │   │
│  │ - MCP 工具调用 / 客户系统接入                         │   │
│  └──────────────┬─────────────────┬──────────────────────┘   │
│                 │                 │                          │
│       ┌─────────▼──────┐ ┌────────▼────────┐                 │
│       │ 4G 通信模组     │ │ 7014 蓝牙模组    │                 │
│       │ UART PPP / USB │ │ PAN / A2DP      │                 │
│       └─────────┬──────┘ └────────┬────────┘                 │
│                 │                 │                          │
│             SIM / 4G 天线        蓝牙天线                     │
│                                                              │
│  ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐    │
│  │ LCD / RGB UI │ │ I2S AMP+SPK  │ │ Battery / PMIC     │    │
│  │ 状态显示      │ │ TTS / 音乐    │ │ 电池 / 充电管理      │    │
│  └──────────────┘ └──────────────┘ └────────────────────┘    │
│                                                              │
└──────────────────────────────────────────────────────────────┘

云端 / 私有化后端:
┌──────────────────────────────────────────────────────────────┐
│ OTA │ WebSocket │ ASR │ LLM │ TTS │ MCP │ 客户业务系统 │ 日志 │
└──────────────────────────────────────────────────────────────┘

这里建议采用 ESP32-S3 主控 + VB6824 语音前端 + 4G 模组 + 7014 蓝牙协处理器 的平台架构。ESP32-S3 负责联网、UI、AI 业务、WebSocket、OTA 和二次开发;VB6824 负责唤醒、降噪、AEC、打断和改唤醒词;4G 模组负责无 Wi-Fi 场景联网;7014 负责蓝牙 PAN 上网或蓝牙音箱模式。

需要注意:ESP32-S3 本身主要提供 Wi-Fi + BLE,若产品要求"经典蓝牙音箱 A2DP"或"手机蓝牙 PAN 上网",建议增加 7014 或独立蓝牙音频 / PAN 协处理器。AI 选型表中也单独给出了 7014 蓝牙 PAN AI 手机伴侣方案,特点是可通过手机网络通信,解决户外网络问题。


3. 硬件方案

3.1 主控:ESP32-S3 / ESPS3-32

推荐选型:

复制代码
ESPS3-32-N16R8
或 ESPS3-32E-N16R8

配置建议:

模块 推荐配置 说明
主控 ESP32-S3R8 双核 240MHz,适合 AI、UI、联网、音频流调度
Flash 16MB 支持 OTA 双分区、资源、日志、配置
PSRAM 8MB 支持 WebSocket buffer、UI buffer、音频 buffer
Wi-Fi 2.4GHz 家庭 / 办公 / 展厅主联网方式
BLE BluFi 配网 小程序 / App 配网
GPIO 充足 屏幕、功放、4G、按键、RGB、外设扩展

乐鑫系模组选型手册中,ESPS3-32 / ESPS3-32E 属于 ESP32-S3 系列,面向音视频 / AI 市场,ESPS3-32 尺寸为 18 × 25.5mm,ESPS3-32E 尺寸为 18 × 19.2mm,兼容 ESP32-S3-WROOM 系列模组。

3.2 语音前端:VB6824

VB6824 建议作为独立语音前端,负责:

复制代码
1. 离线唤醒;
2. 远距离拾音;
3. AEC 回声消除;
4. 噪声抑制;
5. AI 播报中的实时打断;
6. 唤醒词升级;
7. 语音状态上报;
8. 与 ESP32-S3 通过 UART / GPIO 协同。

开发宝典中对 VB6824 的描述是:采用语音识别算法和语音降噪算法,支持更远距离唤醒、更低误唤醒率、更强抗噪音能力、更快响应识别时间,并支持免联网纯离线识别。

3.3 4G 模组

推荐两种接入方式:

复制代码
方式 A:UART + PPPoS
优点:接线简单、软件成熟、适合语音流和控制流。
缺点:吞吐量较低。

方式 B:USB ECM / RNDIS
优点:吞吐量更高,适合图片、视频、资源下载。
缺点:USB Host 和驱动复杂度更高。

开发宝典中 AI-C5 章节说明,该开发板支持 2.4G / 5G Wi-Fi、4G 模组、喇叭、麦克风、电池和 2 寸屏幕;虽然 AI-C5 是 C5 架构,但其 4G 模组接入和 Wi-Fi / 4G 模式切换逻辑可以迁移到 S3 4G 音箱版本。

3.4 蓝牙能力

蓝牙建议拆成三层:

复制代码
1. BLE 配网:
   ESP32-S3 自带 BLE,可用于 BluFi 配网。

2. 蓝牙 PAN 上网:
   建议外接 7014,通过手机网络给设备联网。

3. 蓝牙音箱:
   如需 A2DP 蓝牙音乐播放,建议用 7014 或独立蓝牙音频芯片。

3.5 音频与声学结构

推荐音频链路:

复制代码
MIC
  ↓
VB6824 语音前端
  ↓
ESP32-S3 AI 会话
  ↓
WebSocket / ASR / LLM / TTS
  ↓
I2S 功放
  ↓
喇叭
  ↓
AEC 回采
  ↓
VB6824

结构建议:

复制代码
1. MIC 远离喇叭出音口和倒相孔;
2. MIC 孔避开空气流道和外壳振动位置;
3. 4G 天线、Wi-Fi 天线远离 MIC 走线和音频前级;
4. 功放电源与 4G 电源分区;
5. 4G 模组峰值电流要留足裕量;
6. 喇叭播放必须保留回采链路,用于 AEC 和实时打断;
7. 高噪场景下应单独调试唤醒阈值、AEC 参数和降噪参数。

4. 三模联网策略

4.1 联网优先级

建议默认优先级:

复制代码
第一优先级:Wi-Fi
  - 成本低
  - 带宽稳定
  - 适合家庭、办公室、展厅
  - OTA 优先走 Wi-Fi

第二优先级:4G
  - 户外、移动、无 Wi-Fi 场景
  - 适合商用部署
  - 需要做流量控制和弱网重连

第三优先级:蓝牙 PAN
  - 手机伴侣场景
  - 无 SIM 卡或 4G 弱信号时兜底
  - 可作为临时联网方式

4.2 模式切换交互

开发宝典中给出的 4G / Wi-Fi 模式切换逻辑可以直接参考:在 4G 模式下重新开机,屏幕显示等待网络时双击 boot 键,可切换到 Wi-Fi 配网模式;在 Wi-Fi 配网模式界面双击 boot 键,可切换到 4G 模式并自动连接。

推荐扩展为:

复制代码
开机:
  1. 读取 NVS 中 last_link。
  2. 优先尝试 Wi-Fi。
  3. Wi-Fi 超时失败,启动 4G。
  4. 4G 无卡 / 注册失败 / 信号弱,启动蓝牙 PAN。
  5. 三路均不可用,进入 BluFi / SoftAP 配网。

运行中:
  1. WebSocket 心跳失败 3 次,标记当前链路异常。
  2. 后台预连接备用链路。
  3. AI 正在录音或播放时不立即切网。
  4. 当前轮对话结束后切换默认路由。
  5. OTA 时优先 Wi-Fi,避免 4G 流量消耗。

5. 软件系统架构

建议基于 ESP-IDF + DOIT_AI 工程扩展。

复制代码
main/
  app_main.c

components/
  board/
    board_config.h
    board_init.c

  audio/
    audio_focus.c
    tts_player.c
    wakeup_frontend.c
    barge_in.c

  network/
    wifi_manager.c
    lte_modem.c
    bt_pan_manager.c
    network_policy.c

  ai/
    websocket_client.c
    ai_session.c
    mcp_uart_bridge.c

  ui/
    display_manager.c
    led_manager.c
    key_manager.c

  ota/
    ota_manager.c
    resource_ota.c

  customer/
    customer_http.c
    customer_mqtt.c
    customer_uart.c

  factory/
    factory_test.c

FreeRTOS 任务建议:

任务 优先级 作用
audio_frontend_task 唤醒、打断、录音状态
ai_session_task WebSocket 对话、TTS 播放
network_policy_task 中高 Wi-Fi / 4G / 蓝牙 PAN 链路选择
lte_modem_task 中高 4G 注册、拨号、重连
mcp_uart_task 中高 AT+MCP 指令注册和帧解析
ui_task 屏幕、LED、按键、状态显示
ota_task 固件 OTA、资源 OTA
factory_task 产测、老化、SN 写入

6. 编译与开发流程

开发宝典说明,小智 AI 硬件设备的开源代码可以在 VSCode 中安装 ESP-IDF 扩展和编译工具进行编译和烧录;S3 相关工程需要设置目标芯片为 esp32s3,部分工程要求 ESP-IDF 版本大于等于 5.4.1。

复制代码
git clone https://github.com/SmartArduino/DOIT_AI.git
cd DOIT_AI

idf.py set-target esp32s3
idf.py menuconfig
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

推荐 menuconfig 配置项:

复制代码
Xiaozhi Assistant
  Board Type:
    Sibo AI Speaker 4G S3

Network:
  Wi-Fi Enable = y
  BluFi Enable = y
  LTE 4G Enable = y
  Bluetooth PAN Enable = optional

Audio:
  VB6824 Enable = y
  USE_DEVICE_AEC = y
  USE_SERVER_AEC = n

AI:
  WebSocket Enable = y
  MCP Enable = y

OTA:
  OTA Enable = y
  Resource OTA Enable = y

实时打断方面,开发宝典中说明可在 Kconfig.projbuild 中增加 USE_DEVICE_AECUSE_SERVER_AEC 相关配置;USE_SERVER_AEC 需要服务器支持,如果服务器不支持,则选择 USE_DEVICE_AEC 后重新编译。


7. 关键代码示例

下面代码为工程框架级示例,GPIO 需要根据实际 PCB 修改。


7.1 板级配置 board_config.h

复制代码
#pragma once

#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/spi_master.h"

/*
 * 四博 AI 智能音箱 4G S3 版本
 * ESP32-S3R8 + 16M Flash + 8M PSRAM + VB6824 + 4G + optional 7014
 */

#define BOARD_NAME                      "SIBO_AI_SPEAKER_4G_S3"

/* ---------- VB6824 / MCP UART ---------- */
#define AI_UART                         UART_NUM_1
#define AI_UART_BAUD                    115200
#define AI_UART_TX_GPIO                 GPIO_NUM_17
#define AI_UART_RX_GPIO                 GPIO_NUM_18
#define AI_RESET_GPIO                   GPIO_NUM_21

/* ---------- 4G Modem UART ---------- */
#define LTE_UART                        UART_NUM_2
#define LTE_UART_BAUD                   115200
#define LTE_TX_GPIO                     GPIO_NUM_39
#define LTE_RX_GPIO                     GPIO_NUM_40
#define LTE_PWRKEY_GPIO                 GPIO_NUM_41
#define LTE_RESET_GPIO                  GPIO_NUM_42
#define LTE_STATUS_GPIO                 GPIO_NUM_2

/* ---------- 7014 Bluetooth PAN / A2DP,可选 ---------- */
#define BT7014_UART                     UART_NUM_0
#define BT7014_TX_GPIO                  GPIO_NUM_43
#define BT7014_RX_GPIO                  GPIO_NUM_44
#define BT7014_EN_GPIO                  GPIO_NUM_45

/* ---------- I2S Speaker ---------- */
#define I2S_BCLK_GPIO                   GPIO_NUM_6
#define I2S_LRCK_GPIO                   GPIO_NUM_7
#define I2S_DOUT_GPIO                   GPIO_NUM_8
#define AMP_EN_GPIO                     GPIO_NUM_5

/* ---------- LCD,可选 ---------- */
#define LCD_SPI_HOST                    SPI2_HOST
#define LCD_CS_GPIO                     GPIO_NUM_10
#define LCD_DC_GPIO                     GPIO_NUM_11
#define LCD_RST_GPIO                    GPIO_NUM_12
#define LCD_BL_GPIO                     GPIO_NUM_13

/* ---------- Keys ---------- */
#define KEY_BOOT_GPIO                   GPIO_NUM_0
#define KEY_VOL_UP_GPIO                 GPIO_NUM_14
#define KEY_VOL_DOWN_GPIO               GPIO_NUM_15
#define KEY_MUTE_GPIO                   GPIO_NUM_16

/* ---------- RGB LED ---------- */
#define RGB_LED_GPIO                    GPIO_NUM_38

/* ---------- Network Policy ---------- */
#define WIFI_CONNECT_TIMEOUT_MS         10000
#define LTE_CONNECT_TIMEOUT_MS          25000
#define BTPAN_CONNECT_TIMEOUT_MS        15000
#define WS_HEARTBEAT_TIMEOUT_MS         15000
#define NETWORK_FAILOVER_THRESHOLD      3

7.2 Kconfig:启用 S3 4G 板型与实时打断

复制代码
menu "Sibo AI Speaker 4G S3"

config BOARD_SIBO_AI_SPEAKER_4G_S3
    bool "Sibo AI Speaker 4G S3"
    default y
    help
        ESP32-S3R8 + 16M Flash + VB6824 + 4G + optional 7014.

config USE_DEVICE_AEC
    bool "Use device side AEC for real-time barge-in"
    depends on BOARD_SIBO_AI_SPEAKER_4G_S3
    default y
    help
        Enable device-side AEC for wake-word barge-in while TTS is playing.

config USE_SERVER_AEC
    bool "Use server side AEC"
    depends on BOARD_SIBO_AI_SPEAKER_4G_S3
    default n
    help
        Enable only when the backend WebSocket server supports server-side AEC.

config LTE_ENABLE
    bool "Enable 4G LTE network"
    default y

config BT_PAN_ENABLE
    bool "Enable Bluetooth PAN network"
    default y

config MCP_ENABLE
    bool "Enable AT+MCP UART bridge"
    default y

endmenu

7.3 三模联网策略代码

复制代码
#include <stdbool.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define TAG "NET_POLICY"

typedef enum {
    NET_LINK_NONE = 0,
    NET_LINK_WIFI,
    NET_LINK_LTE,
    NET_LINK_BT_PAN,
} net_link_t;

typedef struct {
    bool up;
    int rssi;
    int latency_ms;
    int fail_count;
    int priority;
    int power_cost;
    int traffic_cost;
} net_metric_t;

static net_link_t s_current_link = NET_LINK_NONE;
static net_link_t s_pending_link = NET_LINK_NONE;

static net_metric_t s_wifi = {
    .up = false,
    .rssi = -90,
    .latency_ms = 999,
    .priority = 3,
    .power_cost = 1,
    .traffic_cost = 0,
};

static net_metric_t s_lte = {
    .up = false,
    .rssi = -100,
    .latency_ms = 999,
    .priority = 2,
    .power_cost = 4,
    .traffic_cost = 4,
};

static net_metric_t s_btpan = {
    .up = false,
    .rssi = -90,
    .latency_ms = 999,
    .priority = 1,
    .power_cost = 2,
    .traffic_cost = 1,
};

static bool ai_session_is_busy(void)
{
    /*
     * TODO:
     * 返回 true 表示正在录音、AI TTS 播放或 WebSocket 音频流传输。
     * 对话中不强制切网,避免音频中断。
     */
    return false;
}

static void wifi_update_metric(net_metric_t *m)
{
    /*
     * TODO:
     * 1. esp_wifi_sta_get_ap_info 获取 RSSI。
     * 2. ping AI gateway 获取 latency。
     * 3. WebSocket 心跳失败则 fail_count++。
     */
    m->up = true;
    m->rssi = -55;
    m->latency_ms = 35;
}

static void lte_update_metric(net_metric_t *m)
{
    /*
     * TODO:
     * 1. AT+CSQ 获取信号。
     * 2. AT+CREG? / AT+CEREG? 判断注册状态。
     * 3. 判断 PPP netif 是否 up。
     * 4. ping AI gateway。
     */
    m->up = true;
    m->rssi = -72;
    m->latency_ms = 95;
}

static void btpan_update_metric(net_metric_t *m)
{
    /*
     * TODO:
     * 1. 查询 7014 PAN 是否已连接手机。
     * 2. 查询手机网络共享状态。
     * 3. ping AI gateway。
     */
    m->up = false;
    m->rssi = -80;
    m->latency_ms = 120;
}

static int calc_score(const net_metric_t *m)
{
    if (!m->up) {
        return -100000;
    }

    int score = 0;
    score += m->priority * 1000;
    score += m->rssi;
    score -= m->latency_ms / 5;
    score -= m->fail_count * 500;
    score -= m->power_cost * 30;
    score -= m->traffic_cost * 50;

    return score;
}

static net_link_t choose_best_link(void)
{
    int wifi_score = calc_score(&s_wifi);
    int lte_score  = calc_score(&s_lte);
    int bt_score   = calc_score(&s_btpan);

    ESP_LOGI(TAG, "score wifi=%d lte=%d btpan=%d",
             wifi_score, lte_score, bt_score);

    if (wifi_score >= lte_score && wifi_score >= bt_score) {
        return NET_LINK_WIFI;
    }

    if (lte_score >= wifi_score && lte_score >= bt_score) {
        return NET_LINK_LTE;
    }

    if (bt_score >= wifi_score && bt_score >= lte_score) {
        return NET_LINK_BT_PAN;
    }

    return NET_LINK_NONE;
}

static void set_default_link(net_link_t link)
{
    switch (link) {
    case NET_LINK_WIFI:
        ESP_LOGI(TAG, "switch to Wi-Fi");
        /*
         * TODO:
         * esp_netif_set_default_netif(wifi_netif);
         * websocket_reconnect();
         */
        break;

    case NET_LINK_LTE:
        ESP_LOGI(TAG, "switch to 4G LTE");
        /*
         * TODO:
         * esp_netif_set_default_netif(ppp_netif);
         * websocket_reconnect();
         */
        break;

    case NET_LINK_BT_PAN:
        ESP_LOGI(TAG, "switch to Bluetooth PAN");
        /*
         * TODO:
         * 通过 7014 建立 PAN 数据链路。
         * websocket_reconnect();
         */
        break;

    default:
        ESP_LOGW(TAG, "no network available, enter provisioning");
        /*
         * TODO:
         * start_blufi_or_softap_provisioning();
         */
        break;
    }

    s_current_link = link;
}

void network_policy_task(void *arg)
{
    while (1) {
        wifi_update_metric(&s_wifi);
        lte_update_metric(&s_lte);
        btpan_update_metric(&s_btpan);

        net_link_t best = choose_best_link();

        if (best != s_current_link) {
            if (ai_session_is_busy()) {
                s_pending_link = best;
                ESP_LOGI(TAG, "AI busy, pending link=%d", best);
            } else {
                set_default_link(best);
                s_pending_link = NET_LINK_NONE;
            }
        }

        if (!ai_session_is_busy() && s_pending_link != NET_LINK_NONE) {
            set_default_link(s_pending_link);
            s_pending_link = NET_LINK_NONE;
        }

        vTaskDelay(pdMS_TO_TICKS(10000));
    }
}

7.4 4G 模组 AT + PPP 初始化框架

复制代码
#include <string.h>
#include "esp_log.h"
#include "esp_timer.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "board_config.h"

#define TAG "LTE"

static void modem_send_cmd(const char *cmd)
{
    uart_write_bytes(LTE_UART, cmd, strlen(cmd));
    uart_write_bytes(LTE_UART, "\r\n", 2);
    ESP_LOGI(TAG, "MCU -> LTE: %s", cmd);
}

static bool modem_wait_keyword(const char *keyword, int timeout_ms)
{
    uint8_t buf[256];
    int total = 0;
    int64_t start_ms = esp_timer_get_time() / 1000;

    while ((esp_timer_get_time() / 1000 - start_ms) < timeout_ms) {
        int n = uart_read_bytes(
            LTE_UART,
            buf + total,
            sizeof(buf) - 1 - total,
            pdMS_TO_TICKS(100)
        );

        if (n > 0) {
            total += n;
            buf[total] = 0;

            if (strstr((char *)buf, keyword)) {
                ESP_LOGI(TAG, "LTE <- %s", (char *)buf);
                return true;
            }

            if (total > sizeof(buf) - 32) {
                total = 0;
            }
        }
    }

    ESP_LOGW(TAG, "timeout waiting: %s", keyword);
    return false;
}

static bool modem_at_ok(const char *cmd, int timeout_ms)
{
    modem_send_cmd(cmd);
    return modem_wait_keyword("OK", timeout_ms);
}

static void modem_power_on(void)
{
    gpio_set_direction(LTE_PWRKEY_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_direction(LTE_RESET_GPIO, GPIO_MODE_OUTPUT);

    gpio_set_level(LTE_RESET_GPIO, 1);
    vTaskDelay(pdMS_TO_TICKS(100));

    gpio_set_level(LTE_PWRKEY_GPIO, 1);
    vTaskDelay(pdMS_TO_TICKS(1200));
    gpio_set_level(LTE_PWRKEY_GPIO, 0);

    vTaskDelay(pdMS_TO_TICKS(5000));
}

bool lte_modem_start_ppp(const char *apn)
{
    char cmd[128];

    modem_power_on();

    for (int i = 0; i < 5; i++) {
        if (modem_at_ok("AT", 1000)) {
            break;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    modem_at_ok("ATE0", 1000);
    modem_at_ok("AT+CPIN?", 1000);
    modem_at_ok("AT+CSQ", 1000);
    modem_at_ok("AT+CREG?", 1000);
    modem_at_ok("AT+CEREG?", 1000);
    modem_at_ok("AT+CGATT=1", 5000);

    snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    modem_at_ok(cmd, 1000);

    /*
     * 不同 4G 模组拨号命令可能不同。
     * 常见命令:ATD*99***1# 或 ATD*99#
     */
    modem_send_cmd("ATD*99***1#");

    if (!modem_wait_keyword("CONNECT", 10000)) {
        ESP_LOGE(TAG, "PPP dial failed");
        return false;
    }

    /*
     * TODO:
     * 收到 CONNECT 后,将 UART 交给 esp_modem / esp_netif PPP。
     */
    ESP_LOGI(TAG, "PPP data mode entered");

    return true;
}

7.5 AT+MCP:语音到设备控制

开发宝典中的 AT+MCP 适合把"自然语言"映射成 MCU 可执行的设备控制帧。协议默认 UART 为 115200, 8N1,所有 AT 指令以回车换行结束;AI 模组到 MCU 的帧格式为 0x55 0xAA LEN CMD DATA... 0xAA 0x55;当 MCU 收到 55 AA 01 FC AA 55 时,需要重启 AI 模组并重新发送 MCP 映射。

7.5.1 MCP 工具设计

复制代码
set_speaker_volume      设置音量
set_lamp_color          设置 RGB 氛围灯
set_network_mode        切换 Wi-Fi / 4G / 蓝牙 PAN
enter_pairing           进入配网模式
play_local_prompt       播放提示音
customer_scene          接入客户业务系统

7.5.2 MCP UART 代码

复制代码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "board_config.h"

#define TAG "MCP"

#define FRAME_H1        0x55
#define FRAME_H2        0xAA
#define FRAME_T1        0xAA
#define FRAME_T2        0x55

#define CMD_STATUS      0xFF
#define CMD_RECOVER     0xFC

#define CMD_RGB         0xF1
#define CMD_VOLUME      0xF2
#define CMD_NET_MODE    0xF3
#define CMD_PAIRING     0xF4
#define CMD_CUSTOMER    0xF5

typedef enum {
    AI_STARTING     = 0x01,
    AI_CONFIGURING  = 0x02,
    AI_IDLE         = 0x03,
    AI_CONNECTING   = 0x04,
    AI_LISTENING    = 0x05,
    AI_SPEAKING     = 0x06,
    AI_UPGRADING    = 0x07,
    AI_ACTIVATING   = 0x08,
} ai_status_t;

static void ai_reset_module(void)
{
    ESP_LOGW(TAG, "reset AI/VB6824 module");

    gpio_set_direction(AI_RESET_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(AI_RESET_GPIO, 0);
    vTaskDelay(pdMS_TO_TICKS(300));
    gpio_set_level(AI_RESET_GPIO, 1);
    vTaskDelay(pdMS_TO_TICKS(1500));
}

static void mcp_send_line(const char *line)
{
    uart_write_bytes(AI_UART, line, strlen(line));
    uart_write_bytes(AI_UART, "\r\n", 2);
    ESP_LOGI(TAG, "MCU -> AI: %s", line);
}

static void ui_set_ai_status(ai_status_t status)
{
    switch (status) {
    case AI_STARTING:
        ESP_LOGI(TAG, "AI starting");
        break;
    case AI_CONFIGURING:
        ESP_LOGI(TAG, "AI configuring");
        break;
    case AI_IDLE:
        ESP_LOGI(TAG, "AI idle");
        break;
    case AI_CONNECTING:
        ESP_LOGI(TAG, "AI connecting");
        break;
    case AI_LISTENING:
        ESP_LOGI(TAG, "AI listening");
        break;
    case AI_SPEAKING:
        ESP_LOGI(TAG, "AI speaking");
        break;
    case AI_UPGRADING:
        ESP_LOGI(TAG, "AI upgrading");
        break;
    case AI_ACTIVATING:
        ESP_LOGI(TAG, "AI activating");
        break;
    default:
        ESP_LOGW(TAG, "unknown AI status=0x%02X", status);
        break;
    }
}

static void speaker_set_rgb(uint8_t r, uint8_t g, uint8_t b)
{
    ESP_LOGI(TAG, "RGB r=%u g=%u b=%u", r, g, b);
    /*
     * TODO:
     * WS2812 用 RMT;
     * 三路 PWM 灯用 LEDC。
     */
}

static void speaker_set_volume(uint8_t volume)
{
    if (volume > 100) {
        volume = 100;
    }

    ESP_LOGI(TAG, "volume=%u", volume);

    /*
     * TODO:
     * 1. 设置播放器音量。
     * 2. 设置功放或 Codec 音量。
     * 3. 保存到 NVS。
     */
}

static void speaker_set_network_mode(uint8_t mode)
{
    ESP_LOGI(TAG, "network mode=%u", mode);

    /*
     * mode:
     * 0x01 Wi-Fi
     * 0x02 4G
     * 0x03 Bluetooth PAN
     */
}

static void speaker_enter_pairing(void)
{
    ESP_LOGI(TAG, "enter pairing mode");
    /*
     * TODO:
     * start_blufi_or_softap_provisioning();
     */
}

static void customer_scene_control(uint8_t scene_id, uint8_t action)
{
    ESP_LOGI(TAG, "customer scene=%u action=%u", scene_id, action);

    /*
     * TODO:
     * 1. MQTT 发布到客户平台;
     * 2. HTTP 调客户 API;
     * 3. UART / RS485 控制外部设备。
     */
}

static void mcp_register_tools(void)
{
    mcp_send_line("AT");
    mcp_send_line("AT+WIFICFG=0");
    mcp_send_line("AT+CONNECT");

    /*
     * 用户:"把氛围灯调成红色"
     */
    mcp_send_line(
        "AT+ADDMCP=1,set_lamp_color,设置音箱氛围灯颜色,F1,3,R,G,B"
    );

    /*
     * 用户:"音量调到 60"
     */
    mcp_send_line(
        "AT+ADDMCP=1,set_speaker_volume,设置音箱音量,F2,1,volume"
    );

    /*
     * 用户:"切换到 4G 网络"
     */
    mcp_send_line(
        "AT+ADDMCP=1,set_network_mode,切换联网方式,F3,1,mode"
    );

    /*
     * 用户:"重新配网"
     */
    mcp_send_line(
        "AT+ADDMCP=0,enter_pairing,让音箱进入配网模式,2,F4,01"
    );

    /*
     * 用户:"打开客户系统的一号场景"
     */
    mcp_send_line(
        "AT+ADDMCP=1,customer_scene,控制客户业务系统,F5,2,scene_id,action"
    );
}

static void mcp_handle_frame(uint8_t cmd, const uint8_t *data, uint8_t len)
{
    if (cmd == CMD_RECOVER) {
        ai_reset_module();
        mcp_register_tools();
        return;
    }

    if (cmd == CMD_STATUS && len >= 1) {
        ui_set_ai_status((ai_status_t)data[0]);
        return;
    }

    switch (cmd) {
    case CMD_RGB:
        if (len >= 3) {
            speaker_set_rgb(data[0], data[1], data[2]);
        }
        break;

    case CMD_VOLUME:
        if (len >= 1) {
            speaker_set_volume(data[0]);
        }
        break;

    case CMD_NET_MODE:
        if (len >= 1) {
            speaker_set_network_mode(data[0]);
        }
        break;

    case CMD_PAIRING:
        speaker_enter_pairing();
        break;

    case CMD_CUSTOMER:
        if (len >= 2) {
            customer_scene_control(data[0], data[1]);
        }
        break;

    default:
        ESP_LOGW(TAG, "unknown MCP cmd=0x%02X len=%u", cmd, len);
        break;
    }
}

static void mcp_rx_task(void *arg)
{
    uint8_t frame[160];
    uint8_t pos = 0;
    int expected = -1;

    while (1) {
        uint8_t b = 0;
        int n = uart_read_bytes(AI_UART, &b, 1, pdMS_TO_TICKS(100));

        if (n <= 0) {
            continue;
        }

        if (pos == 0 && b != FRAME_H1) {
            continue;
        }

        if (pos == 1 && b != FRAME_H2) {
            pos = 0;
            expected = -1;
            continue;
        }

        frame[pos++] = b;

        if (pos == 3) {
            uint8_t len = frame[2];

            if (len == 0 || len > 150) {
                pos = 0;
                expected = -1;
                continue;
            }

            expected = 2 + 1 + len + 2;
        }

        if (expected > 0 && pos >= expected) {
            uint8_t len = frame[2];

            if (frame[3 + len] == FRAME_T1 &&
                frame[4 + len] == FRAME_T2) {

                uint8_t cmd = frame[3];
                const uint8_t *data = &frame[4];
                uint8_t data_len = len - 1;

                mcp_handle_frame(cmd, data, data_len);
            } else {
                ESP_LOGW(TAG, "bad frame tail");
            }

            pos = 0;
            expected = -1;
        }

        if (pos >= sizeof(frame)) {
            pos = 0;
            expected = -1;
        }
    }
}

void mcp_uart_bridge_start(void)
{
    uart_config_t cfg = {
        .baud_rate = AI_UART_BAUD,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    uart_driver_install(AI_UART, 2048, 2048, 0, NULL, 0);
    uart_param_config(AI_UART, &cfg);
    uart_set_pin(
        AI_UART,
        AI_UART_TX_GPIO,
        AI_UART_RX_GPIO,
        UART_PIN_NO_CHANGE,
        UART_PIN_NO_CHANGE
    );

    ai_reset_module();
    mcp_register_tools();

    xTaskCreate(mcp_rx_task, "mcp_rx", 4096, NULL, 10, NULL);
}

7.6 音频焦点与实时打断

复制代码
#include <stdbool.h>
#include "esp_log.h"

#define TAG "AUDIO_FOCUS"

typedef enum {
    AUDIO_SRC_NONE = 0,
    AUDIO_SRC_SYSTEM_ALERT,
    AUDIO_SRC_AI_LISTENING,
    AUDIO_SRC_AI_TTS,
    AUDIO_SRC_ALARM,
    AUDIO_SRC_BT_MUSIC,
    AUDIO_SRC_UI_TONE,
} audio_source_t;

static audio_source_t s_current_audio = AUDIO_SRC_NONE;

static int audio_priority(audio_source_t src)
{
    switch (src) {
    case AUDIO_SRC_SYSTEM_ALERT: return 100;
    case AUDIO_SRC_AI_LISTENING: return 90;
    case AUDIO_SRC_AI_TTS:       return 80;
    case AUDIO_SRC_ALARM:        return 70;
    case AUDIO_SRC_BT_MUSIC:     return 50;
    case AUDIO_SRC_UI_TONE:      return 30;
    default:                     return 0;
    }
}

static void stop_tts_player(void)
{
    ESP_LOGI(TAG, "stop TTS");
    /*
     * TODO:
     * 1. 停止 I2S 播放;
     * 2. 清空 TTS ringbuffer;
     * 3. 通知 WebSocket 当前 TTS 被打断。
     */
}

static void duck_bt_music(void)
{
    ESP_LOGI(TAG, "duck Bluetooth music");
    /*
     * TODO:
     * 通过 7014 或蓝牙音频芯片降低 A2DP 音量。
     */
}

bool audio_request_focus(audio_source_t src)
{
    if (audio_priority(src) < audio_priority(s_current_audio)) {
        return false;
    }

    if (s_current_audio == AUDIO_SRC_AI_TTS &&
        src == AUDIO_SRC_AI_LISTENING) {
        stop_tts_player();
    }

    if (s_current_audio == AUDIO_SRC_BT_MUSIC &&
        src == AUDIO_SRC_AI_LISTENING) {
        duck_bt_music();
    }

    s_current_audio = src;
    return true;
}

void on_wakeup_word_detected(void)
{
    ESP_LOGI(TAG, "wake word detected");
    audio_request_focus(AUDIO_SRC_AI_LISTENING);

    /*
     * TODO:
     * 1. 更新 UI 到 Listening;
     * 2. 停止 TTS 或降低蓝牙音乐;
     * 3. 打开上行音频流;
     * 4. 通知 AI session 开始新一轮对话。
     */
}

void on_realtime_barge_in_detected(void)
{
    ESP_LOGI(TAG, "real-time barge-in detected");
    audio_request_focus(AUDIO_SRC_AI_LISTENING);
}

void on_ai_tts_start(void)
{
    audio_request_focus(AUDIO_SRC_AI_TTS);
}

void on_ai_tts_finish(void)
{
    s_current_audio = AUDIO_SRC_NONE;
}

VB6824 自定义唤醒词流程中,开发宝典说明升级完成后可以支持选定唤醒词,并且在 AI 说话过程中随时打断。


7.7 客户系统接入示例:MQTT / HTTP

MQTT 上报设备状态

复制代码
#include <stdio.h>
#include "esp_log.h"

#define TAG "CUSTOMER_MQTT"

typedef struct {
    int wifi_rssi;
    int battery;
    int volume;
    int network_mode;
    const char *ai_state;
    const char *fw_version;
} device_shadow_t;

static void mqtt_publish_shadow(const char *payload)
{
    /*
     * TODO:
     * esp_mqtt_client_publish(
     *   mqtt,
     *   "sibo/speaker/speaker_001/shadow/update",
     *   payload,
     *   0,
     *   1,
     *   0
     * );
     */
    ESP_LOGI(TAG, "shadow=%s", payload);
}

void customer_upload_shadow(const device_shadow_t *s)
{
    char payload[512];

    snprintf(payload, sizeof(payload),
        "{"
        "\"fw_version\":\"%s\","
        "\"wifi_rssi\":%d,"
        "\"battery\":%d,"
        "\"volume\":%d,"
        "\"network_mode\":%d,"
        "\"ai\":{\"state\":\"%s\"}"
        "}",
        s->fw_version,
        s->wifi_rssi,
        s->battery,
        s->volume,
        s->network_mode,
        s->ai_state
    );

    mqtt_publish_shadow(payload);
}

HTTP 调客户场景接口

复制代码
#include <stdio.h>
#include <string.h>
#include "esp_http_client.h"
#include "esp_log.h"

#define TAG "CUSTOMER_HTTP"

esp_err_t customer_http_call_scene(uint8_t scene_id, uint8_t action)
{
    char body[128];

    snprintf(body, sizeof(body),
             "{\"device_id\":\"speaker_001\",\"scene_id\":%u,\"action\":%u}",
             scene_id,
             action);

    esp_http_client_config_t config = {
        .url = "https://api.customer.com/v1/scene/control",
        .method = HTTP_METHOD_POST,
        .timeout_ms = 8000,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);

    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_header(client, "Authorization", "Bearer ${token}");
    esp_http_client_set_post_field(client, body, strlen(body));

    esp_err_t err = esp_http_client_perform(client);
    int status = esp_http_client_get_status_code(client);

    ESP_LOGI(TAG, "customer api status=%d err=%s",
             status,
             esp_err_to_name(err));

    esp_http_client_cleanup(client);
    return err;
}

8. 私有化后端与 AI 链路

开发宝典中说明,设备默认可连接小智官方服务,也可以运行开源后端服务,让设备连接自己的后端;后端提供 OTA 接口和 WebSocket 接口,并可配置 LLM、TTS、人设等参数。

推荐后端结构:

复制代码
Nginx / Caddy
 ├── HTTPS OTA
 ├── WSS WebSocket
 ├── Device Auth
 ├── ASR
 ├── LLM
 ├── TTS
 ├── MCP Tool Server
 ├── Customer API Gateway
 └── Log / Device Shadow

配置示例:

复制代码
server:
  websocket_url: "wss://ai.customer.com/xiaozhi/v1/"
  ota_url: "https://ai.customer.com/xiaozhi/ota/"
  mqtt_uri: "mqtts://mqtt.customer.com"

device:
  product_id: "sibo_ai_speaker_4g_s3"
  auth_type: "token"
  shadow_enable: true
  log_upload_enable: true

ai:
  ASR: "FunASR"
  LLM: "CustomerLLM"
  TTS: "DoubaoTTS"
  Intent: "function_call"

capabilities:
  - voice_wakeup
  - realtime_barge_in
  - wifi
  - lte_4g
  - bluetooth_pan
  - bluetooth_speaker
  - display
  - mcp
  - ota
  - customer_api

9. OTA 与分区设计

推荐 16MB Flash 分区:

复制代码
# Name,     Type, SubType, Offset,   Size
nvs,        data, nvs,     0x9000,   0x6000
otadata,    data, ota,     0xF000,   0x2000
phy_init,   data, phy,     0x11000,  0x1000
factory,    app,  factory, 0x20000,  0x300000
ota_0,      app,  ota_0,   0x320000, 0x300000
ota_1,      app,  ota_1,   0x620000, 0x300000
storage,    data, fat,     0x920000, 0x500000
coredump,   data, coredump,0xE20000, 0x100000

OTA 策略:

复制代码
1. 主控固件 OTA:
   ESP32-S3 双 OTA 分区,失败自动回滚。

2. 资源 OTA:
   屏幕素材、提示音、默认配置、语音提示资源。

3. VB6824 固件:
   单独走语音芯片升级流程,避免和主控 OTA 混淆。

4. 4G 流量控制:
   4G 下只允许紧急升级或小版本补丁;
   大版本升级建议提示用户切换 Wi-Fi。

5. 灰度发布:
   按 product_id / batch_id / device_id 分批升级。

6. 安全:
   HTTPS OTA、固件签名、NVS 加密保存 token。

10. 产测方案

10.1 工厂测试项目

复制代码
主控:
  - ESP32-S3 Flash / PSRAM 检测
  - Wi-Fi MAC / BLE MAC
  - SN 写入
  - NVS 读写
  - 固件版本读取

语音:
  - VB6824 UART 通信
  - MIC 电平
  - 喇叭播放 1kHz
  - 唤醒词测试
  - TTS 播报中打断测试
  - 高噪环境唤醒测试

网络:
  - Wi-Fi 扫描
  - Wi-Fi 连接
  - 4G SIM 检测
  - 4G CSQ
  - 4G 注册
  - PPP 拨号
  - 蓝牙广播
  - 蓝牙 PAN / A2DP 检测

UI:
  - LCD 显示
  - RGB LED
  - 按键单击 / 双击 / 三击 / 长按
  - 音量控制

云端:
  - OTA 请求
  - WebSocket 连接
  - 设备激活
  - MCP 指令闭环
  - 客户 API 调用

10.2 产测命令建议

复制代码
FACTORY ENTER
FACTORY SET_SN=SBOS3G20260001
FACTORY WIFI_SCAN
FACTORY WIFI_CONNECT=ssid,password
FACTORY LTE_CSQ
FACTORY LTE_DIAL
FACTORY BT_ADV
FACTORY MIC_LEVEL
FACTORY SPK_PLAY=1000HZ
FACTORY WAKE_TEST
FACTORY BARGE_IN_TEST
FACTORY LCD_TEST
FACTORY LED_RGB=255,0,0
FACTORY MQTT_CONNECT
FACTORY OTA_CHECK
FACTORY MCP_TEST
FACTORY EXIT

11. 推荐产品版本

11.1 标准 4G S3 音箱版

复制代码
ESP32-S3R8 + 16M Flash + 8M PSRAM
VB6824
4G Cat.1 UART PPP
Wi-Fi + BLE BluFi
1.3 / 1.54 寸屏
3W / 4Ω 喇叭
单麦 / 双麦
电池 + Type-C
小智 / 豆包 / ChatGPT / 私有后端
AT+MCP 二次开发

适合:家庭音箱、儿童故事机、AI 桌面助手、商用语音终端。

11.2 增强三模版

复制代码
ESP32-S3R8 + 16M Flash + 8M PSRAM
VB6824
4G Cat.1 / Cat.4
7014 蓝牙 PAN + 蓝牙音箱
Wi-Fi STA / SoftAP
BLE BluFi
LCD 屏幕
RGB 氛围灯
3W~5W 喇叭
实时打断
自定义唤醒词
OTA + 产测 + 私有化后端

适合:户外音箱、展厅导览、车载 AI 音箱、移动 AI 设备。

11.3 B 端客户系统版

复制代码
ESP32-S3R8
VB6824
Wi-Fi / 4G / 蓝牙 PAN
客户私有 LLM
客户知识库
客户业务 API
MQTT / HTTP / WebSocket
远程运维
批量 OTA
日志上报

适合:酒店、门店、展厅、养老、教育、智能家居中控、行业私有化部署。


12. 方案总结

四博 AI 智能音箱 4G S3 版本建议采用 ESP32-S3R8 + 16M Flash + 8M PSRAM + VB6824 + 4G 模组 + 可选 7014 蓝牙协处理器 的平台化架构。

ESP32-S3 负责 Wi-Fi、BLE、4G 管理、屏幕 UI、WebSocket、OTA、MCP 和客户系统接入;VB6824 负责远距离拾音、离线唤醒、AEC、降噪、唤醒词打断和自定义唤醒词;4G 模组负责无 Wi-Fi 场景联网;7014 负责蓝牙 PAN 上网和蓝牙音箱能力。软件上基于 DOIT_AI、小智协议、AT+MCP、BluFi 配网、实时打断固件和私有化后端,可快速形成一套适合品牌客户、方案商客户和 B 端行业客户的可量产 AI 智能音箱方案。

相关推荐
skywalk81631 小时前
基于 Kotti CMS 的 AI 共创社区插件 —— 实现 AI 资源共享、协作交流和项目孵化(先放弃)
人工智能
qq_411262421 小时前
四博AI智能拍学机方案设计
人工智能·智能音箱
格林威1 小时前
面阵相机 vs 线阵相机:堡盟与Basler选型差异全解析 +C# 实战演示
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
爱上好庆祝2 小时前
学习js的第三天
前端·css·人工智能·学习·计算机外设·js
隔壁大炮2 小时前
10.PyTorch_元素类型转换
人工智能·pytorch·深度学习·算法
杨浦老苏2 小时前
AI使用追踪和代理网关GoModel
人工智能·docker·ai·api·群晖
大龄码农-涵哥2 小时前
Spring Cloud微服务架构详解:从服务注册到配置中心,阿里面试核心知识点
spring cloud·微服务·架构
roman_日积跬步-终至千里2 小时前
【案例题-知识点】架构风格与架构模式(2):高频架构模式与选型
架构
企业架构师老王2 小时前
药品生产环节:用实在Agent自动生成批记录与打印领料单的合规设计与架构落地
大数据·人工智能·ai·架构