最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ESP32-C3 上 Wi-Fi 和 BLE Mesh 同时工作的方案,以及示例代码和相关注意事项。由于 BLE Mesh 和 Wi-Fi 都要占用 2.4 GHz 射频

运维笔记admin27浏览0评论

下面的示例展示了一个 ESP32-C3Wi-FiBLE Mesh 同时工作的方案,以及示例代码和相关注意事项。由于 BLE Mesh 和 Wi-Fi 都要占用 2.4 GHz 射频,需要进行**协议栈共存(coexistence)**的配置。本文将尽可能给出详细的思路和代码示例,以便读者在 ESP-IDF 环境下进行编译和二次开发。

说明

  • ESP32-C3 不支持经典蓝牙(BR/EDR),只能使用 BLE(蓝牙低功耗),因此我们使用 ESP BLE Mesh(基于 BLE)来实现蓝牙 Mesh 功能。
  • 下面的示例基于 ESP-IDF v4.4 及以上 并启用了 CONFIG_IDF_TARGET_ESP32C3
  • 为了演示,我们会写一个简单的 BLE Mesh 节点Wi-Fi STA 模式同时运行的示例,供大家参考。
  • 真正商用前,请根据项目需要对 BLE Mesh 功能(如模型、配置、消息处理等)进行完善。

一、总体设计思路

  1. BLE Mesh 初始化

    • ESP BLE Mesh 和普通 BLE 应用略有不同,需要初始化 BLE Mesh 专用的 Host 和 Controller,并注册相应的回调函数、配置各个 Model(Generic OnOff、Vendor Model 等)。
    • Mesh 节点需要进行网络层的配置(Provisioning),要么作为普通 Node,要么作为 Provisioner。这里演示 Node 的简化流程。
  2. Wi-Fi 初始化

    • 使用 STA(station)模式连入路由器,也可以视需求变成 AP 或双模(AP+STA)。
    • 需要配置好 Wi-Fi 共存:ESP-IDF 默认在硬件层面支持 BT/Wi-Fi 共存,SDK 会在运行时调度射频。
    • 建议只在 BLE 模式下使用CONFIG_BTDM_CTRL_MODE_BLE_ONLY),因为 ESP32-C3 只有 BLE 控制器,没有经典蓝牙的硬件支持。
  3. 任务和事件处理

    • BLE Mesh 使用事件回调机制(esp_ble_mesh_register_prov_callbackesp_ble_mesh_register_config_server_callback 等)处理各类事件,如完成配网、接收到消息等。
    • Wi-Fi 使用 esp_event_loop_create_default() + esp_event_handler_register() 来监听 Wi-Fi 连接、断开事件。
    • 两套事件都在各自的回调中执行,互不干扰,但需要注意内存实时性,以免过度占用资源。
  4. 通信示例

    • BLE Mesh 端可以通过 Mesh 网络进行节点间的消息收发;
    • Wi-Fi 端可以通过 TCP/HTTP/MQTT 等方式与服务器通信;
    • 在应用层,可根据需要编写一个“网关”或“桥接”逻辑,把 BLE Mesh 消息透传到服务器,或从服务器下发命令到 Mesh 节点。

二、sdkconfig 重点配置

menuconfig 中需要关注以下几点(仅举例):

  1. Target: 选择 ESP32C3 (如果使用的是 ESP32-C3)。
  2. Component config -> Bluetooth:
    • 勾选 [*] Bluetooth
    • 勾选 [*] Bluetooth LE SPP, [*] BLE Mesh support(或类似选项)
    • Bluetooth controller 中选择 BLE only
  3. Component config -> ESP BLE Mesh:
    • 根据需求启用 BLE Mesh NodeBLE Mesh Provisioner 功能;
    • 如果需要打开日志,可以设置调试等级。
  4. Component config -> Wi-Fi:
    • 确保 Wi-Fi 驱动已启用
    • 根据需求配置 STA 或 AP 的最大连接数、事件回调等
  5. PSRAM(如无则忽略): 如果是 ESP32-C3 没有外接 PSRAM,那么无需启用。

完成后保存并退出 menuconfig


三、示例代码详解

下面是一份简化的示例,实现了:

  1. 初始化 Wi-Fi (STA 模式) 并连接到路由器
  2. 初始化 BLE Mesh 节点 (Generic OnOff Server)
  3. 处理 BLE Mesh 事件回调、Wi-Fi 事件回调
  4. 两套协议同时工作

注意:示例中省略了大量“正式 BLE Mesh Node”所需的模型定义和事件处理,仅作演示。如果你要实现真正可用的 BLE Mesh 功能,请参考 Espressif 官方示例:ESP BLE Mesh Examples。


1. 头文件引用与全局变量

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"

// Wi-Fi 相关
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "lwip/sockets.h"

// BLE Mesh 相关
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_networking_api.h"
#include "esp_ble_mesh_provisioning_api.h"

static const char *TAG = "BLE_MESH_WIFI_C3";

#define EXAMPLE_WIFI_SSID      "YOUR_SSID"
#define EXAMPLE_WIFI_PASS      "YOUR_PASS"

// provision 数据,可根据需求修改
static esp_ble_mesh_prov_t prov = {
    .uuid = { 0x32, 0xC3, 0xAB, 0xCD, 0x00, 0x11, 0x22, 0x33, // 随便写
              0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B },
    // 其他回调在 init 里注册
};

// 简单示例: 定义一个 Generic OnOff Server 模型
ESP_BLE_MESH_MODEL_PUB_DEFINE(gen_onoff_pub, 2 + 3, ROLE_NODE);

static esp_ble_mesh_model_t sig_models[] = {
    ESP_BLE_MESH_MODEL_CFG_SRV(), // 配置服务器模型
    ESP_BLE_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_pub, NULL) // Generic OnOff
};

// 元素
static esp_ble_mesh_elem_t elements[] = {
    ESP_BLE_MESH_ELEMENT(0, sig_models, ESP_BLE_MESH_MODEL_NONE),
};

// 完整的组合
static esp_ble_mesh_comp_t composition = {
    .cid = 0x02E5,     // 这里是 Espressif 的公司 ID
    .elements = elements,
    .element_count = ARRAY_SIZE(elements),
};

上面定义了一个最基本的 BLE Mesh 节点:

  • 包含了配置服务器模型 CFG_SRV
  • 一个通用 OnOff 服务器模型;
  • 有 1 个 element;
  • prov 中保存了设备的 UUID,实际项目中要根据自己的硬件信息来设置。

2. BLE Mesh 事件回调

下面是一个示例性的事件回调函数,用于处理 BLE Mesh Provisioning 事件和配置服务器事件(如成功配网、收到配置命令等)。根据需要还可增加更多模型的回调处理(如通用 OnOff Server 回调等)。

static void ble_mesh_prov_callback(esp_ble_mesh_prov_cb_event_t event,
                                   esp_ble_mesh_prov_cb_param_t *param)
{
    switch (event) {
    case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT:
        ESP_LOGI(TAG, "BLE Mesh Provisioning注册完成");
        break;
    case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT:
        ESP_LOGI(TAG, "BLE Mesh Node 可以被绑定进行配网");
        break;
    case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT:
        ESP_LOGI(TAG, "配网链接已打开");
        break;
    case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT:
        ESP_LOGI(TAG, "配网链接已关闭");
        break;
    case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT:
        ESP_LOGI(TAG, "BLE Mesh 配网完成, 得到 NetKeyIdx=%d, Addr=0x%04X",
            param->node_prov_complete.net_idx,
            param->node_prov_complete.addr);
        break;
    case ESP_BLE_MESH_NODE_PROV_RESET_EVT:
        ESP_LOGI(TAG, "设备被重置,需要重新配网");
        break;
    default:
        ESP_LOGW(TAG, "Unhandled provisioning event: %d", event);
        break;
    }
}

// 配置服务器回调(这里只演示简单打印,实际需处理AppKey绑定等)
static void ble_mesh_config_server_cb(esp_ble_mesh_cfg_server_cb_event_t event,
                                      esp_ble_mesh_cfg_server_cb_param_t *param)
{
    switch (event) {
    case ESP_BLE_MESH_CFG_SERVER_STATE_CHANGE_EVT:
        ESP_LOGI(TAG, "配置服务器 State 改变");
        break;
    default:
        ESP_LOGW(TAG, "Unhandled config server event: %d", event);
        break;
    }
}

若需要处理 Generic OnOff Server 的事件,还需注册 esp_ble_mesh_register_generic_server_callback() 并实现对应的回调。


3. 初始化 BLE Mesh

在主函数或单独封装的初始化函数中,执行 BLE Mesh 的初始化:

static void ble_mesh_init(void)
{
    esp_err_t err;

    // 1. 初始化 BLE Mesh 核心栈
    esp_ble_mesh_init_param_t init_param = {
        .prov = &prov,
        .comp = &composition,
    };

    // 注册回调
    err = esp_ble_mesh_register_prov_callback(ble_mesh_prov_callback);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to register prov callback");
        return;
    }
    err = esp_ble_mesh_register_config_server_callback(ble_mesh_config_server_cb);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to register config server callback");
        return;
    }
    // 如果有 Generic Server,则还要 register_generic_server_callback(...)
    // ...

    // 2. 初始化 BLE Mesh
    err = esp_ble_mesh_init(&init_param);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "esp_ble_mesh_init failed (err %d)", err);
        return;
    }

    // 3. Enable Provisioning (使能节点可被配网)
    err = esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to enable mesh node prov");
        return;
    }

    // 4. 打开蓝牙低功耗子系统
    // ESP-IDF 中,esp_ble_mesh_init() 已经做了大部分 BT 控制器/主机初始化
    // 不过可以根据需要选择性地控制广播功率等

    ESP_LOGI(TAG, "BLE Mesh Node init done, waiting for provisioning...");
}

4. 初始化 Wi-Fi 并连接

4.1 Wi-Fi 事件回调

static void event_handler(void* arg, esp_event_base_t event_base,
                          int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        ESP_LOGI(TAG, "Wi-Fi STA start, connecting...");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        ESP_LOGW(TAG, "Wi-Fi STA disconnected, retry...");
        esp_wifi_connect(); // 断线重连
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "Got IP: %s",
                 ip4addr_ntoa(&event->ip_info.ip));
    }
}

4.2 Wi-Fi STA 初始化

static void wifi_init_sta(void)
{
    // 初始化NVS,这样才能读写Wi-Fi配置
    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());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // 创建默认事件循环
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 初始化 Wi-Fi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件回调
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
                                              &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
                                              &event_handler, NULL));

    // 设置 Wi-Fi 模式
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

    // 配置 STA 的 SSID/PASS
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_WIFI_SSID,
            .password = EXAMPLE_WIFI_PASS,
            .scan_method = WIFI_FAST_SCAN
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    // 启动 Wi-Fi
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");
}

注意:

  • 如果需要在应用中动态配置 Wi-Fi 参数,可以通过 SmartConfig 或命令行等方式获取 SSID、密码,然后再调用 esp_wifi_set_config()
  • 如果使用 menuconfig 预先配置,也可以在 Kconfig 中让用户填写 Wi-Fi 信息。

5. 入口函数 app_main()

一个最简单的“先启动 Wi-Fi,再启动 BLE Mesh”的主函数如下所示:

void app_main(void)
{
    ESP_LOGI(TAG, "==== ESP32-C3 BLE Mesh + Wi-Fi demo start ====");

    // 1. 初始化并连接 Wi-Fi
    wifi_init_sta();

    // 2. 初始化 BLE Mesh 核心
    ble_mesh_init();

    // 后面可创建任务,或者直接在这里做些业务逻辑
    // 例如循环监测一些传感器数据,通过 BLE Mesh / Wi-Fi 上报等
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
        ESP_LOGI(TAG, "Main loop running, Wi-Fi + BLE Mesh coexisting...");
    }
}
  • 这样一来,设备上电后会先连接 Wi-Fi,然后开启 BLE Mesh 处于可配网状态;
  • 如果手机使用 BLE Mesh App(如 nRF MeshEsp BLE Mesh APP)对设备进行 Mesh 配网,就会触发前述 ble_mesh_prov_callback() 中的一系列事件。
  • 同时,通过 Wi-Fi 也可以对设备进行 OTA、MQTT 通信、HTTP 请求等操作。

四、注意事项和可能的扩展

  1. 内存使用

    • ESP32-C3 的内存较紧张,同时跑 Wi-Fi STA 和 BLE Mesh 时,一定要注意堆剩余情况;
    • 如果 BLE Mesh 的网络规模较大,或 Wi-Fi 需要加载 TLS/HTTPD 等,可能要对堆进行优化,也可以在 menuconfig 中把日志等级调低、精简组件。
  2. BLE Mesh 的模型扩展

    • 如果你要实现灯控(通用 OnOff)、传感器模型、Vendor 模型等,需要在 elements[] 里添加更多模型,并在回调中处理对应的事件;
    • Espressif 官方有比较完整的例程,比如 examples/bluetooth/esp_ble_mesh/ble_mesh_node/onoff_server/,可参考其处理流程。
  3. 并发通信

    • 当 BLE Mesh 处于激烈的组网或数据广播时,Wi-Fi 的吞吐可能会降低,反之亦然(2.4GHz 共存);
    • ESP-IDF 的硬件共存机制(Coexistence)会在后台调度射频资源,通常无需开发者手动干预,但要预留足够 CPU/内存,提高 RTOS 优先级等。
    • 如果对实时性要求特别高,需要在 menuconfig -> Component config -> Bluetooth -> Bluetooth controller -> Coexistence configuration 里做相关设置。
  4. 安全与加密

    • BLE Mesh 默认使用加密的网钥、应用钥等,但需要正确的 Provisioning 流程和数据保护。
    • Wi-Fi 端也需注意启用 WPA2/WPA3,或者在上层数据加上 SSL/TLS 等,防止窃听。
  5. 功耗

    • 同时开启 Wi-Fi + BLE Mesh 会加大功耗,对低功耗设备来说不一定合适;
    • 可以在非必须时段关闭 Wi-Fi 或降低 BLE Mesh 广播频率、进入低功耗模式。
  6. 调试与日志

    • menuconfig 可以打开 ESP_BLE_MESH_LOG_LEVEL_DEBUG 或更高等级,以便查看更详细的 BLE Mesh 调试信息;
    • 也要注意大量日志会占用 CPU 和内存,最终产品中可设置到 INFO 或 WARN 等更低等级。

五、总结

  1. 硬件层面:ESP32-C3 内部有射频共存调度,可以支持 BLE 和 Wi-Fi 在 2.4GHz 上同时工作;
  2. 软件层面:在 ESP-IDF 中分别初始化 Wi-Fi 和 ESP BLE Mesh 协议栈,并注册各自的事件回调;
  3. 资源管理:需要关注内存、CPU、功耗等问题,合理设置日志等级、模型数量,以及尽量精简不必要的功能;
  4. 扩展:在此基础上可以开发更高级的功能,例如在 Wi-Fi 侧做网关,把 BLE Mesh 网络的数据上报到云端,或从云端下发命令到 Mesh 节点。

以上就是一个在 ESP32-C3 上同时启用 BLE Mesh(节点)和 Wi-Fi(STA) 的整体方案与代码示例,涵盖从 menuconfig 配置,到事件回调处理,再到并发注意事项。希望能帮助大家快速上手并根据自身需求进行定制。祝开发顺利!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论