1303|2

328

帖子

5

TA的资源

纯净的硅(中级)

楼主
 

【Beetle ESP32 C6迷你开发板】低功耗蓝牙(BLE)功能初步使用 [复制链接]

 
本帖最后由 HonestQiao 于 2024-5-21 18:50 编辑

一、BLE功能了解

ESP32-C6搭载160MHz的高性能RISC-V 32位处理器,支持Wi-Fi 6、Bluetooth 5、Zigbee 3.0、Thread 1.3通讯协议,可接入多种通讯协议的物联网网络。

其中的Bluetooth 5,是蓝牙标准第5代,和Bluetooth 4对比如下:

  从上面的对比可以看到,Bluetooth 5比Bluetooth 4提升了一大截。

 

乐鑫的esp-idf,也为ESP32-C6提供了完善的低功耗蓝牙(BLE)支持能力。

 

二、BLE扫描功能使用

为了方便分享,本篇分享的实例,使用了友好的Arduino开发平台。

在Arduino的ESP32支持库Arduino-ESP32中,有BLE能力的支持库:https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE

BLE的扫描功能,主要用于设备发现,通过周边BLE设备的广播信息,获取BLE设备的基础信息。

 

要使用设备发现功能,首先需要调用几个关键的库文件:

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

上面的库文件,提供了BLE基本功能支持,扫描功能支持,和设备广播信息支持。

 

然后实例化一个BLE设备对象:

BLEScan *pBLEScan;

在开启扫描功能:

  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

同时,还要设置一个广播信息获取后的回调,以便把扫描到的BLE谁被信息输出,具体如下:

class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
  }
};


  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

上面代码中,定一个基类BLEAdvertisedDeviceCallbacks的类MyAdvertisedDeviceCallbacks,其中的onResult用于接收扫描到的设备信息。

 

然后,启动扫描功能即可进行扫描和输出信息:

  BLEScanResults *foundDevices = pBLEScan->start(5, false);
  Serial.println(foundDevices->getCount()); // 获取扫描到的BLE设备数量
  pBLEScan->clearResults();  // 清理扫描结果

 

完整的代码如下:

/*
   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);
}

 

在Arduino中,按照下面的设置,进行编译烧录:

 

烧录后,打开串口监控,可以看到扫描输出信息:

  上面输出中的L4260,就是我家打印机了。

 

三、BLE UART功能使用

BLE设备可以通过GATT的GAP定义自身的角色,例如:外围设备(Peripheral)和中心设备(Central)。

GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范。

GAP定义了设备如何彼此发现、建立连接以及如何实现绑定,同时描述了设备如何成为广播者和观察者,并且实现无需连接的传输。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。

当做为外围设备(Peripheral),可以对外发布自身能够提供的服务。其中,最常见的一项服务是UART服务,可以通过低功耗蓝牙,实现乐思串口通讯的能力。UART服务最早是Nordic Semiconductor 的 BLE 模组提供的自定义服务Nordic UART Service,现在只要遵循其标准定义,那么都可以提供该服务。

 

通过BLE对外提供服务,主要需要定义服务(Service) 和 标签(Characteristic),以便告知其他设备,自己能够提供的服务和具体的功能:

 

 

而在UART服务中,Service包含两个Characteristic,一个被配置只读的通道(RX),另一个配置为只写的通道(TX)。

通常情况下,Service和Characteristic都是一个UUID,UART对应的如下:

  • UART Service:6E400001-B5A3-F393-E0A9-E50E24DCCA9E
  • RX标签:6E400002-B5A3-F393-E0A9-E50E24DCCA9E,表示可以从该标签接收到信息
  • TX标签:6E400003-B5A3-F393-E0A9-E50E24DCCA9E ,表示可以向改标签写入信息

 

要在ESP32-C6上提供UART 服务,需要按照以下的步骤进行:

  1.    创建BLE服务端
  2.    创建BLE服务
  3.    创建BLE服务对应的标签
  4.    设置标签对应的描述信息
  5.    启动服务
  6.    启动广播

参考官方的实例,测试代码如下:

#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[i]);
      }

      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;
  }
}

 

在上述代码中的setup()部分,按照以上步骤,进行了对应的服务的创建,标签的创建,以及服务和广播的启动。

另外,还定义了两个回调类,分别是MyServerCallbacks用于处理蓝牙连接状态,MyCallbacks用于处理收到信息时的处理。

 

在loop()循环调用中,则进行了信息发送的处理,以及根据连接情况决定是否启动广播的处理。

 

在Arduino中编译烧录后,串口监听输出如下:

 表示ESP32-C6启动了广播,可以用手机或者电脑连接获取服务了。 

 

此时,用前面的扫描程序,可以扫描到:

  从上图中可以看到,找到了 ESP32-C6 UART Service 了。

 

此时,用BLE工具,可以进行连接,就能够收到对应的信息了:

 

在上面代码的loop()中,有两个设置发送信息的处理:

    pTxCharacteristic->setValue(&txValue, 1);
    pTxCharacteristic->notify();

    pTxCharacteristic->setValue("\nESP32-C6\n");
    pTxCharacteristic->notify();

第一个表示发送数值,大小为1字节;第二个表示发送一个字符串。

 

把BLE工具的接收HEX打开,就可以看到发送的数值信息了:

 

 

 

然后,在BLE工具中,给ESP32-C6发送信息:

 

串口监控就会收到发送的信息了:

 

四、总结

刚开始用BLE功能时,觉得基于GATT的处理过程很麻烦,但是一旦用上了后,会非常的好用,因为已经有很多标准化的处理了,符合标准定义的设备,可以轻松的进行互联和通信。

如果你的设备,按照标准定义来广播Service和通过Characteristic提供对应的实际功能,那么其他符合这个标准的设备,就能够快速连接获取数据或者发送数据。

现在有很多BLE低功耗设备,如心率传感器、手环、温度传感器、光纤传感器等采用,你不用了解它具体的工作细节,仅需要通过BLE扫描发现对应的BLE设备和服务,然后就可以通过标准定义的Characteristic来获取数据了,非常的方便。

最新回复

第三部分,BLE UART功能使用 有个图片不显示, [attach]810018[/attach]     详情 回复 发表于 2024-5-21 07:29
点赞 关注
 
 

回复
举报

6810

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

第三部分,BLE UART功能使用

有个图片不显示,

 

点评

多谢提醒,修改了,再看看  详情 回复 发表于 2024-5-21 18:51
 
 
 

回复

328

帖子

5

TA的资源

纯净的硅(中级)

板凳
 
Jacktang 发表于 2024-5-21 07:29 第三部分,BLE UART功能使用 有个图片不显示,  

多谢提醒,修改了,再看看

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
模拟电路基础教程(书)

程序编号:799 程序名称: 模拟电路基础教程(书) ...

嵌入式C程序员面试应注意的一些问题

1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 36 ...

原创--IAR for AVR入门学习笔记

AVR单片机的编译软件五花八门,用宋丹丹的话就是:那是相当的多 汇编语言的开发平台就不说了(俺不大会,呵呵,说不出什么道道 ...

移植好的ucos2工程(基于LPC2000和Keil MDK)

欢迎大家提意见,谢谢!!

《GPSR+GPS开发板》之一透明传输

之前本人把自己DIY的《GPSR+GPS开发板》开发板Show出来,可程序一直没有完成。所以,代码至今没有开源。后面我们将逐步把相关内 ...

提前解锁!9月的汽车测试展有什么?

一年一度的Automotive Testing Expo(汽车测试博览会) 将于9月在上海举行,这一行业盛会每年都会吸引汽车测试行业的厂商进行 ...

TPS546D24_C23动态调压

本帖最后由 qwqwqw2088 于 2020-9-27 08:46 编辑 根据PMBUS 1.3.1版本协议,第二节8.2部分,本文将简述如何通过VOUT_COMMAND ...

【拥抱AIGC 应用ChatGPT和OpenAI API】尝试API,简单的对话

821129 还在看书学习呢!OPENAI就停服了。核心技术真的不能依赖国外啊! 821133 书中提供的例子,国内都是无法访问的 ...

三电平二极管钳位型变频器的输出

二极管型中点钳位型变频器,在待机状态下,由于钳位二极管连接每一相中间两个开关管,又和母线电容的中点连接,因此在待机状态下 ...

【第二轮入围名单】《大规模语言模型:从理论到实践》

感谢网友参与《大规模语言模型:从理论到实践》的申请,以下是入围网友信息。 入围网友请在2024年12月26日12:00前: (1) ...

关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表