锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

ESP32 入门笔记05: BLE 蓝牙客户端和服务器 (ESP32 for Arduino IDE)

时间:2022-12-08 19:30:00 p492传感器2300bk温度传感器bk电源连接器

目录

  • 1. BLE 服务器和客户端
  • 2. GATT 协议
  • 3. UUID
  • 4. 项目概述
    • 4.1 材料清单
    • 4.2 ESP32 BLE 服务器
    • 4.3 ESP32 BLE 客户端
  • 参考资料


1. BLE 服务器和客户端

蓝牙低功耗设备有两种:服务器客户端。ESP32 既可以作为客户端,也可以作为服务器。

服务器 宣传它的存在,所以它可以被其他设备发现和包含客户端 可读数据。客户扫描附近的设备,当它找到正在寻找的服务器时,它会建立连接和监控传输的数据。这就是所谓的点对点通信(还有其他可能的通信模式,如广播模式网状网络)。

确定:谁是服务器,谁是客户端?(滑板) (手柄)

2. GATT 协议

GATT (Generic Attribute Profile) 它代表一般属性,定义为连接 BLE 分层数据结构公开设备。这意味着 GATT 定义了两个 BLE 发送和接收标准信息的方式。理解这个层次结构很重要,因为它更容易理解如何理解 BLE 与 ESP32 一起使用。

在这里插入图片描述

在示例中,我们将创建一个有两个特征服务

  • 用于温度
  • 用于湿度。

保存实际温度和湿度读数特性值上。每个特征都有notify属性,以便在值变更时通知客户端。

3. UUID

每个 服务特征描述符 都有一个 UUID(通用唯一标识符)。UUID 是唯一的 128 位(16 例如:

55072829-bc9e-4c53-938a-74a6d4c78776

如果你的应用程序需要你自己的应用程序 UUID,你可以用这个UUID 生成器网站生成它。

总之,UUID 用于唯一的识别信息。例如,它可以识别蓝牙设备提供的特定服务。


4. 项目概述

在这个教程中,你将学习如何在两个 ESP32 板之间建立 BLE 连接。一个 ESP32 将作为 BLE 服务器,另一个 ESP32 将作为 BLE 客户端。

本项目分为两部分:

  • 第 1 部分 – ESP32 BLE 服务器 (手柄)
  • 第 2 部分 – ESP32 BLE 客户端(滑板)

4.1 材料清单

Parts Required

ESP32 BLE 服务器:

ESP32 BLE 客户端:

  • ESP32 WROOM 32开发板

  • BLDC三相直流无刷直流电机控制器

  • ESP32 扩展板


4.2 ESP32 BLE 服务器

我们将在这部分设置 BLE 服务器宣传包括两个特征服务:一个是PWM信号值,另一个是DIR方向信号。具有这些特征 Notify通知属性 通知客户端新值。

遇到问题,当 ESP32 加入服务器端OLED屏幕显示部分程序,ESP32服务器端和ESP32客户端无法配对蓝牙,无法连接。

//Server V.05.10 #include  #include  #include  #include  #include  // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA) #include  #include   #define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 

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

const int ADC_PIN = 34;           // ADC GPIO 34 (Analog ADC1_CH6) 电源
const int ADC_RS_Y_PIN = 35;         // Rocker switch forward&back GPIO 35 (ADC1_CH7) (摇杆-Y-前后)
const int DIR_PIN = 26;           // Direction GPIO 26 
const int BUT_D_PIN = 18;            // Button down GPIO 18 (按键-下)


// 定义电源ADC相关变量 GPIO34
int adcValue = 0;          // 存放读取的ADC数值0~4095
// 定义摇杆Y轴ADC相关变量 GPIO35
int rsyadcValue = 0;          // 存放读取的ADC数值0~4095 

// 定义PWM相关变量
int dutyCycle = 0;               // PWM 占空比

// 定义DIR方向相关变量
unsigned int dirState = HIGH; //BUT_D_PIN按下控制方向变化
boolean buttonState = false;  


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

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


// 服务、以摄氏度为单位的温度特性,湿度定义UUID
#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59" // 服务 UUID

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


// 
BLECharacteristic dirStateCharacteristic(
                      "ca73b3ba-39f6-4ab3-91ae-186dc9577d99", 
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

BLEDescriptor dirStateDescriptor(BLEUUID((uint16_t)0x2903));


// OLED I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

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



//oled 屏幕显示
void printReadings(){ 
        
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
  
    display.setCursor(0, 5);//设置显示坐标
    display.print(F("PWM: "));
    display.println(dutyCycle);
  
    display.setCursor(0, 20);//设置显示坐标
    display.print(F("ADC: "));  display.print(adcValue); display.print(" ");display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比% 
    
  display.setCursor(0, 35);//设置显示坐标
    display.print(F("rsyAdc: "));  display.println(rsyadcValue);  
  
    display.display(); // 屏幕上实际显示文本
}

void setup() { 
        
  Serial.begin(115200);
  pinMode(DIR_PIN, OUTPUT); digitalWrite(DIR_PIN,dirState);         //GPIO26 电机方向 设置为输出模式 初始化为HIGH
  pinMode(BUT_D_PIN, INPUT_PULLDOWN);   //GPIO18 按键按-下

  // OLED 屏幕初始化
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
         // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); 
  }
  // OLED 屏幕显示"BLE Server"
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE,0);
  display.setCursor(0,25);
  display.print("BLE Server");
  display.display();

  //使用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 服务

  // Temperature
    bmeService->addCharacteristic(&dutyCycleCharacteristic);
    dutyCycleDescriptor.setValue("dutyCycle");
    dutyCycleCharacteristic.addDescriptor(&dutyCycleDescriptor);
 

  // Humidity
  bmeService->addCharacteristic(&dirStateCharacteristic);
  dirStateDescriptor.setValue("BME humidity");
  dirStateCharacteristic.addDescriptor(new BLE2902());
  
  // 启动服务,服务器启动广告
  bmeService->start(); 
  
  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}


void loop() { 
        
// ADC 电源
  adcValue = analogRead(ADC_PIN);
  Serial.print("- ADC:");Serial.println(adcValue);

// ADC 摇杆-Y-前后
    rsyadcValue = analogRead(ADC_RS_Y_PIN);
    Serial.print("-RSY ADC:");Serial.println(rsyadcValue);

// PWM 控制电机速度
  dutyCycle = (map(analogRead(ADC_RS_Y_PIN),2047,4095,0,255));
  Serial.print("- PWM = ");Serial.println(dutyCycle);
  if(dutyCycle <= 0 || dutyCycle > 255){ 
        dutyCycle = 0;}

// Dir 控制电机方向
    if(digitalRead(BUT_D_PIN) == HIGH && buttonState == false)
  { 
        
    dirState = !digitalRead(DIR_PIN);     //GPIO26电机方向控制
    digitalWrite(DIR_PIN,dirState);       // GPIO26 方向控制 输出HIGH/LOW
    buttonState = true;
  }
  else if (digitalRead(BUT_D_PIN) == LOW && buttonState == true)
  { 
        
    buttonState = false;
  }  
  
// 蓝牙通信
  if (deviceConnected) { 
        
    
    // Serial.println("client connected.");
    if ((millis() - lastTime) > timerDelay) { 
        

    
        static char dutyCycleTemp[6];
        dtostrf(dutyCycle, 6, 0, dutyCycleTemp);// dtostrf()函数:将float数据转换成char型字符串 6是输出字符串的总位数;2是输出字符串小数点后的位数
        dutyCycleCharacteristic.setValue(dutyCycleTemp);
        dutyCycleCharacteristic.notify();
    
        static char dirStateTemp[6];
        dtostrf(dirState, 6, 0, dirStateTemp);
        dirStateCharacteristic.setValue(dirStateTemp);  
        dirStateCharacteristic.notify();   

        Serial.print(" - PWM: ");
        Serial.println(dutyCycleTemp); 
        Serial.print(" - DIR: ");
        Serial.println(dirStateTemp);

        printReadings();// 屏幕显示

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

4.3 ESP32 BLE 客户端

在这一部分中,我们将设置 BLE 客户端来接收服务器来宣传的两个特征服务:一个是PWM信号值,另一个是DIR方向信号(其实还需要有一个BK刹车信号)。并显示在OLED屏幕上。

值得注意的是,dirStateChar = (char*)pData; 接收到的是字符指针,需要将char* 型数据转换成 int 型数据。

//Client
#include "BLEDevice.h"
#include  // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA)
#include 
#include 

#define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 

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

static BLEUUID pwmServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");              // bmeServiceUUID TO pwmServiceUUID
static BLEUUID dutyCycleCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518"); // temperatureCharacteristicUUID TO dutyCycleCharacteristicUUID 
static BLEUUID dirStateCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");   // humidityCharacteristicUUID TO dirStateCharacteristicUUID

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

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

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

// I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

char* dutyCycleChar; //服务器接收到的数据 temperatureChar TO dutyCycleChar
char* dirStateChar;  // humidityChar TO dirStateChar

boolean newdutyCycle = false; //是否有新的数据 newTemperature TO newdutyCycle
boolean newdirState = false;  // newHumidity TO newdirState


//连接服务器
bool connectToServer(BLEAddress pAddress) { 
        
   BLEClient* pClient = BLEDevice::createClient();
 
  pClient->connect(pAddress);//连接服务器
  Serial.println(" - Connected to server");
 
  // 获取服务器中的参数UUID
  BLERemoteService* pRemoteService = pClient->getService(pwmServiceUUID);
  if (pRemoteService == nullptr) { 
        
    Serial.print("Failed to find our service UUID: ");
    Serial.println(pwmServiceUUID.toString().c_str());
    return (false);
  }
 
  // 获取服务器中的特征
  dutyCycleCharacteristic = pRemoteService->getCharacteristic(dutyCycleCharacteristicUUID);
  dirStateCharacteristic = pRemoteService->getCharacteristic(dirStateCharacteristicUUID);

  if (dutyCycleCharacteristic == nullptr || dirStateCharacteristic == nullptr) { 
        
    Serial.print("Failed to find our characteristic UUID");
    return false;
  }
  Serial.println(" - Found our characteristics");
 
  //分配回调函数,处理接收到的新数据
  dutyCycleCharacteristic->registerForNotify(dutyCycleNotifyCallback);
  dirStateCharacteristic->registerForNotify(dirStateNotifyCallback);
  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后开始建立连接
      Serial.println("Device found. Connecting!");
    }
  }
};
 
//数据更新通知 回调函数
static void dutyCycleNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                        uint8_t* pData, size_t length, bool isNotify) { 
        
  //store dutyCycle value
  dutyCycleChar = (char*)pData;
  newdutyCycle = true;
}
static void dirStateNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                    uint8_t* pData, size_t length, bool isNotify) { 
        
  //store dirState value
  dirStateChar = (char*)pData;
  newdirState = true;
  // Serial.print(newdirState);
}

//oled 屏幕显示
void printReadings(){ 
        
  
  display.clearDisplay(); 
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("PWM: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(dutyCycleChar);
  
  //display dirState 
  display.setTextSize(1);
  display.setCursor(0, 35);
  display.print("DIR: ");
  display.setTextSize(2);
  display.setCursor(0, 45);
  display.print(dirStateChar);
  display.display();
  
  Serial.print("- PWM:");
  Serial.println(dutyCycleChar);
  Serial.print("- DIR:");
  Serial.println(dirStateChar); 
}

void setup() { 
        
  Serial.begin(115200);// 115200 的波特率启动串行通信
  
  // OLED 屏幕初始化
  if(!display.

相关文章