写点什么

ESP32-C3 应用 篇(实例二、通过蓝牙将传感器数据发送给手机,手机端控制 SK6812 LED)

作者:矜辰所致
  • 2022 年 10 月 06 日
    江苏
  • 本文字数:6217 字

    阅读完需:约 20 分钟

ESP32-C3 应用 篇(实例二、通过蓝牙将传感器数据发送给手机,手机端控制 SK6812 LED)
ESP32-C3 蓝牙部分我们学习了GATT,本文尝试使用蓝牙做一个简单的小应用。
复制代码


前言

前面文章说过,蓝牙协议博主了解不是很深入,只进行一些基础的了解,示例的测试,和初学者一样,基本上蓝牙专栏系列博文都是一步一步摸索过来的,功夫不负有心人,到目前为止,多多少少对蓝牙 GATT 有了一定的认识。


那么我们今天就要学以致用,使用 ESP32-C3 的蓝牙 GATT,做一个数据通信的应用实例。


与本实例相关的 ESP32-C3 专栏系列博文如下:


ESP32-C3 学习 蓝牙 篇系列博文连接:

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

https://xie.infoq.cn/article/30387388381a0d915b2494f91


开发环境是乐鑫官方的 ESP-IDF, 基于 VScode 插件搭建好的:

ESP32-C3 VScode 开发环境搭建(基于乐鑫官方 ESP-IDF——Windows 和 Ubuntu 双环境)

https://xie.infoq.cn/article/5b639e112cabba00cc1b8941a


蓝牙篇系列相关博文:

ESP32-C3 学习测试 蓝牙 篇(一、认识 ESP-IDF 的蓝牙框架、简单的了解蓝牙协议栈)

https://xie.infoq.cn/article/efbe5651b35c9d54c69bc0ab2

ESP32-C3 学习测试 蓝牙 篇(二、蓝牙调试 APP、开发板手机连接初体验)

https://xie.infoq.cn/article/a422913b3b0420b1ae7c1436d

ESP32-C3 学习测试 蓝牙 篇(三、认识蓝牙 GATT 协议)

https://xie.infoq.cn/article/9c35d44a61bf2f11aa0e5c407

ESP32-C3 学习测试 蓝牙 篇(四、GATT Server 示例解析)

https://xie.infoq.cn/article/743c10571e00b2e895cecb5d1

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

https://xie.infoq.cn/article/7bb73c3b43f678e773c369d0c

ESP32-C3 学习测试 蓝牙 篇(六、添加 Service)

https://xie.infoq.cn/article/a14a26ed4948ede0650ac279b

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

https://xie.infoq.cn/article/5811ee36243e1eae36c552e72

本应用涉及的博文除了上面之外还有:

ESP-IDF 工程结构说明:

认识 ESP-IDF-v4.3+工程结构(ESP32-C3 应用调整示例)

https://xie.infoq.cn/article/ddb67ebf28bfe7fecce6a2368

设备和传感器驱动:

ESP32-C3 入门教程 基础篇(四、I2C 总线 — 与 SHT21 温湿度传感器通讯)

https://xie.infoq.cn/article/75a22c7d3499c2d5428f726e8

ESP32-C3 入门教程 基础篇(五、RMT 应用 — 控制 SK6812 全彩 RGB 灯)

https://xie.infoq.cn/article/375c5cc5c6790b56117d0232e



一、整体框架

整个实例功能还是比较简单的,毕竟我们也刚接触 ESP32-C3 的蓝牙,直接用下图表示:



.


安卓手机 APP 我得考虑一下 ,因为我是 java 小白,虽然照猫画虎做过 APP, 有时间当然搞起来没问题,但是得花些时间,而博主最近时间不太够用…… 所以这个,再看把 = =!


废话不多说,我们直接开始……

二、数据传输部分

先实现把传感器数据上传至手机的部分功能。

2.1 添加温湿度驱动组件

在我的博文 《认识 ESP-IDF-v4.3+工程结构(ESP32-C3 应用调整示例)》中提到过,如何调整一个工程以及如何添加自己的组件代码,我们按照步骤先把需要的组件添加。


组件的添加,我们如果熟悉工程架构,可以直接把文件夹复制到工程中,那我们温湿度驱动组件,我们先用一下标准的组件添加方式,如下图:



当然,这里只是搭建了一个标准的组件框架,我们得把以前的 sht21 驱动代码复制到这两个文件中。

2.2 传感器数据传输程序

驱动代码移植好了,我们要使用起来,这个地方主要就是在于怎么把数据传输出去,在蓝牙系列的文章《ESP32-C3 学习测试 蓝牙 篇(七、GATT 数据通信 — 发送自定义数据)》中我们做过分析,我们如何传输自定义数据。


所以在那个基础之上我做了如下处理:


case ESP_GATTS_READ_EVT:            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT, conn_id %d, trans_id %d, handle %d\n",                                     param->read.conn_id, param->read.trans_id, param->read.handle);            // ESP_LOGI(GATTS_TABLE_TAG, "VAL_B %d\n", heart_rate_handle_table[IDX_CHAR_VAL_B]);            if(heart_rate_handle_table[IDX_CHAR_VAL_B] == param->read.handle){                int thread;                float T = 0;                float H = 0;
char th_data[20]; thread = SHT2X_THMeasure(I2C_MASTER_NUM); if(thread == ESP_ERR_TIMEOUT){} else{ T = (getTemperature()/100.0); H = (getHumidity()/100.0); } sprintf(th_data,"tem:%.2f , hum:%.2f",T,H); esp_ble_gatts_set_attr_value(param->read.handle,sizeof(th_data),(uint8_t*)th_data); } break;
复制代码


温湿度的逻辑不是一定得写在这里,程序任何地方都可以。需要连续读取 2 才才能正确的读到实时数据。


请看下面详细说明。

再次说明: ESP_GATTS_READ_EVT 事件

这里要先再次说明一个问题,就是回调函数中断的事件,我们这个 ESP_GATTS_READ_EVT 事件。


ESP_GATTS_READ_EVT 事件,回调函数的事件,是在事件发生之后。回调函数中的事件都是在事件发生了,才会触发的!


READ 读取的数据不是靠回调函数里面写逻辑给的数据,而是一直存在的 characteristic 的 value 值。 事件只是告诉你有读事件发生!


改变 characteristic 的 value 值的逻辑,可以在程序中任意地方修改,手机读取时候只取当时的 characteristic 的 value 值。


在上一篇博文



.


回到我们上面给出的代码要注意的地方:


1、 是用上面的方式,需要在下一次读取的时候才能读到上一次温湿度的值,如果需要实时数据,可以采取连续读取 2 次的方式(下文有优化代码);


这个是一个逻辑问题,就是如何传输实时数据,比如可以另外创建一个任务,不停的周期性读取温湿度数据,放入 characteristic 的 value 中。 那么每次 READ 就可以直接读取到最新的 温湿度数据。


2、还是需要注意示例中 value 的长度不能操作 20 字节(我们上一篇文章测试的结果);


在上面程序中使用了组合字符串的 sprintf 函数,注意长度不能过长。

2.3 功能测试

我们完成上述代码,测试一下。


记得添加了新的组件,最好是 make clean 一下,这里就是清除一下编译,重新编译。


烧录测试:



数据传输成功!


再次说明这个数据逻辑问题,可以自行处理,我这里计划读取实时数据时候连续读取 2 次,取第 2 次的数据。

2.4 代码优化

本来想着让大家自己测试的,后来想想这种连续读取 2 次确实不太妥当,干脆自己改个,反正简单:


就是新建一个任务,如下图:



READ 事件中可以声明都不用做:



本以为没问题,没想到= =!


连接的时候就出问题了,确实不太明白:



后来考虑了一下,我直接在手机与设备连接上了以后再创建任务,断开了之后再删除任务不是挺好的?


于是改了一下任务创建的位置:


在 GATT 回调函数,找到设备连接上的事件,进行对应的修改(图中有点问题,下文有修改说明):



感觉可以,测试一下:



OK! 第一次就是读到已经更新过的值!


但是上面的代码 还是有个问题,就是断开链接删除任务的时候会出错,上面犯了一个错误,vTaskDelete() 的参数应该是任务句柄,我在创建任务的时候没有给任务句柄。


具体原因可以查看我的 FreeRTOS 博文:


FreeRTOS 记录(二、FreeRTOS 任务 API 认识和源码简析)

https://xie.infoq.cn/article/250c67b1cc68b91241347f8e6


所以代码还是得修改一下,如下图:




.


到这里,我们成功实现了手机通过蓝牙实时读取开发板的温湿度数据!


额外说明:


这个地方还是得说一下,我通过创建任务的的方式可以读取实时数据,但是我多次测试下来,有时候还是会出问题,在连接的时候有可能重启,但是因为会立即重启恢复,所以到不影响使用测试。


还有一个就是设置的 THread 任务栈太小了,后来把任务放到 2048 ,就没有栈溢出的问题。


出问题的情况就是 THread 任务,如果任务时正常创建,然后手动断开,都没有问题,只有在连接过后,也没有手动断开,过了很长一段时间可能会出现。


猜测原因:估计就是在没有正常连接的时候不应该对 characteristic 的 value 进行修改操作(后面知道原因再来更新说明)。

三、控制部分

控制部分我们的目标是通过手机控制板载的 SK6812 全彩 RGB 灯。

3.1 添加 LED 驱动组件

首先还是 LED 驱动的组件添加,上面我们延时了通过标准架构方式添加组件,在熟悉组件的基础之上,我们可以直接把文件复制过来。



复制过来,我们得做个基础的测试:


具体的使用方式可以查看专栏中的博文《ESP32-C3 入门教程 基础篇(五、RMT 应用 — 控制 SK6812 全彩 RGB 灯)》,在这个工程中:



做个简单的测试,在我们上面的温湿度读取任务中,切换一下灯的状态:



上面给出的代码:


1、 一旦手机与开发板建立蓝牙连接,LED 灯红绿蓝三色切换;


这里其实知识为了测试一下驱动是否正常,但是这里让我想到,也可以使用一个单独的 LED 来表示蓝牙连接状态,无蓝牙连接一种状态,有蓝牙连接另一种状态;


2、在 ESP-IDF 中使用 FreeRTOS 的延时函数vTaskDelay,需要得到精准延时,需要加上pdMS_TO_TICKS


习惯了 stm32 中 FreeRTOS 的vTaskDelay 的使用容易在这里出问题,在上面vTaskDelay(pdMS_TO_TICKS(2000));才是延时 2s,如果不加时间不准确。


3、在上面创建 THread 任务的时候,给的任务栈是 1024 ,但是还是会有栈溢出,所以这里根据需要可以适当加大。


测试正常,SK6812 灯驱动添加成功。

3.2 控制 SK6812 程序

驱动添加成功,现在来考虑一下如何使用蓝牙控制开发板的 SK6812 。


我们使用了一个 characteristic 来做传感器数据传输,我们同样可以用一个 characteristic 来做数据接收,这次我们使用一个只写的 characteristic :



通过我们以前可知,Service 中的 C characteristic 为只写的,而且我们知道他的句柄为 47。


而且我们对会回调函数的理解,这个灯的操作我们可以直接在回调函数中实现,本次主要是为了表达这种方法,所以我们使用比较简单的控制指令,看代码就知道:


(下图代码有问题,粗心大意!)



上面代码直接使用 3 字节的指令控制 0xA5 0x03 0x01 打开红灯,其他类似,记得把上面任务中 SK6812 测试代码去掉。


编译烧录测试…… ,我擦,有问题……:



这个是因为上面代码粗心大意!! 判断符写成了赋值,== 写成了= ,除了这个还有颜色红色和绿色搞反了,最后修改代码:


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_C] == param->write.handle && param->write.len == 3){                    if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x03)&&(param->write.value[2] == 0x01)){                        ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn green!!");                        strip->set_pixel(strip, 0, 255, 0, 0);                        strip->refresh(strip, 100);                    }                    else if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x03)&&(param->write.value[2] == 0x02)){                        ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn red!!");                        strip->set_pixel(strip, 0, 0, 255, 0);                        strip->refresh(strip, 100);                    }                    else if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x03)&&(param->write.value[2] == 0x03)){                        ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn blue!!");                        strip->set_pixel(strip, 0, 0, 0, 255);                        strip->refresh(strip, 100);                    }                                 }                // 后面省略
复制代码


说明,上文中的 RGB 对应的位置有点问题,红色 和 绿色 的值对应位置好像反了,是因为 ESP-IDF 驱动中的颜色处理驱动函数是反过来的:



可以自行改正过来!


实际测试,效果一切正常:



到这里,我们成功实现了手机通过蓝牙控制开发板的 SK6812 LED


额外说明:


当然示例只是提供了最基本简单的思路,SK6812 是可以多个串联,然后还能支持调光的,使用蓝牙当然也可以实现。


虽然我们在示例中,直接在事件中通过判断写入的值来进行操作,但是也要明白,这个写入的值最后是直接写入了这个 characteristic 的 value 中,我们使用这个 characteristic 的值来改变 SK6812 LED 的颜色也是可以的。

3.3 代码优化

本来要结尾了,糟糕完成以后回家路上想了想,我可以发送 3 个字节,那么我可以直接发送 RGB 的颜色值啊? 这不是正好 3 个字节代表颜色吗?还是傻乎乎的自定义指令……


我们在代码中添加一个分支做控制,因为颜色组成为 3 个字节,我们把上面测试用到的控制代码,变成 2 个字节好做区分,实现代码很简单:


if(heart_rate_handle_table[IDX_CHAR_VAL_C] == param->write.handle ){         if(param->write.len == 3){             ESP_LOGI(GATTS_TABLE_TAG, "SK6812 set color!");             strip->set_pixel(strip, 0, param->write.value[0], param->write.value[1], param->write.value[2]);             strip->refresh(strip, 100);         }         else if(param->write.len == 2){             if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x01)){                 ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn green!!");                 strip->set_pixel(strip, 0, 255, 0, 0);                 strip->refresh(strip, 100);             }             else if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x02)){                 ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn red!!");                 strip->set_pixel(strip, 0, 0, 255, 0);                 strip->refresh(strip, 100);             }             else if((param->write.value[0] == 0xA5)&&(param->write.value[1] == 0x03)){                 ESP_LOGI(GATTS_TABLE_TAG, "SK6812 turn blue!!");                 strip->set_pixel(strip, 0, 0, 0, 255);                 strip->refresh(strip, 100);             }           }                    }
复制代码


测试下来,好用许多!开发板图就算了,放一下 LOG 图,想要什么颜色,对着颜色表输入 RGB 值即可:


四、APP 部分

APP 部分,如果要做,至少我想实现的是全色调光,实时的改变 LED 的颜色。这个市面上已经有许多成型的产品和 APP 了。


我这个... java 实在一言难尽,先暂时搁置,容我好好想想先 ... ...


结语

本文使用 ESP32-C3 的蓝牙,实现了数据的上传和下行的控制,也算完成我们最初的小目标了。


本应用所有的代码,只要看过我的博文,各个部分都是有分析说明的,如果真的需要可以联系我。


从最开始的面对蓝牙的迷茫,到现在实现了一个简单的应用。


乘风破浪会有时 ,直挂云帆济长海 !


发布于: 刚刚阅读数: 4
用户头像

矜辰所致

关注

CSDN、知乎、微信公众号: 矜辰所致 2022.08.02 加入

不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开! 为了活下去的嵌入式工程师,画画板子,敲敲代码,玩玩RTOS,搞搞Linux ...

评论

发布
暂无评论
ESP32-C3 应用 篇(实例二、通过蓝牙将传感器数据发送给手机,手机端控制 SK6812 LED)_蓝牙_矜辰所致_InfoQ写作社区