【Beetle ESP32 C6迷你开发板】低功耗蓝牙(BLE)功能初步使用
<div class='showpostmsg'> 本帖最后由 HonestQiao 于 2024-5-21 18:50 编辑<p><strong>一、BLE功能了解</strong></p>
<p>ESP32-C6搭载160MHz的高性能RISC-V 32位处理器,支持Wi-Fi 6、Bluetooth 5、Zigbee 3.0、Thread 1.3通讯协议,可接入多种通讯协议的物联网网络。</p>
<p>其中的Bluetooth 5,是蓝牙标准第5代,和Bluetooth 4对比如下:</p>
<p> 从上面的对比可以看到,Bluetooth 5比Bluetooth 4提升了一大截。</p>
<p> </p>
<p>乐鑫的esp-idf,也为ESP32-C6提供了完善的低功耗蓝牙(BLE)支持能力。</p>
<p> </p>
<p><strong>二、BLE扫描功能使用</strong></p>
<p>为了方便分享,本篇分享的实例,使用了友好的Arduino开发平台。</p>
<p>在Arduino的ESP32支持库Arduino-ESP32中,有BLE能力的支持库:<a href="https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE" target="_blank">https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE</a></p>
<p>BLE的扫描功能,主要用于设备发现,通过周边BLE设备的广播信息,获取BLE设备的基础信息。</p>
<p> </p>
<p>要使用设备发现功能,首先需要调用几个关键的库文件:</p>
<pre>
<code class="language-cpp">#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h></code></pre>
<p>上面的库文件,提供了BLE基本功能支持,扫描功能支持,和设备广播信息支持。</p>
<p> </p>
<p>然后实例化一个BLE设备对象:</p>
<pre>
<code class="language-cpp">BLEScan *pBLEScan;</code></pre>
<p>在开启扫描功能:</p>
<pre>
<code class="language-cpp">pBLEScan = BLEDevice::getScan();//create new scan
pBLEScan->setActiveScan(true);//active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);// less or equal setInterval value</code></pre>
<p>同时,还要设置一个广播信息获取后的回调,以便把扫描到的BLE谁被信息输出,具体如下:</p>
<pre>
<code>class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());</code></pre>
<p>上面代码中,定一个基类BLEAdvertisedDeviceCallbacks的类MyAdvertisedDeviceCallbacks,其中的onResult用于接收扫描到的设备信息。</p>
<p> </p>
<p>然后,启动扫描功能即可进行扫描和输出信息:</p>
<pre>
<code class="language-cpp">BLEScanResults *foundDevices = pBLEScan->start(5, false);
Serial.println(foundDevices->getCount()); // 获取扫描到的BLE设备数量
pBLEScan->clearResults();// 清理扫描结果</code></pre>
<p> </p>
<p>完整的代码如下:</p>
<pre>
<code class="language-cpp">/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 5;//In seconds
BLEScan *pBLEScan;
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan();//create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);//active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);// less or equal setInterval value
}
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults *foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices->getCount());
Serial.println("Scan done!");
pBLEScan->clearResults();// delete results fromBLEScan buffer to release memory
delay(2000);
}</code></pre>
<p> </p>
<p>在Arduino中,按照下面的设置,进行编译烧录:</p>
<p> </p>
<p>烧录后,打开串口监控,可以看到扫描输出信息:</p>
<p> 上面输出中的L4260,就是我家打印机了。</p>
<p> </p>
<p><strong>三、BLE UART功能使用</strong></p>
<p>BLE设备可以通过GATT的GAP定义自身的角色,例如:外围设备(Peripheral)和中心设备(Central)。</p>
<p>GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范。</p>
<p>GAP定义了设备如何彼此发现、建立连接以及如何实现绑定,同时描述了设备如何成为广播者和观察者,并且实现无需连接的传输。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。</p>
<p>当做为外围设备(Peripheral),可以对外发布自身能够提供的服务。其中,最常见的一项服务是UART服务,可以通过低功耗蓝牙,实现乐思串口通讯的能力。UART服务最早是Nordic Semiconductor 的 BLE 模组提供的自定义服务Nordic UART Service,现在只要遵循其标准定义,那么都可以提供该服务。</p>
<p> </p>
<p>通过BLE对外提供服务,主要需要定义服务(Service) 和 标签(Characteristic),以便告知其他设备,自己能够提供的服务和具体的功能:</p>
<p> </p>
<p> </p>
<p>而在UART服务中,Service包含两个Characteristic,一个被配置只读的通道(RX),另一个配置为只写的通道(TX)。</p>
<p>通常情况下,Service和Characteristic都是一个UUID,UART对应的如下:</p>
<ul>
<li>UART Service:6E400001-B5A3-F393-E0A9-E50E24DCCA9E</li>
<li>RX标签:6E400002-B5A3-F393-E0A9-E50E24DCCA9E,表示可以从该标签接收到信息</li>
<li>TX标签:6E400003-B5A3-F393-E0A9-E50E24DCCA9E ,表示可以向改标签写入信息</li>
</ul>
<p> </p>
<p>要在ESP32-C6上提供UART 服务,需要按照以下的步骤进行:</p>
<ol>
<li> 创建BLE服务端</li>
<li> 创建BLE服务</li>
<li> 创建BLE服务对应的标签</li>
<li> 设置标签对应的描述信息</li>
<li> 启动服务</li>
<li> 启动广播</li>
</ol>
<p>参考官方的实例,测试代码如下:</p>
<pre>
<code class="language-cpp">#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"// UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer *pServer) {
deviceConnected = false;
}
};
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue);
}
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("ESP32-C6 UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
txValue++;
delay(1000);// bluetooth stack will go into congestion, if too many packets are sent
pTxCharacteristic->setValue("\nESP32-C6\n");
pTxCharacteristic->notify();
delay(1000);// bluetooth stack will go into congestion, if too many packets are sent
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising();// restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}</code></pre>
<p> </p>
<p>在上述代码中的setup()部分,按照以上步骤,进行了对应的服务的创建,标签的创建,以及服务和广播的启动。</p>
<p>另外,还定义了两个回调类,分别是MyServerCallbacks用于处理蓝牙连接状态,MyCallbacks用于处理收到信息时的处理。</p>
<p> </p>
<p>在loop()循环调用中,则进行了信息发送的处理,以及根据连接情况决定是否启动广播的处理。</p>
<p> </p>
<p>在Arduino中编译烧录后,串口监听输出如下:</p>
<p> 表示ESP32-C6启动了广播,可以用手机或者电脑连接获取服务了。 </p>
<p> </p>
<p>此时,用前面的扫描程序,可以扫描到:</p>
<p> 从上图中可以看到,找到了 ESP32-C6 UART Service 了。</p>
<p> </p>
<p>此时,用BLE工具,可以进行连接,就能够收到对应的信息了:</p>
<p> </p>
<p>在上面代码的loop()中,有两个设置发送信息的处理:</p>
<pre>
<code class="language-cpp"> pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
pTxCharacteristic->setValue("\nESP32-C6\n");
pTxCharacteristic->notify();</code></pre>
<p>第一个表示发送数值,大小为1字节;第二个表示发送一个字符串。</p>
<p> </p>
<p>把BLE工具的接收HEX打开,就可以看到发送的数值信息了:</p>
<p> </p>
<p> </p>
<p> </p>
<p>然后,在BLE工具中,给ESP32-C6发送信息:</p>
<p> </p>
<p>串口监控就会收到发送的信息了:</p>
<p> </p>
<p><strong>四、总结</strong></p>
<p>刚开始用BLE功能时,觉得基于GATT的处理过程很麻烦,但是一旦用上了后,会非常的好用,因为已经有很多标准化的处理了,符合标准定义的设备,可以轻松的进行互联和通信。</p>
<p>如果你的设备,按照标准定义来广播Service和通过Characteristic提供对应的实际功能,那么其他符合这个标准的设备,就能够快速连接获取数据或者发送数据。</p>
<p>现在有很多BLE低功耗设备,如心率传感器、手环、温度传感器、光纤传感器等采用,你不用了解它具体的工作细节,仅需要通过BLE扫描发现对应的BLE设备和服务,然后就可以通过标准定义的Characteristic来获取数据了,非常的方便。</p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>第三部分,BLE UART功能使用</p>
<p>有个图片不显示,</p>
<p> </p>
Jacktang 发表于 2024-5-21 07:29
第三部分,BLE UART功能使用
有个图片不显示,
<p>多谢提醒,修改了,再看看</p>
页:
[1]