这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » [ESP32-S31Functioncoreboard-1测评]重构环境并实现E

共2条 1/1 1 跳转至页

[ESP32-S31Functioncoreboard-1测评]重构环境并实现ESP-CLAW与蓝牙共存

助工
2026-10-12 15:18:08     打赏

        接上一篇[ESP32-S31Functioncoreboard-1测评]开箱-电子产品世界论坛 ,开始时使用win10虚拟机,编译操作速度太慢了,改用Ubuntu之后速度翻倍。

        重新简单说明一下Linux环境下安装ESP-IDF:(不再使用EIM工具下载,老是报错空间不足)

空间不足.png

  1. Ctrl+Alt+T,打开终端

  2. mkdir  Espressif,在家目录下新建个目录,用于存放项目工程与工具

  3. cd Espressif,进入目录

  4. git clone --depth 1 --branch v6.1 https://git.espressif.com.cn/espressif/esp-idf.git esp-idf,在家目录下使用乐鑫国内镜像下载IDF

  5. cd ~/Espressif/esp-idf,下载完成进入idf目录

  6. export IDF_GITHUB_ASSETS="dl.espressif.cn/github_assets",使用国内镜像

  7. ./install.sh esp32s31,指定目标芯片运行官方安装脚本

  8. source ~/Espressif/esp-idf/export.sh,每次进入 ESP-IDF 环境即每次打开新终端,需要先设置环境变量

    环境加载成功.png

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

    idf安装成功.png


        本地编译烧录ESP-CLAW

  1. cd ~/Espressif,进入这个目录

  2. pip install esp-bmgr-assist,安装 ESP Board Manager 包

    管理包安装成功.png

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

    下载esp-claw成功.png

  4. cd esp-claw/application/edge_agent

  5. idf.py bmgr -c ./boards -l,查看支持的开发板列

    板子列表.png

  6. cp -r ./managed_components/espressif__esp_boards/esp32_s31_function_coreboard_1 ./boards/esp32_s31_function_coreboard_2

  7. 对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

  8. 对对board_info.yaml修改

    board: esp32_s31_function_coreboard_2

  9. idf.py bmgr -c ./boards -b esp32_s31_function_coreboard_2,选择开发板

  10. idf.py reconfigure,重新配置

  11. idf.py build,编译

  12. idf.py -p /dev/ttyUSB0 -b 921600 flash,烧录

        之后使用电脑连接该ESP32-S31的热点,手动打开浏览器输入192.168.4.1打开配置页面,配置wifi、LLM、IM与搜索API。

2026-09-10T03_46_02.145Z-385711.gif


        编译烧录bt_audio,实现蓝牙播放音乐

       需要外接一个4欧3瓦的喇叭

外接喇叭.png

  1. idf.py create-project-from-example "espressif/esp_bt_audio=0.8.1:bt_audio"

  2. cd bt_audio

  3. idf.py --preview set-target esp32s31

  4. mkdir board

  5. cp -r ./managed_components/espressif__esp_boards/esp32_s31_function_coreboard_1 ./boards/esp32_s31_function_coreboard1

  6. 修改 board_info.yaml, -> board: esp32_s31_function_coreboard1

  7. 修改 sdkconfig.defaults.board,增加下面内容

    1. # This configuration is temporarily disabled due to an issue in IDF 6.1.

    2. CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=n

    3. CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=n

  8. idf.py bmgr -c ./boards -b esp32_s31_function_coreboard1

  9. idf.py reconfigure

  10. idf.py menuconfig,增加经典蓝牙功能,不需要低功耗蓝牙减少内存使用

    ①先是打开蓝牙功能

1打开蓝牙.png

        ②仅开启经典蓝牙功能

2仅需要经典蓝牙.png

        ③音频播放配置

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 文件找不到

缺失设置1.png

        也需要把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

降低频率.png

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

        之后编译烧录就可以一边连接蓝牙播放音乐,一边与Agent聊天了。





关键词: ESP32-S31     ESP-CLAW     蓝牙    

院士
2026-10-12 17:54:34     打赏
2楼

谢谢分享。

这喇叭带不带得动?


共2条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]