ESP32-C3 学习测试 蓝牙 篇(五、添加 characteristic)

前面的蓝牙部分都是基础说明,示例分析,本文就来改他一改

前言

又隔了一段时间没接着玩 ESP32-C3 ,又把自己的蓝牙文章重新看了一遍才可以继续进行……

嗯…… 看完忽然想了好多问题,尤其是针对上一篇文章的示例,在想到蓝牙初始化,通讯的时候,那些流程细节,虽然知道基本的步骤,但是每一个本质上是进行了哪些细节操作还是完全没头绪啊,有时候使用这种完全不知道本质的代码,感觉心慌慌,嗯……

算了,先不烦这些,认清目标:

我们不是来研究蓝牙协议的,我们是来使用 ESP32-C3 跑蓝牙协议,用来 数据传输通讯的。我们要会用 ESP—IDF 提供的示例框架,根据自己的实际需求修改代码,这就是我们最终的目的 !(熟悉蓝牙协议栈的朋友就随意了 (灬ꈍ ꈍ灬))

最终我们要实现一个小示例,通过蓝牙通讯,把传感器的数据发送给手机,然后手机也可以控制开发板上的LED灯。

我们在上文说过蓝牙应用程序开发,就是开发 service 和 characteristic。通过API,添加自己需要的characteristic 和 service。

所以今天,我们势必得在示例基础上动一动,就先在现有 Service 的基础上,添加一个额外的characteristic。

ESP32-C3 入门专栏目录【导航】ESP32-C3 入门教程目录 【快速跳转】
❤️
ESP32-C3学习 蓝牙 篇系列博文连接:

❤️
测试使用的开发板:
自己画一块ESP32-C3 的开发板(第一次使用立创EDA)(PCB到手)

❤️
测试使用的开发环境:
ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

❤️
蓝牙篇系列相关博文:
ESP32-C3 学习测试 蓝牙 篇(一、认识 ESP-IDF 的蓝牙框架、简单的了解蓝牙协议栈)
ESP32-C3 学习测试 蓝牙 篇(二、蓝牙调试APP、开发板手机连接初体验)
ESP32-C3 学习测试 蓝牙 篇(三、认识蓝牙 GATT 协议)
ESP32-C3 学习测试 蓝牙 篇(四、GATT Server 示例解析)


一、示例更换说明

在我们前面几篇文章,都是从 GATT Server 示例说起的,本来也是计划从 GATT Server 示例来做以后测试的修改框架。

但是自己想增加一个 characteristic 的时候,发现无从下手。正好在上一篇文章《ESP32-C3 学习测试 蓝牙 篇(四、GATT Server 示例解析)》后又小伙伴留言问过,官方是不是推推荐 gatt_server_service_table ?

gatt_server_service_table

所以还是回头看了自己蓝牙篇的第一篇文章,看了下这两个的比较(当时属于概括,也没有什么特别说明):

在这里插入图片描述

于是乎还是得找到官方自己的示例说明,仔细看了一下示例说明,官方对 GATT Server 示例其实有说明,推荐了 gatt_server_service_table 示例,如下图:

在这里插入图片描述

在乐鑫官方论坛我也找到过类似的问题,其中有说明 GATT Server 是一种根据事件串行创建服务、特征值和描述符的,创建多服务或者特征值时不太方便,如下图:

在这里插入图片描述.

所以我们需要使用示例 gatt_server_service_table 作为我们的测试修改的示例,我们正常的新建一个示例,先简单看看示例框架:

/*
	gatt_server_service_table  示例初始化
	gatts_table_creat_demo.c
*/
void app_main(void)
{
    esp_err_t ret;
    /* Initialize NVS. */
    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_enable(ESP_BT_MODE_BLE);
    ret = esp_bluedroid_init();
    ret = esp_bluedroid_enable();
    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    ret = esp_ble_gap_register_callback(gap_event_handler);
    ret = esp_ble_gatts_app_register(ESP_APP_ID);
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
    }
}

初始化流程是和上一篇文章分析的 GATT Server 示例是一样的, 所以这里我们就不再次分析了。

❤️ 在ESP-IDF 中 GATT Server 的示例中,官方提供了一个 方便添加修改特征值和服务的示例 gatt_server_service_table 以供开发者使用。

.

二、示例测试

新的例程,不管三七二十一,先跑起来看看,程序正常便宜烧录,然后通过手机蓝牙助手可以看到设备:

在这里插入图片描述

我们前面已经有几篇文章的基础,所以本文不用讲太多理论,我们直接用图文说明几个地方。

2.1 广播包

Server 设备还没有连接的时候,会一直发送广播包,我们在手机端可以点击设备查看广播包等信息。

在 gatt_server_service_table 示例程序中广播包的定义用数组raw_adv_data表示,如下图:

gatt_server_service_table示例

这里在额外放一下 GATT Server 示例 的广播包,除了前面3个字节,后面都对不上,这里暂时我也不明白 = =!

GATT Server示例

2.1.1 设备名称

对于我们上一篇文章说明的 GATT Server 示例 ,我们直接修改宏定义就可以改变设备名称:

在这里插入图片描述

但是对于本文 gatt_server_service_table 示例来说,修改宏定义并不能改变设备名称:

在这里插入图片描述

说明: 这里我们是看表象,或许本质上,只是个关联设置问题,但是我们现在目的是快速使用,里面的本质这些我们暂时不考虑。

在广播包中会包含一些设置好的信息,其中就有设备名称,在我们的 gatt_server_service_table 示例中,可以看到广播包中有关于设备名称的部分:

在这里插入图片描述

所以,如果我们需要修改设备名称,我们直接在这个数组里面修改,但是不要忘了长度,修改示例如下:

在这里插入图片描述

2.2 服务与特征值

我们点击连接设备,可以看到 gatt_server_service_table 示例中的 Service 与 characteristic ,如下图:

在这里插入图片描述

对于这些不同的 characteristic ,我结合程序做了个示意图说明:

在这里插入图片描述

.

2.3 读写操作

读写操作在上一篇文章中,我们已经测试过了,换了一个示例,其实读写的简单测试还是一样的,这里针对几个地方再分别介绍一下。

2.3.1 通知 notify

通知,也算是一种可传递的消息,在我们介绍 GATT 协议的时候讲过,如果 characteristic 具有 notify 或 indicate 操作功能时,必须为其添加响应的CCCD(characteristic descriptor),这一点在程序中创建 characteristic 的时候就有体现:

在这里插入图片描述

对于通知的发送,是通过esp_ble_gatts_send_indicate 函数发送,如下图:

在这里插入图片描述

我们可以修改一下通知的值,测试一下看看:

在这里插入图片描述

接收不全? 看了下输出日志,应该是太长了,提示最大应该是20字节:

在这里插入图片描述

再改一次,这次OK:

在这里插入图片描述

.

这里还有一个地方需要说明,esp_ble_gatts_send_indicate 函数的最后一个参数,当为 false 时候,不需要得到回复,为 true 时,需要得到回复。

在这里插入图片描述

2.3.2 值 value

每一个 characteristic 都有自己的 value ,对于示例来,与 characteristic 的 value 关联的地方如下图:

在这里插入图片描述

既然知道了位置,那么和通知一样,我们简单修改测试一下:

在这里插入图片描述
.

❤️ 这里说明一下,在上面通知部分,我们看到测试示例的日志输出的提示为 attribute value too long, 我们以前说过 characteristic 的条目的实现都是通过 attribute 来表达的,所以对于notify 的最大长度为 20, 对于 characteristic value 来说也是一样的,最大长度为20。

上面的示例修改,如果数据长度超过20会有问题,手机端读取不到。

这个还有个小疑问, value 的长度大小可不可以自行设定?哪里设定?

这个问题后面如果有需要用到或者学到了再来解答,暂时先提出来。

三、示例修改(添加一个characteristic)

经过上面的基础测试,我们开始在示例结构上添加一个新的 characteristic 。

这里的步骤我就直接使用图文说明,我会把添加的文件名称,代码位置都截图出来:

3.1 UUID 定义

在这里插入图片描述

3.2 枚举 定义

在这里插入图片描述

3.3 添加属性表

在这里插入图片描述
.

完成编译,忽然发现好简单= =!

通过手机查看一下效果:

在这里插入图片描述

测试过后,读写操作都正常。

最后还是得感叹一下,使用 gatt_server_service_table 添加 characteristic 真的是太方便了。

结语

本文我们更换了一下蓝牙 GATT 学习的示例程序,通过进一步的的程序测试,让我们更加的熟悉了 ESP-IDF 提供的示例框架,也知道了如何添加 characteristic。

当然这只算是我们修改的开始,我们接下来要在此基础上学会添加 Service,通过我们自己添加的 Service和 characteristic 传输我们自己的应用数据,
然后把以前传感器驱动整合进工程,最后实现我们小目标:通过蓝牙通讯,把传感器的数据发送给手机,然后手机也可以控制开发板上的LED灯等设备。

路要一步一步走,饭要一口一口吃,本文就到这把,谢谢!

  • 30
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
ESP32蓝牙启动流程如下: 1. 初始化蓝牙协议栈 2. 配置蓝牙参数,如设备名称、服务UUID等 3. 注册蓝牙事件回调函数 4. 启动蓝牙广播 5. 开启蓝牙可见性 6. 等待连接 以下是一个简单的ESP32蓝牙串口调试的示例: ```C #include "esp_bt.h" #include "esp_bt_main.h" #include "esp_gap_ble_api.h" #define GATTS_TAG "GATTS_DEMO" #define TEST_DEVICE_NAME "ESP32_BLE_UART" #define TEST_MANUFACTURER_DATA_LEN 17 /* The max length of characteristic value. When the gatt client write or prepare write, * the data length must be less than MAX_VALUE_LENGTH. */ #define MAX_VALUE_LENGTH 500 /* Declare global variable */ static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x4c, 0x00, 0x02, 0x15, 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4, 0xA1, 0x2F, 0x17, 0xD1, 0xAD}; static uint8_t test_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value 0x13, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, //second uuid, 32bit, [12], [13], [14], [15] is the value 0x14, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, }; static uint8_t test_service_uuid[16] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value 0x01, 0x2B, //second uuid, 32bit, [12], [13], [14], [15] is the value 0x01, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, }; static uint8_t test_char_uuid[16] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value 0x02, 0x2B, //second uuid, 32bit, [12], [13], [14], [15] is the value 0x02, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, }; static esp_gatt_char_prop_t test_property = 0; static uint8_t char1_str[] = {0x11,0x22,0x33}; static esp_attr_value_t gatts_demo_char1_val = { .attr_max_len = MAX_VALUE_LENGTH, .attr_len = sizeof(char1_str), .attr_value = char1_str, }; static uint16_t gatts_demo_handle_table[3]; /* 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(test_service_uuid), sizeof(test_service_uuid), (uint8_t *)&test_service_uuid}, ESP_GATT_UUID_PRI_SERVICE, ESP_GATT_PERM_READ, sizeof(test_service_uuid), sizeof(test_service_uuid), (uint8_t *)&test_service_uuid, 0 }, /* Characteristic Declaration */ [IDX_CHAR_READ] = { {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 *)&test_property}, ESP_GATT_UUID_CHAR_DECLARE, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&test_property, 0 }, /* Characteristic Value */ [IDX_CHAR_VAL_READ] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_128, (uint8_t *)&test_char_uuid, ESP_GATT_PERM_READ, MAX_VALUE_LENGTH, sizeof(gatts_demo_char1_val), gatts_demo_char1_val.attr_value}, ESP_UUID_LEN_128, ESP_GATT_PERM_READ, MAX_VALUE_LENGTH, sizeof(gatts_demo_char1_val), gatts_demo_char1_val.attr_value, 0 }, }; static esp_ble_adv_data_t adv_data = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0x20, .max_interval = 0x40, .appearance = 0x00, .manufacturer_len = TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = test_manufacturer, .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = sizeof(test_service_uuid), .p_service_uuid = test_service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; 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, .peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, .peer_addr_type = BLE_ADDR_TYPE_PUBLIC, .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: esp_ble_gap_start_advertising(&adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(GATTS_TAG, "advertising start failed"); } break; 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) { switch (event) { case ESP_GATTS_REG_EVT: esp_ble_gap_set_device_name(TEST_DEVICE_NAME); esp_ble_gap_config_adv_data(&adv_data); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: if (param->add_attr_tab.status != ESP_GATT_OK){ ESP_LOGE(GATTS_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_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_TAG, "create attribute table successfully, the number handle = %d\n",param->add_attr_tab.num_handle); memcpy(gatts_demo_handle_table, param->add_attr_tab.handles, sizeof(gatts_demo_handle_table)); esp_ble_gatts_start_service(gatts_demo_handle_table[IDX_SVC]); } break; case ESP_GATTS_CONNECT_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT"); break; case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT"); esp_ble_gap_start_advertising(&adv_params); break; case ESP_GATTS_WRITE_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_WRITE_EVT"); break; case ESP_GATTS_MTU_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu); break; case ESP_GATTS_CONF_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT"); break; case ESP_GATTS_EXEC_WRITE_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_EXEC_WRITE_EVT"); break; case ESP_GATTS_START_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_START_EVT"); break; case ESP_GATTS_STOP_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_STOP_EVT"); break; case ESP_GATTS_OPEN_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_OPEN_EVT"); break; case ESP_GATTS_CANCEL_OPEN_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_CANCEL_OPEN_EVT"); break; case ESP_GATTS_CLOSE_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_CLOSE_EVT"); break; case ESP_GATTS_LISTEN_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_LISTEN_EVT"); break; case ESP_GATTS_CONGEST_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONGEST_EVT"); break; case ESP_GATTS_UNREG_EVT: case ESP_GATTS_DELETE_EVT: default: break; } } void app_main() { esp_err_t ret; ESP_LOGI(GATTS_TAG, "ESP_BLUETOOTH_BLE example started."); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ret = esp_bt_controller_init(&bt_cfg); if (ret) { ESP_LOGE(GATTS_TAG, "%s initialize controller failed\n", __func__); return; } ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); if (ret) { ESP_LOGE(GATTS_TAG, "%s enable controller failed\n", __func__); return; } ret = esp_bluedroid_init(); if (ret) { ESP_LOGE(GATTS_TAG, "%s init bluetooth failed\n", __func__); return; } ret = esp_bluedroid_enable(); if (ret) { ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed\n", __func__); return; } ret = esp_ble_gatts_register_callback(gatts_event_handler); if (ret){ ESP_LOGE(GATTS_TAG, "gatts register error, error code = %x", ret); return; } ret = esp_ble_gap_register_callback(gap_event_handler); if (ret){ ESP_LOGE(GATTS_TAG, "gap register error, error code = %x", ret); return; } ret = esp_ble_gatts_app_register(ESP_APP_ID); if (ret){ ESP_LOGE(GATTS_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_TAG, "set local MTU failed, error code = %x", local_mtu_ret); } ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, ESP_APP_ID); if (ret){ ESP_LOGE(GATTS_TAG, "create attr table failed, error code = %x", ret); } } ``` 在这个示例中,我们使用了ESP-IDF提供的蓝牙协议栈和GATT Server框架,实现了一个简单的GATT Server,并且开启了蓝牙广播和可见性,使得其他蓝牙设备可以扫描到并连接我们的设备。在连接建立后,我们可以向GATT Server中的特定Characteristic写入数据,也可以从特定Characteristic读取数据。
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

矜辰所致

您们的鼓励是我奋斗的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值