当前位置: 首页 > news >正文

【ESP32S3】GATT Server service table传送数据到调试助手

前言

在初步学习esp32蓝牙的过程中,借鉴了官方的GATT Server Service Table Example,可以在readme中看到,此demo是采用低功耗蓝牙的通用属性服务器来创建订阅服务和特性。如果你接触过MQTT,你会发现GATT Server这一特性和MQTT的订阅服务方面特别相似。只不过GATT server的服务模型是基于服务表(service table),而MQTT的服务模型则是基于主题(topic)的。
具体的GATT server代码以及有关函数解释可以直接去看官方文档,我相信比起本人来讲,官方的描述肯定更好。
所以就还是直接放代码吧。

主要内容

本人在这个demo里面分别添加了控制led开关、读随机值、获取芯片内部温度以及读取MPU6050加速度计和陀螺仪值的功能。
前面两个功能的参考链接是ESP32+idf开发—蓝牙通信入门之ble数据收发(notify)
框架依旧是ESP-IDF
好了
先看看工程的框架
工程框架
其实也没什么框架可言, 可以看到我把服务处理的初始化和寄存器读写之类的一些杂七杂八函数放在了别的文件里,而具体的蓝牙操作函数是放在demo文件里(这个demo工程直接从官方welcome界面导入就好了)。
然后就可以开始加功能叻!
定义相关蓝牙参数、数据的结构体和i2c的句柄;

#define GATTS_TABLE_TAG "GATTS_TABLE_DEMO 0325"

#define PROFILE_NUM                 1
#define PROFILE_APP_IDX             0
#define ESP_APP_ID                  0x55
#define SAMPLE_DEVICE_NAME          "mwtESP_GATTS_DEMO"
#define SVC_INST_ID                 0

/* The max length of characteristic value. When the GATT client performs a write or prepare write operation,
*  the data length must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
*/
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 500
#define PREPARE_BUF_MAX_SIZE        1024
#define CHAR_DECLARATION_SIZE       (sizeof(uint8_t))

#define ADV_CONFIG_FLAG             (1 << 0)
#define SCAN_RSP_CONFIG_FLAG        (1 << 1)

// mpu6050 send buffer size
#define SENSOR_DATA_SIZE (3 * sizeof(float) + 3 * sizeof(float) + sizeof(float))
bool is_bt_connected = true; // 标记蓝牙是否连接

static uint8_t adv_config_done       = 0;

uint16_t heart_rate_handle_table[HRS_IDX_NB];

typedef struct {
    uint8_t                 *prepare_buf;
    int                     prepare_len;
} prepare_type_env_t;

static prepare_type_env_t prepare_write_env;

// 定义 I2C 总线句柄和设备句柄。
i2c_master_bus_handle_t bus_handle = NULL;
i2c_master_dev_handle_t dev_handle = NULL;

处理蓝牙数据格式以及设备uid之类的配置;

#define CONFIG_SET_RAW_ADV_DATA
#ifdef CONFIG_SET_RAW_ADV_DATA
static uint8_t raw_adv_data[] = {
    /* Flags */
    0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
    /* TX Power Level */
    0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xEB,
    /* Complete 16-bit Service UUIDs */
    0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xFF, 0x00,
    /* Complete Local Name */
    0x12, ESP_BLE_AD_TYPE_NAME_CMPL,
    'm', 'w', 't','E', 'S', 'P', '_', 'G', 'A', 'T', 'T', 'S', '_', 'D', 'E', 'M', 'O'
};

static uint8_t raw_scan_rsp_data[] = {
    /* Flags */
    0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
    /* TX Power Level */
    0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xEB,
    /* Complete 16-bit Service UUIDs */
    0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xFF, 0x00
};

#else
static uint8_t service_uuid[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
};

/* The length of adv data must be less than 31 bytes */
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp        = false,
    .include_name        = true,
    .include_txpower     = true,
    .min_interval        = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
    .max_interval        = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
    .appearance          = 0x00,
    .manufacturer_len    = 0,    //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, //test_manufacturer,
    .service_data_len    = 0,
    .p_service_data      = NULL,
    .service_uuid_len    = sizeof(service_uuid),
    .p_service_uuid      = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp        = true,
    .include_name        = true,
    .include_txpower     = true,
    .min_interval        = 0x0006,
    .max_interval        = 0x0010,
    .appearance          = 0x00,
    .manufacturer_len    = 0, //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, //&test_manufacturer[0],
    .service_data_len    = 0,
    .p_service_data      = NULL,
    .service_uuid_len    = sizeof(service_uuid),
    .p_service_uuid      = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
#endif /* CONFIG_SET_RAW_ADV_DATA */

esp的广播设备参数以及gatts规范的参数;

static esp_ble_adv_params_t adv_params = {
    .adv_int_min         = 0x20, // 广播时间间隔的最小值
    .adv_int_max         = 0x40, // 广播时间间隔的最大值
    .adv_type            = ADV_TYPE_IND,
    .own_addr_type       = BLE_ADDR_TYPE_PUBLIC,
    .channel_map         = ADV_CHNL_ALL,
    .adv_filter_policy   = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

定义并初始化 gatts_profile_inst 类型的数组,用于管理 BLE 的 GATT 服务实例。
初始状态下,gatts_if 被设置为无效值 ESP_GATT_IF_NONE,等待蓝牙协议栈分配有效值;在此之前需要先声明gatts_profile_event_handler函数

static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
					esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst heart_rate_profile_tab[PROFILE_NUM] = {
    [PROFILE_APP_IDX] = {
        .gatts_cb = gatts_profile_event_handler,
        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
};

原来的代码保留,添加前面说的四个服务;

/* Service */
static const uint16_t GATTS_SERVICE_UUID_TEST      = 0x00FF;
static const uint16_t GATTS_CHAR_UUID_TEST_A       = 0xFF01;
static const uint16_t GATTS_CHAR_UUID_TEST_B       = 0xFF02;
static const uint16_t GATTS_CHAR_UUID_TEST_C       = 0xFF03;
static const uint16_t GATTS_CHAR_UUID_TEST_LED     = 0xFF04; // 加灯
static const uint16_t GATTS_CHAR_UUID_TEST_TEMP    = 0xFF05; // 加芯片内部温度
static const uint16_t GATTS_CHAR_UUID_TEST_RAND    = 0xFF06; // 加随机值
static const uint16_t GATTS_CHAR_UUID_MPU_SENSER   = 0xFF07; // 加sensor

static const uint16_t primary_service_uuid         = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid   = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
static const uint8_t char_prop_read                =  ESP_GATT_CHAR_PROP_BIT_READ;
static const uint8_t char_prop_write               = ESP_GATT_CHAR_PROP_BIT_WRITE;

static const uint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY; // add for temperature,rand,sensor

static const uint8_t char_prop_read_write_notify   = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
/**
 * heart_measurement_ccc变量,用于控制某个特征值(Characteristic)的通知(Notification)或指示(Indication)功能。
 * 通知(Notification):允许从设备(Peripheral)主动向主设备(Central)发送数据,而无需主设备请求。
 * 指示(Indication):类似于通知,但需要主设备确认接收到数据。
 * {0x00, 0x00} 表示既不启用通知也不启用指示。{0x01, 0x00} 表示启用通知。{0x02, 0x00} 表示启用指示。
 */
static const uint8_t heart_measurement_ccc[2]      = {0x00, 0x00};
static const uint8_t char_value[4]                 = {0x11, 0x22, 0x33, 0x44};
static const uint8_t led_value[1] = {0x00};
static const uint8_t temp_value[1] = {0x00};
static const uint8_t rand_value[1] = {0x00};
static const uint8_t sensor_value[SENSOR_DATA_SIZE] = {0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

然后把要订阅的服务添加到表里;

/* Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
{
    // Service Declaration
    [IDX_SVC] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},

    /* Characteristic Declaration */
    [IDX_CHAR_A] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},

    /* Characteristic Value */
    [IDX_CHAR_VAL_A] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},

    /* Client Characteristic Configuration Descriptor */
    [IDX_CHAR_CFG_A] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},

    /* Characteristic Declaration */
    [IDX_CHAR_LED] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},

    /* Characteristic Value */
    [IDX_CHAR_VAL_LED] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_LED, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(led_value), (uint8_t *)led_value}},

    /* Characteristic Declaration */
    [IDX_CHAR_TEMP] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}},

    /* Characteristic Value */
    [IDX_CHAR_VAL_TEMP] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_TEMP, ESP_GATT_PERM_READ, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(temp_value), (uint8_t *)temp_value}},

    /* Client Characteristic Configuration Descriptor */
    [IDX_CHAR_CFG_TEMP] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},

    /* Characteristic Declaration srand*/
    [IDX_CHAR_RAND] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}},

    /* Characteristic Value srand*/
    [IDX_CHAR_VAL_RAND] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_RAND, ESP_GATT_PERM_READ, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(rand_value), (uint8_t *)rand_value}},

    /* Client Characteristic Configuration Descriptor srand*/
    [IDX_CHAR_CFG_RAND] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},
    
    /* Characteristic Declaration sensor*/
    [IDX_CHAR_SENSOR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}},

    /* Characteristic Value sensor*/
    [IDX_CHAR_VAL_SENSOR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_MPU_SENSER, ESP_GATT_PERM_READ, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(sensor_value), (uint8_t *)sensor_value}},

    /* Client Characteristic Configuration Descriptor sensor*/
    [IDX_CHAR_CFG_SENSOR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},

    /* Characteristic Declaration */
    [IDX_CHAR_B] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}},

    /* Characteristic Value */
    [IDX_CHAR_VAL_B] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},

    /* Characteristic Declaration */
    [IDX_CHAR_C] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_write}},

    /* Characteristic Value */
    [IDX_CHAR_VAL_C] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_C, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
};

添加读取芯片内部温度、随机值和sensor函数的任务句柄、标志位和处理函数;

TaskHandle_t *temp_task_handle = NULL;  // TEMP 特征值的任务句柄
TaskHandle_t *rand_task_handle = NULL;  // RAND 特征值的任务句柄
TaskHandle_t sensor_task_handle = NULL; // SENSOR 特征值的任务句柄
volatile bool temp_notify_flag = false;
volatile bool rand_notify_flag = false;
volatile bool sensor_notify_flag = false;

static void get_rand_value(void *arg)
{
    // srand(time(NULL));
    while (1)
    {
        if (rand_notify_flag == true)
        {
            uint8_t randv = (uint8_t)esp_random();
            // 更新 BLE 特征值
            esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_RAND], 1, &randv);
            // 发送通知
            esp_ble_gatts_send_indicate(
                heart_rate_profile_tab[0].gatts_if, 
                heart_rate_profile_tab[0].conn_id,
                heart_rate_handle_table[IDX_CHAR_VAL_RAND], 
                1, 
                &randv, 
                false);
        }else{
            vTaskDelete(NULL);
        }
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}
static void get_temp_value(void *arg)
{
    float temp_value = 0.0f;  // 存储温度值
    uint8_t temp_uint8 = 0;   // 转换为整数后发送

    while (1)
    {
        if (temp_notify_flag == true)
        {
            esp_err_t ret = get_current_temperature(&temp_value);
            if (ret != ESP_OK) {
                ESP_LOGE(GATTS_TABLE_TAG, "Failed to get temperature value");
                vTaskDelay(pdMS_TO_TICKS(2000));
                continue;
            }

            temp_uint8 = (uint8_t)(temp_value + 0.5f);  // 四舍五入

            // 更新 BLE 特征值
            esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_TEMP], 1, &temp_uint8);

            // 发送通知
            esp_ble_gatts_send_indicate(
                heart_rate_profile_tab[0].gatts_if,
                heart_rate_profile_tab[0].conn_id,
                heart_rate_handle_table[IDX_CHAR_VAL_TEMP],
                1,
                &temp_uint8,
                false
            );

            ESP_LOGI(GATTS_TABLE_TAG, "Sent temperature value: %d", temp_uint8);
        }
        else
        {
            vTaskDelete(NULL);  // 删除任务
        }

        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

static void get_mpu6050_value(void *arg)
{
    // 初始化 I2C 总线和设备
    if(bus_handle == NULL)
    {
        esp_err_t ret = i2c_master_init(&bus_handle, &dev_handle);
        if(ret != ESP_OK)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "I2C initialized failed");
            return;
        }
        ESP_LOGI(GATTS_TABLE_TAG, "I2C initialized successfully");
    }

    // 初始化 MPU6050 任务
    mpu6050_task_init(dev_handle);

    SensorData_t received_data;
    static SensorData_t last_received_data = {0}; // 上一次的有效数据
    uint8_t sensor_data_buffer[SENSOR_DATA_SIZE];

    while (1)
    {
        if (sensor_notify_flag == true)
        {
            // 从队列中接收数据
            if (xQueueReceive(sensor_data_queue, &received_data, pdMS_TO_TICKS(2000)) == pdPASS)
            {
                memcpy(&last_received_data, &received_data, sizeof(SensorData_t)); // 更新有效数据
            }
            else
            {
                ESP_LOGW(GATTS_TABLE_TAG, "No new data in queue, sending last valid data");
            }

            // 序列化数据
            memcpy(sensor_data_buffer, &last_received_data.accel, 3 * sizeof(float));
            memcpy(sensor_data_buffer + 3 * sizeof(float), &last_received_data.gyro, 3 * sizeof(float));
            memcpy(sensor_data_buffer + 6 * sizeof(float), &last_received_data.temp, sizeof(float));

            // 更新 BLE 特征值
            esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_SENSOR], SENSOR_DATA_SIZE, sensor_data_buffer);

            esp_ble_gatts_send_indicate(
                heart_rate_profile_tab[0].gatts_if,
                heart_rate_profile_tab[0].conn_id,
                heart_rate_handle_table[IDX_CHAR_VAL_SENSOR],
                SENSOR_DATA_SIZE,
                sensor_data_buffer,
                false);
        }
        else
        {
            ESP_LOGI(GATTS_TABLE_TAG, "Stopping MPU6050 tasks and freeing resources");

            // 如果蓝牙已断开,则释放 I2C 资源
            if (!is_bt_connected)
            {
                if (dev_handle != NULL)
                {
                    ESP_ERROR_CHECK(i2c_master_bus_rm_device(dev_handle));
                    dev_handle = NULL;
                }
                if (bus_handle != NULL)
                {
                    ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle));
                    bus_handle = NULL;
                }
                ESP_LOGI(GATTS_TABLE_TAG, "I2C resources released after Bluetooth disconnection");
            }
            // 删除当前任务
            vTaskDelete(NULL);
        }

        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

然后我们就需要在gatts_profile_event_handler函数里面添加相应的功能处理函数了,刚开始能够看到里面有一大堆case,有的case涉及到广播、连接、断连、设置MTU以及请求数据读写等内容,但是我们自己写上去的功能怎么添加?在哪里添加什么?不要着急,先了解一下这个函数。
首先看到static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)这行,需要知道,BLE GATT Server框架是基于回调事件触发处理函数的,从第一个参数点进去可以看到esp_gatts_api.h文件里有一系列用于处理回调的标志位,它被用枚举形式装起来了:

/// GATT Server callback function events
typedef enum {
    ESP_GATTS_REG_EVT                 = 0,       /*!< When register application id, the event comes */
    ESP_GATTS_READ_EVT                = 1,       /*!< When gatt client request read operation, the event comes */
    ESP_GATTS_WRITE_EVT               = 2,       /*!< When gatt client request write operation, the event comes */
    ESP_GATTS_EXEC_WRITE_EVT          = 3,       /*!< When gatt client request execute write, the event comes */
    ESP_GATTS_MTU_EVT                 = 4,       /*!< When set mtu complete, the event comes */
    ESP_GATTS_CONF_EVT                = 5,       /*!< When receive confirm, the event comes */
    ESP_GATTS_UNREG_EVT               = 6,       /*!< When unregister application id, the event comes */
    ESP_GATTS_CREATE_EVT              = 7,       /*!< When create service complete, the event comes */
    ESP_GATTS_ADD_INCL_SRVC_EVT       = 8,       /*!< When add included service complete, the event comes */
    ESP_GATTS_ADD_CHAR_EVT            = 9,       /*!< When add characteristic complete, the event comes */
    ESP_GATTS_ADD_CHAR_DESCR_EVT      = 10,      /*!< When add descriptor complete, the event comes */
    ESP_GATTS_DELETE_EVT              = 11,      /*!< When delete service complete, the event comes */
    ESP_GATTS_START_EVT               = 12,      /*!< When start service complete, the event comes */
    ESP_GATTS_STOP_EVT                = 13,      /*!< When stop service complete, the event comes */
    ESP_GATTS_CONNECT_EVT             = 14,      /*!< When gatt client connect, the event comes */
    ESP_GATTS_DISCONNECT_EVT          = 15,      /*!< When gatt client disconnect, the event comes */
    ESP_GATTS_OPEN_EVT                = 16,      /*!< When connect to peer, the event comes */
    ESP_GATTS_CANCEL_OPEN_EVT         = 17,      /*!< When disconnect from peer, the event comes */
    ESP_GATTS_CLOSE_EVT               = 18,      /*!< When gatt server close, the event comes */
    ESP_GATTS_LISTEN_EVT              = 19,      /*!< When gatt listen to be connected the event comes */
    ESP_GATTS_CONGEST_EVT             = 20,      /*!< When congest happen, the event comes */
    /* following is extra event */
    ESP_GATTS_RESPONSE_EVT            = 21,      /*!< When gatt send response complete, the event comes */
    ESP_GATTS_CREAT_ATTR_TAB_EVT      = 22,      /*!< When gatt create table complete, the event comes */
    ESP_GATTS_SET_ATTR_VAL_EVT        = 23,      /*!< When gatt set attr value complete, the event comes */
    ESP_GATTS_SEND_SERVICE_CHANGE_EVT = 24,      /*!< When gatt send service change indication complete, the event comes */
} esp_gatts_cb_event_t;

通过查看官方文档可以发现请求读和请求写,还有MTU设置,蓝牙连接/断连这部分可以在本次demo中用到

    ESP_GATTS_READ_EVT                = 1,       /*!< When gatt client request read operation, the event comes */
    ESP_GATTS_WRITE_EVT               = 2,       /*!< When gatt client request write operation, the event comes */
    ...
    ESP_GATTS_MTU_EVT                 = 4,       /*!< When set mtu complete, the event comes */
    ...
    ESP_GATTS_CONNECT_EVT             = 14,      /*!< When gatt client connect, the event comes */
    ESP_GATTS_DISCONNECT_EVT          = 15,      /*!< When gatt client disconnect, the event comes */

在这个函数下找到case ESP_GATTS_WRITE_EVT:往里面添加我们的任务

case ESP_GATTS_WRITE_EVT:
        if (!param->write.is_prep)
        {
            // the data length of gattc write  must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
            ESP_LOGI(GATTS_TABLE_TAG, "GATT_WRITE_EVT, handle = %d, value len = %d, value :", param->write.handle, param->write.len);
            ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, param->write.value, param->write.len);
            if (heart_rate_handle_table[IDX_CHAR_VAL_LED] == param->write.handle)
            {
                ESP_LOGI(GATTS_TABLE_TAG, "write:0x%x", param->write.value[0]);
                if (param->write.value[0] == 0x00)
                {
                    led_off(); // 收到数据0x00关灯
                    ESP_LOGI(GATTS_TABLE_TAG, "recv led off\n");
                }
                else if (param->write.value[0] == 0x01)
                {
                    led_on(); // 收到数据0x01开灯
                    ESP_LOGI(GATTS_TABLE_TAG, "recv led on\n");
                }
            }
            if (heart_rate_handle_table[IDX_CHAR_CFG_TEMP] == param->write.handle && param->write.len == 2)
            {
                uint16_t descr_value = param->write.value[1] << 8 | param->write.value[0];
                if (descr_value == 0x0001)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "temp notify enable");
                    xTaskCreate(get_temp_value, "get temp value", 8192, NULL, 10, temp_task_handle);
                    temp_notify_flag=true;
                }
                else if (descr_value == 0x0002)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "temp indicate enable");
                    uint8_t indicate_data[15];
                    for (int i = 0; i < sizeof(indicate_data); ++i)
                    {
                        indicate_data[i] = i % 0xff;
                    }
                    // if want to change the value in server database, call:
                    // esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_A], sizeof(indicate_data), indicate_data);
                    
                    // the size of indicate_data[] need less than MTU size
                    esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
                                                sizeof(indicate_data), indicate_data, true);
                }
                else if (descr_value == 0x0000)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "temp notify/indicate disable ");
                    temp_notify_flag=false;
                    //vTaskDelete(pTask);
                }
                else
                {
                    ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
                    ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, param->write.value, param->write.len);
                }
            }
            if (heart_rate_handle_table[IDX_CHAR_CFG_RAND] == param->write.handle && param->write.len == 2)
            {
                uint16_t descr_value = param->write.value[1] << 8 | param->write.value[0];
                if (descr_value == 0x0001)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "rand notify enable");
                    xTaskCreate(get_rand_value, "get rand value", 8192, NULL, 10, rand_task_handle);
                    rand_notify_flag=true;
                }
                else if (descr_value == 0x0002)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "rand indicate enable");
                    uint8_t indicate_data[15];
                    for (int i = 0; i < sizeof(indicate_data); ++i)
                    {
                        indicate_data[i] = i % 0xff;
                    }
                    // if want to change the value in server database, call:
                    // esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_A], sizeof(indicate_data), indicate_data);

                    // the size of indicate_data[] need less than MTU size
                    esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
                                                sizeof(indicate_data), indicate_data, true);
                }
                else if (descr_value == 0x0000)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "rand notify/indicate disable ");
                    rand_notify_flag=false;
                    //vTaskDelete(pTask);
                }
                else
                {
                    ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
                    ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, param->write.value, param->write.len);
                }
            }
            if (heart_rate_handle_table[IDX_CHAR_CFG_SENSOR] == param->write.handle && param->write.len == 2)
            {
                uint16_t descr_value = param->write.value[1] << 8 | param->write.value[0];
                if (descr_value == 0x0001)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "mup6050 notify enable");
                    xTaskCreate(get_mpu6050_value, "get mpu6050 value", 8192, NULL, 10, &sensor_task_handle);
                    sensor_notify_flag=true;
                }
                else if (descr_value == 0x0002)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "mup6050 indicate enable");
                    uint8_t indicate_data[15];
                    for (int i = 0; i < sizeof(indicate_data); ++i)
                    {
                        indicate_data[i] = i % 0xff;
                    }
                    // if want to change the value in server database, call:
                    // esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_A], sizeof(indicate_data), indicate_data);

                    // the size of indicate_data[] need less than MTU size
                    esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
                                                sizeof(indicate_data), indicate_data, true);
                }
                else if (descr_value == 0x0000)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "mup6050 notify/indicate disable ");
                    sensor_notify_flag=false;
                    //vTaskDelete(pTask);
                }
                else
                {
                    ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
                    ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, param->write.value, param->write.len);
                }
            }
            /* send response when param->write.need_rsp is true*/
            if (param->write.need_rsp)
            {
                esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
            }
        }
        else
        {
            /* handle prepare write */
            example_prepare_write_event_env(gatts_if, &prepare_write_env, param);
        }
        break;

感觉还是蓝牙连接和断连这个比较用得到,因为我要删掉一些i2c的资源,不释放的话缓存就要就爆了:

        case ESP_GATTS_CONNECT_EVT:
            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);
            ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, param->connect.remote_bda, 6);
            esp_ble_conn_update_params_t conn_params = {0};
            memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
            /* For the iOS system, please refer to Apple official documents about the BLE connection parameters restrictions. */
            conn_params.latency = 0;
            conn_params.max_int = 0x20;    // max_int = 0x20*1.25ms = 40ms
            conn_params.min_int = 0x10;    // min_int = 0x10*1.25ms = 20ms
            conn_params.timeout = 400;    // timeout = 400*10ms = 4000ms
            heart_rate_profile_tab[0].conn_id = param->connect.conn_id;
            is_bt_connected = true; // 蓝牙已连接flag
            //start sent the update connection parameters to the peer device.
            esp_ble_gap_update_conn_params(&conn_params);
            break;
        case ESP_GATTS_DISCONNECT_EVT:
            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason);
            is_bt_connected = false; // 蓝牙已断开flag
            clean_resources();
            // 重新广播
            esp_err_t adv_ret = esp_ble_gap_start_advertising(&adv_params);
            if (adv_ret != ESP_OK) 
            {
                ESP_LOGE(GATTS_TABLE_TAG, "Failed to start advertising, error code: %s", esp_err_to_name(adv_ret));
            } 
            else 
            {
                ESP_LOGI(GATTS_TABLE_TAG, "Advertising restarted successfully.");
            }
            break;

clean_resources()部分内容

static void clean_resources(void)
{
    // 停止 MPU6050 任务
    if (sensor_task_handle != NULL) 
    {
        vTaskDelete(sensor_task_handle);
        sensor_task_handle = NULL;
    }

    // 删除队列
    if (sensor_data_queue != NULL) 
    {
        vQueueDelete(sensor_data_queue);
        sensor_data_queue = NULL;
    }
    // 如果蓝牙已断开,则释放 I2C 资源
    if ((is_bt_connected == false) && (sensor_notify_flag == false))
    {
        i2c_master_deinit(&bus_handle, &dev_handle);
        ESP_LOGI(GATTS_TABLE_TAG, "I2C resources released after Bluetooth disconnection");
    }
}

然后就是app_main()函数:

void app_main(void)
{
    esp_err_t ret;

    led_init();
    ret = temperature_sensor_init();
    if (ret != ESP_OK) 
    {
        ESP_LOGE(GATTS_TABLE_TAG, "Failed to initialize temperature sensor");
        return;
    }
    /* Initialize NVS. */
    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_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
    // 初始化蓝牙
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    // 使能蓝牙,使用BLE模式
    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    // 初始化蓝牙栈
     /*
        蓝牙栈bluedroid stack包括了BT和BLE使用的基本的define和API
        初始化蓝牙栈以后并不能直接使用蓝牙功能,
        还需要用FSM管理蓝牙连接情况
    */
    ret = esp_bluedroid_init();
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    // 使能蓝牙栈
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    // 建立蓝牙的FSM(有限状态机)
    // 这里使用回调函数来控制每个状态下的响应,需要将其在GATT和GAP层的回调函数注册
    /*gatts_event_handler和gap_event_handler处理蓝牙栈可能发生的所有情况,达到FSM的效果*/
    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
        return;
    }

    ret = esp_ble_gap_register_callback(gap_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
        return;
    }

    ret = esp_ble_gatts_app_register(ESP_APP_ID);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);
        return;
    }
    /*
    设置了MTU的值(经过MTU交换,从而设置一个PDU中最大能够交换的数据量)。
    例如:主设备发出一个1000字节的MTU请求,但是从设备回应的MTU是500字节,那么今后双方要以较小的值500字节作为以后的MTU。
    即主从双方每次在做数据传输时不超过这个最大数据单元。
    */
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
    if (local_mtu_ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
    }
}

至于为什么led和temperature的初始化要放在这而mpu6050的初始化是放在get_mpu6050_value()函数里,这是程序跑崩了带给我的教训… …
和前两个不一样,mpu6050获取数据要先配置i2c,断开蓝牙连接后要释放i2c的句柄和资源,还有就是我想让用户使用到0xFF07这个服务才配置i2c资源,断开蓝牙连接之后(不是程序退出的时候!只是蓝牙断连!!!)才释放i2c资源,如果你想让用户一连上蓝牙就让程序配置i2c和初始化可以把get_mpu6050_value()函数里;

// 初始化 I2C 总线和设备
    if(bus_handle == NULL)
    {
        esp_err_t ret = i2c_master_init(&bus_handle, &dev_handle);
        if(ret != ESP_OK)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "I2C initialized failed");
            return;
        }
        ESP_LOGI(GATTS_TABLE_TAG, "I2C initialized successfully");
    }

    // 初始化 MPU6050 任务
    mpu6050_task_init(dev_handle);

这部分的代码放到ESP_GATTS_CONNECT_EVT里面(我是不建议了,即使bus_handle 和dev_handle是全局变量,但是对开发者不太友好,后续要改代码的时候跳这跳那的,况且用户连接蓝牙之后不一定会用到读mpu sensor这个服务,开了也是浪费空间)。
接下来是led部分的代码,这部分内容我直接copy的上面那条参考链接里的代码

led.c

#include <stdio.h>
#include "led.h"
#include<driver/gpio.h>

void led_init()
{
    gpio_config_t cfg={
        .pin_bit_mask = LED_PIN_SEL,
        .mode=GPIO_MODE_OUTPUT,
        .intr_type=GPIO_INTR_DISABLE,
        .pull_up_en=GPIO_PULLUP_DISABLE,
        .pull_down_en=GPIO_PULLDOWN_DISABLE,
    };
    ESP_ERROR_CHECK(gpio_config(&cfg));
}

void led_on()
{
    ESP_ERROR_CHECK(gpio_set_level(LED_PIN,1));
}

void led_off()
{
    ESP_ERROR_CHECK(gpio_set_level(LED_PIN,0));
}

led.h 灯的引脚我是改成了我板子上的0x38

#ifndef __LED_H__
#define __LED_H__

#define LED_PIN GPIO_NUM_38
#define LED_PIN_SEL  (1ULL<<LED_PIN)
void led_init();
void led_on();
void led_off();
#endif

internal_temperature.c

这部分代码是用官方demo的

#include "internal_temperature.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/temperature_sensor.h"

static const char *TAG = "INTERNAL_TEMP";
static temperature_sensor_handle_t temp_sensor = NULL;

// 初始化温度传感器
esp_err_t temperature_sensor_init(void)
{
    ESP_LOGI(TAG, "Install temperature sensor, expected temp range: 10~50 ℃");
    temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50);
    esp_err_t ret = temperature_sensor_install(&temp_sensor_config, &temp_sensor);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to install temperature sensor");
        return ret;
    }

    ESP_LOGI(TAG, "Enable temperature sensor");
    ret = temperature_sensor_enable(temp_sensor);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to enable temperature sensor");
        return ret;
    }
    return ESP_OK;
}

// 获取当前温度值
esp_err_t get_current_temperature(float *temperature)
{
    if (!temp_sensor) {
        ESP_LOGE(TAG, "Temperature sensor not initialized");
        return ESP_ERR_INVALID_STATE;
    }

    esp_err_t ret = temperature_sensor_get_celsius(temp_sensor, temperature);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read temperature");
        return ret;
    }
    return ESP_OK;
}

internal_temperature.h

#ifndef INTERNAL_TEMPERATURE_H
#define INTERNAL_TEMPERATURE_H

#include <stdint.h>
#include "esp_err.h"

/**
 * @brief 初始化温度传感器
 *
 * @return esp_err_t ESP_OK success,others is fail
 */
esp_err_t temperature_sensor_init(void);

/**
 * @brief 获取当前温度值
 *
 * @param temperature 指向存储温度值的变量(单位:摄氏度)
 * @return esp_err_t ESP_OK 表示成功,其他值表示失败
 */
esp_err_t get_current_temperature(float *temperature);

#endif // INTERNAL_TEMPERATURE_H

mpu6050.c

然后是mpu6050部分的代码,这部分代码是从以前做过的AT32F403A部分移植过来的,寄存器读写部分的函数也是找官方文档复制过来的,哈哈;

#include "mpu6050.h"

#include <time.h> 
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"


static const char *TAG = "MPU6050";
QueueHandle_t sensor_data_queue = NULL;
/**
 * @brief Read a sequence of bytes from a MPU6050 sensor registers
 */
static esp_err_t mpu6050_register_read(i2c_master_dev_handle_t dev_handle, uint8_t reg_addr, uint8_t *data, size_t len)
{
    return i2c_master_transmit_receive(dev_handle, &reg_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}

/**
 * @brief Write a byte to a MPU6050 sensor register
 */
static esp_err_t mpu6050_register_write_byte(i2c_master_dev_handle_t dev_handle, uint8_t reg_addr, uint8_t data)
{
    uint8_t write_buf[2] = {reg_addr, data};
    return i2c_master_transmit(dev_handle, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}

int16_t MPU6050_Byte_to_HalfWord(uint8_t DataL, uint8_t DataH)
{
    int16_t Data;
    Data = (DataH << 8) | DataL;
    return Data;
}

/**
 * @brief i2c master initialization
 */
esp_err_t i2c_master_init(i2c_master_bus_handle_t *bus_handle, i2c_master_dev_handle_t *dev_handle)
{
    if (*bus_handle != NULL) {
        ESP_LOGW(TAG, "I2C bus already initialized");
        return ESP_OK;
    }
    i2c_master_bus_config_t bus_config = {
        .i2c_port = I2C_MASTER_NUM,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };
    // 初始化 I2C 总线
    //ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, bus_handle));
    esp_err_t ret = i2c_new_master_bus(&bus_config, bus_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize I2C bus");
        return ret;
    }

    i2c_device_config_t dev_config = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address = MPU6050_SENSOR_ADDR,
        .scl_speed_hz = I2C_MASTER_FREQ_HZ,
    };
    //ESP_ERROR_CHECK(i2c_master_bus_add_device(*bus_handle, &dev_config, dev_handle));
    ret = i2c_master_bus_add_device(*bus_handle, &dev_config, dev_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to add I2C device");
        i2c_del_master_bus(*bus_handle); // 清理已分配的资源
        *bus_handle = NULL;
        return ret;
    }

    ESP_LOGI(TAG, "I2C bus and device initialized successfully");
    return ESP_OK;
}

void i2c_master_deinit(i2c_master_bus_handle_t *bus_handle, i2c_master_dev_handle_t *dev_handle)
{
    if (*dev_handle != NULL) {
        ESP_ERROR_CHECK(i2c_master_bus_rm_device(*dev_handle));
        *dev_handle = NULL;
    }

    if (*bus_handle != NULL) {
        ESP_ERROR_CHECK(i2c_del_master_bus(*bus_handle));
        *bus_handle = NULL;
    }
    ESP_LOGI(TAG, "I2C resources released successfully");
}

esp_err_t mpu6050_init(i2c_master_dev_handle_t dev_handle, MPU6050_Info_t *mpu6050_info)
{
    if(mpu6050_info->init_flag)
    {
        ESP_LOGW(TAG,"MPU6050 is already initialized.");
        return ESP_ERR_INVALID_STATE;
    }
    // Wake up the MPU6050 (disable sleep mode)
    ESP_ERROR_CHECK(mpu6050_register_write_byte(dev_handle, MPU6050_PWR_MGMT_1_REG_ADDR, 0x00));

    // Set accelerometer full-scale range (±2g)
    ESP_ERROR_CHECK(mpu6050_register_write_byte(dev_handle, MPU6050_ACCEL_CONFIG, mpu6050_info->accel_fs_sel << 3));

    // Set gyroscope full-scale range (±250dps)
    ESP_ERROR_CHECK(mpu6050_register_write_byte(dev_handle, MPU6050_GYRO_CONFIG, mpu6050_info->gyro_fs_sel << 3));

    // Set DLPF configuration (Low Pass Filter)
    ESP_ERROR_CHECK(mpu6050_register_write_byte(dev_handle, MPU6050_DLPF_CONFIG, mpu6050_info->lpf_cutoff_freq));

    // Set sample rate divider
    ESP_ERROR_CHECK(mpu6050_register_write_byte(dev_handle, MPU6050_SMPLRT_DIV, mpu6050_info->sample_rate));
    mpu6050_info->init_flag = 1;
    return ESP_OK;
}

void mpu6050_task_init(i2c_master_dev_handle_t dev_handle)
{
    uint8_t data_who = 0;
    esp_err_t ret;
    // 创建队列
    sensor_data_queue = xQueueCreate(10, sizeof(SensorData_t));
    if (sensor_data_queue == NULL) {
        ESP_LOGE(TAG, "Failed to create queue");
        return;
    }
    ret = mpu6050_register_read(dev_handle, MPU6050_WHO_AM_I_REG_ADDR, &data_who, 1);
    if (ret != ESP_OK) 
    {
        ESP_LOGE(TAG, "Failed to read WHO_AM_I register: %s", esp_err_to_name(ret));
        return;
    }
    ESP_LOGI(TAG, "WHO_AM_I = %X", data_who);
    if(data_who == 0x68)
    {
        // 启动 MPU6050 任务
        xTaskCreate(mpu6050_task, "mpu6050_task", 4096, (void *)dev_handle, 5, NULL);
    }
}

void MPU6050_GetACCEL(i2c_master_dev_handle_t dev_handle,int16_t *ACCEL_Array)
{
	uint8_t data_accel[6] = {0};
    //ESP_ERROR_CHECK(mpu6050_register_read(dev_handle, MPU6050_ACCEL_XOUT_L, data_accel, 6));
    esp_err_t ret = mpu6050_register_read(dev_handle, MPU6050_ACCEL_XOUT_L, data_accel, 6);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read accelerometer data: %s", esp_err_to_name(ret));
        return;
    }
    ACCEL_Array[0] = MPU6050_Byte_to_HalfWord(data_accel[0], data_accel[1]);
    ACCEL_Array[1] = MPU6050_Byte_to_HalfWord(data_accel[2], data_accel[3]);
    ACCEL_Array[2] = MPU6050_Byte_to_HalfWord(data_accel[4], data_accel[5]);
}

void MPU6050_GetGYRO(i2c_master_dev_handle_t dev_handle,int16_t *GYRO_Array)
{
	uint8_t data_gyro[6] = {0};
    //ESP_ERROR_CHECK(mpu6050_register_read(dev_handle, MPU6050_GYRO_XOUT_L, data_gyro, 6));
    esp_err_t ret = mpu6050_register_read(dev_handle, MPU6050_GYRO_XOUT_L, data_gyro, 6);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read gyroscope data: %s", esp_err_to_name(ret));
        return;
    }
    GYRO_Array[0] = MPU6050_Byte_to_HalfWord(data_gyro[0], data_gyro[1]);
    GYRO_Array[1] = MPU6050_Byte_to_HalfWord(data_gyro[2], data_gyro[3]);
    GYRO_Array[2] = MPU6050_Byte_to_HalfWord(data_gyro[4], data_gyro[5]);
}

void MPU6050_GetAccel_Value(i2c_master_dev_handle_t dev_handle,MPU6050_Info_t *mpu6050_t,float *Accel_Value)
{
	int16_t ACCEL_Array[3] = {0};
	
	MPU6050_GetACCEL(dev_handle,ACCEL_Array);
	
	switch(mpu6050_t->accel_fs_sel)
	{
		case ACCEL_FS_SEL_2G:
			Accel_Value[0] = (float)ACCEL_Array[0] / 16384.0;
			Accel_Value[1] = (float)ACCEL_Array[1] / 16384.0;
			Accel_Value[2] = (float)ACCEL_Array[2] / 16384.0;
			break;

		case ACCEL_FS_SEL_4G:
			Accel_Value[0] = (float)ACCEL_Array[0] / 8192.0;
			Accel_Value[1] = (float)ACCEL_Array[1] / 8192.0;
			Accel_Value[2] = (float)ACCEL_Array[2] / 8192.0;
			break;

		case ACCEL_FS_SEL_8G:
			Accel_Value[0] = (float)ACCEL_Array[0] / 4096.0;
			Accel_Value[1] = (float)ACCEL_Array[1] / 4096.0;
			Accel_Value[2] = (float)ACCEL_Array[2] / 4096.0;
			break;

		case ACCEL_FS_SEL_16G:
			Accel_Value[0] = (float)ACCEL_Array[0] / 2048.0;
			Accel_Value[1] = (float)ACCEL_Array[1] / 2048.0;
			Accel_Value[2] = (float)ACCEL_Array[2] / 2048.0;
			break;

		default:
				break;
	}
}

void MPU6050_GetGyro_Value(i2c_master_dev_handle_t dev_handle,MPU6050_Info_t *mpu6050_t,float *Gyro_Value)
{
	int16_t Gyro_Array[3] = {0};
	
	MPU6050_GetGYRO(dev_handle,Gyro_Array);
	
	switch(mpu6050_t->gyro_fs_sel)
	{
		case GYRO_FS_SEL_250DPS:
			Gyro_Value[0] = (float)Gyro_Array[0] / 131.0;
			Gyro_Value[1] = (float)Gyro_Array[1] / 131.0;
			Gyro_Value[2] = (float)Gyro_Array[2] / 131.0;
			break;

		case GYRO_FS_SEL_500DPS:
			Gyro_Value[0] = (float)Gyro_Array[0] / 65.5;
			Gyro_Value[1] = (float)Gyro_Array[1] / 65.5;
			Gyro_Value[2] = (float)Gyro_Array[2] / 65.5;
			break;

		case GYRO_FS_SEL_1000DPS:
			Gyro_Value[0] = (float)Gyro_Array[0] / 32.8;
			Gyro_Value[1] = (float)Gyro_Array[1] / 32.8;
			Gyro_Value[2] = (float)Gyro_Array[2] / 32.8;
			break;

		case GYRO_FS_SEL_2000DPS:
			Gyro_Value[0] = (float)Gyro_Array[0] / 16.4;
			Gyro_Value[1] = (float)Gyro_Array[1] / 16.4;
			Gyro_Value[2] = (float)Gyro_Array[2] / 16.4;
			break;

		default:
			break;
	}
}

float MPU6050_GetTemp_Value(i2c_master_dev_handle_t dev_handle)
{
	uint8_t data_temp[2] = {0};
    //ESP_ERROR_CHECK(mpu6050_register_read(dev_handle, MPU6050_TEMP_OUT_L, data_temp, 2));
    esp_err_t ret = mpu6050_register_read(dev_handle, MPU6050_TEMP_OUT_L, data_temp, 2);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read temperature data: %s", esp_err_to_name(ret));
    }
    int16_t temp_raw = MPU6050_Byte_to_HalfWord(data_temp[0], data_temp[1]);
	return ((float)temp_raw)/340.0 + 36.53;
}

void mpu6050_task(void *pvParameters)
{
    SensorData_t sensor_data;
    i2c_master_dev_handle_t dev_handle = (i2c_master_dev_handle_t)pvParameters;
    // 初始化 MPU6050
    MPU6050_Info_t mpu6050_info = {
        .lpf_cutoff_freq = LPF_CUTOFF_FREQ_5HZ,
        .sample_rate = SAMPLE_RATE_DIV8,
        .accel_fs_sel = ACCEL_FS_SEL_16G,
        .gyro_fs_sel = GYRO_FS_SEL_2000DPS,
        .init_flag = 0
    };
    ESP_ERROR_CHECK(mpu6050_init(dev_handle, &mpu6050_info));

    for (;;)
    {
        // 读取加速度值
        MPU6050_GetAccel_Value(dev_handle, &mpu6050_info, sensor_data.accel);

        // 读取陀螺仪值
        MPU6050_GetGyro_Value(dev_handle, &mpu6050_info, sensor_data.gyro);

        // 读取温度值
        sensor_data.temp = MPU6050_GetTemp_Value(dev_handle);

        // 将数据发送到队列
        if (sensor_data_queue != NULL) 
        {
            xQueueSend(sensor_data_queue, &sensor_data, portMAX_DELAY);
        }
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
    // // 释放 I2C 资源
    // ESP_ERROR_CHECK(i2c_master_bus_rm_device(dev_handle));
    // ESP_ERROR_CHECK(i2c_del_master_bus(NULL));
    // ESP_LOGI(TAG, "I2C de-initialized successfully");
    // vTaskDelete(NULL);
}

mpu6050.h

#ifndef __MPU6050_H__
#define __MPU6050_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "freertos/queue.h"

#define CONFIG_I2C_MASTER_SCL 4
#define CONFIG_I2C_MASTER_SDA 5
#define CONFIG_I2C_MASTER_FREQUENCY 400000

#define I2C_MASTER_SCL_IO           CONFIG_I2C_MASTER_SCL       /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO           CONFIG_I2C_MASTER_SDA       /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM              I2C_NUM_0                   /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ          CONFIG_I2C_MASTER_FREQUENCY /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE   0                           /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0                           /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS       1000

#define MPU6050_SENSOR_ADDR         0x68        /*!< Address of the MPU6050 sensor */
#define MPU6050_WHO_AM_I_REG_ADDR   0x75        /*!< Register addresses of the "who am I" register */
#define MPU6050_PWR_MGMT_1_REG_ADDR 0x6B        /*!< Register addresses of the power management register 1*/
#define	MPU6050_PWR_MGMT_2_REG_ADDR	0x6C        /*!< Register addresses of the power management register 2*/
#define MPU6050_RESET_BIT           7

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_DLPF_CONFIG		0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C
 
#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40

#define	MPU6050_TEMP_OUT_H		0x41     
#define	MPU6050_TEMP_OUT_L		0x42

#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48


typedef struct 
{
    float accel[3];
    float gyro[3];
    float temp;
} SensorData_t;

extern QueueHandle_t sensor_data_queue;

/*AFS_SEL*/
typedef enum {
    ACCEL_FS_SEL_2G = 0,    // ±2g
    ACCEL_FS_SEL_4G,        // ±4g
    ACCEL_FS_SEL_8G,        // ±8g
    ACCEL_FS_SEL_16G        // ±16g
} MPU6050_ACCEL_FS_SEL_t;

/*FS_SEL*/
typedef enum {
    GYRO_FS_SEL_250DPS = 0,  // ±250dps
    GYRO_FS_SEL_500DPS,      // ±500dps
    GYRO_FS_SEL_1000DPS,     // ±1000dps
    GYRO_FS_SEL_2000DPS      // ±2000dps
} MPU6050_GYRO_FS_SEL_t;

/*DLPF_CFG*/
typedef enum {
    LPF_CUTOFF_FREQ_5HZ = 0,    //截止频率5HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_10HZ,       //截止频率10HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_21HZ,       //截止频率21HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_44HZ,       //截止频率44HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_94HZ,       //截止频率94HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_184HZ,      //截止频率184HZ   初始采样率1KHZ
    LPF_CUTOFF_FREQ_260HZ,      //截止频率260HZ   初始采样率8KHZ
    LPF_CUTOFF_FREQ_3600HZ      //禁用低通滤波器   初始采样率8KHZ
} MPU6050_LPF_CUTOFF_FREQ_t;

/*SMPLRT_DIV*/
typedef enum {
    SAMPLE_RATE_DIV0 = 0,       //0分频
    SAMPLE_RATE_DIV2,           //2分频
    SAMPLE_RATE_DIV4,           //4分频
    SAMPLE_RATE_DIV8,           //8分频
    SAMPLE_RATE_DIV16,          //16分频
    SAMPLE_RATE_DIV32,          //32分频
    SAMPLE_RATE_DIV64,          //64分频
    SAMPLE_RATE_DIV128          //128分频
} MPU6050_SAMPLE_RATE_t;

typedef struct 
{
	i2c_master_bus_handle_t *bus_handle;		// i2c handle
	i2c_master_status_t *i2c_status;			// i2c status
	MPU6050_LPF_CUTOFF_FREQ_t lpf_cutoff_freq;  // DLPF_CFG
	MPU6050_SAMPLE_RATE_t sample_rate;          // SMPLRT_DIV
	MPU6050_ACCEL_FS_SEL_t accel_fs_sel;        // AFS_SEL
	MPU6050_GYRO_FS_SEL_t gyro_fs_sel;          // FS_SEL
	uint8_t init_flag;							// is init?
}MPU6050_Info_t;
esp_err_t i2c_master_init(i2c_master_bus_handle_t *bus_handle, i2c_master_dev_handle_t *dev_handle);
void i2c_master_deinit(i2c_master_bus_handle_t *bus_handle, i2c_master_dev_handle_t *dev_handle);
esp_err_t mpu6050_init(i2c_master_dev_handle_t dev_handle, MPU6050_Info_t *mpu6050_info);
void mpu6050_task_init(i2c_master_dev_handle_t dev_handle);
void mpu6050_task(void *pvParameters);
#endif

然后是gatts_table_creat_demo.h的内容,里面加了几个服务

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> 

/* Attributes State Machine */
enum
{
    IDX_SVC,
    IDX_CHAR_A,
    IDX_CHAR_VAL_A,
    IDX_CHAR_CFG_A,

    IDX_CHAR_LED,
    IDX_CHAR_VAL_LED,

    IDX_CHAR_TEMP,
    IDX_CHAR_VAL_TEMP,
    IDX_CHAR_CFG_TEMP,


    IDX_CHAR_RAND,
    IDX_CHAR_VAL_RAND,
    IDX_CHAR_CFG_RAND,
        
    IDX_CHAR_SENSOR,
    IDX_CHAR_VAL_SENSOR,
    IDX_CHAR_CFG_SENSOR,
    
    IDX_CHAR_B,
    IDX_CHAR_VAL_B,

    IDX_CHAR_C,
    IDX_CHAR_VAL_C,

    HRS_IDX_NB,
};

gatts_table_creat_demo.c用到的头文件如下

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "gatts_table_creat_demo.h"
#include "esp_gatt_common_api.h"
#include "driver/i2c_master.h"

#include "esp_random.h"
#include "esp_mac.h"
#include "led.h"
#include "internal_temperature.h"
#include "mpu6050.h"

调试

调试的时候发现,蓝牙dev数据的时候总是告诉我数据太长了,给我缩到了20byte,但是我看到我代码里面已经设置了mtu size为500了啊… …
sensor data too long
设置mtu size;

/*
设置了MTU的值(经过MTU交换,从而设置一个PDU中最大能够交换的数据量)。
例如:主设备发出一个1000字节的MTU请求,但是从设备回应的MTU是500字节,那么今后双方要以较小的值500字节作为以后的MTU。
即主从双方每次在做数据传输时不超过这个最大数据单元。
*/
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
if (local_mtu_ret)
{
    ESP_LOGE(GATTS_TABLE_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
}

很痛苦,手册上也写了范围是23byte到517byte,到底是为什么呢,难道是没有使能?但是在文档里好像没看到说要使能mtu size config的配置;
文档
后面发现调试助手上可以自己手动修改MTU长度
3
4
心里麻麻的… …
然后去问ai去了:

mtu必须要在蓝牙调试助手(客户端)处修改吗?难道不能在esp32处事先设置mtu的最大值?

ai是这样回复我的:

在 BLE(Bluetooth Low Energy)通信中,MTU(Maximum Transmission
Unit)的协商是由主设备(Central)和从设备(Peripheral)共同完成的。虽然你可以在 ESP32(作为
Peripheral)上设置本地支持的最大 MTU 值,但实际使用的 MTU 值是由主设备发起并协商决定的。因此,ESP32
无法单方面强制使用某个 MTU 值。 以下是详细解释:

  1. MTU 的协商机制 BLE 协议规定,MTU 的协商过程如下: 默认 MTU:在没有进行 MTU 交换时,默认的 MTU 值为 23 字节(包括协议头和有效载荷)。 主设备发起请求:主设备(例如手机或 BLE 调试工具)通过发送 Exchange MTU Request
    消息来请求更大的 MTU 值。 从设备响应:从设备(例如 ESP32)会根据自身支持的最大 MTU 值响应 Exchange MTU
    Response 消息。 最终 MTU 值:最终使用的 MTU 值是双方支持的最小值。
  2. 在 ESP32 上设置本地最大 MTU 尽管主设备发起 MTU 请求,但你可以通过以下方式在 ESP32 上设置本地支持的最大 MTU 值: (1) 使用 esp_ble_gatt_set_local_mtu() 在 ESP-IDF 中,可以调用
    esp_ble_gatt_set_local_mtu() 函数来设置 ESP32 支持的最大 MTU 值。例如:
    esp_ble_gatt_set_local_mtu(517); // 设置本地支持的最大 MTU 值为 517 字节
    这个函数的作用是告诉主设备,ESP32 最多可以支持 517 字节的 MTU。 如果主设备请求的 MTU 值小于或等于 517,则最终使用的
    MTU 值为主设备请求的值。 如果主设备请求的 MTU 值大于 517,则最终使用的 MTU 值为 517。 (2) 监听 MTU 事件 在
    BLE 回调函数中监听 ESP_GATTS_MTU_EVT 事件,确认协商后的 MTU 值:
    case ESP_GATTS_MTU_EVT:
    ESP_LOGI(GATTS_TABLE_TAG, “ESP_GATTS_MTU_EVT, MTU %d”, param->mtu.mtu);
    break;

okay… …那就只能这样了。
这漫长的编译过程… …
5
来看看结果吧,一下log分别提示了广播成功,客户端连接esp32蓝牙成功,修改MTU Size为500,控制板载led灯亮灭、芯片内部温度获取、随机值获取以及mpu6050数据获取;
6
手动修改MTU
7
8
9
10
11
12
最后再看看客户端上的log,前面连接超时是因为手机蓝牙连到了另一个设备,导致esp32一直连不上我的手机;
13
参考链接:
gatt_server_service_table
蓝牙 API
i2c mpu9250
temperature sensor
【完】

相关文章:

  • 2025.04.05 广东汕尾两日游记
  • DNS正反向解析复习,DNS主从服务,转发服务及DNS和nginx联合案例(不断更新)
  • BERT-DDP
  • Google ADK(Agent Development Kit)简要示例说明
  • 凌科加密芯片LKT4304在充电桩上的应用
  • 【LeetCode 题解】算法:36.有效的数独
  • C++20新特性:协程
  • 一文解析2025年移动游戏用户获取策略 | 增长方法论
  • 【CF】Day30——Codeforces Round 824 (Div. 2) C + Codeforces Round 825 (Div. 2) BC1
  • 数字内容体验的核心价值是什么?
  • 【前后端】npm包mysql2的使用__nodejs mysql包升级版
  • 四、TorchRec的推理优化
  • RAG(检索增强生成)系统,提示词(Prompt)表现测试(数据说话)
  • 如何在AMD MI300X 服务器上部署 DeepSeek R1模型?
  • c++关键字new
  • CExercise_07_1指针和数组_2数组元素的逆序数组逆序(指针版 reverse_by_ptr 和下标版 reverse_arr)
  • NVIDIA H100 vs A100:新一代GPU架构性能对比分析
  • 第五章:5.1 ESP32物联网应用 - MQTT协议深度教程
  • 大厂文章阅读
  • 速盾:高防CDN节点对收录有影响吗?
  • 网站的收费窗口怎么做/seo网站搜索优化
  • 社区微网站建设需求分析/便宜的seo网络营销推广
  • 珠海网站建设公司有哪些/互联网营销怎么赚钱
  • 企业网站 ecs 虚拟主机/站长工具推荐
  • 买域名做网站跳转/网站服务器查询
  • 做网站界面的软件/百度竞价排名叫什么