四博 AI 智能音箱方案
基于四博 ESP32-S3 AI-Speaker 核心方案
1. 方案概述
四博 AI 智能音箱是一套基于 ESP32-S3 / 四博 AI-Speaker 开发板 打造的智能语音硬件方案,面向 AI 陪伴、学习问答、家庭助手、儿童故事机、桌面智能终端、AI 玩具、AI 音响等场景。
方案以 ESP32-S3R8 高性能主控 + 麦克风采集 + 喇叭播放 + LCD 显示 + Wi-Fi/BLE 联网 + 云端/本地智能体接入 为核心,实现语音唤醒、实时语音对话、知识库问答、声音克隆、MCP 工具调用、OTA 升级、素材在线更新等能力。
四博 AI-Speaker 开发板本身为全开源方案,带 240×240 分辨率 1.3 寸屏幕,支持"四博小助手"小程序,支持声音克隆、知识库、自建大模型和 MCP 扩展能力,源码基于 DOIT_AI 工程。
2. 核心硬件选型
2.1 主控方案
推荐主控:
| 模块 | 推荐型号 | 说明 |
|---|---|---|
| 主控 SoC | ESP32-S3R8 / ESPS3-32-N16R8 | 双核 240MHz,适合 AI 音频、LCD、联网、UI |
| 存储 | 16MB Flash + 8MB PSRAM | 支持音频缓存、UI 资源、OTA、协议栈 |
| 网络 | 2.4GHz Wi-Fi + BLE | 支持 Wi-Fi 联网、BLE/BluFi 配网 |
| 显示 | 1.3 寸 240×240 或 1.54 寸 LCD | 显示表情、状态、歌词、学习卡片 |
| 音频 | ES8311 Codec / I2S Mic / VB6824 可选 | 支持采音、播放、离线唤醒 |
| 功放 | NS4150B / MAX98357A / Class-D 功放 | 驱动 4Ω/3W 或 8Ω/1W 喇叭 |
| 电源 | Type-C 5V + 锂电池可选 | 支持桌面供电或便携式 |
四博选型资料中,ESP32-S3 系列定位于音视频 / AI 市场,ESPS3-32、ESPS3-32E 系列兼容乐鑫官方模组封装;S3 系列可选 S3R2、S3R8 等规格,适合做通用 AI 方案。
ESP32-S3 相比 C2/C3 更适合 AI 智能音箱,因为它具备双核 240MHz、更多 GPIO、较大的 SRAM/PSRAM 扩展能力,并支持 LCD SPI/RGB/I8080 等显示接口。
3. 产品功能定义
3.1 基础 AI 音箱功能
-
语音唤醒
支持本地唤醒词,例如"小智小智""你好四博"等。
可选方案:
-
ESP-SR 本地唤醒;
-
VB6824 离线语音芯片唤醒;
-
按键唤醒;
-
触摸唤醒。
-
-
实时语音对话
麦克风采集用户语音,ESP32-S3 通过 WebSocket / HTTP 上传音频流,由云端完成 ASR、LLM、TTS,再回传语音流播放。
-
AI 学习问答
支持百科问答、英语口语陪练、古诗词讲解、数学题讲解、亲子故事、课程知识库问答等。
-
专业知识库接入
通过"四博小助手"或后台绑定自定义知识库,让音箱回答指定资料中的内容,例如产品手册、课件、说明书、企业资料。
-
声音克隆
用户录制少量语音后,可生成专属音色,让设备用指定声音播报。四博 AI-S3 方案资料中明确提到可通过小助手实现声音克隆、知识库接入、MCP 扩展和素材/固件在线更新。
-
MCP 工具调用
通过大模型工具调用机制,实现"语音控制设备"。例如:
-
"把音量调到 60%"
-
"打开小夜灯"
-
"播放英语单词练习"
-
"切换成睡眠模式"
-
"查询今天的课程表"
-
-
趣味游戏娱乐
支持成语接龙、猜谜语、口算训练、英语单词闯关、故事续写、角色扮演等。
-
OTA 升级
支持固件在线升级、提示音资源更新、UI 素材更新、参数远程配置。开发宝典中 DOIT_AI 工程示例包含 OTA URL 配置项。
4. 系统架构
┌──────────────────────────────┐
│ 云端 AI 服务平台 │
│ ASR / LLM / TTS / 知识库 / MCP │
└──────────────┬───────────────┘
│ WebSocket / HTTPS
┌──────────────▼───────────────┐
│ ESP32-S3 AI 主控 │
│ Wi-Fi / BLE / 音频 / LCD / OTA │
├──────────────┬───────────────┤
│ │ │
│ 音频系统 │
│ Mic → I2S/Codec → S3 → Amp │
│ │ │
├──────────────┼───────────────┤
│ LCD UI │ 按键/触摸/灯效 │
│ 表情/状态/字幕 │ 音量/唤醒/模式 │
└──────────────┴───────────────┘
ESP32-S3 不建议直接在本地运行完整大语言模型。更合理的分工是:
-
ESP32-S3 负责: 音频采集、音频播放、Wi-Fi/BLE、LCD UI、按键、灯效、OTA、协议通信、缓存管理。
-
云端或局域网服务负责: ASR、LLM 推理、TTS、知识库检索、声音克隆、复杂 MCP 工具编排。
-
离线语音芯片或 ESP-SR 负责: 唤醒词、简单离线命令、弱网兜底控制。
5. 开发环境
开发环境建议:
| 项目 | 建议配置 |
|---|---|
| IDE | VSCode |
| SDK | ESP-IDF 5.4.1 / 5.5.x |
| 工程 | SmartArduino/DOIT_AI |
| 目标芯片 | esp32s3 |
| 配网方式 | SoftAP / BluFi / 四博小助手 |
| 编译方式 | idf.py 或 VSCode ESP-IDF 插件 |
开发宝典中,四博 AI 系统硬件设备开源代码可在 VSCode 中安装 ESP-IDF 扩展和编译工具进行编译烧录;文档示例中安装 ESP-IDF 时选择 v5.5,AI-Speaker S3 编译教程使用 esp-idf 5.4.1。
开发宝典还明确说明,S3 开源工程推荐 ESP-IDF 版本不低于 5.4.0,C5 则需要不低于 5.5.0。
6. 工程配置示例
基于 DOIT_AI 工程,可在:
DOIT_AI/main/boards/doit-ai-speaker/sdkconfig.defaults.board
加入或确认类似配置:
# 目标芯片
CONFIG_IDF_TARGET="esp32s3"
# Wi-Fi / BLE
CONFIG_BT_ENABLED=y
CONFIG_USE_BLUFI_NET_CONFIGURING=y
# OTA 地址,实际量产时换成自己的 OTA 服务器
CONFIG_OTA_URL="https://your-domain.com/v1/ota/"
# PSRAM,S3R8 建议打开
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
# FreeRTOS Tick,音频场景建议 1000Hz
CONFIG_FREERTOS_HZ=1000
# 日志级别
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
# WebSocket / HTTPS
CONFIG_ESP_TLS_USING_MBEDTLS=y
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
# 分区表建议使用 OTA 分区
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_ota.csv"
编译命令示例:
cd DOIT_AI
# 导入 ESP-IDF 环境
. $IDF_PATH/export.sh
# 设置目标芯片
idf.py @main/boards/doit-ai-speaker/build.cfg set-target esp32s3
# 编译
idf.py @main/boards/doit-ai-speaker/build.cfg build
# 合并固件
idf.py @main/boards/doit-ai-speaker/build.cfg merge-bin
# 烧录和监视
idf.py -p COMx flash monitor
开发宝典中给出的 S3 AI-Speaker 编译流程包括克隆 DOIT_AI、配置 doit-ai-speaker 板级配置、设置 esp32s3 目标、执行 build 和 merge-bin。
7. 软件任务设计
建议软件分为 8 个核心任务:
| 任务 | 作用 |
|---|---|
wifi_task |
Wi-Fi 连接、断线重连、配网 |
audio_capture_task |
麦克风采集、VAD、上传音频 |
audio_play_task |
TTS 播放、提示音播放 |
ai_session_task |
WebSocket 长连接、AI 会话状态机 |
ui_task |
LCD 表情、字幕、状态显示 |
button_task |
按键、音量、打断、配网触发 |
ota_task |
版本检测、固件升级 |
mcp_task |
工具调用、外设控制、学习应用逻辑 |
状态机建议:
BOOT
↓
INIT_AUDIO / INIT_LCD / INIT_WIFI
↓
PROVISIONING 配网
↓
IDLE 待机
↓ 唤醒词 / 按键
LISTENING 录音
↓
THINKING AI 推理中
↓
SPEAKING 播放回复
↓ 用户打断
INTERRUPTED
↓
LISTENING
8. 关键代码示例
下面代码是基于 ESP-IDF 的技术骨架,实际引脚需要按照四博 AI-Speaker 原理图替换。
8.1 板级配置 board_config.h
#pragma once
#include "driver/gpio.h"
#include "driver/spi_master.h"
/*
* 注意:
* 下面引脚仅为工程模板示例。
* 量产时请按四博 AI-Speaker / AI-S3 原理图修改。
*/
#define BOARD_NAME "DOIT_AI_SPEAKER_S3"
/* I2S 音频 */
#define PIN_I2S_BCLK GPIO_NUM_4
#define PIN_I2S_WS GPIO_NUM_5
#define PIN_I2S_DIN GPIO_NUM_6 // Mic / Codec ADC data
#define PIN_I2S_DOUT GPIO_NUM_7 // DAC / Amp data
#define PIN_I2S_MCLK GPIO_NUM_16
/* I2C Codec,例如 ES8311 */
#define PIN_I2C_SDA GPIO_NUM_8
#define PIN_I2C_SCL GPIO_NUM_9
#define I2C_PORT_AUDIO I2C_NUM_0
/* LCD SPI */
#define LCD_HOST SPI2_HOST
#define PIN_LCD_MOSI GPIO_NUM_11
#define PIN_LCD_SCLK GPIO_NUM_12
#define PIN_LCD_CS GPIO_NUM_10
#define PIN_LCD_DC GPIO_NUM_13
#define PIN_LCD_RST GPIO_NUM_14
#define PIN_LCD_BL GPIO_NUM_15
/* 按键 */
#define PIN_KEY_WAKE GPIO_NUM_0
#define PIN_KEY_VOL_UP GPIO_NUM_1
#define PIN_KEY_VOL_DOWN GPIO_NUM_2
/* 功放使能 */
#define PIN_AMP_EN GPIO_NUM_17
/* 指示灯 */
#define PIN_RGB_LED GPIO_NUM_48
/* 音频参数 */
#define AUDIO_SAMPLE_RATE 16000
#define AUDIO_BITS_PER_SAMPLE 16
#define AUDIO_CHANNELS 1
#define AUDIO_FRAME_MS 20
#define AUDIO_FRAME_SAMPLES (AUDIO_SAMPLE_RATE * AUDIO_FRAME_MS / 1000)
8.2 主程序 app_main.c
#include <stdio.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "board_config.h"
#include "wifi_manager.h"
#include "audio_capture.h"
#include "audio_player.h"
#include "ai_ws_client.h"
#include "ui_display.h"
#include "button_service.h"
#include "ota_service.h"
#include "mcp_service.h"
static const char *TAG = "AI_SPEAKER";
void app_main(void)
{
ESP_LOGI(TAG, "启动 %s", BOARD_NAME);
/* 1. NVS 初始化 */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
/* 2. 系统事件和网络栈 */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* 3. 外设初始化 */
ui_display_init();
ui_display_show_text("四博AI音箱启动中...");
button_service_init();
audio_player_init();
audio_capture_init();
mcp_service_init();
/* 4. Wi-Fi / BluFi 配网 */
wifi_manager_init();
wifi_manager_start();
/* 5. AI WebSocket 会话 */
ai_ws_client_init();
/* 6. OTA */
ota_service_init();
/* 7. 启动任务 */
xTaskCreatePinnedToCore(audio_capture_task,
"audio_capture",
8192,
NULL,
5,
NULL,
0);
xTaskCreatePinnedToCore(audio_player_task,
"audio_player",
8192,
NULL,
5,
NULL,
1);
xTaskCreatePinnedToCore(ai_ws_client_task,
"ai_ws_client",
10240,
NULL,
6,
NULL,
1);
xTaskCreatePinnedToCore(ota_service_task,
"ota_service",
6144,
NULL,
3,
NULL,
0);
ui_display_show_text("等待配网/唤醒");
ESP_LOGI(TAG, "系统初始化完成");
}
8.3 I2S 麦克风采集 audio_capture.c
#include <string.h>
#include "esp_log.h"
#include "driver/i2s_std.h"
#include "board_config.h"
#include "ai_ws_client.h"
#include "audio_capture.h"
static const char *TAG = "AUDIO_CAPTURE";
static i2s_chan_handle_t rx_chan = NULL;
static volatile bool g_capture_enabled = false;
void audio_capture_start(void)
{
g_capture_enabled = true;
}
void audio_capture_stop(void)
{
g_capture_enabled = false;
}
esp_err_t audio_capture_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0,
I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(AUDIO_SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = PIN_I2S_MCLK,
.bclk = PIN_I2S_BCLK,
.ws = PIN_I2S_WS,
.dout = I2S_GPIO_UNUSED,
.din = PIN_I2S_DIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
ESP_LOGI(TAG, "I2S 采集初始化完成: %dHz, %dbit, mono",
AUDIO_SAMPLE_RATE, AUDIO_BITS_PER_SAMPLE);
return ESP_OK;
}
void audio_capture_task(void *arg)
{
int16_t pcm[AUDIO_FRAME_SAMPLES];
size_t bytes_read = 0;
while (1) {
if (!g_capture_enabled) {
vTaskDelay(pdMS_TO_TICKS(20));
continue;
}
esp_err_t ret = i2s_channel_read(rx_chan,
pcm,
sizeof(pcm),
&bytes_read,
pdMS_TO_TICKS(100));
if (ret == ESP_OK && bytes_read > 0) {
/*
* 可在这里加入:
* 1. VAD 检测
* 2. AEC 回声消除
* 3. AGC 自动增益
* 4. OPUS 编码
*/
ai_ws_send_audio((const uint8_t *)pcm, bytes_read);
} else {
ESP_LOGW(TAG, "I2S read failed: %s", esp_err_to_name(ret));
}
}
}
8.4 AI WebSocket 通信 ai_ws_client.c
#include <string.h>
#include "esp_log.h"
#include "esp_websocket_client.h"
#include "cJSON.h"
#include "ai_ws_client.h"
#include "audio_player.h"
#include "ui_display.h"
#include "mcp_service.h"
static const char *TAG = "AI_WS";
#define AI_WS_URL "wss://your-ai-server.com/v1/audio/chat"
static esp_websocket_client_handle_t client = NULL;
static volatile bool ws_connected = false;
static void ai_ws_send_hello(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "hello");
cJSON_AddStringToObject(root, "device", "doit_ai_speaker_s3");
cJSON_AddStringToObject(root, "audio_format", "pcm_s16le");
cJSON_AddNumberToObject(root, "sample_rate", 16000);
cJSON_AddNumberToObject(root, "channels", 1);
char *json = cJSON_PrintUnformatted(root);
esp_websocket_client_send_text(client, json, strlen(json), portMAX_DELAY);
cJSON_free(json);
cJSON_Delete(root);
}
static void handle_json_message(const char *json, int len)
{
char *buf = calloc(1, len + 1);
if (!buf) return;
memcpy(buf, json, len);
cJSON *root = cJSON_Parse(buf);
if (!root) {
free(buf);
return;
}
cJSON *type = cJSON_GetObjectItem(root, "type");
if (!cJSON_IsString(type)) {
cJSON_Delete(root);
free(buf);
return;
}
if (strcmp(type->valuestring, "stt") == 0) {
cJSON *text = cJSON_GetObjectItem(root, "text");
if (cJSON_IsString(text)) {
ui_display_show_text(text->valuestring);
ESP_LOGI(TAG, "用户说: %s", text->valuestring);
}
} else if (strcmp(type->valuestring, "llm") == 0) {
cJSON *text = cJSON_GetObjectItem(root, "text");
if (cJSON_IsString(text)) {
ui_display_show_text(text->valuestring);
ESP_LOGI(TAG, "AI回答: %s", text->valuestring);
}
} else if (strcmp(type->valuestring, "tts_start") == 0) {
ui_display_show_text("AI正在说话...");
audio_player_start();
} else if (strcmp(type->valuestring, "tts_end") == 0) {
audio_player_stop();
ui_display_show_text("等待唤醒");
} else if (strcmp(type->valuestring, "mcp") == 0) {
mcp_service_handle(root);
}
cJSON_Delete(root);
free(buf);
}
static void websocket_event_handler(void *handler_args,
esp_event_base_t base,
int32_t event_id,
void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ws_connected = true;
ESP_LOGI(TAG, "WebSocket 已连接");
ui_display_show_text("AI服务已连接");
ai_ws_send_hello();
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ws_connected = false;
ESP_LOGW(TAG, "WebSocket 已断开");
ui_display_show_text("AI服务断开,重连中");
break;
case WEBSOCKET_EVENT_DATA:
if (data->op_code == 0x1) {
handle_json_message(data->data_ptr, data->data_len);
} else if (data->op_code == 0x2) {
/*
* 二进制数据作为 TTS 音频流。
* 格式可以是 PCM / OPUS / MP3,按服务端约定处理。
*/
audio_player_feed((const uint8_t *)data->data_ptr, data->data_len);
}
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGE(TAG, "WebSocket 错误");
break;
default:
break;
}
}
esp_err_t ai_ws_client_init(void)
{
esp_websocket_client_config_t cfg = {
.uri = AI_WS_URL,
.buffer_size = 4096,
.network_timeout_ms = 10000,
.reconnect_timeout_ms = 3000,
};
client = esp_websocket_client_init(&cfg);
if (!client) {
return ESP_FAIL;
}
esp_websocket_register_events(client,
WEBSOCKET_EVENT_ANY,
websocket_event_handler,
NULL);
return ESP_OK;
}
void ai_ws_client_task(void *arg)
{
esp_websocket_client_start(client);
while (1) {
/*
* 这里可以加入心跳、token刷新、断线重连、状态上报。
*/
if (ws_connected) {
const char *ping = "{\"type\":\"ping\"}";
esp_websocket_client_send_text(client,
ping,
strlen(ping),
pdMS_TO_TICKS(1000));
}
vTaskDelay(pdMS_TO_TICKS(15000));
}
}
esp_err_t ai_ws_send_audio(const uint8_t *data, size_t len)
{
if (!ws_connected || !client || !data || len == 0) {
return ESP_FAIL;
}
int ret = esp_websocket_client_send_bin(client,
(const char *)data,
len,
pdMS_TO_TICKS(100));
return ret > 0 ? ESP_OK : ESP_FAIL;
}
8.5 按键唤醒与实时打断
#include "esp_log.h"
#include "driver/gpio.h"
#include "audio_capture.h"
#include "audio_player.h"
#include "ai_ws_client.h"
#include "ui_display.h"
#include "board_config.h"
static const char *TAG = "BUTTON";
static void IRAM_ATTR key_isr_handler(void *arg)
{
uint32_t gpio_num = (uint32_t)arg;
if (gpio_num == PIN_KEY_WAKE) {
/*
* 真实项目中不要在 ISR 里直接处理复杂逻辑,
* 建议投递到队列,这里为了说明简化。
*/
}
}
void button_service_init(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << PIN_KEY_WAKE) |
(1ULL << PIN_KEY_VOL_UP) |
(1ULL << PIN_KEY_VOL_DOWN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_isr_handler_add(PIN_KEY_WAKE, key_isr_handler, (void *)PIN_KEY_WAKE);
}
/*
* 建议由 button_task 判断短按、长按、三击:
* 短按:开始对话 / 打断 TTS
* 长按:进入配网
* 三击:恢复出厂或重新配网
*/
void button_on_wake_pressed(void)
{
ESP_LOGI(TAG, "唤醒键触发");
/*
* 如果 AI 正在说话,用户按键表示打断。
*/
audio_player_stop();
ai_ws_send_control("{\"type\":\"interrupt\"}");
ui_display_show_text("请说话...");
audio_capture_start();
}
开发宝典中 S3 双目板的 QA 也提到,烧录后进入配网状态,可通过三次点按按键进入配网流程;这类按键逻辑可以复用到 AI 音箱方案中。
8.6 MCP 工具调用示例
云端大模型返回:
{
"type": "mcp",
"tool": "speaker.set_volume",
"arguments": {
"volume": 70
}
}
设备端解析:
#include <string.h>
#include "esp_log.h"
#include "cJSON.h"
#include "audio_player.h"
#include "ui_display.h"
#include "mcp_service.h"
static const char *TAG = "MCP";
void mcp_service_init(void)
{
ESP_LOGI(TAG, "MCP 服务初始化完成");
}
void mcp_service_handle(cJSON *root)
{
cJSON *tool = cJSON_GetObjectItem(root, "tool");
cJSON *args = cJSON_GetObjectItem(root, "arguments");
if (!cJSON_IsString(tool) || !cJSON_IsObject(args)) {
ESP_LOGW(TAG, "MCP 参数错误");
return;
}
if (strcmp(tool->valuestring, "speaker.set_volume") == 0) {
cJSON *volume = cJSON_GetObjectItem(args, "volume");
if (cJSON_IsNumber(volume)) {
int vol = volume->valueint;
if (vol < 0) vol = 0;
if (vol > 100) vol = 100;
audio_player_set_volume(vol);
char text[64];
snprintf(text, sizeof(text), "音量已设置为 %d%%", vol);
ui_display_show_text(text);
ESP_LOGI(TAG, "设置音量: %d", vol);
}
} else if (strcmp(tool->valuestring, "speaker.play_story") == 0) {
cJSON *name = cJSON_GetObjectItem(args, "name");
if (cJSON_IsString(name)) {
ui_display_show_text("正在播放故事");
ESP_LOGI(TAG, "播放故事: %s", name->valuestring);
/*
* 这里可以请求云端生成故事 TTS,
* 或播放本地 LittleFS / SD 卡中的音频资源。
*/
}
} else if (strcmp(tool->valuestring, "speaker.set_light") == 0) {
cJSON *mode = cJSON_GetObjectItem(args, "mode");
if (cJSON_IsString(mode)) {
ESP_LOGI(TAG, "切换灯效: %s", mode->valuestring);
/*
* 例如:
* mode = sleep / rainbow / breathing / off
*/
}
} else {
ESP_LOGW(TAG, "未知 MCP 工具: %s", tool->valuestring);
}
}
开发宝典中 AT+MCP 部分提到,四博 AI-01 AT+MCP 固件可以通过 UART AT 指令把"人话"映射为 MCU 能执行的控制帧,并且可用于开灯、调温、拉窗帘等语义控制场景;同样思想可以迁移到 S3 AI 音箱,用 JSON/WebSocket 或 UART 方式实现工具调用。
8.7 OTA 版本检测示例
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_ota_ops.h"
#include "esp_https_ota.h"
#include "ota_service.h"
static const char *TAG = "OTA";
#define OTA_FIRMWARE_URL "https://your-domain.com/firmware/ai_speaker_s3.bin"
void ota_service_init(void)
{
ESP_LOGI(TAG, "OTA 服务初始化");
}
void ota_service_task(void *arg)
{
while (1) {
/*
* 量产版本建议:
* 1. 先请求 version.json
* 2. 对比 version / project_name / chip / md5
* 3. 确认需要升级后再执行 OTA
*/
vTaskDelay(pdMS_TO_TICKS(3600 * 1000));
}
}
esp_err_t ota_start_upgrade(void)
{
esp_http_client_config_t http_cfg = {
.url = OTA_FIRMWARE_URL,
.timeout_ms = 15000,
};
esp_https_ota_config_t ota_cfg = {
.http_config = &http_cfg,
};
ESP_LOGI(TAG, "开始 OTA: %s", OTA_FIRMWARE_URL);
esp_err_t ret = esp_https_ota(&ota_cfg);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA 成功,准备重启");
esp_restart();
} else {
ESP_LOGE(TAG, "OTA 失败: %s", esp_err_to_name(ret));
}
return ret;
}
9. 云端接口建议
9.1 设备上线
{
"type": "hello",
"device_id": "DOIT-S3-000001",
"model": "doit_ai_speaker_s3",
"firmware": "1.0.0",
"audio": {
"format": "pcm_s16le",
"sample_rate": 16000,
"channels": 1
},
"features": {
"interrupt": true,
"mcp": true,
"knowledge_base": true,
"voice_clone": true
}
}
9.2 音频上传
WebSocket Binary Frame:
PCM 16kHz / 16bit / mono / 20ms frame
9.3 云端下发 TTS
WebSocket Binary Frame:
PCM / OPUS / MP3
9.4 云端下发字幕
{
"type": "llm",
"text": "这个问题可以这样理解..."
}
9.5 云端下发工具调用
{
"type": "mcp",
"tool": "speaker.set_light",
"arguments": {
"mode": "sleep"
}
}
10. AI 智能音箱应用场景
10.1 儿童学习音箱
-
英语单词跟读
-
古诗词背诵
-
数学口算训练
-
作文素材启发
-
百科问答
-
睡前故事
10.2 桌面 AI 助手
-
日程提醒
-
天气播报
-
语音备忘
-
智能问答
-
会议摘要播放
-
设备控制入口
10.3 AI 陪伴音箱
-
角色扮演
-
情绪陪伴
-
声音克隆
-
专属人格设定
-
长期记忆
-
对话背景音乐
10.4 智能家居语音中控
-
灯光控制
-
空调控制
-
插座控制
-
窗帘控制
-
场景模式控制
-
通过 MCP / UART / MQTT 对接外部 MCU 或网关
11. 量产建议
11.1 硬件建议
| 模块 | 建议 |
|---|---|
| 麦克风 | 单麦可做入门款,双麦可做降噪和远场增强 |
| Codec | 推荐 ES8311,便于 I2S 录放一体化 |
| 功放 | 3W Class-D,预留 EN 脚做静音控制 |
| 屏幕 | 1.3 寸 240×240 做状态屏,1.54 寸可做高级版本 |
| 存储 | 16MB Flash + 8MB PSRAM 更适合 OTA 和资源缓存 |
| 电源 | 桌面款 Type-C 5V,便携款增加锂电池和充电管理 |
| 结构 | 喇叭腔体与麦克风隔离,减少回声和啸叫 |
11.2 软件建议
-
量产必须做设备唯一 ID
用 MAC / SN / 证书绑定云端账号。
-
音频链路必须支持打断
用户说话时应立即停止 TTS 播放,切换到录音状态。
-
弱网必须做重连和本地提示
Wi-Fi 断开、AI 服务断开时,LCD 和语音都要提示。
-
OTA 必须做版本回滚
使用 A/B 分区,升级失败自动回退。
-
隐私设计必须明确
本地唤醒后才上传音频;可以增加物理静音键。
-
MCP 工具必须做白名单
只允许模型调用已注册工具,避免生成非法控制指令。
12. 对外宣传版描述
四博 AI 智能音箱基于 ESP32-S3 高性能 AIoT 核心平台打造,集语音交互、智能问答、学习陪伴、知识库接入、声音克隆、屏幕显示、MCP 工具调用和 OTA 在线升级于一体。设备支持 Wi-Fi/BLE 联网,可通过"四博小助手"完成配网、智能体配置、知识库管理、音色设置和固件更新。凭借 ESP32-S3 双核处理能力、丰富外设接口和四博成熟 AI 硬件开发生态,该方案可快速应用于儿童学习机、AI 故事机、桌面音箱、智能玩具、智能家居语音中控和品牌定制 AI 终端等场景。