前言
代码最终实现功能:蓝牙从机(服务器端)通过Notify向蓝牙主机(客户端)发送数据
一、蓝牙从机(服务器端)
// 首先是蓝牙设备(Device)作为服务器使用(Server);
// 一个服务器中运行了一个或多个服务(Service),每个服务由一个UUID来标识;
// 每个服务中含有一个或多个特征(Characteristic),每个特征由一个UUID来标识;
// 每个特征中包含一个值(Value),另外还包含零个或多个描述(Descriptor),每个描述由一个UUID来标识;
// 通过notify向主机也就是客户端发送数据,因为notify是非阻塞,不会确认客户端是否收到,但默认情况智慧发送value的前20字节数据
#include <BLEDevice.h>
#include <BLE2902.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" // 自定义UUID
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" // 自定义UUID
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
uint32_t value = 0;
// Server回调函数声明
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("设备接入~");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("设备断开~");
// 在有设备接入后Advertising广播会被停止,所以要在设备断开连接时重新开启广播
// 不然的话只有重启ESP32后才能重新搜索到
pServer->startAdvertising(); //该行效果同 BLEDevice::startAdvertising();
}
};
// 特征回调函数声明
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue(); // 读取特征的新值
Serial.print("特征值已更新,新值为: ");
Serial.println(value.c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println();
BLEDevice::init("ESP32-BLE");
BLEServer *pServer = BLEDevice::createServer(); // 创建服务器
BLEService *pService = pServer->createService(SERVICE_UUID); // 创建服务
// pCharacteristic = pService->createCharacteristic( // 创建特征
// CHARACTERISTIC_UUID,
// BLECharacteristic::PROPERTY_NOTIFY | // 启用通知
// BLECharacteristic::PROPERTY_INDICATE // 启用通知
// );
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pCharacteristic->setValue("Hello World! ");
pCharacteristic->addDescriptor(new BLE2902()); // 不加这行可能无法使用通知
pServer->setCallbacks(new MyServerCallbacks()); // 绑定回调函数
pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); // 绑定特征回调函数
pService->start(); // 启动服务
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID); // 广播服务的UUID
BLEDevice::startAdvertising();
}
unsigned long previousMillis = 0;
const long interval = 2000;
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
pCharacteristic->notify(); // 每隔interval时间主动推送一次数据
}
}
二、蓝牙主机(客户端)
#include <BLEDevice.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
boolean doScan = true;
boolean doConnect = false;
boolean connected = false;
BLEAdvertisedDevice* pServer = nullptr;
BLERemoteCharacteristic* pRemoteCharacteristic = nullptr;
BLEClient* pClient = nullptr;
// 搜索到设备时回调功能
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
public:
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveName() && advertisedDevice.getName()=="ESP32-BLE") {
advertisedDevice.getScan()->stop(); // 停止当前扫描
pServer = new BLEAdvertisedDevice(advertisedDevice); // 暂存设备
doScan = false;
doConnect = true;
Serial.println("发现想要连接的设备");
}
}
};
// 客户端与服务器连接与断开回调功能
class MyClientCallback : public BLEClientCallbacks {
public:
void onConnect(BLEClient* pclient) {
connected = true;
Serial.println("连接设备成功");
}
void onDisconnect(BLEClient* pclient) {
connected = false;
doScan = true;
Serial.println("失去与设备的连接");
if (pClient) {
delete pClient;
pClient = nullptr;
}
}
};
// 收到服务推送的数据时的回调函数
void NotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
char buf[length + 1];
memcpy(buf, pData, length);
buf[length] = '\0';
Serial.printf("该消息长度为: %d; 内容为: %s\r\n", length, buf);
}
// 用来连接设备获取其中的服务与特征
bool ConnectToServer() {
pClient = BLEDevice::createClient();
if (!pClient) {
Serial.println("创建客户端失败");
return false;
}
pClient->setClientCallbacks(new MyClientCallback()); // 添加客户端与服务器连接与断开回调功能
if (!pClient->connect(pServer)) { // 尝试连接设备
Serial.println("连接设备失败");
delete pClient;
pClient = nullptr;
return false;
}
BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID); // 尝试获取设备中的服务
if (!pRemoteService) {
Serial.println("获取服务失败");
pClient->disconnect();
return false;
}
pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
if (!pRemoteCharacteristic) {
Serial.println("获取特性失败");
pClient->disconnect();
return false;
}
Serial.println("获取特征成功");
if (pRemoteCharacteristic->canRead()) { // 如果特征值可以读取则读取数据
Serial.printf("该特征值可以读取并且当前值为: %s\r\n", pRemoteCharacteristic->readValue().c_str());
}
if (pRemoteCharacteristic->canNotify()) {
pRemoteCharacteristic->registerForNotify(NotifyCallback);
}
return true;
}
void setup() {
Serial.begin(115200);
Serial.println();
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(100);
pBLEScan->setWindow(80);
}
void loop() {
// 如果需要扫描则进行扫描
if (doScan) {
Serial.println("开始搜索设备");
BLEDevice::getScan()->clearResults();
BLEDevice::getScan()->start(0); // 持续搜索设备
}
// 如果找到设备就尝试连接设备
if (doConnect) {
if (ConnectToServer()) {
connected = true;
} else {
doScan = true;
}
doConnect = false;
}
// 如果已经连接就可以向设备发送数据
if (connected && pRemoteCharacteristic && pRemoteCharacteristic->canWrite()) {
String newValue = "mytime: " + String(millis()/1000);
Serial.printf("像特征写入消息: %s\r\n", newValue.c_str());
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
delay(3500); // 非阻塞延时或计时器控制发送间隔
}
}
三、从机通过Notify向主机发送十六进制数据
3.1 从机部分的修改
unsigned long previousMillis = 0;
const long interval = 2000;
int16_t testArr[10] = {0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0011};
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
pCharacteristic->setValue((uint8_t*)&testArr, sizeof(testArr));
pCharacteristic->notify(); // 每隔interval时间主动推送一次数据
}
}
3.2 主机部分的修改
void NotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
int numValues = length / sizeof(int16_t);
int16_t* dataArr = (int16_t*)pData;
Serial.printf("Received %d int16_t values:\n", numValues);
for (int i = 0; i < numValues; ++i) {
// Handle endianness if necessary
int16_t value = dataArr[i]; // This retrieves each int16_t value
Serial.printf("Value %d: %04x\n", i, value);
}
}
四、客户端修改MTU大小
MTU大小决定Notify每次可以发送多少字节,默认大小是23字节,最大只能发送20字节的数据,我们可以修改MTU大小来解决发送超出20字节的问题,MTU大小的修改范围为23~512,下面是需要修改的地方
4.1 从机部分的修改
可以通过自定义一个256*2字节的数组来测试是否修改成功
int16_t testArr[256] = {0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, 0x0128, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0126, 0x0127, 0x0128 };
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
pCharacteristic->setValue((uint8_t*)&testArr, sizeof(testArr));
pCharacteristic->notify(); // 每隔interval时间主动推送一次数据
}
}
4.2 主机部分的修改
class MyClientCallback : public BLEClientCallbacks {
public:
void onConnect(BLEClient* pclient) {
connected = true;
Serial.println("连接设备成功");
// 设置MTU大小为你想要的值
if (pClient->setMTU(512)) { // 尝试将MTU设置为512字节
Serial.println("MTU set successfully.");
} else {
Serial.println("Failed to set MTU.");
}
}
void onDisconnect(BLEClient* pclient) {
connected = false;
doScan = true;
Serial.println("失去与设备的连接");
if (pClient) {
delete pClient;
pClient = nullptr;
}
}
};
4.3 试验结果打印

五、总结
整体代码是从以下两个链接总结而来,如果有不明白的地方可以参考以下两个文章:
784

被折叠的 条评论
为什么被折叠?



