ESP32经典蓝牙和BLE的使用 (基于Arduino)

本文介绍了ESP32如何实现经典蓝牙串口通信,并详细讲解了低功耗蓝牙BLE(BLE)的GATT协议、服务与特征的使用,包括服务器端与客户端的通信实例,展示了如何通过BLE控制LED灯和模拟传感器数据交换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ESP32蓝牙和BLE简述和基本使用方法

经典蓝牙串口通信
#include "BluetoothSerial.h"   //引入蓝牙函数库
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif                               //判断蓝牙是否启用
const int led =  2;
BluetoothSerial SerialBT;         //定义一个自拟的蓝牙串口通信名称
void setup()
{
  pinMode(led,OUTPUT);
  digitalWrite(led,LOW);
  SerialBT.begin("ESP32_Blue");   //启动蓝牙串口并设置蓝牙的名称
  Serial.begin(115200);
}

void loop()
{
  if(SerialBT.available())   //判断蓝牙串口是否收到数据
  {
    char res = SerialBT.read();   //将收到的数据读取出来,下面分别处理
    if(res == 'A')
    {
      digitalWrite(led,LOW);
      Serial.println("Get OFF");
    }
    else if(res == 'B')
    {
      digitalWrite(led,HIGH);
      Serial.println("Get ON");
    }
  }
  delay(10);
}

该程序效果如下

  • ESP32完成初始化后生成一个蓝牙信号

  • 在这里插入图片描述

  • 手机通过蓝牙串口发送字符 B ,LED点亮

  • 手机通过蓝牙串口发送字符 A ,LED熄灭

低功耗蓝牙 BLE (常用)

  • BLE GATT协议

GATT全称Generic Attribute Profile, GATT 代表通用属性,它定义了暴露给连接的BLE设备的分层数据结构。这意味着,GATT 定义了两个BLE设备,发送和接收标准通讯的方式。了解此层次结构很重要,因为这样可以,更轻松地了解如何使用BLE和编写应用程序。
下图为BLE的基本结构,需要记清楚
在这里插入图片描述

  • 低功耗特性
    特征始终归服务所有,它是层次结构(值)中包含实际数据的地方。特征始终具有两个属性:特征声明(提供有关数据的元数据)和特征值。
    此外,特征值后面可以跟描述符,这进一步扩展了特征声明中包含的元数据。
    这些属性描述了如何与特征值交互。基本上,它包含可以与特征一起使用的操作和过程:
    广播
    阅读
    写无响应

    通知
    表示
    经过身份验证的签名写入扩展属性

  • 用户名
    每个服务,特征,描述符都有一个UUID(通用唯一标识符)。UUID是唯一的128位(16 byte)数字
    在线UUID生成

  • 创建 BLE 服务器代码流程

    1,创建一个BLE服务器。在这种情况下,ESP32充当BLE服务器。

    2,创建BLE服务。

    3,在服务上创建BLE特性。

    4,在特征上创建一个BLE描述符。

    5,启动服务。

    6,开始广播,以便其他设备可以找到它。

  • 代码示例

//此程序通过BLE广播,其他设备接受广播,手机通过nRF Connet软件连接,
//手机下发字符 1 点亮LED,字符 0 关闭LED

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEService.h>
#include <BLECharacteristic.h>
#define SERVICE_UUID "b408e1a0-3d8a-11ed-b878-0242ac120002"         //服务UUID
#define CHARACTERISTIC_UUID "dfd84064-3d8a-11ed-b878-0242ac120002"  //特征UUID
#define CONTROL_UUID "de045162-3d97-11ed-b878-0242ac120002"         //控制特征UUID

const int led = 2;
char state = 48;

BLECharacteristic controlCharacteristic(CONTROL_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);

//设置服务器所需要的配置
void setup() {
  Serial.begin(115200);
  BLEDevice::init("BLE_HWD");                                   //创建设备
  BLEServer *pServer = BLEDevice::createServer();               //设置为服务器
  BLEService *pService = pServer->createService(SERVICE_UUID);  //使用上面的服务UUID创建服务
  // 添加一个带有对象名(官方UUID)的特征,不带对象,这个特征不会改变
  BLECharacteristic *nameCharacteristic = pService->createCharacteristic(BLEUUID((uint16_t)0x2A00), BLECharacteristic::PROPERTY_READ);
  nameCharacteristic->setValue("LED");                  //显示特征名
  pService->addCharacteristic(&controlCharacteristic);  //增加一个控制LED的特性
  controlCharacteristic.setValue(&state);

  //设置特征, 使用上面的特征UUID,需要将特征的属性作为参数传递。此情况下是读或写
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
    CHARACTERISTIC_UUID,
    BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic->setValue("Hello HWD!!!");  //创建完特征后,可以使用setValue()方法为其在此赋值
  //此值可以是其他传感器的值
  pService->start();
  //下面是启动服务和广播,以便其他BLE设备找到此 BLE 设备
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();  //BLE广播
  pAdvertising->addServiceUUID(SERVICE_UUID);                  //广播添加服务 UUID
  pAdvertising->setScanResponse(true);                         //广播扫描响应
  pAdvertising->setMinPreferred(0x06);                         //广播设置最小首选
  pAdvertising->setMinPreferred(0x12);                         //广播设置最小首选
  BLEDevice::startAdvertising();                               //BLE 设备启动广播,特征已经定义,可以在手机中读取它
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  Serial.println("Characteristic defined! Now you can read it in your phone!");  //提示消息
}

void loop() {
  std::string controlValue = controlCharacteristic.getValue();  //字符串控件获取特征值
  if (controlValue[0] != state) {
    state = controlValue[0];
    if (state == 48) {
      digitalWrite(led, LOW);
    } else if (state == 49) {
      digitalWrite(led, HIGH);
    }
  }
}
BLE 服务器和客户端通信

下面程序实现了客户端不断从服务器读取模拟传感器的值并显示出来
程序模拟了电压电流和功率信息的传递

服务器端代码

#include "Arduino.h"
#include "BLEDevice.h"              //BLE驱动库
#include "BLEServer.h"              //BLE蓝牙服务器库
#include "BLEUtils.h"               //BLE实用程序库
#include "BLE2902.h"                //特征添加描述符库
#include <BLECharacteristic.h>      //BLE特征函数库

float Volt = 220.00;    //电压值设置为220V
float Current = 20.00;  //电流值设置为20A
float Power = 4400.00;  //功率设置位4400W
//可通过外接传感器改变这些值

#define bleServerName "BLE_HWD_SER"  //BLE服务器的名称
#define SERVICE_UUID "f9da736b-dc49-4b7d-a023-8ecee2e72741"  //服务的UUID

BLECharacteristic V_Characteristics("308a35f8-3e7b-11ed-b878-0242ac120002",BLECharacteristic::PROPERTY_NOTIFY);  //电压的特征值
BLEDescriptor V_Descriptor(BLEUUID((uint16_t)0x2901));  //电压描述符
BLECharacteristic I_Characteristics("92ddd762-3e7d-11ed-b878-0242ac120002",BLECharacteristic::PROPERTY_NOTIFY);  //电流的特征值
BLEDescriptor I_Descriptor(BLEUUID((uint16_t)0x2902));  //电流描述符
BLECharacteristic W_Characteristics("92ddd762-3e7d-11ed-b878-0242ac120002",BLECharacteristic::PROPERTY_NOTIFY);  //功率的特征值
BLEDescriptor W_Descriptor(BLEUUID((uint16_t)0x2903));  //功率描述符

bool connected_state = false;   //创建设备连接标识符 

class MyServerCallbacks:public BLEServerCallbacks   //创建连接和断开调用类
{
    void Connected(BLEServer *pServer)//开始连接函数
    {
        connected_state = true;   //设备正确连接
    }
    void Disconnected(BLEServer *pServer)//断开连接函数
    {
        connected_state = false;  //设备连接错误
    }

};

void setup()
{
    BLEDevice::init(bleServerName);  //创建BLE并设置名称
    BLEServer *pServer = BLEDevice::createServer();  //创建BLE服务器
    pServer->setCallbacks(new MyServerCallbacks());  //设置连接和断开调用类
    BLEService *pService = pServer->createService(SERVICE_UUID); //创建BLE服务

    //创建 BLE 特征和描述符
    pService->addCharacteristic(&V_Characteristics); //创建电压特征
    V_Descriptor.setValue("Volt");  //创建电压描述符
    V_Characteristics.addDescriptor(new BLE2902());  //给电压特征添加描述符

    pService->addCharacteristic(&I_Characteristics); //创建电流特征
    I_Descriptor.setValue("Current");  //创建电流描述符
    I_Characteristics.addDescriptor(new BLE2902());  //给电流特征添加描述符

    pService->addCharacteristic(&W_Characteristics); //创建功耗特征
    W_Descriptor.setValue("Power");  //创建功耗描述符
    W_Characteristics.addDescriptor(new BLE2902());  //给功耗特征添加描述符

    pService->start();  //启动服务
    pServer->getAdvertising()->start();   //开始广播
}

void loop()
{
    if(connected_state)   //有设备连接
    {
        static char V_Value[8];  //定义局部静态变量 char型存放电压值字符串
        dtostrf(Volt,6,2,V_Value);  //将浮点数转化为char型字符串 char Volt_Value[8] = {'2','2','0','.','0','0'}
        V_Characteristics.setValue(V_Value);  //设置电压的特征值
        V_Characteristics.notify();   //发送特征值通知

        static char I_Value[8];  //定义局部静态变量 char型存放电流值字符串
        dtostrf(Current,6,2,I_Value);  
        I_Characteristics.setValue(I_Value);  //设置电流的特征值
        I_Characteristics.notify();   //发送特征值通知

        static char W_Value[8];  //定义局部静态变量 char型存放功率值字符串
        dtostrf(Power,6,2,W_Value);  
        W_Characteristics.setValue(W_Value);  //设置功率的特征值
        W_Characteristics.notify();   //发送特征值通知
    }
}

客户端代码

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLECharacteristic.h>
#include <BLEServer.h>
#include<BLEService.h>
//引入头文件
#define bleServer "BLE_HWD_SER"
static BLEUUID ServiceUUID("f9da736b-dc49-4b7d-a023-8ecee2e72741");           //服务的UUID
static BLEUUID V_CharacteristicUUID("308a35f8-3e7b-11ed-b878-0242ac120002");  //电压特征UUID
static BLEUUID I_CharacteristicUUID("92ddd762-3e7d-11ed-b878-0242ac120002");  //电流特征UUID
static BLEUUID W_CharacteristicUUID("6b74077b-607f-4078-b179-4afeb03c34b7");  //功率特征UUID
static boolean doconnect = false;   //连接标识
static boolean connected = false;   //启动标识
static BLEAddress *pServerAddress;  //BLE服务器地址,外围设备的地址,地址在扫描过程中找到
static BLERemoteCharacteristic *V_Characteristic;  //需要读写的特征的指针
static BLERemoteCharacteristic *I_Characteristic;  //需要读写的特征的指针
static BLERemoteCharacteristic *W_Characteristic;  //需要读写的特征的指针
const uint8_t notificationON[] = {0x1, 0x0};   //激活打开
const uint8_t notificationOFF[] = {0x0,0x0};   //通知关闭

char *V_value;  //存储电压值
char *I_value;  //存储电流值
char *W_value;  //存储功率值
boolean newV_value = false; //新电压值是否可用标志位
boolean newI_value = false; //新电流值是否可用标志位
boolean newW_value = false; //新功率值是否可用标志位

//当BLE服务器发送带有通知属性的新电压读数 回调函数
static void V_NotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,uint8_t* pData, size_t length, bool isNotify)
{
  V_value = (char*)pData;   //用于存储新的电压值
  newV_value = true;        //将新电压值判断置为 真
}

//当BLE服务器发送带有通知属性的新电流读数 回调函数
static void I_NotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,uint8_t* pData, size_t length, bool isNotify)
{
  I_value = (char*)pData;   //用于存储新的电流值
  newI_value = true;        //将新电流值判断置为 真
}

//当BLE服务器发送带有通知属性的新功率读数 回调函数
static void W_NotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,uint8_t* pData, size_t length, bool isNotify)
{
  W_value = (char*)pData;   //用于存储新的功率值
  newW_value = true;        //将新功率值判断置为 真
}


bool connectToServer(BLEAddress pAddress)
{
  BLEClient *pClint = BLEDevice::createClient();  //BLE创建客户端
  pClint->connect(pAddress);  //连接到服务器
  BLERemoteService *pRemoteService = pClint->getService(ServiceUUID);   //在远程BLE服务器中获取我们所需要的服务器的引用
  if(pRemoteService == nullptr)
  {
    return false;
  }
  V_Characteristic = pRemoteService->getCharacteristic(V_CharacteristicUUID);  //获取远程BLE服务器服务中的特征属性
  I_Characteristic = pRemoteService->getCharacteristic(I_CharacteristicUUID);  //获取远程BLE服务器服务中的特征属性
  W_Characteristic = pRemoteService->getCharacteristic(W_CharacteristicUUID);  //获取远程BLE服务器服务中的特征属性
  if(V_Characteristic == nullptr || I_Characteristic == nullptr || W_Characteristic == nullptr)
  {
    return false;   //如果未找到特征,返回false;
  }
  //为特征分配各自的调用函数
  V_Characteristic->registerForNotify(V_NotifyCallback);  //特征注册通知
  I_Characteristic->registerForNotify(I_NotifyCallback);  //特征注册通知
  W_Characteristic->registerForNotify(W_NotifyCallback);  //特征注册通知
  return true;
}

class MyAdvertisedDeviceCallbacks:public BLEAdvertisedDeviceCallbacks  //当接收到另一个设备的广播时,调用广播
{
  void onResult(BLEAdvertisedDevice advertiseDevice)   //BLE广播函数
  {
    if(advertiseDevice.getName() == bleServer)  //检查名字是否匹配
    {
        advertiseDevice.getScan()->stop();  //停止扫描,我们已经扫描到了需要的BLE服务器
        pServerAddress = new BLEAddress(advertiseDevice.getAddress());   //BLE广播地址是需要的
        doconnect = true;  //设置指示器,已经准备好连接
        Serial.println("Found Correctly!!");
    }
  }
};


void UARTprint()    //串口输出信息函数
{
  Serial.print("V_value: ");
  Serial.print(V_value);
  Serial.print("V,  ");
  Serial.print("I_value: ");
  Serial.print(I_value);
  Serial.print("A,   ");
  Serial.print("W_value: ");
  Serial.print(W_value);
  Serial.println("W");
}


/****************************************************************************/
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  BLEDevice::init("");    //初始化BLE客户端设备
  BLEScan *pBLEScan = BLEDevice::getScan();   //检索扫描器并设置调用
  //pBLEScan->setAdvertisedDeviceCallbacks(new );  //设置广播并调用"广播类"
  pBLEScan->setActiveScan(true);  //设置为主动扫描
  pBLEScan->start(30);    //扫描运行30秒
}

void loop() {
  // put your main code here, to run repeatedly:
  if(doconnect == true) //如果doconnect为真,说明已经扫描到了需要的BLE服务器
  {
    if(connectToServer(*pServerAddress))  //找到服务器之后连接到服务器地址
    {
      Serial.println("Now Connected");
      V_Characteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationON,2,true);//启动电压特征通知属性
      I_Characteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationON,2,true);//启动电流特征通知属性
      W_Characteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationON,2,true);//启动功率特征通知属性
      connected = true;  //一旦连接到BLE服务器,将连接标志设置为真
    }
    else
    {
      Serial.println("Can't connect,Please turn on again");
    }
    doconnect = false;  //将连接标志设置为假
  }
  if(newV_value && newI_value && newW_value)  //如果有新的度数可用,串口输出信息
  {
    newV_value = false;
    newI_value = false;
    newW_value = false;   //重新设置为假
    UARTprint();  //调用自定义串口信息输出函数
  }
  delay(1000);
}

接到BLE服务器,将连接标志设置为真
}
else
{
Serial.println(“Can’t connect,Please turn on again”);
}
doconnect = false; //将连接标志设置为假
}
if(newV_value && newI_value && newW_value) //如果有新的度数可用,串口输出信息
{
newV_value = false;
newI_value = false;
newW_value = false; //重新设置为假
UARTprint(); //调用自定义串口信息输出函数
}
delay(1000);
}

### ESP32 GATT BLE Implementation and Protocol Details In the context of Bluetooth Low Energy (BLE), Generic Attribute Profile (GATT) plays a crucial role in defining how data is structured and exchanged between devices. For ESP32, implementing GATT-based communication involves setting up services, characteristics, and descriptors that facilitate interaction with other BLE-enabled devices. The ESP32 supports creating both central and peripheral roles within BLE communications. When acting as a peripheral device, an ESP32 module advertises its presence to potential centrals through advertising packets containing service UUIDs or other identifying information[^1]. Once connected, interactions occur over established connections where attributes are read from or written into based on predefined rules set forth under GATT specifications. For developing applications involving custom protocols like the non-standard BLE Serial Port Profile (SPP), developers need to define specific services along with their associated characteristics which mimic traditional serial port behavior. This includes handling data transmission commands such as sending/receiving bytes streams while ensuring proper framing mechanisms are employed so messages do not get corrupted during transfer. To implement these functionalities programmatically: ```cpp #include <esp_bt.h> #include "nvs_flash.h" #include "esp_ble_gatts_api.h" // Define your own Service UUID here. #define SERVICE_UUID 0x180F #define CHARACTERISTIC_UUID 0x2A19 void setup() { // Initialize NVS — if using the partition table default nvs esp_err_t ret = nvs_flash_init(); // Code for initializing BLE stack... } void loop() { // Main code logic goes here... } ``` This snippet initializes necessary components required before establishing any form of connection but does not cover all aspects involved when working directly at lower levels of abstraction provided by Espressif's SDK.
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值