ESP32-C3 学习测试 蓝牙 篇(七、GATT 数据通信 — 发送自定义数据)

前面我们已经入门了 GATT 的开发,更进一步,进行想要的数据通信 。

前言

本来计划直接做一个蓝牙的小应用,首先得实现一下自己想要数据的传输,虽然我们前面已经测试过示例的读写了,但是还是发现一些问题,如何传输自己想要的数据呢?

网上实在是没有现成的示例,博主只能自己一遍一遍测试,失败又失败,修改再修改,最后画了大半天时间,总算是搞好了。

本文我们来实现一下 GATT 的通讯,文中并不涉及蓝牙理论的专业各种分析解释,博主也是蓝牙初学者,
只通过查看示例,查看源码进行的修改测试,结果是成功,能够实现自己传输自己想要的传感器数据。

对于理解蓝牙协议的伙伴来说或许觉得太简单,因为没有太深入的理论支持,博主的过程可能会走了弯路,还望理解。

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 示例解析)
ESP32-C3 学习测试 蓝牙 篇(五、添加 characteristic)
ESP32-C3 学习测试 蓝牙 篇(六、添加 Service)


1、通信问题思考

只是按照自己实际的现象说明思考,只代表个人思路,不一定是正确的,所以仅供参考。

我们目的是想用一个 characteristic 传输温湿度数据。首先能确保手机端能够接受到自己每次不同的想要发送的数据。

在我们前面文章测试的过程中,虽然能够正常读取到数据,也能正常的写数据,看似按照自己定义的数据传输,但是存在一个问题,就是发送数据以后,读取到的数据会变成发送的数据:

在这里插入图片描述

回头看了一下示例中的,READ事件:

在这里插入图片描述

看到 gatt_server_service_table 示例中的 READ事件,啥也没做啊?这下疑问更多了,啥也没做手机怎么能收到数据?不是好歹得有个发送数据的函数?怎么才能传输不同的数据呢?

想起来我们曾经在 gatt_server 示例中分析过,想要发送什么数据在这个事件中写就可以了:

在这里插入图片描述
.

2、 如何才能每次传输不同的数据

看了一下代码,新建了一个变量 rsp, 函数esp_ble_gatts_send_response也是有的,所以这个代码我们直接复制过来是没问题的,我们来测试一下,直接复制过来看一下是不是就固定了? 于是乎我们改了下代码,如下图:

在这里插入图片描述

这样我们读数据,是不是每次都可以读到上面写的,然后设置一个变量,每次修改,就可以读到不同的数据了?

程序修改编译没有问题,下载测试,还是老样子,没有改变,测试图就不重复放了,现象就是发送以后读取到的数据是自己发送的。

需要注意一下 LOG 输出的消息:

在这里插入图片描述

第一次尝试失败!

琢磨了一阵子,考虑了一下想到了: 我想要传输数据,我应该使用一个单独的只读的 characteristic 用于数据传输,这样就不会被写的数据打扰了(在这个示例框架中是这样),对!

于是我不测试可读可写的 characteristic ,用示例中的只读 characteristic 测试:

在这里插入图片描述

只读的 characteristic 思路是对的,但是使用 gatt_server 示例中的代码还是不行,读到的只是初始化中的数据,这个数据是在初始化决定的,好像不太好改啊,怎么才能修改这个数据呢。

然后我又注意到,通知部分是临时定义的数组决定的值,不会因为写数据改变,然后想到通知部分的处理方法:

在这里插入图片描述

我可不可以用通知这个地方的函数发送过去?不管行不行试一试再说!

修改代码:

在这里插入图片描述

这里还是编译正常,在说明测试结果之前,要说一下对 handle 的认识理解。

3、 对 handle 的认识

对于蓝牙的应用测试,我们期初只介绍了基本概念在对于 ESP-IDF的示例我们分析也只是了解框架,设计离线和思路,函数意义我们也没有深入讲解,在本次测试过程中,因为测试这个问题,让我加深了某一小部分问题的理解,比如 handle 这个东西。

因为 WRITE 事件中有一句判断:

if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->write.handle && param->write.len == 2)

我要把这个用到 READ 事件中,所以看了看这部分代码,以前忽略的这些东西,稍微理解了一些。

对于 handle 来说,如下图的总结:
在这里插入图片描述

attribute 我们前面说过,characteristic 每个值都是用 attribute 表达的:

在这里插入图片描述

结合上面,对于我们示例来说,对每个 characteristic 的管理都是通过这个 handle 来实现的,而这个 handle 在我们示例中的对应关系为:

在这里插入图片描述

上面的是枚举中,是不算 HRS_IDX_NB 还是不算 IDX_SVC 不太清楚,图中这样不算 HRS_IDX_NB 从 0到 9 开始标记10个handle, 如果是不算 IDX_SVC , 从 1到 10开始标记也是 10 个handle 。

只需要记住的是 对 value 操作的值对应的 handle 即可,不管哪种方式,characteristic value 的 handle 值都不变,所以我们完全可以确定自己要操作的是哪个 characteristic 的哪个属性,当然一般都是对 value 的操作。

4、继续尝试

完成上面分析,我们修改一下代码(上面贴出的代码中,忘了把 param->write.handle 改成 param->read.handle),我们要操作的是 handle 为 45 的 B:

在这里插入图片描述

作为测试还加了一句:

在这里插入图片描述

编译正常,烧录测试一下:

在这里插入图片描述

还是有问题……

这样看来,使用示例中几个简单看上去能够发送的函数都不行,怎么办呢?

后来一下子也不知道怎么办才好,去官方论坛搜索相关问题,无果,不能吃现成的= =!

没办法,于是到官网去搜索 gatt 相关的东西,虽然我也不知道要怎么找,翻看官网的函数 API ,感觉漫无目的的往下看,忽然看到一个函数,眼前一亮:

在这里插入图片描述

5、测试成功

在官网看到的函数名称一看就知道什么东西,设置 characteristic 的值?
… …

在这里插入图片描述

… …

简直就是醍醐灌顶,茅塞顿开:

读写得写,不就是对读 characteristic 的 value,写 characteristic 的 value ,初始化的时候给了 characteristic 一个 value,我们可以正常读到,如果写了 characteristic ,就是改变了 value 的值,那么读到的数据当然是自己写的数据,我们想要读自己的数据,就改变 characteristic 值就可以了!!!

于是立马看了看整函数,纳尼……………… 这么巧 (还是自己太蠢了……)?

我们前面2中方式测试的函数就在这个函数上面:

在这里插入图片描述
.

速度搞起! 这把一定行,立马修改测试起来:

在这里插入图片描述

测试结果:

在这里插入图片描述

先开心一下,上结果:

在这里插入图片描述

改过来了!改过来了!

但是这里注意一个细节,第一次读取 还是原始的值,从第二次才改过来,说明一个问题,就是读取的时候立马就取了当时 characteristic 的 value。

这个 ESP_GATTS_READ_EVT 事件应该是读取完毕才会触发 。

这就说明,如果我们想要读取实时数据,不能在这个这个时间触发后去修改数据,而应该在另外的地方把实时数据 更新到 characteristic 的 value ,等到客户端想要读取数据的时候,就是实时数据。

结语

本文其实算下来也算简单,主要要记住两个点:

❤️

每次的读取都是读取对应 characteristic 的 value,使用esp_ble_gatts_set_attr_value函数可以修改 characteristic 的 value 值。

ESP_GATTS_READ_EVT 事件是读取成功才会触发的。

❤️

好了,本文也算有一定收获,至少知道了如何想要发送自定义数据,有了今天的测试基础,我们下一篇文章就直接来更新使用 ESP32-C3 做蓝牙小应用了,小激动!

本文就到这里,谢谢大家!

  • 37
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 36
    评论
您好!您的问题是关于ESP32蓝牙发送和读取数据的。下面是简要的回答: 要在ESP32上使用蓝牙通信,您需要使用ESP-IDF中的蓝牙库。以下是一个简单的代码示例,演示如何在ESP32上使用蓝牙发送和接收数据: ```c #include "esp_bt.h" #include "esp_bt_main.h" #include "esp_gap_ble_api.h" #define GATTS_TAG "GATTS_DEMO" #define PROFILE_NUM 1 #define PROFILE_A_APP_ID 0 #define ESP_APP_ID 0x55 #define SAMPLE_DEVICE_NAME "ESP_GATTS_DEMO" static uint8_t adv_config_done = 0; static esp_ble_adv_params_t adv_params = { .adv_int_min = 0x20, .adv_int_max = 0x40, .adv_type = ADV_TYPE_NONCONN_IND, .own_addr_type = BLE_ADDR_TYPE_PUBLIC, .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; static esp_ble_adv_data_t adv_data = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0, .max_interval = 0, .appearance = 0, .manufacturer_len = 0, .p_manufacturer_data = NULL, .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = 0, .p_service_uuid = NULL, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_gatts_cb_t gatts_cb; 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: adv_config_done &= (~ADV_CONFIG_FLAG); if (adv_config_done == 0) { 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\n"); } break; case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(GATTS_TAG, "Advertising stop failed\n"); } else { ESP_LOGI(GATTS_TAG, "Stop adv successfully\n"); } break; case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: ESP_LOGI(GATTS_TAG, "Update connetion params status = %d\n", param->update_conn_params.status); 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_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); if (set_dev_name_ret) { ESP_LOGE(GATTS_TAG, "set device name failed, error code = %x", set_dev_name_ret); } esp_err_t create_adv_config_ret = esp_ble_gap_config_adv_data(&adv_data); if (create_adv_config_ret) { ESP_LOGE(GATTS_TAG, "config adv data failed, error code = %x", create_adv_config_ret); } adv_config_done |= ADV_CONFIG_FLAG; esp_err_t start_adv_ret = esp_ble_gap_start_advertising(&adv_params); if (start_adv_ret) { ESP_LOGE(GATTS_TAG, "start adv failed, error code = %x", start_adv_ret); } 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"); break; case ESP_GATTS_READ_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_READ_EVT"); break; case ESP_GATTS_WRITE_EVT: ESP_LOGI(GATTS_TAG, "ESP_GATTS_WRITE_EVT"); esp_gatt_status_t status = ESP_GATT_OK; esp_gatt_rsp_t rsp = {0}; rsp.attr_value.handle = param->write.handle; rsp.attr_value.len = param->write.len; rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; memcpy(rsp.attr_value.value, param->write.value, param->write.len); esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, &rsp); break; default: break; } } void app_main() { esp_err_t ret; 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: %s\n", __func__, esp_err_to_name(ret)); return; } ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (ret) { ESP_LOGE(GATTS_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); return; } ret = esp_bluedroid_init(); if (ret) { ESP_LOGE(GATTS_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret)); return; } ret = esp_bluedroid_enable(); if (ret) { ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret)); return; } esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); esp_ble_gatts_app_register(PROFILE_A_APP_ID); } ``` 在这个示例中,我们注册了一个`gatts_event_handler()`,它会处理所有GATT服务器事件。在这个函数中,我们检测到`ESP_GATTS_WRITE_EVT`事件,它发生在远程GATT客户端写入数据时。我们在这里处理接收到的数据,并将其发送回客户端。 要发送数据,您可以使用`esp_ble_gatts_send_indicate()`函数。以下是一个简单的示例,演示如何在ESP32发送数据: ```c esp_gatt_status_t status = ESP_GATT_OK; esp_ble_gatts_send_indicate(gatts_if, conn_id, attr_handle, data_len, data, false); ``` 在这个示例中,我们使用`esp_ble_gatts_send_indicate()`函数发送数据。该函数需要以下参数: - `gatts_if`:GATT服务器接口 - `conn_id`:连接标识符 - `attr_handle`:属性句柄 - `data_len`:要发送数据长度 - `data`:要发送数据 - `need_confirm`:是否需要确认 希望这些代码对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

矜辰所致

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

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

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

打赏作者

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

抵扣说明:

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

余额充值