目录

  • 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 一起使用。

  • 服务集合 Profile: 针对特定用例的标准服务集合;
  • 服务 Service: 收集相关信息,如传感器读数、电池电量、心率等;
  • 特征 Characteristic: 它是实际数据保存在层次结构(值)上的位置;
  • 描述 Descriptor: 关于数据的元数据;
  • 属性 Properties: 描述如何与特征值交互。例如:读、写、通知、广播、指示等。

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

  • 一个用于温度
  • 一个用于湿度。

实际温度和湿度读数保存在其特性值上。每个特征都有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 WROOM 32开发板
  • 微型指拨摇杆开关 C2685355
  • 0.96 OLED液晶屏
  • ESP32 扩展板

ESP32 BLE 客户端:

  • ESP32 WROOM 32开发板

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

  • ESP32 扩展板


4.2 ESP32 BLE 服务器

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

遇到一个问题,当 ESP32 服务器端加入OLED屏幕显示部分程序,ESP32服务器端和ESP32客户端不能蓝牙配对,连接不上。注释掉

//Server V.05.10
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Wire.h>       // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>#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 电机方向 设置为输出模式 初始化为HIGHpinMode(BUT_D_PIN, INPUT_PULLDOWN);   //GPIO18 按键按-下// OLED 屏幕初始化if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32Serial.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                                          bmeServiceBLEService *bmeService = pServer->createService(SERVICE_UUID); // 使用之前定义的服务 UUID 启动 BLE 服务// TemperaturebmeService->addCharacteristic(&dutyCycleCharacteristic);dutyCycleDescriptor.setValue("dutyCycle");dutyCycleCharacteristic.addDescriptor(&dutyCycleDescriptor);// HumiditybmeService->addCharacteristic(&dirStateCharacteristic);dirStateDescriptor.setValue("BME humidity");dirStateCharacteristic.addDescriptor(new BLE2902());// 启动服务,服务器启动广告bmeService->start(); // Start advertisingBLEAdvertising *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/LOWbuttonState = 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 <Wire.h>       // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>#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 dirStateCharacteristicUUIDstatic 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 dirStateCharboolean 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");// 获取服务器中的参数UUIDBLERemoteService* 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 valuedutyCycleChar = (char*)pData;newdutyCycle = true;
}
static void dirStateNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {//store dirState valuedirStateChar = (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.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32Serial.println(F("SSD1306 allocation failed"));for(;;); // Don't proceed, loop forever}// OLED 屏幕显示"BLE Client"display.clearDisplay();display.setTextSize(2);display.setTextColor(WHITE,0);display.setCursor(0,25);display.print("BLE Client");display.display();Serial.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)) {Serial.println("We are now connected to the BLE Server.");dutyCycleCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);dirStateCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);connected = true;} else {Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");}doConnect = false;}//数据更新,刷新屏幕显示if (newdutyCycle || newdirState){newdutyCycle = false;newdirState = false;printReadings();// OLED}delay(100);
}

参考资料

  • [1] 【RNT】在 Arduino IDE 上开始使用 ESP32 蓝牙低功耗 (BLE)
  • [2]【RNT】ESP32 BLE 服务器和客户端(蓝牙低功耗)
  • [3] 【CSDN@Breeze_yk】 蓝牙开发中常用参数的解释

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

  1. Unity 入门笔记 - 05 - 动画事件类音效对话框

    Unity 入门笔记 - 05 - 动画事件&类&音效&对话框 前言:无 目录 Unity 入门笔记 - 05 - 动画事件&类&音效&对话框 一.动画 ...

  2. ESP32 入门笔记03:PWM (ESP32 for Arduino IDE)

    先导知识 ESP32 入门笔记01:开发板信息.开发环境搭建以及学资料准备 ESP32 入门笔记02: GPIO参考指南 ESP32 有一个 LED PWM 控制器,具有 16 个独立通道,可配置为生 ...

  3. ESP32 入门笔记08:1.54寸(240*240)彩色TFT 显示高清IPS LCD 屏幕 SPI接口

    目录 1.屏幕规格 2.原理图 3.程序实现 3.1引脚定义 3.2Adafruit_GFX / Arduino_ST7789版 3.3TFT_eSPI库版 3.3.1配置TFT_eSPI a.选择屏 ...

  4. ESP32 入门笔记01:乐鑫ESP32-DevKitC开发板信息、开发环境搭建以及学资料准备

    文章目录 一.ESP32-DevKitC 开发板介绍 二.ESP32 开发环境 1. Arduino IDE 1.1 开发环境搭建步骤 1.2 实验:使用 Arduino IDE 将代码上传到 ESP ...

  5. MicroPython开发ESP32入门笔记 -- 蓝牙篇

    文章目录 前言 一. ESP32 和 Micropython 简介 二.蓝牙模组通讯原理简介 三.手机端和ESP32蓝牙通讯 1. ESP32蓝牙呼吸灯代码 2. 手机端准备 总结 前言 博主之前学习 ...

  6. ESP32入门基础之ble spp client 和 ble spp server 的学习理解

    文章目录 1 工程简介 2 工程分析 2.1 工程 ble_spp_client 分析 2.1.1 初始化分析 2.1.2 两BLE扫描连接.配置.参数同步分析 2.1.3 蓝牙数据发送流程分析 2. ...

  7. ESP32 入门笔记07: ESP-NOW (ESP32 for Arduino)

    [B站乐鑫] ESP-NOW 无线通信方案 简介 ESP-NOW 是一种快速.无连接的通信技术,具有短数据包传输的特点. ESP-NOW 是乐鑫开发的一种"协议",它使多个设备无需 ...

  8. [ESP32]学习笔记05

    ESP32 I2C外设的使用 读写AT24C04 FreeRTOS嵌入式实时操作系统思想 今天我们通过使用ESP32的I2C外设来进行对EEPROM的读写操作,本次我们使用FreeRTOS进行创建任务 ...

  9. 10.SVN入门笔记——使用 SVN 独立客户端TortoiseSVN

    TortoiseSVN 简介 TortoiseSVN 是一个 Windows 下的版本控制系统 Apache™ Subversion®的客户端工具. TortoiseSVN 的优良特性 外壳集成 To ...

最新文章

  1. word2vec安装以及使用
  2. 为什么ConcurrentHashMap是弱一致的
  3. ffmpeg + x264+ x265 + libde265 + opengl es display
  4. Linux ps指令
  5. div固定大小文字溢出自动缩小_Figma 教程 | 文字工具
  6. Charles笔记-配置Charles代理抓取HTTP和HTTPS数据包,安卓模拟器连接Charles
  7. php远程文件无法编辑,“脚本编辑器”远程文件编辑漏洞
  8. oracle type rowtype详解
  9. 解决办法:Type safety: The expression of type List needs unchecked conversion to conform
  10. Shallow Neural Network Week 3
  11. 哥德尔 艾舍尔 巴赫--集异璧之大成
  12. 6.1行为型模式--模板方法模式
  13. PCB测试流程分析介绍
  14. 加州房价篇 (三) : 模型的训练,评估和房价的预测
  15. openEuler-risc-v学习笔记
  16. 31.SUM() 函数
  17. javaee实验:使用mvc模式 设计一个图书管理系统
  18. PDF阅读软件哪个好用?思路提供
  19. if……else if……else注意事项与基本用法
  20. 规则引擎 Drools:规则引擎概述

热门文章

  1. python输入数字并求和_Python如何输入数字并求和
  2. python-onvif 库踩坑
  3. Andrej Karpathy读博建议和写论文的方法
  4. An error occurred at line: [14] in the generated java file:
  5. 获取百度网盘真实链接
  6. java实现表情符号过滤
  7. 【机器学习实战系列】读书笔记之DecisionTree(ID3算法)(三)
  8. 6、Flutter Widgets 之 InkWell 和 Ink
  9. js中写文档write和innerHTML的区别
  10. 【NVMe-MI 1.2a - 1】NVM Express Management Interface介绍