双ESP32蓝牙通讯-Arduino

前言

        代码最终实现功能:蓝牙从机(服务器端)通过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 试验结果打印

五、总结

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

ESP32Arduino之间的蓝牙通信可以通过使用ESP32作为蓝牙服务器和Arduino作为蓝牙客户端来实现。以下是一个简单的例子,演示了如何在ESP32Arduino之间进行蓝牙通信: 1. 首先,需要在ESP32上启用蓝牙服务器功能。可以使用Arduino IDE和ESP32的BLE库来实现。以下是一个简单的代码片段,演示了如何在ESP32上启用蓝牙服务器功能: ```c++ #include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> BLEServer* pServer = NULL; BLECharacteristic* pCharacteristic = NULL; bool deviceConnected = false; bool oldDeviceConnected = false; uint8_t txValue = 0; #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; }; void onDisconnect(BLEServer* pServer) { deviceConnected = false; } }; void setup() { Serial.begin(115200); BLEDevice::init("ESP32_BLE_Server"); pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE ); pCharacteristic->addDescriptor(new BLE2902()); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); pAdvertising->addServiceUUID(pService->getUUID()); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x06); pAdvertising->setMinPreferred(0x12); BLEDevice::startAdvertising(); Serial.println("Waiting for a client connection to notify..."); } void loop() { if (deviceConnected) { pCharacteristic->setValue(&txValue, 1); pCharacteristic->notify(); txValue++; delay(10); // bluetooth stack will go into congestion, if too many packets are sent } 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; } if (deviceConnected && !oldDeviceConnected) { // do stuff here on connecting oldDeviceConnected = deviceConnected; } } ``` 2. 接下来,需要在Arduino上编写蓝牙客户端代码。以下是一个简单的代码片段,演示了如何在Arduino上连接到ESP32蓝牙服务器并发送数据: ```c++ #include <SoftwareSerial.h> SoftwareSerial BTSerial(10, 11); // RX | TX void setup() { Serial.begin(9600); BTSerial.begin(9600); // HC-05 default speed in AT command more } void loop() { if (BTSerial.available()) { Serial.write(BTSerial.read()); } if (Serial.available()) { BTSerial.write(Serial.read()); } } ``` 在这个例子中,Arduino使用SoftwareSerial库将数据发送到ESP32蓝牙服务器。可以使用Serial Monitor来查看从ESP32返回的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mao十七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值