四博 AI 智能音箱 4G S3 版本工程落地方案:三模联网、远场唤醒、打断播放与 AI 会话框架
四博 AI 智能音箱 4G S3 版本工程落地方案:三模联网、远场唤醒、打断播放与 AI 会话框架
1. 方案定位
四博 AI 智能音箱 4G S3 版本是一套基于ESP32-S3 + VB6824 + Wi-Fi/BLE/4G的 AI 语音终端方案。它不是传统意义上的蓝牙音箱,而是面向家庭、厨房、展厅、户外、门店和 B 端定制场景的联网 AI 音箱。
四博模组选型资料中,ESP32-S3 系列被定位在音视频 / AI 市场,ESPS3-32、ESPS3-32E 系列兼容官方 ESP32-S3 模组封装,适合 AI 音箱、AI 相机、多模态设备等产品开发。 四博 AI 方案资料中也明确给出 ESP32-C2/C3/S3 + VB6824 语音方案,VB6824 可完成音频编解码、AEC、语音唤醒和唤醒词修改,让主控芯片专注通信及 UI。
本方案采用“三层解耦”架构:
1. 网络平面:Wi-Fi / BLE / 4G
2. 语音平面:VB6824 / 唤醒 / 打断 / 离线命令
3. AI 平面:WebSocket / 大模型 / TTS / OTA / 工具调用
2. 硬件组合建议
主控:ESP32-S3R8 / ESPS3-32 N16R8
语音:VB6824
联网:Wi-Fi + BLE + 4G Cat.1
音频:MIC + 功放 + 喇叭
显示:LED 灯效 / 小屏 / 点阵屏可选
存储:16MB Flash + 8MB PSRAM
电源:Type-C / 锂电池 / DC 供电
扩展:外置经典蓝牙音频芯片 / TF Card / 按键
需要特别说明:
ESP32-S3 原生支持 Wi-Fi + BLE,BLE 适合配网、绑定、小程序控制;如果要实现真正的手机蓝牙音乐播放,也就是 A2DP 经典蓝牙音频,建议增加外置蓝牙音频芯片或采用 7014 类蓝牙音频方案。四博资料中也单独列出了 7014 蓝牙 PAN AI 手机伴侣方案和 AI 智能音响方案,强调可做蓝牙音响、闹钟、声音克隆、声纹识别、APP、小程序和改唤醒词等功能。
3. 总体软件框架
这次不采用普通while(1)轮询写法,而是采用Bus + Manager + Port + Policy的工程结构。
sibo_ai_speaker_4g_s3/
├── app/
│ ├── app_main.c
│ ├── app_bus.c
│ ├── app_bus.h
│ ├── app_fsm.c
│ └── app_fsm.h
├── board/
│ ├── board_config.h
│ └── board_init.c
├── network/
│ ├── net_policy.c
│ ├── wifi_port.c
│ ├── ble_provision.c
│ └── modem_4g_port.c
├── voice/
│ ├── vb6824_port.c
│ ├── voice_cmd_map.c
│ └── wakeup_controller.c
├── audio/
│ ├── audio_route.c
│ ├── tts_player.c
│ └── prompt_player.c
├── ai/
│ ├── ai_session.c
│ ├── ai_protocol.c
│ └── ai_action.c
├── ota/
│ └── ota_service.c
└── ui/
├── speaker_ui.c
└── led_effect.c
核心思想:
驱动层只负责收发数据;
Manager 层只负责状态管理;
Policy 层只负责选择策略;
App 层只处理业务事件;
AI 层只处理协议与云端交互。
4. board_config.h
#pragma once
#include "driver/gpio.h"
#include "driver/uart.h"
#define PRODUCT_NAME "SIBO_AI_SPEAKER_4G_S3"
#define DEVICE_ID "SIBO_S3_4G_001122334455"
/* AI Server */
#define AI_WS_URI "wss://ai.example.com/sibo/speaker/ws"
/* VB6824 UART */
#define VB6824_UART UART_NUM_1
#define VB6824_TXD GPIO_NUM_17
#define VB6824_RXD GPIO_NUM_18
#define VB6824_BAUD 115200
/* 4G UART */
#define MODEM_UART UART_NUM_2
#define MODEM_TXD GPIO_NUM_40
#define MODEM_RXD GPIO_NUM_41
#define MODEM_PWR GPIO_NUM_42
#define MODEM_BAUD 115200
/* Audio */
#define I2S_BCLK GPIO_NUM_5
#define I2S_WS GPIO_NUM_6
#define I2S_DOUT GPIO_NUM_7
#define I2S_DIN GPIO_NUM_8
#define AMP_EN GPIO_NUM_9
/* Keys */
#define KEY_WAKEUP GPIO_NUM_0
#define KEY_VOL_UP GPIO_NUM_10
#define KEY_VOL_DOWN GPIO_NUM_11
/* Network Policy */
#define WIFI_SCORE 100
#define MODEM_4G_SCORE 70
#define NET_RETRY_INTERVAL_MS 3000
5. 应用事件总线
AI 音箱有大量异步事件:网络上线、4G 断线、语音唤醒、播放中打断、AI 返回、TTS 播放完成、OTA 通知。建议统一走事件总线。
#pragma once
#include <stdint.h>
#include <stddef.h>
typedef enum {
BUS_EVT_NONE = 0,
BUS_EVT_WIFI_UP,
BUS_EVT_WIFI_DOWN,
BUS_EVT_4G_UP,
BUS_EVT_4G_DOWN,
BUS_EVT_BLE_CONFIG_OK,
BUS_EVT_NET_SELECTED,
BUS_EVT_NET_LOST,
BUS_EVT_WAKEUP,
BUS_EVT_BARGE_IN,
BUS_EVT_CMD_CHAT,
BUS_EVT_CMD_STOP,
BUS_EVT_CMD_VOL_UP,
BUS_EVT_CMD_VOL_DOWN,
BUS_EVT_CMD_BT_MODE,
BUS_EVT_CMD_MUSIC,
BUS_EVT_CMD_ALARM,
BUS_EVT_AI_CONNECTED,
BUS_EVT_AI_DISCONNECTED,
BUS_EVT_AI_JSON,
BUS_EVT_TTS_START,
BUS_EVT_TTS_DONE,
BUS_EVT_OTA_NOTIFY,
BUS_EVT_OTA_START,
BUS_EVT_OTA_DONE,
BUS_EVT_OTA_FAIL,
} bus_evt_id_t;
typedef struct {
bus_evt_id_t id;
void *payload;
size_t length;
} bus_evt_t;
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "app_bus.h"
static QueueHandle_t s_bus_queue = NULL;
void app_bus_init(void)
{
s_bus_queue = xQueueCreate(32, sizeof(bus_evt_t));
}
bool app_bus_post(bus_evt_id_t id, void *payload, size_t length)
{
if (!s_bus_queue) {
return false;
}
bus_evt_t evt = {
.id = id,
.payload = payload,
.length = length,
};
return xQueueSend(s_bus_queue, &evt, pdMS_TO_TICKS(20)) == pdTRUE;
}
QueueHandle_t app_bus_queue(void)
{
return s_bus_queue;
}
6. 三模联网策略:Wi-Fi 优先,4G 兜底,BLE 配网
这版代码用“评分制”来选择网络,比简单 if/else 更适合后续扩展。
typedef enum {
NET_IF_NONE = 0,
NET_IF_WIFI,
NET_IF_4G,
} net_if_t;
typedef struct {
bool wifi_ready;
bool g4_ready;
int wifi_rssi;
int g4_csq;
net_if_t active;
} net_policy_ctx_t;
static net_policy_ctx_t s_net;
static int net_score_wifi(void)
{
if (!s_net.wifi_ready) {
return 0;
}
if (s_net.wifi_rssi > -60) {
return 100;
} else if (s_net.wifi_rssi > -75) {
return 85;
}
return 60;
}
static int net_score_4g(void)
{
if (!s_net.g4_ready) {
return 0;
}
if (s_net.g4_csq >= 20) {
return 80;
} else if (s_net.g4_csq >= 12) {
return 70;
}
return 50;
}
static void net_policy_select(void)
{
int wifi_score = net_score_wifi();
int g4_score = net_score_4g();
net_if_t next = NET_IF_NONE;
if (wifi_score >= g4_score && wifi_score > 0) {
next = NET_IF_WIFI;
} else if (g4_score > 0) {
next = NET_IF_4G;
}
if (next != s_net.active) {
s_net.active = next;
if (next == NET_IF_NONE) {
app_bus_post(BUS_EVT_NET_LOST, NULL, 0);
} else {
app_bus_post(BUS_EVT_NET_SELECTED, NULL, 0);
}
}
}
void net_policy_on_event(bus_evt_id_t evt)
{
switch (evt) {
case BUS_EVT_WIFI_UP:
s_net.wifi_ready = true;
break;
case BUS_EVT_WIFI_DOWN:
s_net.wifi_ready = false;
modem_4g_connect_async();
break;
case BUS_EVT_4G_UP:
s_net.g4_ready = true;
break;
case BUS_EVT_4G_DOWN:
s_net.g4_ready = false;
break;
case BUS_EVT_BLE_CONFIG_OK:
wifi_connect_saved_profile();
break;
default:
break;
}
net_policy_select();
}
const char *net_policy_active_name(void)
{
switch (s_net.active) {
case NET_IF_WIFI:
return "wifi";
case NET_IF_4G:
return "4g";
default:
return "none";
}
}
7. 4G 模块 AT 命令封装
#include "driver/uart.h"
#include "esp_log.h"
#include "board_config.h"
static const char *TAG = "MODEM4G";
void modem_4g_init(void)
{
uart_config_t cfg = {
.baud_rate = MODEM_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(MODEM_UART, 4096, 4096, 0, NULL, 0);
uart_param_config(MODEM_UART, &cfg);
uart_set_pin(MODEM_UART, MODEM_TXD, MODEM_RXD,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
ESP_LOGI(TAG, "4G modem uart initialized");
}
static void modem_send_line(const char *cmd)
{
uart_write_bytes(MODEM_UART, cmd, strlen(cmd));
uart_write_bytes(MODEM_UART, "\r\n", 2);
}
void modem_4g_probe_basic(void)
{
modem_send_line("AT");
vTaskDelay(pdMS_TO_TICKS(300));
modem_send_line("ATE0");
vTaskDelay(pdMS_TO_TICKS(300));
modem_send_line("AT+CPIN?");
vTaskDelay(pdMS_TO_TICKS(300));
modem_send_line("AT+CSQ");
vTaskDelay(pdMS_TO_TICKS(300));
modem_send_line("AT+CREG?");
vTaskDelay(pdMS_TO_TICKS(300));
modem_send_line("AT+CGATT?");
vTaskDelay(pdMS_TO_TICKS(300));
}
void modem_4g_start_data_call(void)
{
modem_send_line("AT+CGDCONT=1,\"IP\",\"cmnet\"");
vTaskDelay(pdMS_TO_TICKS(500));
modem_send_line("ATD*99#");
/*
* 后续进入 PPP:
* 1. esp_modem 建立 DTE/DCE
* 2. esp_netif_attach PPP
* 3. 等待 IP_EVENT_PPP_GOT_IP
* 4. 成功后 app_bus_post(BUS_EVT_4G_UP)
*/
}
8. VB6824 串口流式解析器
上一版是简单判断buf[0] == 0xAA,真实产品建议写成流式解析器,避免串口分包、粘包导致解析失败。
协议示例:
AA 55 LEN CMD PAYLOAD... CHECKSUM
typedef enum {
PARSE_WAIT_AA = 0,
PARSE_WAIT_55,
PARSE_WAIT_LEN,
PARSE_WAIT_BODY,
PARSE_WAIT_SUM,
} vb_parse_state_t;
typedef struct {
vb_parse_state_t state;
uint8_t len;
uint8_t index;
uint8_t cmd;
uint8_t body[32];
uint8_t sum;
} vb_parser_t;
static vb_parser_t s_vb_parser;
static uint8_t vb_sum_calc(uint8_t len, uint8_t cmd, uint8_t *body)
{
uint8_t sum = 0xAA + 0x55 + len + cmd;
for (int i = 0; i < len - 1; i++) {
sum += body[i];
}
return sum;
}
static void vb_dispatch_cmd(uint8_t cmd)
{
switch (cmd) {
case 0x01:
app_bus_post(BUS_EVT_WAKEUP, NULL, 0);
break;
case 0x02:
app_bus_post(BUS_EVT_BARGE_IN, NULL, 0);
break;
case 0x03:
app_bus_post(BUS_EVT_CMD_CHAT, NULL, 0);
break;
case 0x04:
app_bus_post(BUS_EVT_CMD_STOP, NULL, 0);
break;
case 0x05:
app_bus_post(BUS_EVT_CMD_VOL_UP, NULL, 0);
break;
case 0x06:
app_bus_post(BUS_EVT_CMD_VOL_DOWN, NULL, 0);
break;
case 0x07:
app_bus_post(BUS_EVT_CMD_BT_MODE, NULL, 0);
break;
default:
break;
}
}
static void vb_parser_input(uint8_t byte)
{
switch (s_vb_parser.state) {
case PARSE_WAIT_AA:
if (byte == 0xAA) {
s_vb_parser.state = PARSE_WAIT_55;
}
break;
case PARSE_WAIT_55:
if (byte == 0x55) {
s_vb_parser.state = PARSE_WAIT_LEN;
} else {
s_vb_parser.state = PARSE_WAIT_AA;
}
break;
case PARSE_WAIT_LEN:
s_vb_parser.len = byte;
s_vb_parser.index = 0;
if (s_vb_parser.len == 1) {
s_vb_parser.state = PARSE_WAIT_SUM;
} else {
s_vb_parser.state = PARSE_WAIT_BODY;
}
break;
case PARSE_WAIT_BODY:
if (s_vb_parser.index == 0) {
s_vb_parser.cmd = byte;
} else {
s_vb_parser.body[s_vb_parser.index - 1] = byte;
}
s_vb_parser.index++;
if (s_vb_parser.index >= s_vb_parser.len) {
s_vb_parser.state = PARSE_WAIT_SUM;
}
break;
case PARSE_WAIT_SUM: {
uint8_t calc = vb_sum_calc(
s_vb_parser.len,
s_vb_parser.cmd,
s_vb_parser.body
);
if (calc == byte) {
vb_dispatch_cmd(s_vb_parser.cmd);
}
memset(&s_vb_parser, 0, sizeof(s_vb_parser));
s_vb_parser.state = PARSE_WAIT_AA;
break;
}
default:
s_vb_parser.state = PARSE_WAIT_AA;
break;
}
}
串口任务:
void vb6824_task(void *arg)
{
uint8_t buf[64];
while (1) {
int len = uart_read_bytes(VB6824_UART, buf, sizeof(buf), pdMS_TO_TICKS(100));
for (int i = 0; i < len; i++) {
vb_parser_input(buf[i]);
}
}
}
9. 音频状态机:远场唤醒 + 播放中打断
typedef enum {
AUDIO_IDLE = 0,
AUDIO_LISTENING,
AUDIO_UPLOADING,
AUDIO_PLAYING_TTS,
AUDIO_BT_MUSIC,
AUDIO_INTERRUPTED,
} audio_state_t;
static audio_state_t s_audio_state = AUDIO_IDLE;
static int s_volume = 70;
void audio_set_state(audio_state_t state)
{
s_audio_state = state;
}
void audio_barge_in(void)
{
if (s_audio_state == AUDIO_PLAYING_TTS ||
s_audio_state == AUDIO_BT_MUSIC) {
audio_player_stop();
s_audio_state = AUDIO_INTERRUPTED;
speaker_ui_status("已打断,请继续说");
}
}
void audio_volume_up(void)
{
if (s_volume < 100) {
s_volume += 5;
}
audio_player_set_volume(s_volume);
}
void audio_volume_down(void)
{
if (s_volume > 0) {
s_volume -= 5;
}
audio_player_set_volume(s_volume);
}
VB6824 的价值就在这里:它把唤醒、AEC、离线命令和抗噪能力前置到语音芯片,让 ESP32-S3 主要负责通信、状态机和 AI 协议。四博 AI 开发宝典中提到,VB6824 具备更远距离唤醒、更低误唤醒率、更强抗噪能力和更快响应识别时间。
10. AI 会话协议
设备上线:
{
"type": "hello",
"device_id": "SIBO_S3_4G_001122334455",
"product": "SIBO_AI_SPEAKER_4G_S3",
"net": "wifi",
"capability": {
"wifi": true,
"ble": true,
"g4": true,
"far_field": true,
"barge_in": true,
"wake_word": "小博小博"
}
}
用户语音请求:
{
"type": "chat",
"session_id": "session_0001",
"text": "帮我设置一个晚上八点的提醒",
"scene": "ai_speaker",
"tts": true
}
AI 返回:
{
"type": "ai_result",
"text": "好的,已为你设置晚上八点的提醒。",
"tts_url": "https://cdn.example.com/tts/001.mp3",
"action": {
"name": "set_alarm",
"params": {
"time": "20:00"
}
}
}
WebSocket 客户端:
static esp_websocket_client_handle_t s_ws;
static void ai_ws_event_handler(void *arg,
esp_event_base_t base,
int32_t event_id,
void *event_data)
{
esp_websocket_event_data_t *data = event_data;
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
app_bus_post(BUS_EVT_AI_CONNECTED, NULL, 0);
break;
case WEBSOCKET_EVENT_DISCONNECTED:
app_bus_post(BUS_EVT_AI_DISCONNECTED, NULL, 0);
break;
case WEBSOCKET_EVENT_DATA:
if (data->op_code == 0x1) {
char *json = calloc(1, data->data_len + 1);
if (json) {
memcpy(json, data->data_ptr, data->data_len);
app_bus_post(BUS_EVT_AI_JSON, json, data->data_len);
}
}
break;
default:
break;
}
}
void ai_session_start(void)
{
esp_websocket_client_config_t cfg = {
.uri = AI_WS_URI,
.reconnect_timeout_ms = 3000,
.network_timeout_ms = 10000,
};
s_ws = esp_websocket_client_init(&cfg);
esp_websocket_register_events(s_ws, WEBSOCKET_EVENT_ANY, ai_ws_event_handler, NULL);
esp_websocket_client_start(s_ws);
}
static void ai_send_json(cJSON *root)
{
if (!s_ws || !esp_websocket_client_is_connected(s_ws)) {
return;
}
char *text = cJSON_PrintUnformatted(root);
if (!text) {
return;
}
esp_websocket_client_send_text(s_ws, text, strlen(text), portMAX_DELAY);
cJSON_free(text);
}
11. 设备 Hello 上报
void ai_send_hello(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "hello");
cJSON_AddStringToObject(root, "device_id", DEVICE_ID);
cJSON_AddStringToObject(root, "product", PRODUCT_NAME);
cJSON_AddStringToObject(root, "net", net_policy_active_name());
cJSON *cap = cJSON_CreateObject();
cJSON_AddBoolToObject(cap, "wifi", true);
cJSON_AddBoolToObject(cap, "ble", true);
cJSON_AddBoolToObject(cap, "g4", true);
cJSON_AddBoolToObject(cap, "far_field", true);
cJSON_AddBoolToObject(cap, "barge_in", true);
cJSON_AddStringToObject(cap, "wake_word", "小博小博");
cJSON_AddItemToObject(root, "capability", cap);
ai_send_json(root);
cJSON_Delete(root);
}
12. AI 返回解析与 Action 分发
static void ai_handle_action(cJSON *action)
{
cJSON *name = cJSON_GetObjectItem(action, "name");
cJSON *params = cJSON_GetObjectItem(action, "params");
if (!cJSON_IsString(name)) {
return;
}
if (strcmp(name->valuestring, "set_alarm") == 0) {
cJSON *time = cJSON_GetObjectItem(params, "time");
if (cJSON_IsString(time)) {
alarm_set(time->valuestring);
}
} else if (strcmp(name->valuestring, "play_music") == 0) {
cJSON *keyword = cJSON_GetObjectItem(params, "keyword");
if (cJSON_IsString(keyword)) {
music_search_and_play(keyword->valuestring);
}
} else if (strcmp(name->valuestring, "switch_bt") == 0) {
audio_route_set_bt_mode(true);
}
}
void ai_parse_json(const char *json)
{
cJSON *root = cJSON_Parse(json);
if (!root) {
return;
}
cJSON *type = cJSON_GetObjectItem(root, "type");
if (cJSON_IsString(type) && strcmp(type->valuestring, "ai_result") == 0) {
cJSON *text = cJSON_GetObjectItem(root, "text");
cJSON *tts = cJSON_GetObjectItem(root, "tts_url");
cJSON *action = cJSON_GetObjectItem(root, "action");
if (cJSON_IsString(text)) {
speaker_ui_text(text->valuestring);
}
if (cJSON_IsString(tts)) {
audio_set_state(AUDIO_PLAYING_TTS);
tts_player_play_url(tts->valuestring);
}
if (cJSON_IsObject(action)) {
ai_handle_action(action);
}
}
if (cJSON_IsString(type) && strcmp(type->valuestring, "ota_notify") == 0) {
cJSON *url = cJSON_GetObjectItem(root, "firmware_url");
if (cJSON_IsString(url)) {
char *copy = strdup(url->valuestring);
app_bus_post(BUS_EVT_OTA_NOTIFY, copy, strlen(copy));
}
}
cJSON_Delete(root);
}
13. 音频路由:AI TTS / 本地提示音 / 蓝牙音频
typedef enum {
AUDIO_ROUTE_PROMPT = 0,
AUDIO_ROUTE_AI_TTS,
AUDIO_ROUTE_BT_AUDIO,
} audio_route_t;
static audio_route_t s_route = AUDIO_ROUTE_PROMPT;
void audio_route_set(audio_route_t route)
{
s_route = route;
switch (route) {
case AUDIO_ROUTE_PROMPT:
external_bt_audio_enable(false);
amp_enable(true);
break;
case AUDIO_ROUTE_AI_TTS:
external_bt_audio_enable(false);
amp_enable(true);
break;
case AUDIO_ROUTE_BT_AUDIO:
external_bt_audio_enable(true);
amp_enable(true);
audio_set_state(AUDIO_BT_MUSIC);
break;
}
}
void audio_route_set_bt_mode(bool enable)
{
if (enable) {
audio_route_set(AUDIO_ROUTE_BT_AUDIO);
speaker_ui_status("蓝牙音箱模式");
} else {
audio_route_set(AUDIO_ROUTE_PROMPT);
speaker_ui_status("AI 音箱模式");
}
}
14. App 事件分发
static void app_handle_event(bus_evt_t *evt)
{
switch (evt->id) {
case BUS_EVT_WIFI_UP:
case BUS_EVT_WIFI_DOWN:
case BUS_EVT_4G_UP:
case BUS_EVT_4G_DOWN:
case BUS_EVT_BLE_CONFIG_OK:
net_policy_on_event(evt->id);
break;
case BUS_EVT_NET_SELECTED:
speaker_ui_status("网络已连接");
ai_session_start();
break;
case BUS_EVT_AI_CONNECTED:
speaker_ui_status("AI 服务在线");
ai_send_hello();
break;
case BUS_EVT_WAKEUP:
audio_barge_in();
audio_set_state(AUDIO_LISTENING);
prompt_player_play("/spiffs/wakeup.mp3");
speaker_ui_status("我在,请说");
break;
case BUS_EVT_BARGE_IN:
audio_barge_in();
break;
case BUS_EVT_CMD_CHAT:
speaker_ui_status("正在思考...");
ai_send_chat_text("用户发起语音对话");
break;
case BUS_EVT_CMD_STOP:
audio_player_stop();
audio_set_state(AUDIO_IDLE);
speaker_ui_status("已停止");
break;
case BUS_EVT_CMD_VOL_UP:
audio_volume_up();
break;
case BUS_EVT_CMD_VOL_DOWN:
audio_volume_down();
break;
case BUS_EVT_CMD_BT_MODE:
audio_route_set_bt_mode(true);
break;
case BUS_EVT_AI_JSON:
ai_parse_json((char *)evt->payload);
free(evt->payload);
break;
case BUS_EVT_OTA_NOTIFY:
ota_start((char *)evt->payload);
free(evt->payload);
break;
default:
break;
}
}
void app_dispatch_task(void *arg)
{
QueueHandle_t q = app_bus_queue();
bus_evt_t evt;
while (1) {
if (xQueueReceive(q, &evt, portMAX_DELAY) == pdTRUE) {
app_handle_event(&evt);
}
}
}
15. app_main.c
void app_main(void)
{
nvs_flash_init();
app_bus_init();
board_init();
speaker_ui_init();
speaker_ui_status("四博 AI 智能音箱启动中...");
audio_route_init();
wifi_port_init();
ble_provision_init();
modem_4g_init();
vb6824_port_init();
ota_service_init();
xTaskCreate(app_dispatch_task, "app_dispatch", 8192, NULL, 8, NULL);
xTaskCreate(vb6824_task, "vb6824_task", 4096, NULL, 7, NULL);
wifi_connect_saved_profile();
ble_provision_start();
modem_4g_probe_basic();
speaker_ui_status("正在连接 Wi-Fi / 4G...");
}
16. sdkconfig.defaults
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_TX_BUFFER_TYPE_DYNAMIC=y
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=8192
CONFIG_LWIP_TCP_WND_DEFAULT=8192
CONFIG_LWIP_TCP_RECVMBOX_SIZE=16
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_PPP_SUPPORT=y
17. 总结
四博 AI 智能音箱 4G S3 版本的关键不是“能播放声音”,而是构建一套稳定的 AI 语音终端架构:
ESP32-S3:系统主控、三模联网、AI 协议、OTA
VB6824:远场唤醒、AEC、离线命令、高噪声识别
Wi-Fi:家庭和固定场景主网络
4G:户外、门店、展厅兜底网络
BLE:小程序配网、绑定、控制
WebSocket:AI 长连接、TTS、工具调用
外置蓝牙音频芯片:扩展传统蓝牙音箱能力
这套架构适合 AI 音箱、厨房语音助手、户外便携音箱、门店讲解终端、展厅接待设备和 B 端定制语音硬件。
