ESP32 使用Iphone手机发送数据给BLE蓝牙服务端
目录
实验效果
使用Iphone手机通过蓝牙发送数据给ESP32,ESP32显示接收到的数据。
ESP32 & BLE的关键概念
-
ESP32系列支持的低功耗蓝牙(BLE)协议
ESP32蓝牙规格:BR/EDR + Bluetooth LE v4.2
ESP32-S3蓝牙规格:Bluetooth LE 5.0 -
什么是 Bluetooth 和 Bluetooth LE (Low Energy)
Bluetooth 5.0 在早期的版本基础上,增加了一些新功能和改进,包括更高的数据传输速度,更大的范围,以及更好的低功耗性能等。
Bluetooth LE 是这些特性中的一部分,主要针对需要低功耗和长电池寿命的设备,比如健康和健身设备,安全设备和家居自动化产品等。
Bluetooth LE 和 Bluetooth 并不是完全相同。Bluetooth 包含 Bluetooth LE,但 Bluetooth 还有更多的特性和功能。 -
ESP32开发板 BLE的兼容性
使用Arduino IDE ESP系列开发版中内置的BLE库和例程,经测试在ESP32-S3中不能使用,需要另外安装其他BLE的库,之后,再分享一篇ESP32-S3的的蓝牙系列例子 -
什么是蓝牙低功耗服务应用(CHARACTERISTIC)
每个蓝牙设备都有一个服务(SERVER UUID),
在服务之下,可以有多个不同的服务应用(CHARACTERISTIC UUID),又或者叫特性。
服务应用也有描述和值两个属性,并且可以配置不一样的功能或作用:- Broadcast 广播
- Read 读取
- Write without response 无响应写入
- Write 写入
- Notify 通知
- Indicate 指示
- Authenticated Signed Writes 鉴权签名写入
- Extended Properties 扩展属性
例如,一个带蓝牙的温湿度时钟,设备就是一个服务(Server UUID),其功能读取温度是一个服务应用(CHARACTERISTIC UUID),读取湿度是另一个服务应用(CHARACTERISTIC UUID),设置闹钟时写入时间信息又是另一个服务应用(CHARACTERISTIC UUID)。
-
UUID(通用唯一标识符)
在蓝牙设备中每个服务、服务应用和描述符都有一个 UUID。 UUID 是一个唯一的 128 位(16 字节)数字。 -
GAP和GATT
GAP和GATT在BLE(蓝牙低功耗)通信中扮演着至关重要的角色,它们共同定义了蓝牙设备如何进行发现、连接以及数据交换的基本框架。下面是关于两者的简要概述:
-
GAP(Generic Access Profile,通用访问规则):GAP提供了设备广播自身以供其他设备发现的机制,并且规定了如何建立和维护设备间的连接。GAP提供两种主要的设备角色,它们共同构成了蓝牙设备的通信网络。
-
外围装置(Peripheral):这类设备通常是体积小、功能单一的设备,它们充当数据的提供者。外围装置通常也被称为“从模式-Slave”或“服务端(server)”。它们特征是低功耗和较小的处理能力。举例来说,手环,耳机等等外围装置。
-
中心装置(Central):中心装置一般具有更强大的计算能力和资源,它们负责管理和连接一个或多个外围装置。中心装置也被称作“主模式-Master”或“客户端(client)”。一个常见的例子是智能手机,它可以同时连接多个外围装置,如智能手环、心率监测器等,并对这些设备提供的数据进行处理和记录。
-
GATT(Generic Attribute Profile,通用属性规则):在GAP定义的连接建立之后,GATT决定了设备之间如何进行数据交换。GATT运行在更低层次的属性协议(ATT)之上,并通过"服务"和"特性"的概念来组织和管理数据。简单地说,一个“服务”由许多“特性”组成,每个“特性”又包含了实际的数据值和一些相关的描述符(Descriptor),这些都是数据交互的基础。
两者之间的关系可以类比为GAP是建立连接的“握手”过程,确保设备能够互相识别和连接;而GATT则是确立了一旦连接建立后,如何进行有效的数据沟通。这两个框架是BLE技术能够在如此低功耗下提供稳定、灵活的数据交换的基石。
-
BOM
ESP32 开发板 x1
Iphone 手机 x1
接线
只用使用USB线连接ESP32开发板,然后在电脑上传程序即可。
之后就是在手机操作连接了。
程序提点
ESP32 可以充当 BLE 服务器或 BLE 客户端。Arduino IDE 的 ESP32 BLE 库中有多个 ESP32 的 BLE 示例 。当您在 Arduino IDE 上安装 ESP32 时,默认安装该库。但正如之前说明的,这个原生的BLE库对ESP32-S3开发板不兼容。
本文主要关注 ESP32 做 BLE 服务器,使用Iphone手机通过蓝牙发送数据给ESP32。
1,加载需要的库
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
2,定义服务和特性的UUID:
接着定义了两个变量 SERVICE_UUID
和 CHARACTERISTIC_UUID
。这两个变量是固定格式的字符串,分别表示我们要创建的BLE服务和特性的唯一标识符(UUID)。
// Service unique identification
// 服务唯一标识
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
// Characteristics unique identification
// 服务应用唯一标识
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
3, 定义回调类
MyCallbacks
类被定义为处理BLE服务应用写入时的回调,它会将写入的值输出到串行端口。
// 定义一个名为"MyCallbacks"的类,从"BLECharacteristicCallbacks"类公开继承
// 这意味着"MyCallbacks"类将继承"BLECharacteristicCallbacks"类的所有公开方法和属性。
class MyCallbacks: public BLECharacteristicCallbacks {
// 定义一个名为"onWrite"的方法,
// 用于处理BLECharacteristic类对象的写入操作。它接收一个指向BLECharacteristic类对象的指针作为参数。
void onWrite(BLECharacteristic *pCharacteristic) {
// 定义了一个字符串"value",并使用传入的服务应用获取特性的值。
std::string value = pCharacteristic->getValue();
if (value.length() > 0) { // 判断 value的长度是否大于0
// 大于 0 表示 value 非空,则在串口中输出其值
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++)
Serial.print(value[i]);
Serial.println();
Serial.println("*********");
}
}
};
4,在Setup 中配置BLE的参数
// 初始化BLE设备并设置其名称为"LingShunLAB"。这里可以设置成你喜欢的名称。
BLEDevice::init("LingShunLAB");
// 创建一个BLE服务器,并用指针pServer指向它。
BLEServer *pServer = BLEDevice::createServer();
// 使用SERVICE_UUID 在pServer指向的BLE服务器上创建一个服务,
// 并用指针pService指向这个服务。
BLEService *pService = pServer->createService(SERVICE_UUID);
// 在pService指向的BLE服务上开始创建一个服务应用
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID, // 定义此服务应用的CHARACTERISTIC_UUID
BLECharacteristic::PROPERTY_READ | //设置这个特性的属性为可读和可写。
BLECharacteristic::PROPERTY_WRITE
);
// 为这个服务应用设置回调,当应用发生读取、写入等操作时,会调用MyCallbacks类的相应方法。
pCharacteristic->setCallbacks(new MyCallbacks());
// 将该服务应用的初始值设置为"Hello World"字符串。
pCharacteristic->setValue("Hello World");
pService->start(); // 启动pService指向的服务,使其开始工作。
// 获取pServer指向的服务器的广告对象,并用pAdvertising指向它。
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start(); //启动广告,让BLE设备对外广播,这让其他BLE设备可以发现并连接到这个BLE服务器。
程序代码
本例程,是默认安装库里的例子,在此基础上进行一些注释,方便理解。
打开例子的路径:「File」-> 「Example」-> 「ESP32 BLE Arduino」-> 「BLE_write」
以下完整代码是我修改了一下,并添加了一些注释:
// Welcome to Lingshunlab.com
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
// 服务唯一标识
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
// 服务应用唯一标识
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
// 定义一个名为"MyCallbacks"的类,从"BLECharacteristicCallbacks"类公开继承
// 这意味着"MyCallbacks"类将继承"BLECharacteristicCallbacks"类的所有公开方法和属性。
class MyCallbacks: public BLECharacteristicCallbacks {
// 定义一个名为"onWrite"的方法,
// 用于处理BLECharacteristic类对象的写入操作。它接收一个指向BLECharacteristic类对象的指针作为参数。
void onWrite(BLECharacteristic *pCharacteristic) {
// 定义了一个字符串"value",并使用传入的服务应用获取特性的值。
std::string value = pCharacteristic->getValue();
if (value.length() > 0) { // 判断 value的长度是否大于0
// 大于 0 表示 value 非空,则在串口中输出其值
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++)
Serial.print(value[i]);
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
Serial.println("1- Download and install an BLE scanner app in your phone");
Serial.println("2- Scan for BLE devices in the app");
Serial.println("3- Connect to MyESP32");
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
Serial.println("5- See the magic =)");
// 初始化BLE设备并设置其名称为"LingShunLAB"。这里可以设置成你喜欢的名称。
BLEDevice::init("LingShunLAB");
// 创建一个BLE服务器,并用指针pServer指向它。
BLEServer *pServer = BLEDevice::createServer();
// 使用SERVICE_UUID 在pServer指向的BLE服务器上创建一个服务,
// 并用指针pService指向这个服务。
BLEService *pService = pServer->createService(SERVICE_UUID);
// 在pService指向的BLE服务上开始创建一个服务应用
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID, // 定义此服务应用的CHARACTERISTIC_UUID
BLECharacteristic::PROPERTY_READ | //设置这个特性的属性为可读和可写。
BLECharacteristic::PROPERTY_WRITE
);
// 为这个服务应用设置回调,当应用发生读取、写入等操作时,会调用MyCallbacks类的相应方法。
pCharacteristic->setCallbacks(new MyCallbacks());
// 将该服务应用的初始值设置为"Hello World"字符串。
pCharacteristic->setValue("Hello World");
pService->start(); // 启动pService指向的服务,使其开始工作。
// 获取pServer指向的服务器的广告对象,并用pAdvertising指向它。
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start(); //启动广告,让BLE设备对外广播,这让其他BLE设备可以发现并连接到这个BLE服务器。
}
void loop() {
// put your main code here, to run repeatedly:
delay(2000);
}
Iphone连接ESP32 BLE发送数据
已经确保把程序上传成功后,就可以继续以下iphone手机连接蓝牙的操作:
1,下载可连接BLE的APP
在这个例子中使用Iphone系统的蓝牙搜索是无法找到ESP32蓝牙服务端的,需要下载一个「LightBlue」的App进系连接和操作。
打开「App Store」在搜索框中输入「lightblue」,找到如下App,进行安装
2,打开App,查找ESP32的BLE,名称为「LingShunLAB」
3,点击Write应用服务,发送数据到ESP32 BLE服务端
这里发送的是HEX(16进制的数据),例如:
"LingShunLab" 通过ASCII 码表对照转换成如下:
"76 105 110 103 83 104 117 110 76 65 66",这段10进制再转换成16进制如下:
"4c 69 6e 67 53 68 75 6e 4c 41 42",但在App上输入的不能有空格,最后在APP输入为
"4c696e675368756e4c4142",按下「Done」发送即可。
4,ESP32 BLE服务端收到数据并在PC的串口显示
此时,ESP32 将会收到Iphone发送过来的数据,并且在串口中显示。