如何利用ESP32和Arduino制作BLE检测器

描述

  接近传感器可以描述为一种借助光、电磁场或声音检测附近物体的开关。通常,这些类型的设备旨在检测附近的对象,并且大多数这些传感器将用于实际应用中。但是在某些情况下,对象远离传感器或对象被遮挡障碍物,在这些类型的情况下,我们可以使用BLE(低功耗蓝牙)设备来检测和感知物体的接近度。 ESP32 开发板内置 BLE,我们在很多其他项目中都使用过。

  在本文中,我将向您展示如何在ESP32 和 Arduino 的帮助 下制作一个简单的BLE 存在检测器,最后,我们将在我的智能手机和智能手表上使用 BLE 测试这些设备。

  什么是低功耗蓝牙 (BLE)?

  BLE 代表低功耗蓝牙,它于 2011 年进入我们的日常生活,因为每年的那个时候,每个主要制造商都开始在他们的设备中嵌入 BLE 技术。BLE 是一种低功耗无线通信技术,专为电池供电应用而开发,可用于短距离设备之间的通信。您每天使用的一些设备都内置了蓝牙,例如您的智能手机、智能手表、无线 耳塞、无线 扬声器、智能 家居 设备。,以及更多的嵌入式蓝牙进行通信或获取位置数据。

  BLE 是一项相对较新的技术,BLE 协议是由蓝牙特别兴趣小组 (SIG)开发的,其主要目标是使低功耗设备成为现实。虽然新开发的协议名称保持不变,但新开发的 BLE 协议不向后兼容,这意味着我们的蓝牙经典设备无法与 BLE 设备通信,尽管该技术存在缺点,但它使开发人员能够产生非常低的功率能量- 使用小型纽扣电池可持续数月甚至数年的高效设备。

  BLE 通信如何工作?

  BLE 使用分层数据结构来发送和接收信息。充当服务器的 BLE 设备将发布客户端可以检测到的服务和特征,一旦信息交换成功,BLE 设备可以同时相互通信。用技术术语来说,这个信息堆栈一起被称为 BLE 设备的属性。它是使用 GATT(通用属性)配置文件定义和实现的。在这些配置文件中,我们按层次顺序排列了服务、特征和值。服务包含特征,特征包含值,通过读取特征,我们可以读取值和值随时间的变化。

ESP32

  可以处理特征以包括读取或写入信息。包含读取组件的设备可以发布信息,包含写入特性的设备可以从客户端接收数据。

  定义服务和特性的GATT 配置文件称为通用唯一标识符 (UUID)。SIG 公司定义和保留了一些标准服务和特性,如果我们读取 BLE 设备的 UUID,我们可以立即知道它是什么类型的设备。稍后再讨论这个话题。

  BLE 数据以非常小的封装传输和接收,当TCP 数据包为 60 字节或更多时,BLE 数据包总共只有 31 字节。最后,要记住的重要一点是,BLE 数据包需要正确构造,然后可以在服务器端和客户端一致地序列化和反序列化。

  BLE 接近传感器如何工作?

  正如我之前所说,接近传感器可以借助光、电磁波或声音来检测物体。之前我们已经制作了一些基于PIR 传感器和IR 传感器的项目,如果您想了解更多有关该主题的信息,可以查看这些项目。

ESP32

  BLE 服务器定期广播广告信号,以便客户端可以搜索并连接到它。这个广播信号包含一个唯一的 BLE MAC(媒体访问控制)地址,它与Wi-Fi 中使用的 MAC 地址非常相似,因为我们的 ESP32 模块内置了蓝牙,我们可以很容易地检测到这个广播信号并将其与查找表,以检测已知设备的存在。验证设备后,我们可以在本地打开灯,也可以使用 Adafruit IO在我们的 android 应用程序上触发通知。我们之前也用 Adafruit IO 做过项目,如果你想实现 Adafruit IO,可以查看这些项目。

  这个项目的要求?

ESP32

  正如您在上图中看到的,对这个项目没有太多要求,一个 esp32 开发板和一个支持 BLE 的设备,这里我使用的是我的 MI Band,就是这样。

ESP32 BLE 接近传感器的 Arduino 代码

在演示中,我们将对 ESP32 控制器进行编程以感应已知的 BLE 信标,一旦已知设备接近,我们将点亮板载 LED。可以在本页底部找到执行此操作的完整程序。代码解释如下。

我们通过添加所需的头文件并声明变量来开始我们的程序。

当我们使用 BLEScan 类时,我们需要包含 BLEScan 的头文件以及其他 BLE 头文件。

 

#include 
#include 
#include 
#include 

 

接下来,我们将声明一些变量,首先是我们存储已知 BLE MAC 地址的数组。接下来,我们将声明阈值,打破将触发所需操作的阈值。接下来,我们将定义一个布尔值,如果我们数组中的已知设备与扫描的设备列表匹配,它将设置为 true。接下来,我们声明另一个变量来清除已扫描的 BLE 设备,如果我们不这样做,就会出现内存问题。接下来,我们获取一个指向 BLEScan 类的指针。

 

字符串 knownBLEAddresses[] = {"aa:bc:cc:dd:ee:ee", "54:2c:7b:87:71:a2"};
int RSSI_THRESHOLD = -55;
bool device_found;
整数扫描时间 = 5;//片刻之间
BLEScan* pBLEScan;

 

我们必须定义一个回调函数,首先,这个回调函数将每隔几秒调用一次,以检查是否有新的 BLE 设备可用。一旦我们进入回调函数,如果我们找到一个 BLE 设备,我们将设置一个标志并中断循环。最后,我们将打印 BLE 设备信息。

 

类 MyAdvertisedDeviceCallbacks:公共 BLEAdvertisedDeviceCallbacks {
    无效 onResult(BLEAdvertisedDevice 广告设备){
      for (int i = 0; i < (sizeof(knownBLEAddresses) / sizeof(knownBLEAddresses[0])); i++)
      {
        //取消注释以启用调试信息
        //Serial.println("*************开始**************");
        //Serial.println(sizeof(knownBLEAddresses));
        //Serial.println(sizeof(knownBLEAddresses[0]));
        //Serial.println(sizeof(knownBLEAddresses)/sizeof(knownBLEAddresses[0]));
        //Serial.println(advertisedDevice.getAddress().toString().c_str());
        //Serial.println(knownBLEAddresses[i].c_str());
        //Serial.println("*************结束**************");
        if (strcmp(advertisedDevice.getAddress().toString().c_str(), knownBLEAddresses[i].c_str()) == 0)
                        {
          device_found = true;
                          休息;
                        }
        别的
          device_found = 假;
      }
      Serial.printf("广告设备:%s \n", AdvertisementdDevice.toString().c_str());
    }
};

 

接下来是设置部分,我们将LED 引脚声明为输出。此外,我们使用BLEDevice::init 方法初始化BLEDevice,之后,我们从 BLEDevice 获取扫描对象,我们将地址存储到先前声明的pBLEScan 指针中。

接下来,我们设置回调函数,这个回调函数会在几秒钟内调用一次,检查是否有新设备可用,接下来我使用SetActiveScan 方法将其设置为非活动模式,因为如果我们启用此功能,BLE 设备将为我们提供非常准确的结果。最后我们设置setInterval和setWindow方法 来完成设置。

 

  序列号.开始(115200);//在ESP32上启用UART
  Serial.println("正在扫描..."); // 打印扫描
  pinMode(LED_BUILTIN,输出);//
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan(); //创建新的扫描
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //初始化回调函数
  pBLEScan->setActiveScan(true); //主动扫描消耗更多的能量,但更快地得到结果
  pBLEScan->setInterval(100); // 设置扫描间隔
  pBLEScan->setWindow(99); // 小于或等于 setInterval 值

 

在循环部分,我们使用start 方法启动 BLE 设备。scanTime参数定义了 BLE 设备扫描新设备所需的时间。

 

BLEScanResults foundDevices = pBLEScan->start(scanTime, false);

 

扫描完成后,我们将其放入名为foundDevices的变量中。

扫描完成后,我们可以使用found devices.getCount() 方法获取扫描仪找到了多少个设备,我们方便地将其放入for 循环 中,逐个获取每个设备的 RSSI 值,然后,我们比较该设备是否与列表中的设备匹配,我们使用一个简单的if语句来做到这一点。如果我们得到一个已知设备,我们会点亮板载 LED,以表明我们在列表中找到了一个已知设备。否则我们会关闭 LED。

 

for (int i = 0; i < foundDevices.getCount(); i++)
  {
    BLEAdvertisedDevice device = foundDevices.getDevice(i);
    int rssi = device.getRSSI();
    Serial.print("RSSI:");
    序列号.println(rssi);
    如果(rssi > RSSI_THRESHOLD && device_found == true)
      数字写入(LED_BUILTIN,高);
    别的
      数字写入(LED_BUILTIN,低);
  }

 

最后,我们通过调用 -

 

clearResults() 方法
pBLEScan->clearResults(); // 从BLEScan缓冲区中删除结果以释放内存

 

  上传代码后,如果已知设备靠近您的 ESP32 设备,您可以触发任何操作。完整的工作也可以在此页面的底部找到。
 

#include
#include
#include
#include
字符串 knownBLEAddresses[] = {"6E:bc:55:18:cf:7b", "53:3c:cb:56:36:02", "40:99:4b:75:7d:2f", "5c :5b:68:6f:34:96"};
int RSSI_THRESHOLD = -55;
bool device_found;
整数扫描时间 = 5;//片刻之间
BLEScan* pBLEScan;
类 MyAdvertisedDeviceCallbacks:公共 BLEAdvertisedDeviceCallbacks {
无效 onResult(BLEAdvertisedDevice 广告设备){
for (int i = 0; i < (sizeof(knownBLEAddresses) / sizeof(knownBLEAddresses[0])); i++)
{
//取消注释以启用调试信息
//Serial.println("*************开始**************");
//Serial.println(sizeof(knownBLEAddresses));
//Serial.println(sizeof(knownBLEAddresses[0]));
//Serial.println(sizeof(knownBLEAddresses)/sizeof(knownBLEAddresses[0]));
//Serial.println(advertisedDevice.getAddress().toString().c_str());
//Serial.println(knownBLEAddresses[i].c_str());
//Serial.println("*************结束**************");
if (strcmp(advertisedDevice.getAddress().toString().c_str(), knownBLEAddresses[i].c_str()) == 0)
{
device_found = true;
休息;
}
别的
device_found = 假;
}
Serial.printf("广告设备:%s \n", AdvertisementdDevice.toString().c_str());
}
};
无效设置(){
序列号.开始(115200);//在ESP32上启用UART
Serial.println("正在扫描..."); // 打印扫描
pinMode(LED_BUILTIN,输出);//使 BUILTIN_LED 引脚作为输出
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //创建新的扫描
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //初始化回调函数
pBLEScan->setActiveScan(true); //主动扫描消耗更多的能量,但更快地得到结果
pBLEScan->setInterval(100); // 设置扫描间隔
pBLEScan->setWindow(99); // 小于或等于 setInterval 值
}
无效循环(){
// 把你的主要代码放在这里,重复运行:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
for (int i = 0; i < foundDevices.getCount(); i++)
{
BLEAdvertisedDevice device = foundDevices.getDevice(i);
int rssi = device.getRSSI();
Serial.print("RSSI:");
序列号.println(rssi);
如果(rssi > RSSI_THRESHOLD && device_found == true)
数字写入(LED_BUILTIN,高);
别的
数字写入(LED_BUILTIN,低);
}
pBLEScan->clearResults(); // 从BLEScan缓冲区中删除结果以释放内存
}

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分