ESP32+idf開發(fā)之藍牙通信入門—ble數(shù)據(jù)收發(fā)(notify)
一、實現(xiàn)功能:
esp32作為藍牙從機,與手機端藍牙調(diào)試助手(如LightBlue)主機進行通信,實現(xiàn)數(shù)據(jù)的收發(fā)功能:
1、收:藍牙調(diào)試助手發(fā)送數(shù)據(jù)控制esp32開發(fā)板led燈的亮滅;
2、發(fā)(notify):esp32將傳感器數(shù)據(jù)(如溫度數(shù)據(jù))主動每隔2s發(fā)送給藍牙調(diào)試助手,實現(xiàn)通知(notify)功能;
二、藍牙BLE概述:
? 1、BLE(低功耗藍牙)采用了client/server (C/S) 架構(gòu)來進行數(shù)據(jù)交互。這里講的C/S架構(gòu)和前面tcp/udp編程時所講的C/S架構(gòu)是相同的。 一般而言藍牙設(shè)備提供服務,因此設(shè)備是server,手機使用設(shè)備提供的服務,因此手機是client。比如藍牙體溫計,它可以提供 “體溫” 數(shù)據(jù)服務,因此是一個server,而手機則可以請求“體溫”數(shù)據(jù)以顯示在手機上,因此手機是一個client。
? 中央設(shè)備(主機)即通信的發(fā)起者,只能與外圍設(shè)備通信;外圍設(shè)備(從機)無法啟動通信,只能與中央設(shè)備通信;同一時間外圍設(shè)備只能與一個中央設(shè)備通信;外圍設(shè)備無法與其他外圍設(shè)備通信。
? 2、藍牙協(xié)議棧:
? profile 可以理解為一種規(guī)范,建立的藍牙應用任務,藍牙任務實際上分為兩類: 標準藍牙任務規(guī)范 profile(公有任務),非標準藍牙任務規(guī)范 profile(私有任務)。標準藍牙任務規(guī)范 profile:指的是從藍牙特別興趣小組 SIG 的官網(wǎng)上已經(jīng)發(fā)布的 GATT 規(guī)范列表,包括警告通知(alert notification),血壓測量(blood pressure),心率(heart rate),電池(battery)等等。它們都是針對具體的低功耗藍牙的應用實例來設(shè)計的。profile的結(jié)構(gòu)如下:
? service即服務:在 BLE 從機中有多個服務,例如:電量信息服務、系統(tǒng)信息服務等;每個 service 中又包含多個 characteristic 特征值;
? characteristic即特征,上面講到: Service通過characteristic對數(shù)據(jù)進行封裝,每個service中又包含多個 characteristic 特征值。一個characteristic包含三種條目:characteristic聲明,characteristic的值以及characteristic的描述符。Characteristic是在GATT規(guī)范中最小的邏輯數(shù)據(jù)單元,由一個Value和多個描述特性的Desciptior組成。實際上,在與藍牙設(shè)備打交道,主要就是讀寫Characteristic的value來完成。
? Attribute 是屬于 ATT屬性層的東西,它是 ATT層 的核心。Attribute其實就是一條一條的數(shù)據(jù)。前面說過,每個藍牙設(shè)備就是用來提供服務的,而服務就是眾多數(shù)據(jù)的合集,這個合集可以稱為數(shù)據(jù)庫,數(shù)據(jù)庫里面每個條目都是一個attribute。Attribute 由以下 4 部分組成:屬性句柄(Attribute Handler)、屬性類型(AttributeType)、屬性值(Attribute Value)、屬性權(quán)限(Attribute Permissions)。
? UUID(universally uniqueidentifier,通用唯一識別碼)是一個軟件構(gòu)建標準,一個合法的UUID,一定是隨機的、全球唯一的。(UUID并不是BLE獨有的概念)上面提到的 service 和 characteristic,都需要一個唯一的uuid來標識。
? GATT (通用屬性配置,Generic Attribute Profile) ,它定義兩個 BLE 設(shè)備通過 Service 和 Characteristic 進行通信;GATT 就是使用了 ATT(Attribute Protocol)協(xié)議,ATT層定義了一個通信的基本框架,數(shù)據(jù)的基本結(jié)構(gòu),以及通信的指令,Service 和 characteristic 就是GATT層定義的, GATT層用來賦予每個數(shù)據(jù)一個具體的內(nèi)涵,讓數(shù)據(jù)變得有結(jié)構(gòu)和意義。一旦兩個設(shè)備建立起了連接,GATT 就開始起作用了,這里需要說明的是,GATT 連接必需先經(jīng)過 GAP 協(xié)議。
? GAP(通用訪問規(guī)范,Generic Access Profile)在用來控制設(shè)備連接和廣播,用于提供藍牙設(shè)備的通用訪問功能,包括設(shè)備發(fā)現(xiàn)、連接、鑒權(quán)、服務發(fā)現(xiàn)等等。GATT是建立連接后通信規(guī)范, 而藍牙是通過GAP建立通信的。GAP 使你的設(shè)備被其他設(shè)備可見,并決定了你的設(shè)備是否可以或者怎樣與合同設(shè)備進行交互。
三、步驟
1、使用模板gatt_server_service_table創(chuàng)建新工程。
2、修改藍牙名并不使用默認廣播數(shù)據(jù):
3、增加屬性表配置特征值用來描述LED和溫度的相關(guān)信息:(可仿照特征A來寫),同時將相關(guān)的宏定義和相關(guān)保存LED燈狀態(tài)和溫度值的變量進行定義完善。
2、獲取得到主機藍牙調(diào)試助手發(fā)來的數(shù)據(jù),并進行判斷,封裝led組件,調(diào)用組件接口驅(qū)動LED亮滅
3、收到藍牙主機調(diào)試助手的notify訂閱后創(chuàng)建任務進行溫度數(shù)據(jù)采集并發(fā)送到主機,此處采用溫度數(shù)據(jù)模擬;收到取消notify后則任務刪除。
四、完整代碼
gatts_table_creat_demo.c:
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/****************************************************************************
*
* This demo showcases creating a GATT database using a predefined attribute table.
* It acts as a GATT server and can send adv data, be connected by client.
* Run the gatt_client demo, the client demo will automatically connect to the gatt_server_service_table demo.
* Client demo will enable GATT server's notify after connection. The two devices will then exchange
* data.
*
****************************************************************************/
#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 "gatts_table_creat_demo.h"
#include "esp_gatt_common_api.h"
#include <led.h>
#define GATTS_TABLE_TAG "GATTS_TABLE_DEMO"
#define PROFILE_NUM 1
#define PROFILE_APP_IDX 0
#define ESP_APP_ID 0x55
#define SAMPLE_DEVICE_NAME "rocket_iot" // 修改藍牙名
#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)
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;
// #define CONFIG_SET_RAW_ADV_DATA
#ifdef CONFIG_SET_RAW_ADV_DATA
static uint8_t raw_adv_data[] = {
/* flags */
0x02, 0x01, 0x06,
/* tx power*/
0x02, 0x0a, 0xeb,
/* service uuid */
0x03, 0x03, 0xFF, 0x00,
/* device name */
0x0f, 0x09, 'E', 'S', 'P', '_', 'G', 'A', 'T', 'T', 'S', '_', 'D', 'E', 'M', 'O'};
static uint8_t raw_scan_rsp_data[] = {
/* flags */
0x02, 0x01, 0x06,
/* tx power */
0x02, 0x0a, 0xeb,
/* service uuid */
0x03, 0x03, 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 */
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;
};
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 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;
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;
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};
/* 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 */
[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}},
};
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event)
{
#ifdef CONFIG_SET_RAW_ADV_DATA
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &= (~ADV_CONFIG_FLAG);
if (adv_config_done == 0)
{
esp_ble_gap_start_advertising(&adv_params);
}
break;
case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
if (adv_config_done == 0)
{
esp_ble_gap_start_advertising(&adv_params);
}
break;
#else
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
adv_config_done &= (~ADV_CONFIG_FLAG);
if (adv_config_done == 0)
{
esp_ble_gap_start_advertising(&adv_params);
}
break;
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
if (adv_config_done == 0)
{
esp_ble_gap_start_advertising(&adv_params);
}
break;
#endif
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
/* advertising start complete event to indicate advertising start successfully or failed */
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS)
{
ESP_LOGE(GATTS_TABLE_TAG, "advertising start failed");
}
else
{
ESP_LOGI(GATTS_TABLE_TAG, "advertising start successfully");
}
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS)
{
ESP_LOGE(GATTS_TABLE_TAG, "Advertising stop failed");
}
else
{
ESP_LOGI(GATTS_TABLE_TAG, "Stop adv successfully\n");
}
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
param->update_conn_params.status,
param->update_conn_params.min_int,
param->update_conn_params.max_int,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
default:
break;
}
}
void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
{
ESP_LOGI(GATTS_TABLE_TAG, "prepare write, handle = %d, value len = %d", param->write.handle, param->write.len);
esp_gatt_status_t status = ESP_GATT_OK;
if (prepare_write_env->prepare_buf == NULL)
{
prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
prepare_write_env->prepare_len = 0;
if (prepare_write_env->prepare_buf == NULL)
{
ESP_LOGE(GATTS_TABLE_TAG, "%s, Gatt_server prep no mem", __func__);
status = ESP_GATT_NO_RESOURCES;
}
}
else
{
if (param->write.offset > PREPARE_BUF_MAX_SIZE)
{
status = ESP_GATT_INVALID_OFFSET;
}
else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE)
{
status = ESP_GATT_INVALID_ATTR_LEN;
}
}
/*send response when param->write.need_rsp is true */
if (param->write.need_rsp)
{
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
if (gatt_rsp != NULL)
{
gatt_rsp->attr_value.len = param->write.len;
gatt_rsp->attr_value.handle = param->write.handle;
gatt_rsp->attr_value.offset = param->write.offset;
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
if (response_err != ESP_OK)
{
ESP_LOGE(GATTS_TABLE_TAG, "Send response error");
}
free(gatt_rsp);
}
else
{
ESP_LOGE(GATTS_TABLE_TAG, "%s, malloc failed", __func__);
}
}
if (status != ESP_GATT_OK)
{
return;
}
memcpy(prepare_write_env->prepare_buf + param->write.offset,
param->write.value,
param->write.len);
prepare_write_env->prepare_len += param->write.len;
}
void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
{
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf)
{
esp_log_buffer_hex(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len);
}
else
{
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATT_PREP_WRITE_CANCEL");
}
if (prepare_write_env->prepare_buf)
{
free(prepare_write_env->prepare_buf);
prepare_write_env->prepare_buf = NULL;
}
prepare_write_env->prepare_len = 0;
}
TaskHandle_t *pTask = NULL;
volatile bool notify_flag = false;
static void get_temp(void *arg)
{
while (1)
{
if (notify_flag == true)
{
uint8_t temp = 20 + rand() % 11;
esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_TEMP], 1, &temp);
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, false);
}else{
vTaskDelete(NULL);
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
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)
{
switch (event)
{
case ESP_GATTS_REG_EVT:
{
esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
if (set_dev_name_ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "set device name failed, error code = %x", set_dev_name_ret);
}
#ifdef CONFIG_SET_RAW_ADV_DATA
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
if (raw_adv_ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "config raw adv data failed, error code = %x ", raw_adv_ret);
}
adv_config_done |= ADV_CONFIG_FLAG;
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data));
if (raw_scan_ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "config raw scan rsp data failed, error code = %x", raw_scan_ret);
}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#else
// config adv data
esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
}
adv_config_done |= ADV_CONFIG_FLAG;
// config scan response data
ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
if (ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "config scan response data failed, error code = %x", ret);
}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#endif
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);
if (create_attr_ret)
{
ESP_LOGE(GATTS_TABLE_TAG, "create attr table failed, error code = %x", create_attr_ret);
}
}
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT");
break;
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(); // 收到數(shù)據(jù)0x00關(guān)燈
}
else if (param->write.value[0] == 0x01)
{
led_on(); // 收到數(shù)據(jù)0x01開燈
}
}
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, "notify enable");
xTaskCreate(get_temp, "get temp", 8192, NULL, 10, pTask);
notify_flag=true;
// ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
// uint8_t notify_data[15];
// for (int i = 0; i < sizeof(notify_data); ++i)
// {
// notify_data[i] = i % 0xff;
// }
// // the size of notify_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(notify_data), notify_data, false);
}
else if (descr_value == 0x0002)
{
ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i % 0xff;
}
// 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, "notify/indicate disable ");
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;
case ESP_GATTS_EXEC_WRITE_EVT:
// the length of gattc prepare write data must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT");
example_exec_write_event_env(&prepare_write_env, param);
break;
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONF_EVT, status = %d, attr_handle %d", param->conf.status, param->conf.handle);
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
break;
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;
// 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);
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GATTS_CREAT_ATTR_TAB_EVT:
{
if (param->add_attr_tab.status != ESP_GATT_OK)
{
ESP_LOGE(GATTS_TABLE_TAG, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
}
else if (param->add_attr_tab.num_handle != HRS_IDX_NB)
{
ESP_LOGE(GATTS_TABLE_TAG, "create attribute table abnormally, num_handle (%d) \
doesn't equal to HRS_IDX_NB(%d)",
param->add_attr_tab.num_handle, HRS_IDX_NB);
}
else
{
ESP_LOGI(GATTS_TABLE_TAG, "create attribute table successfully, the number handle = %d\n", param->add_attr_tab.num_handle);
memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table));
esp_ble_gatts_start_service(heart_rate_handle_table[IDX_SVC]);
}
break;
}
case ESP_GATTS_STOP_EVT:
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
case ESP_GATTS_UNREG_EVT:
case ESP_GATTS_DELETE_EVT:
default:
break;
}
}
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
/* If event is register event, store the gatts_if for each profile */
if (event == ESP_GATTS_REG_EVT)
{
if (param->reg.status == ESP_GATT_OK)
{
heart_rate_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if;
}
else
{
ESP_LOGE(GATTS_TABLE_TAG, "reg app failed, app_id %04x, status %d",
param->reg.app_id,
param->reg.status);
return;
}
}
do
{
int idx;
for (idx = 0; idx < PROFILE_NUM; idx++)
{
/* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile_tab[idx].gatts_if)
{
if (heart_rate_profile_tab[idx].gatts_cb)
{
heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
}
}
}
} while (0);
}
void app_main(void)
{
esp_err_t ret;
led_init();
/* 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;
}
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;
}
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;
}
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;
}
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相關(guān)代碼有:led.c
#include <stdio.h>
#include "led.h"
#include<driver/gpio.h>
void led_init(){
gpio_config_t cfg={
.pin_bit_mask=1<<LED_PIN,
.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
#ifndef __LED_H__
#define __LED_H__
#define LED_PIN GPIO_NUM_2
void led_init();
void led_on();
void led_off();
#endif
五、測試
1、控制led燈測試,默認不亮,藍牙調(diào)試APP發(fā)送0x01,esp燈亮;發(fā)送0x00,led燈滅。
2、調(diào)試助手APP點擊notify開關(guān)監(jiān)聽ESP32發(fā)來的溫度數(shù)據(jù)并16進制顯示,關(guān)閉后則停止,同時esp32終端打印相關(guān)信息。
文章來源:http://www.zghlxwxcb.cn/news/detail-480313.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-480313.html
到了這里,關(guān)于ESP32+idf開發(fā)—藍牙通信入門之ble數(shù)據(jù)收發(fā)(notify)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!