ESP32 S3 基于Arduino使用蓝牙客户端及服务端进行板间数据交互-综合1

1,435 阅读5分钟

1 蓝牙技术总结

image.png

1.1 蓝牙4.0协议

  • 蓝牙4.0是协议,4.0是协议版本号,蓝牙4.0是2010年6月由SIG(Special Interest Group)发布的蓝牙标准,它有2种模式:
  • BLE(Bluetooth low energy)只能与4.0协议设备通信,适应节能且仅收发少量数据的设备(如家用电子)
  • BR/EDR(Basic Rate / Enhanced Data Rate),向下兼容(能与3.0/2.1/2.0通信),适应收发数据较多的设备(如耳机)这个模式常常也有人称之为“传统蓝牙”或“经典蓝牙”。
  • 可以这样理解,蓝牙4.0协议包含BLE,BLE隶属于蓝牙4.0协议的一部分 image.png

1.2 GATT概念

image.png

2 客户端及服务端进行板间数据交互

2.1 ESP32 S3蓝牙客户端

#include "BLEDevice.h"

// BLE 服务器名称和 UUID
#define bleServerName "ESP32-S-test" // BLE 服务名称 // BLE 服务器名称 服务器客户端的名称必须匹配

static BLEUUID ServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");              // bmeServiceUUID  TO ServiceUUID
static BLEUUID VCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518"); // temperatureCharacteristicUUID TO VCharacteristicUUID 
static BLEUUID ICharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");   // humidityCharacteristicUUID TO ICharacteristicUUID
static BLEUUID PCharacteristicUUID("92ddd762-3e7d-11ed-b878-0242ac120002");   // humidityCharacteristicUUID TO ICharacteristicUUID


static boolean doConnect = false;// 检查是否连接到服务器的变量
static boolean connected = false;

static BLEAddress *pServerAddress;// 要连接的服务器的地址,该地址将在扫描期间找到
 
static BLERemoteCharacteristic* VCharacteristic;// 要读取的特性 temperatureCharacteristic TO VCharacteristic
static BLERemoteCharacteristic* ICharacteristic; // humidityCharacteristic TO ICharacteristic
static BLERemoteCharacteristic* PCharacteristic; // humidityCharacteristic TO ICharacteristic

//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};

char* V_Value; 
char* I_Value;  
char* W_Value;  

boolean new_V_State = false; 
boolean new_I_State = false;  
boolean new_W_State = false;  

//连接服务器
bool connectToServer(BLEAddress pAddress) {
   BLEClient* pClient = BLEDevice::createClient();
 
  pClient->connect(pAddress);//连接服务器
  USBSerial.println(" - Connected to server");
 
  // 获取服务器中的参数UUID
  BLERemoteService* pRemoteService = pClient->getService(ServiceUUID);
  if (pRemoteService == nullptr) {
    USBSerial.print("Failed to find our service UUID: ");
    USBSerial.println(ServiceUUID.toString().c_str());
    return (false);
  }
 
  // 获取服务器中的特征
  VCharacteristic = pRemoteService->getCharacteristic(VCharacteristicUUID);
  ICharacteristic = pRemoteService->getCharacteristic(ICharacteristicUUID);
  PCharacteristic = pRemoteService->getCharacteristic(PCharacteristicUUID);


  if (VCharacteristic == nullptr || ICharacteristic == nullptr || PCharacteristic == nullptr) {
    USBSerial.print("Failed to find our characteristic UUID");
    return false;
  }
  USBSerial.println(" - Found our characteristics");
 
  //分配回调函数,处理接收到的新数据
  VCharacteristic->registerForNotify(VNotifyCallback);
  ICharacteristic->registerForNotify(INotifyCallback);
  PCharacteristic->registerForNotify(PNotifyCallback);
  return true;
}

// 检查服务器 回调函数
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    if (advertisedDevice.getName() == bleServerName) { //检查找到的设备是否具有正确的 BLE 服务器名称
      advertisedDevice.getScan()->stop(); //停止扫描
      pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //获取服务器地址
      doConnect = true; //标识true后开始建立连接
      USBSerial.println("Device found. Connecting!");
    }
  }
};
 
//数据更新通知 回调函数
static void VNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                        uint8_t* pData, size_t length, bool isNotify) {
  V_Value = (char*)pData;
  new_V_State = true;
}
static void INotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                    uint8_t* pData, size_t length, bool isNotify) {
  I_Value = (char*)pData;
  new_I_State = true;
}

static void PNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                    uint8_t* pData, size_t length, bool isNotify) {
  W_Value = (char*)pData;
  new_W_State = true;
}


void printReadings(){
  USBSerial.print("- V_Value:");
  USBSerial.println(V_Value);
  USBSerial.print("- I_Value:");
  USBSerial.println(I_Value); 
  USBSerial.print("- W_Value:");
  USBSerial.println(W_Value); 
}

void setup() {
  USBSerial.begin(115200);// 115200 的波特率启动串行通信  
  USBSerial.println("Starting Arduino BLE Client application...");

  BLEDevice::init(""); //初始化BLE设备
 
  //扫描附近的设备
  BLEScan* pBLEScan = BLEDevice::getScan();//pBLEScan扫描仪
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());//设置回调函数
  pBLEScan->setActiveScan(true);//启动扫描活动
  pBLEScan->start(30);// 扫描30秒
}

void loop() {

  if (doConnect == true) {
    if (connectToServer(*pServerAddress)) {
      USBSerial.println("We are now connected to the BLE Server.");
      VCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      ICharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      PCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
      connected = true;
    } else {
      USBSerial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
    }
    doConnect = false;
  }
  //数据更新,刷新屏幕显示
  if (new_V_State || new_I_State || new_W_State){
    new_V_State = false;
    new_I_State = false;
    new_W_State = false;
    printReadings();// OLED
  }
  delay(100); 
}

2.2 ESP32 S3蓝牙服务端

  • 作为服务器使用,Server可以传入一组回调函数,分别会在有客户端设备接入时和断开连接时触发。 在有设备接入后Advertising广播会被停止,所以要在设备断开连接时重新开启广播,可以通过此处的回调函数知道设备是否断开连接。
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

// BLE 服务器名称 服务器客户端的名称必须匹配
#define bleServerName "ESP32-S-test"

// 计时器变量
unsigned long lastTime = 0;
unsigned long timerDelay = 100;

bool deviceConnected = false; // 设备已连接布尔变量

float Volt = 220.00;    //电压值设置为220V
float Current = 20.00;  //电流值设置为20A
float Power = 440.00;  //功率设置位440W

static char V_Value[8];  //定义局部静态变量 char型存放电压值字符串
static char I_Value[8];  //定义局部静态变量 char型存放电流值字符串
static char W_Value[8];  //定义局部静态变量 char型存放功率值字符串

#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59" // 服务 UUID

//  Characteristic特征  Descriptor描述 
BLECharacteristic VCharacteristic(
                    "cba1d466-344c-4be3-ab3f-189f80dd7518", 
                    BLECharacteristic::PROPERTY_READ   |
                    BLECharacteristic::PROPERTY_WRITE  |
                    BLECharacteristic::PROPERTY_NOTIFY |
                    BLECharacteristic::PROPERTY_INDICATE
                  );
BLEDescriptor VDescriptor(BLEUUID((uint16_t)0x2902));


// Characteristic特征  Descriptor描述 
BLECharacteristic ICharacteristic(
                      "ca73b3ba-39f6-4ab3-91ae-186dc9577d99", 
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

BLEDescriptor IDescriptor(BLEUUID((uint16_t)0x2902));


// Characteristic特征  Descriptor描述 
BLECharacteristic PCharacteristic(
                      "92ddd765-3e7d-11ed-b878-0242ac120002", 
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

BLEDescriptor PDescriptor(BLEUUID((uint16_t)0x2902));


//回调函数 
class MyServerCallbacks: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    deviceConnected = true;// 客户端连接到服务器,状态为true
  };
  void onDisconnect(BLEServer* pServer) {
    deviceConnected = false;
  }
};


void setup() {
  USBSerial.begin(115200);

  //使用BLE服务器名称创建一个新的BLE设备
  BLEDevice::init(bleServerName);

  // 将 BLE 设备设置为服务器并分配回调函数   
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service                                          bmeService
  BLEService *bmeService = pServer->createService(SERVICE_UUID); // 使用之前定义的服务 UUID 启动 BLE 服务

  // VCharacteristic
  bmeService->addCharacteristic(&VCharacteristic);
  VDescriptor.setValue("V");
  VCharacteristic.addDescriptor(&VDescriptor);
 

  // ICharacteristic
  bmeService->addCharacteristic(&ICharacteristic);
  IDescriptor.setValue("I");
  //ICharacteristic.addDescriptor(new BLE2902());
  ICharacteristic.addDescriptor(&IDescriptor);


  // PCharacteristic
  bmeService->addCharacteristic(&PCharacteristic);
  PCharacteristic.setValue("P");
  //PCharacteristic.addDescriptor(new BLE2902());
  PCharacteristic.addDescriptor(&PDescriptor);

  // 启动服务,服务器启动广告
  bmeService->start(); 
  
  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pServer->getAdvertising()->start();
  USBSerial.println("Waiting a client connection to notify...");
}


void loop() {

// 蓝牙通信
if (deviceConnected) {
    USBSerial.println("client connected.");
    if ((millis() - lastTime) > timerDelay) {

        dtostrf(Volt,6,2,V_Value); // dtostrf()函数:将float数据转换成char型字符串 6是输出字符串的总位数;2是输出字符串小数点后的位数
        VCharacteristic.setValue(V_Value);
        VCharacteristic.notify();
    
        dtostrf(Current,6,2,I_Value); // dtostrf()函数:将float数据转换成char型字符串 6是输出字符串的总位数;2是输出字符串小数点后的位数
        ICharacteristic.setValue(I_Value);  //设置电流的特征值
        ICharacteristic.notify();   

        dtostrf(Power,6,2,W_Value); // dtostrf()函数:将float数据转换成char型字符串 6是输出字符串的总位数;2是输出字符串小数点后的位数
        PCharacteristic.setValue(W_Value);  //设置电流的特征值
        PCharacteristic.notify();           
        
        USBSerial.print(" - V_Value: ");
        USBSerial.println(V_Value); 
        USBSerial.print(" - I_Value: ");
        USBSerial.println(I_Value);
        USBSerial.print(" - W_Value: ");
        USBSerial.println(W_Value);

        lastTime = millis();
    }
  }
  else{USBSerial.println("client connection failed.");}
  
  delay(50);
}