接上一篇[ESP32-S31Functioncoreboard-1测评]开箱-电子产品世界论坛 ,开始时使用win10虚拟机,编译操作速度太慢了,改用Ubuntu之后速度翻倍。
重新简单说明一下Linux环境下安装ESP-IDF:(不再使用EIM工具下载,老是报错空间不足)

Ctrl+Alt+T,打开终端
mkdir Espressif,在家目录下新建个目录,用于存放项目工程与工具
cd Espressif,进入目录
git clone --depth 1 --branch v6.1 https://git.espressif.com.cn/espressif/esp-idf.git esp-idf,在家目录下使用乐鑫国内镜像下载IDF
cd ~/Espressif/esp-idf,下载完成进入idf目录
export IDF_GITHUB_ASSETS="dl.espressif.cn/github_assets",使用国内镜像
./install.sh esp32s31,指定目标芯片运行官方安装脚本
source ~/Espressif/esp-idf/export.sh,每次进入 ESP-IDF 环境即每次打开新终端,需要先设置环境变量

df.py --version,查看版本,仅能使用v6.1.0来支持ESP32-S31

本地编译烧录ESP-CLAW
cd ~/Espressif,进入这个目录
pip install esp-bmgr-assist,安装 ESP Board Manager 包

git clone https://githubproxy.cc/https://github.com/espressif/esp-claw.git,直接使用代理服务下载esp-claw

cd esp-claw/application/edge_agent
idf.py bmgr -c ./boards -l,查看支持的开发板列

cp -r ./managed_components/espressif__esp_boards/esp32_s31_function_coreboard_1 ./boards/esp32_s31_function_coreboard_2
对esp32_s31_function_coreboard_2进行修改,在sdkconfig.defaults.board中增加,不然烧录之后一直重启!!!
# This configuration is temporarily disabled due to an issue in IDF 6.1.
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=n
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=n
对对board_info.yaml修改
board: esp32_s31_function_coreboard_2
idf.py bmgr -c ./boards -b esp32_s31_function_coreboard_2,选择开发板
idf.py reconfigure,重新配置
idf.py build,编译
idf.py -p /dev/ttyUSB0 -b 921600 flash,烧录
之后使用电脑连接该ESP32-S31的热点,手动打开浏览器输入192.168.4.1打开配置页面,配置wifi、LLM、IM与搜索API。

编译烧录bt_audio,实现蓝牙播放音乐
需要外接一个4欧3瓦的喇叭

idf.py create-project-from-example "espressif/esp_bt_audio=0.8.1:bt_audio"
cd bt_audio
idf.py --preview set-target esp32s31
mkdir board
cp -r ./managed_components/espressif__esp_boards/esp32_s31_function_coreboard_1 ./boards/esp32_s31_function_coreboard1
修改 board_info.yaml, -> board: esp32_s31_function_coreboard1
修改 sdkconfig.defaults.board,增加下面内容
# This configuration is temporarily disabled due to an issue in IDF 6.1.
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=n
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=n
idf.py bmgr -c ./boards -b esp32_s31_function_coreboard1
idf.py reconfigure
idf.py menuconfig,增加经典蓝牙功能,不需要低功耗蓝牙减少内存使用
①先是打开蓝牙功能

②仅开启经典蓝牙功能

③音频播放配置


11.idf.py build
12.idf.py -p /dev/ttyUSB0 -b 921600 flash
之后用手机打开蓝牙,发现“esp-class”连接就能播放音乐
把蓝牙功能加入到edge_agent中
目前仅是把蓝牙音频功能在edge_agent中在另一任务中实现。把bt_audio/main下的cmd_reg.c、cmd_reg.h、codec_defs.h、pool_reg.c、pool_reg.h、stream_proc.c与stream_proc.h复制到edge_agent/main中,并把bt_audio/main/main.c改名为example_bt_audio.c并复制到edge_agent/main中,修改app_main为bt_audio_task,去除部分重复的代码
/**
* @brief 任务入口
*
* 初始化顺序:
* 1. GMF Pool + Pipeline
* 2. 蓝牙控制器 + Bluedroid Host + esp_bt_audio
* 3. CLI 控制台
*/
void bt_audio_task(void)
{
/* 打开 DAC:48kHz / 16bit / 双声道,初始音量 50 */
esp_codec_dev_sample_info_t fs = {
.sample_rate = CODEC_DAC_SAMPLE_RATE,
.bits_per_sample = CODEC_DAC_BITS_PER_SAMPLE,
.channel = CODEC_DAC_CHANNELS,
};
dev_audio_codec_handles_t *codec_handle = NULL;
ESP_ERROR_CHECK(esp_board_manager_get_device_handle(ESP_BOARD_DEVICE_NAME_AUDIO_DAC, (void **)&codec_handle));
ESP_ERROR_CHECK(esp_codec_dev_open(codec_handle->codec_dev, &fs));
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(codec_handle->codec_dev, 50));
/* 打开 ADC:48kHz / 16bit / 双声道,输入增益 40dB */
fs = (esp_codec_dev_sample_info_t) {
.sample_rate = CODEC_ADC_SAMPLE_RATE,
.bits_per_sample = CODEC_ADC_BITS_PER_SAMPLE,
.channel = CODEC_ADC_CHANNELS,
};
ESP_ERROR_CHECK(esp_board_manager_get_device_handle(ESP_BOARD_DEVICE_NAME_AUDIO_ADC, (void **)&codec_handle));
ESP_ERROR_CHECK(esp_codec_dev_open(codec_handle->codec_dev, &fs));
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(codec_handle->codec_dev, 40.0f));
/* 初始化 GMF Pool 并注册所有 Element / IO */
ESP_ERROR_CHECK(esp_gmf_pool_init(&pool));
ESP_ERROR_CHECK(pool_reg(pool));
/* 创建三条 Pipeline(bt2codec / codec2bt / local2bt) */
stream_proc_init(pool);
/* 初始化并启用蓝牙控制器,模式由 menuconfig 决定 */
#ifdef CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY
uint32_t btmode = ESP_BT_MODE_CLASSIC_BT;
#elif CONFIG_BTDM_CTRL_MODE_BLE_ONLY
uint32_t btmode = ESP_BT_MODE_BLE;
#elif CONFIG_BTDM_CTRL_MODE_BTDM
uint32_t btmode = ESP_BT_MODE_BTDM;
#else /* CONFIG_BTDM_CTRL_MODE_BTDM */
uint32_t btmode = ESP_BT_MODE_BLE;
#endif /* CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY */
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
ESP_ERROR_CHECK(esp_bt_controller_enable(btmode));
/* 初始化 esp_bt_audio:Host 配置、事件回调、Classic 角色 */
esp_bt_audio_host_bluedroid_cfg_t host_cfg = ESP_BT_AUDIO_HOST_BLUEDROID_CFG_DEFAULT();
esp_bt_audio_config_t bt_config = {
.host_config = &host_cfg,
.event_cb = bt_audio_event_cb,
.event_user_ctx = NULL,
#ifdef CONFIG_BT_CLASSIC_ENABLED
.classic.roles = get_classic_roles(),
#ifdef CONFIG_GMF_EXAMPLE_A2DP_SOURCE
.classic.a2dp_src_send_task_core_id = A2DP_SRC_SEND_TASK_CORE_ID,
.classic.a2dp_src_send_task_prio = A2DP_SRC_SEND_TASK_PRIO,
.classic.a2dp_src_send_task_stack_size = A2DP_SRC_SEND_TASK_STACK_SIZE,
#endif /* CONFIG_GMF_EXAMPLE_A2DP_SOURCE */
#endif /* CONFIG_BT_CLASSIC_ENABLED */
};
ESP_ERROR_CHECK(esp_bt_audio_init(&bt_config));
/* 设置扫描模式:Source 只可被发现,Sink 可被发现且可被连接 */
#if defined(CONFIG_GMF_EXAMPLE_A2DP_SOURCE)
ESP_ERROR_CHECK(esp_bt_audio_classic_set_scan_mode(true, false));
#elif defined(CONFIG_GMF_EXAMPLE_A2DP_SINK)
ESP_ERROR_CHECK(esp_bt_audio_classic_set_scan_mode(true, true));
/* Sink 模式下注册 AVRCP 播放通知,才能收到曲目/状态变化 */
ESP_ERROR_CHECK(esp_bt_audio_playback_reg_notifications(ESP_BT_AUDIO_PLAYBACK_EVENT_PLAY_STATUS_CHANGE |
ESP_BT_AUDIO_PLAYBACK_EVENT_TRACK_CHANGE |
ESP_BT_AUDIO_PLAYBACK_EVENT_TRACK_REACHED_END |
ESP_BT_AUDIO_PLAYBACK_EVENT_TRACK_REACHED_START |
ESP_BT_AUDIO_PLAYBACK_EVENT_PLAY_POS_CHANGED |
ESP_BT_AUDIO_PLAYBACK_EVENT_NOW_PLAYING_CHANGE |
ESP_BT_AUDIO_PLAYBACK_EVENT_AVAILABLE_PLAYERS_CHANGE |
ESP_BT_AUDIO_PLAYBACK_EVENT_ADDRESSED_PLAYER_CHANGE));
#endif /* defined(CONFIG_GMF_EXAMPLE_A2DP_SOURCE) || defined(CONFIG_GMF_EXAMPLE_A2DP_SINK) */
/* 启动 CLI 控制台(阻塞在此,等待用户命令) */
cli_init();
vTaskSuspend(NULL);
}CLI 控制台涉及串口方面的设置有冲突,需要优化cli_init
void cli_init()
{
cli_register_sys();
cli_register_bt();
}优化 CMakeLists.txt
idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." REQUIRES "bt" "nvs_flash" "console" "esp_app_format" "cmd_wifi" )
优化idf_component.yml
## IDF Component Manager Manifest File
dependencies:
espressif/esp_board_manager:
version: "^0.6"
public: true
app_claw:
path: ../../../components/common/app_claw
app_config:
path: ../components/app_config
captive_dns:
path: ../../../components/common/captive_dns
espressif/ramfs:
version: "^0.1"
claw_utils:
path: ../../../components/claw_modules/claw_utils
system_ui:
rules:
- if: $CONFIG{ESP_BOARD_DEV_DISPLAY_LCD_SUPPORT} == True
path: ../../../components/common/system_ui
http_reuse:
path: ../../../components/common/http_reuse
http_server:
path: ../components/http_server
settings:
path: ../../../components/common/settings
wifi_manager:
path: ../../../components/common/wifi_manager
esp_video:
path: ../../../components/common/esp_video
matches:
- if: $CONFIG{ESP_BOARD_DEV_CAMERA_SUPPORT} == True
espressif/esp_bt_audio:
version: ^0.8
espressif/gmf_ai_audio:
version: ^0.8
espressif/gmf_audio:
version: ^0.8
espressif/gmf_core:
version: ^0.8
espressif/gmf_io:
version: ^0.8
espressif/gmf_misc:
version: ^0.8优化Kconfig.projbuild
menu "App Config" menu "Default Wi-Fi Settings" config APP_WIFI_SSID string "Default Wi-Fi SSID" default "" help Default Wi-Fi SSID used to populate app settings. config APP_WIFI_PASSWORD string "Default Wi-Fi password" default "" help Default Wi-Fi password used to populate app settings. config APP_WIFI_AP_SSID_PREFIX string "Provisioning AP SSID prefix" default "esp-claw" help Prefix used to build the unique provisioning AP SSID. The full SSID is "<prefix>-<MAC[3..5]>" (e.g. esp-claw-84AEE5). config APP_WIFI_AP_CHANNEL int "Provisioning AP channel" range 1 13 default 1 help Channel used by the provisioning SoftAP. config APP_WIFI_AP_MAX_CONN int "Provisioning AP max connections" range 1 10 default 4 help Maximum number of concurrent stations on the provisioning AP. config APP_WIFI_MAX_RETRY int "STA max retries before AP fallback" range 1 20 default 5 help After this many consecutive STA connection failures the device falls back to pure AP mode and waits for the user to provision new credentials. endmenu menu "Default Search Settings" config APP_SEARCH_HTTP_ALLOWLIST string "Default HTTP Request allowlist" default "*" help Default value used to populate app setting `search_http_allowlist`. Use `*` to allow all hosts, or a comma-separated list of domains/IPs. e.g. "api.example.com,*.example.org,203.0.113.10" endmenu endmenu menu "BT Audio Basic Example (GMF)" menu "Classic Audio Roles Configuration" depends on BT_CLASSIC_ENABLED default GMF_EXAMPLE_A2DP_SINK choice GMF_EXAMPLE_A2DP_ROLE prompt "A2DP role" depends on BT_A2DP_ENABLE config GMF_EXAMPLE_A2DP_SINK bool "A2DP Sink" help Enable A2DP Sink role config GMF_EXAMPLE_A2DP_SOURCE bool "A2DP Source" help Enable A2DP Source role endchoice config GMF_EXAMPLE_HFP_HF depends on BT_HFP_CLIENT_ENABLE && GMF_EXAMPLE_A2DP_SINK bool "HFP Handsfree" default y help Enable HFP Handsfree role config GMF_EXAMPLE_AVRC_CT depends on BT_AVRCP_ENABLED bool "AVRC Controller" default y help Enable AVRC Controller role config GMF_EXAMPLE_AVRC_TG depends on BT_AVRCP_ENABLED bool "AVRC Target" default y help Enable AVRC Target role config GMF_EXAMPLE_PBAP_PCE depends on BT_PBAC_ENABLED && GMF_EXAMPLE_HFP_HF bool "Phone book client equipment" default n help Enable phone book client equipment role endmenu endmenu
最后修改edge_agent/main/main.c中的app_main,在最后加上xTaskCreate((TaskFunction_t)bt_audio_task, "bt audio task", 8192, NULL, 0, NULL);并且在开头声明一下extern void bt_audio_task(void);即可
文件结构为:
edge_agent └── main ├── app_fs.c ├── app_fs.h ├── CMakeLists.txt ├── cmd_reg.c ├── cmd_reg.h ├── codec_defs.h ├── edge_agent_version.c ├── edge_agent_version.h ├── example_bt_audio.c ├── idf_component.yml ├── Kconfig.projbuild ├── main.c ├── pool_reg.c ├── pool_reg.h ├── stream_proc.c └── stream_proc.h
还需要在menuconfig优化配置,缺失这个功能会报 esp_gmf_io_bt.h 文件找不到

也需要把FreeRTOS tick 频率降低,不然配对蓝牙之后一直连接不上,报错为:
I (29751) BT_AUD_A2D_SINK: A2DP connection state: Connecting, addr[04:db:d9:ae:c7:36]
E (29922) BT_OSI: alarm_set failed to start timer, err 0x103

原因为在 WiFi 双模 + HTTP + LLM Agent + GMF 音频管线并存的高负载场景下,CPU0 上的调度压力很大。Bluedroid 协议栈的定时器(alarm)依赖于 FreeRTOS 的软件定时器机制,当系统 tick 过于密集时,定时器创建的回调可能因调度竞争而失败,表现为 alarm_set failed to start timer, err 0x103,目前这个解决方法是最靠谱,后续再看看是否有其他方法。
之后编译烧录就可以一边连接蓝牙播放音乐,一边与Agent聊天了。
我要赚赏金
