首页 > 其他分享 >在不使用手机 SDK 的情况下,您可以使用 ESP32-C3 实现 BLE Mesh Provisioner 的功能

在不使用手机 SDK 的情况下,您可以使用 ESP32-C3 实现 BLE Mesh Provisioner 的功能

时间:2024-11-26 11:57:48浏览次数:12  
标签:err ESP UART esp mesh BLE Mesh Provisioner C3

您好!

您希望将之前的内容整合在一起,设计一个基于 ESP32-C3 实现 BLE Mesh Provisioner 的详细示例代码,且不依赖手机 SDK,全程由 ESP32-C3 完成配置和控制,并提供通过 串口按键 的用户交互界面。我将为您提供尽可能详细的设计和完整的代码。


项目概述

BLE Mesh 是一种基于蓝牙低功耗(BLE)的网状网络协议,允许设备之间形成自组织、自愈合的网络。Provisioner(配置器) 是 BLE Mesh 网络中的关键角色,负责将未配置的节点添加到网络中,分配地址和密钥。

本项目旨在使用 ESP32-C3 作为 BLE Mesh Provisioner,不依赖手机或其他设备,全程由 ESP32-C3 完成所有配置和控制。用户可以通过 串口按键 与设备交互,实现对网络的管理。


目录

  1. 环境准备
  2. 项目创建
  3. 代码实现
    • 3.1 头文件和宏定义
    • 3.2 全局变量
    • 3.3 用户交互接口
      • 3.3.1 串口交互
      • 3.3.2 按键处理
    • 3.4 回调函数
    • 3.5 初始化函数
    • 3.6 app_main 函数
  4. 编译和烧录
  5. 测试与验证
  6. 注意事项
  7. 总结

1. 环境准备

  • 硬件设备:

    • ESP32-C3 开发板
    • 其他支持 BLE Mesh 的设备(作为节点)
    • 按键和 LED(如果开发板上未集成,可以自行连接)
  • 软件工具:

    • ESP-IDF(建议使用 v4.4 或以上版本,支持 ESP32-C3)
    • ESP-IDF 工具链(包含编译器、调试器等)
    • 串口调试工具(如 PuTTY、minicom 等)
  • 必要的驱动:

    • 安装 USB 串口驱动,以便电脑识别开发板

2. 项目创建

  1. 设置 ESP-IDF 环境:

    确保已正确安装和配置 ESP-IDF。可以通过以下命令检查版本:

    idf.py --version
    
  2. 创建项目目录:

    在工作空间创建一个新的项目目录:

    mkdir -p ~/esp/ble_mesh_provisioner
    cd ~/esp/ble_mesh_provisioner
    
  3. 创建项目:

    使用 ESP-IDF 提供的模板创建新项目:

    idf.py create-project my_ble_mesh_provisioner
    cd my_ble_mesh_provisioner
    
  4. 配置项目:

    在项目目录下运行:

    idf.py menuconfig
    
    • 导航到 Bluetooth 配置:

      Component config --->
        Bluetooth --->
          Bluetooth mesh support
      
      • 启用 BLE Mesh:

        • 勾选 Enable Bluetooth Mesh
      • 设置设备角色:

        • Device Role 设置为 Provisioner
    • 启用 UART 串口:

      确保串口配置正确,波特率设置为 115200(或根据需要调整)。


3. 代码实现

下面将详细介绍主要代码的实现,包括每个部分的功能和解释。

3.1 头文件和宏定义

// main.c

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "esp_err.h"

#include "driver/uart.h"
#include "driver/gpio.h"

#include "esp_bt.h"
#include "esp_bt_main.h"

#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_provisioning_api.h"
#include "esp_ble_mesh_config_model_api.h"

#define TAG "BLE_MESH_PROVISIONER"

// UART 配置
#define UART_NUM            UART_NUM_0
#define UART_TX_PIN         GPIO_NUM_1
#define UART_RX_PIN         GPIO_NUM_3
#define UART_BAUD_RATE      115200
#define UART_BUF_SIZE       256

// 按键和 LED 配置
#define BUTTON_GPIO         GPIO_NUM_9
#define LED_GPIO            GPIO_NUM_10

  • 包含必要的头文件:

    • FreeRTOSESP 系统相关头文件
    • UARTGPIO 驱动
    • 蓝牙BLE Mesh API
  • 定义日志标签和硬件配置宏:

    • TAG:用于日志输出时标识模块
    • UARTGPIO 的引脚和参数

3.2 全局变量

/* 定义 Provisioner 的组成元素 */
static esp_ble_mesh_prov_t provisioner_prov = {
    .prov_uuid = {0x32, 0x10}, // Provisioner 的 UUID,可以自行定义
    // 其他配置项可以根据需要添加
};

/* 定义配置客户端模型 */
static esp_ble_mesh_client_t config_client;

/* 定义元素和模型 */
ESP_BLE_MESH_MODEL_PUB_DEFINE(config_client_pub, 16, ROLE_PROVISIONER);

static esp_ble_mesh_model_t root_models[] = {
    ESP_BLE_MESH_MODEL_CFG_CLI(&config_client_pub, &config_client),
};

static esp_ble_mesh_elem_t elements[] = {
    ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE),
};

static esp_ble_mesh_comp_t composition = {
    .cid = 0xFFFF, // 公司 ID,0xFFFF 表示未定义
    .elements = elements,
    .element_count = ARRAY_SIZE(elements),
};

/* UART 事件队列句柄 */
static QueueHandle_t uart_queue;

/* Provisioned 节点列表 */
typedef struct {
    uint16_t unicast_addr;
    uint8_t  dev_uuid[16];
} node_info_t;

#define MAX_NODES 10
static node_info_t nodes[MAX_NODES];
static uint8_t node_count = 0;

  • Provisioner 配置:

    • esp_ble_mesh_prov_t 结构体包含了配置器的配置信息,如 UUID。
  • 配置客户端模型:

    • esp_ble_mesh_client_t 用于处理模型的客户端操作。
    • config_client_pub 是模型的发布上下文。
  • 模型和元素:

    • root_models 定义了根元素的模型,这里添加了配置客户端模型。
    • elements 定义了设备的元素列表。
  • 设备组成(Composition):

    • composition 包含了设备的公司 ID、元素等信息。
  • UART 事件队列和节点信息:

    • uart_queue:用于接收 UART 事件。
    • nodes:存储已配置的节点信息。

3.3 用户交互接口

在这里插入图片描述

3.3.1 串口交互
/* UART 初始化 */
static void uart_init(void)
{
    const uart_config_t uart_config = {
        .baud_rate = UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
    };

    uart_param_config(UART_NUM, &uart_config);
    uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_driver_install(UART_NUM, UART_BUF_SIZE * 2, UART_BUF_SIZE * 2, 20, &uart_queue, 0);
}

/* UART 事件处理任务 */
static void uart_task(void *arg)
{
    uart_event_t event;
    uint8_t data[UART_BUF_SIZE];

    while (1) {
        if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
            switch (event.type) {
                case UART_DATA:
                    memset(data, 0, UART_BUF_SIZE);
                    int len = uart_read_bytes(UART_NUM, data, event.size, portMAX_DELAY);
                    if (len > 0) {
                        data[len] = '\0';
                        ESP_LOGI(TAG, "Received command: %s", data);
                        // 解析命令并执行相应操作
                        if (strcmp((char *)data, "scan\n") == 0) {
                            // 开始扫描未配置节点
                            esp_ble_mesh_provisioner_prov_enable(BLE_MESH_PROV_ADV | BLE_MESH_PROV_GATT);
                            ESP_LOGI(TAG, "Start scanning unprovisioned devices");
                        } else if (strcmp((char *)data, "list\n") == 0) {
                            // 列出已配置节点
                            for (int i = 0; i < node_count; i++) {
                                ESP_LOGI(TAG, "Node %d: Unicast Addr: 0x%04x", i, nodes[i].unicast_addr);
                            }
                        } else {
                            ESP_LOGW(TAG, "Unknown command");
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }
    vTaskDelete(NULL);
}
  • UART 初始化:

    • 配置 UART 参数,安装 UART 驱动,并创建事件队列。
  • UART 任务:

    • 等待 UART 事件队列中的事件。
    • 当接收到数据时,读取并解析命令,执行相应的操作。
  • 支持的命令示例:

    • scan:开始扫描未配置的设备。
    • list:列出已配置的节点。
3.3.2 按键处理
/* 按键中断服务程序 */
static void IRAM_ATTR button_isr_handler(void* arg)
{
    // 通知主任务执行相应操作,例如开始扫描
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(uart_queue, &gpio_num, &xHigherPriorityTaskWoken);
    if (xHigherPriorityTaskWoken) {
        portYIELD_FROM_ISR();
    }
}

/* GPIO 初始化 */
static void gpio_init(void)
{
    gpio_config_t io_conf = {};
    // 配置按键引脚
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << BUTTON_GPIO);
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    gpio_config(&io_conf);

    // 安装 GPIO 中断服务程序
    gpio_install_isr_service(0);
    gpio_isr_handler_add(BUTTON_GPIO, button_isr_handler, (void*) BUTTON_GPIO);

    // 配置 LED 引脚
    gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
}
  • 按键中断处理:

    • 配置按键引脚为输入模式,设置下降沿中断。
    • 安装中断服务程序,在中断中通知主任务执行操作。
  • GPIO 初始化:

    • 初始化按键和 LED 引脚。

3.4 回调函数

/* 配置客户端模型回调函数 */
static void config_client_cb(esp_ble_mesh_cfg_client_cb_event_t event,
                             esp_ble_mesh_cfg_client_cb_param_t *param)
{
    // 处理配置客户端模型事件
    // 根据需要添加代码
}

/* BLE Mesh 事件回调函数 */
static void ble_mesh_provisioning_cb(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, "Provisioner registered");
        break;
    case ESP_BLE_MESH_PROV_UNPROV_DEV_ADV_PKT_EVT:
        ESP_LOGI(TAG, "Unprovisioned device found, UUID:");
        esp_log_buffer_hex(TAG, param->unprov_dev_adv_pkt.dev_uuid, 16);

        // 自动将发现的设备添加到网络
        esp_ble_mesh_unprov_dev_add_t add_dev = {};
        memcpy(add_dev.addr, param->unprov_dev_adv_pkt.addr, 6);
        add_dev.addr_type = param->unprov_dev_adv_pkt.addr_type;
        memcpy(add_dev.uuid, param->unprov_dev_adv_pkt.dev_uuid, 16);
        esp_ble_mesh_provisioner_add_unprov_dev(&add_dev, ADD_DEV_RM_AFTER_PROV);

        break;
    case ESP_BLE_MESH_PROV_PROVISIONER_PROV_COMPLETE_EVT:
        ESP_LOGI(TAG, "Provisioning complete, Node Unicast Addr: 0x%04x", param->provisioner_prov_complete.addr);
        // 保存节点信息
        if (node_count < MAX_NODES) {
            nodes[node_count].unicast_addr = param->provisioner_prov_complete.addr;
            memcpy(nodes[node_count].dev_uuid, param->provisioner_prov_complete.dev_uuid, 16);
            node_count++;
        } else {
            ESP_LOGW(TAG, "Node list is full");
        }
        break;
    default:
        break;
    }
}
  • 配置客户端模型回调:

    • 处理配置客户端模型的事件。
  • BLE Mesh 配置器回调:

    • ESP_BLE_MESH_PROV_UNPROV_DEV_ADV_PKT_EVT:发现未配置的设备,自动添加到网络。
    • ESP_BLE_MESH_PROV_PROVISIONER_PROV_COMPLETE_EVT:配置完成,保存节点信息。

3.5 初始化函数

/* 初始化蓝牙和 BLE Mesh */
static esp_err_t ble_mesh_init(void)
{
    esp_err_t err;

    /* 初始化 NVS */
    err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);

    /* 初始化蓝牙控制器 */
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    err = esp_bt_controller_init(&bt_cfg);
    if (err) {
        ESP_LOGE(TAG, "Bluetooth controller init failed");
        return err;
    }

    err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (err) {
        ESP_LOGE(TAG, "Bluetooth controller enable failed");
        return err;
    }

    /* 初始化 Bluedroid 栈 */
    err = esp_bluedroid_init();
    if (err) {
        ESP_LOGE(TAG, "Bluedroid stack init failed");
        return err;
    }

    err = esp_bluedroid_enable();
    if (err) {
        ESP_LOGE(TAG, "Bluedroid stack enable failed");
        return err;
    }

    /* 注册回调函数 */
    esp_ble_mesh_register_prov_callback(ble_mesh_provisioning_cb);
    esp_ble_mesh_register_config_client_callback(config_client_cb);

    /* 初始化 Mesh 栈 */
    esp_ble_mesh_init_param_t init_param = {
        .prov = &provisioner_prov,
        .comp = &composition,
    };

    err = esp_ble_mesh_init(&init_param);
    if (err) {
        ESP_LOGE(TAG, "BLE Mesh init failed");
        return err;
    }

    /* 设置设备为 Provisioner */
    err = esp_ble_mesh_provisioner_set_dev_uuid(provisioner_prov.prov_uuid, 16);
    if (err) {
        ESP_LOGE(TAG, "Set device UUID failed");
        return err;
    }

    /* 分配地址池 */
    err = esp_ble_mesh_provisioner_set_node_name(0x0005, "Node1");
    if (err) {
        ESP_LOGE(TAG, "Set node name failed");
        return err;
    }

    ESP_LOGI(TAG, "BLE Mesh Provisioner initialized");

    return ESP_OK;
}
  • 蓝牙和 BLE Mesh 初始化:
    • 初始化 NVS蓝牙控制器Bluedroid 栈
    • 注册回调函数。
    • 初始化 BLE Mesh 协议栈,设置设备为 Provisioner

3.6 app_main 函数

void app_main(void)
{
    esp_err_t err;

    /* 初始化 UART */
    uart_init();

    /* 初始化 GPIO */
    gpio_init();

    /* 初始化 BLE Mesh */
    err = ble_mesh_init();
    if (err) {
        ESP_LOGE(TAG, "BLE Mesh init failed");
        return;
    }

    /* 创建 UART 任务 */
    xTaskCreate(uart_task, "uart_task", 4096, NULL, 10, NULL);

    ESP_LOGI(TAG, "ESP32-C3 BLE Mesh Provisioner started");

    /* 主循环 */
    while (1) {
        // 可添加其他逻辑,例如 LED 指示
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
  • 初始化 UART、GPIO 和 BLE Mesh:

    • 调用各自的初始化函数。
  • 创建 UART 任务:

    • 创建用于处理 UART 事件的任务。
  • 主循环:

    • 在循环中可以添加其他逻辑,例如 LED 指示。

4. 编译和烧录

  1. 配置项目:

    在项目目录下,运行以下命令进入菜单配置:

    idf.py menuconfig
    
    • 导航到 Bluetooth 配置:

      Component config --->
        Bluetooth --->
          Bluetooth mesh support
      
      • 启用 BLE Mesh:

        • 勾选 Enable Bluetooth Mesh
      • 设置设备角色:

        • Device Role 设置为 Provisioner
    • 其他配置:

      • 根据需要调整日志等级、串口波特率等。
  2. 编译项目:

    idf.py build
    
  3. 烧录固件:

    idf.py -p /dev/ttyUSB0 flash
    
    • /dev/ttyUSB0 替换为实际的串口端口。
  4. 监控日志:

    idf.py -p /dev/ttyUSB0 monitor
    

5. 测试与验证

  1. 启动 ESP32-C3:

    • 上电或复位 ESP32-C3 开发板,观察串口日志,确认 BLE Mesh 初始化成功。
  2. 通过串口与设备交互:

    • 使用串口调试工具连接到 ESP32-C3,设置波特率为 115200。
    • 输入命令 scan,设备将开始扫描未配置的节点。
    • 在串口日志中,可以看到发现的未配置设备信息。
  3. 准备待配置的节点设备:

    • 将其他支持 BLE Mesh 的设备设置为未配置状态,等待被配置。
  4. 观察配置过程:

    • ESP32-C3 将自动将发现的设备添加到网络中,完成配置。

    • 在串口日志中,您应该能够看到类似以下的输出:

      I (10000) BLE_MESH_PROVISIONER: Provisioning complete, Node Unicast Addr: 0x0005
      
  5. 列出已配置节点:

    • 在串口输入命令 list,可以查看已配置的节点列表。
  6. 通过按键触发操作(可选):

    • 按下按键,触发扫描或其他预设操作。

6. 注意事项

  • 兼容性:

    • 确保节点设备支持标准的 BLE Mesh 协议,以实现互操作性。
  • OOB 认证:

    • 如果节点设备使用 OOB(Out-of-Band)认证,需要在配置器代码中处理相应的逻辑。
  • 日志等级:

    • menuconfig 中调整日志等级为 InfoDebug,以获取更详细的调试信息。
  • 安全性:

    • 正确管理网络密钥、应用密钥和设备密钥,确保网络通信的安全。
  • 资源管理:

    • 注意任务的堆栈大小和内存使用,避免内存泄漏和溢出。

7. 总结

通过上述步骤,您已经成功地设计并实现了一个基于 ESP32-C3BLE Mesh Provisioner,不依赖手机 SDK,全程由 ESP32-C3 完成配置和控制。用户可以通过 串口按键 与设备交互,实现对网络的管理。

扩展功能:

  • 添加对节点的控制:

    • 实现对节点的具体控制,如开关灯等,需要在配置器中添加相应的模型支持。
  • 丰富用户界面:

    • 使用 OLED 显示屏网页服务器,提供更友好的用户界面。
  • 数据持久化:

    • 将已配置节点的信息保存到 NVS 中,断电重启后能够继续管理网络。

参考资料:


如果您在实现过程中遇到任何问题,或者需要进一步的帮助,请随时告诉我!

标签:err,ESP,UART,esp,mesh,BLE,Mesh,Provisioner,C3
From: https://blog.csdn.net/qq_41126242/article/details/144037222

相关文章

  • stm32 CRC32实现代码及软硬方式测试
    一、概叙:1、本文主要是针对常用的crc32的实现方式进行说明2、crc32的软件方式和硬件方式时间上的差异二、实现:1、软件实现代码1/***@brief软件crc32计算*@paramptr输入的32位数组*@paramlen32位数组的长度*@returnu32......
  • AtCoder ABC321F - #(subset sum = K) with Add and Erase 题解 可撤销背包
    题目链接:https://atcoder.jp/contests/abc321/tasks/abc321_f题目大意:给定大小为\(k\)的背包和\(q\)次操作,支持两种操作:插入一个大小为\(x\)的元素;删除一个大小为\(x\)的元素。每次操作后,求装满背包方案数。解题思路:可撤销背包。插入\(x\)时,fori=K->x......
  • 代码随想录算法训练营第十一天|LC150.逆波兰表达式求值|LC239.滑动窗口最大值|LC347.
    150.逆波兰表达式求值-力扣(LeetCode)题目要求:    1、整数除法只保留整数部分;    2、该表达式总会得出有效数值且部存在除数为0的情况;    3、逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。fromtypingimportListfromoperato......
  • 题解:AT_abc381_c [ABC381C] 11/22 Substring
    显然这个“11/22Substring”是以那个“/”为中心对称的。鉴于一个这样的字符串只能有一个“/”,而题目又要求最长,所以确定了“/”就能确定一个满足要求的子串。那思路就很简单了,只有两步:找到所有的“/”两边同时寻找相应的子串。别的,除了判断一下越界之外,就不用管了。......
  • CRC32爆破脚本 + [MoeCTF 2022]cccrrc 题解
    CRC32爆破原理介绍:CRC(循环冗余校验)是一种用于检测数据传输错误的技术。CRC算法生成一个校验值(校验和),这个值可以附加到数据后面,在数据接收方重新计算校验值并与附加的校验值进行比较,以此来确定数据是否在传输过程中发生了错误CRC32是一种常用的CRC算法,它的校验值长度固定为3......
  • M68LC302CAF20VCT,MMC2107CFCPU33,MC9S12UF32PUM,S9S12DJ12F1MPVEMCF52235CVM60MAC7121MA
    NXPSemiconductors公司的产品和技术还广泛应用于安全和身份验证领域,包括智能卡、支付系统、身份识别和生物识别技术。此外,该公司还在电源管理、射频技术和传感器领域拥有丰富的经验和专业知识。恩智浦的产品不仅提供高性能和创新的解决方案,还致力于保证产品的安全性。NXPSem......
  • CSC3100 Problem Scale & Subtasks
    RequirementsCode(90%)YoucanwriteyourcodeinJava,Python,C,orC++.Thetimelimitmayvaryamongdifferentlanguages,dependingontheperformanceofthelanguage.Yourcodemustbeacompleteexcutableprograminsteadofonlyafunction.Weg......
  • DreamMesh4D: Video-to-4D Generation with Sparse-Controlled Gaussian-Mesh HybridR
    目录一、概述二、前置知识1、分数蒸馏采样 2、LBS 3、DQS4、EucDist和GeoDist算法三、相关工作1、三维生成2、4D表示3、4D生成四、DreamMesh4D1、静态阶段 2、动态阶段-可变形图建立 3、动态阶段--自适应可变蒙皮算法 一、概述    该论文提出了......
  • YOLOv11改进策略【卷积层】| ICCV-2023 SAFM 空间自适应特征调制模块 对C3k2进行二次
    一、本文介绍本文记录的是利用空间自适应特征调制模块SAFM优化YOLOv11的目标检测方法研究。SAFM通过更好地利用特征信息来实现模型性能和效率的平衡。本文通过二次创新C3k2,能够动态选择代表性特征,并结合局部上下文信息,提升模型的检测精度。专栏目录:YOLOv11改进目录一览......
  • 如何通过 Service Mesh 构建高效、安全的微服务系统
    1.引言1.1.什么是ServiceMesh?ServiceMesh是一种基础架构层,负责处理微服务之间的通信,它通过在每个服务旁边部署代理(通常称为Sidecar)来捕获和管理服务间的网络流量。这种方式解耦了微服务的业务逻辑和基础设施层的管理工作。ServiceMesh提供了诸如流量管理、服务发......