ESP32之经典蓝牙库BluetoothSerial介绍和实例演示

Bruce小鬼 2024-09-02 13:07:03 阅读 93

ESP32之经典蓝牙库BluetoothSerial介绍和实例演示

1.概述

目前ESP32内置了双模蓝牙(蓝牙4.0版本之前都是经典蓝牙,4.0版本成为BLT低功耗蓝牙转为物联网开发。双模指的就是这款芯片两种模式都支持)。

这篇文章介绍ESP32蓝牙的经典模式使用方法,那么就有疑问了,既然有新版本的蓝牙为什么还要介绍旧版本的蓝牙使用那,这就和使用场景有关了,经典蓝牙库简单,BLT库复杂。如果只是实现首发数据那么经典蓝牙更合适。如果对功耗有要求那么可以使用BLT库开发。

2.蓝牙BluetoothSerial库介绍

经典蓝牙模式开发使用<code>BluetoothSerial库,这个库在Arduino中已经存在,不需要单独下载,下面就来介绍下这个库常用的函数,以及如何使用这些库开发程序。

2.1.主从机模式介绍

不同的蓝牙设备之间的连接他们都有一个身份,这个身份就是主机还是从机。

下面介绍下两个身份的区别:

主机身份:

主机身份可疑主动搜索从机的蓝牙地址,并且和他们建立连接。一个主机可以同时连接多个从机。

从机身份

从机模式不能搜索其他设备的蓝牙不能主动建立连接,只能被搜索。从设备和主机建立连接后可以和主设备收发数据,从设备可以允许多个主设备配对,但只能允许一台主机连接。

2.2.常用库介绍

1.获取自身蓝牙地址

每个设备的蓝牙都有一个唯一的mac地址,它由6个字节组成。使用esp_bt_dev_get_address()函数可以获取本机的蓝牙mac地址。

获取本机蓝牙mac地址示例

#include "esp_bt_main.h"

#include "esp_bt_device.h"

#include "esp32-hal.h"

#include "BluetoothSerial.h"

void setup() { -- -->

//设置蓝牙波特率

Serial.begin(115200);

//开启蓝牙

btStart();

//初始化蓝牙协议

esp_bluedroid_init();

//开启蓝牙协议

esp_bluedroid_enable();

}

void loop() {

//获取本机蓝牙地址

auto address = esp_bt_dev_get_address();

if(address){

for(int i=0; i<6; i++){

// 格式化输出蓝牙6个字节内容

Serial.printf("%02X", address[i]);

if(i<5){

Serial.print(":");

}

}

Serial.println();

delay(1000);

}

}

在Arduino上查看查看蓝牙地址信息时候,需要将波特率调整为115200

在这里插入图片描述

2.建立连接和收发信息函数
函数名称 解释
BluetoothSerial(void) 构造函数
bool begin(String localName=String(), bool isMaster=false) 初始化蓝牙,从机初始化完后就可以接受主机的连接了;localName:蓝牙的名称,如果为空的话,默认为ESP32;isMater:是否是主机,默认是从机,从机可以被主机连接
bool hasClient(void) 是否有设备已经连接
bool disconnect() 关闭当前的spp连接
bool isclosed() spp连接是否已经关闭
bool isReady(bool checkMaster=false, int timeout=0); voidend(void) /spp信道是否可用,如果checkMaster为true还会同时检测本机是否是主机,不是的话也会返回false
voidend(void) 关闭蓝牙功能
bool unpairDevice(uint8 t remoteAddress[]) 解除指定地址的蓝牙设备的配对
3.收发数据函数
函数名称 解释
int available(void) 有多少数据可以读
void setTimeout(int timeoutMS) 设置读写的超时时间,默认是0,马上返回
int peek(void) 读缓冲区第一字节,如果读取错误,返回—1
int read(void) 读取一个字节,如果读取错误,返回—1
size t write(uint8 t c) 发送一个字节
size_t write(const uint8_t *buffer, size t size) 最多发送size字节,返回成功发送的字节 数
size_t print()
size_t printf()
size_t println() 三个print系列的函数都是返回实际发送的字节
void flush() 将数据从缓冲区强制送入信道
size t readBytes(char *buffer, size_t length) 字节方式读取数据
size t readBytesUntil(char terminator, char *buffer, size t length) 字节方式读取数据
String readstring() 字符串方式读取数据
String readStringUntil(char terminator) 字符串方式读取数据

3.ESP32作为从机示例

3.1.最简单的从机发送和接收数据例子

这个例子将ESP32设置为从机,程序中没有使用SPP协议,他不需要密码验证就可以配对连接。

<code>#include "BluetoothSerial.h"

BluetoothSerial SerialBT; //定义一个蓝牙对象

void setup() { -- -->

Serial.begin(115200);

SerialBT.begin("ESP32Fish"); //蓝牙的名字叫ESP32Fish, 从机模式

Serial.println("The device started, now you can pair it with bluetooth!");

}

void loop() {

char buf[129];

//判断是否接收到数据

if(SerialBT.available())

{

auto sz = SerialBT.readBytes(buf, 128); //从蓝牙接收数据

if(sz)

{

buf[sz] = 0;

Serial.println(buf);

strcat(buf, " - Slave");

SerialBT.write((uint8_t*)buf, strlen(buf)); //从蓝牙发送数据

}

}

delay(20);

}

1.将程序复制到ArduinoIDE,然后将程序上传到ESP32.

2.这个时候需要拿出手机,开发蓝牙设置搜索名称为ESP32Fish蓝牙设备,然后配对。

3.配对成功后,在windows电脑上通过浏览器搜索Bluetooth Serial Tool 蓝牙调试工具并安装。

4.打开Bluetooth Serial Tool工具:

点击Refresh刷新然后选择蓝牙名称点击Connect连接输入内容点击发送

在这里插入图片描述

查看ArduinoIDE窗口接收到了消息

在这里插入图片描述

3.2.SPP开启SSP验证最简单例子

开启SSP验证在配对的时候就需要输入验证码才能连接。

下面是开启SSP的函数

<code>//启用SSP认证,配对的时候会主机会产生一个密码发送给从机,我们从机手动确认,主机侧也得确认

void enableSSP();

//设置配对认证请求回调函数

void onConfirmRequest(ConfirmRequestCb cb);

//设置配对认证结果回调函数

void onAuthComplete(AuthCompleteCb cb);

//是否同意连接,true:同意 false:不同意

void confirmReply(boolean confirm);

typedef std: function<void(uint32_t num val)> ConfirmRequestCb;

typedef std: function<void(boolean success)> AuthCompleteCb;

关于配对的一些提示:

如果你两台设备配对过了,你的从机改了认证方式,改了蓝牙名,实测两台设备之前还是处于配对状态。

开启SSP验证示例代码

#include "BluetoothSerial.h"

BluetoothSerial SerialBT; //定义一个蓝牙对象

//认证请求回调

void BTConfirmRequestCallback(uint32_t numVal)

{ -- -->

//numVal是主机发来的识别码

Serial.printf("recv pin: %d \r\n", numVal);

//这里要对这个识别码进行判断,是否和主机一样或是是否是我们从机内置的密码

//然后再判断是否确定连接,我们这里直接确认了

SerialBT.confirmReply(true);

//SerialBT.confirmReply(false); //如果要拒绝就用这句

}

//认证结果回调函数

void BTAuthCompleteCallback(boolean success)

{

if (success)

Serial.println("Pairing success!!");

else

Serial.println("Pairing failed, rejected by user!!");

}

void RecvData(const uint8_t *buffer, size_t size)

{

if(size > 0)

{

Serial.write(buffer, size); //打印出来

SerialBT.write(buffer, size);

SerialBT.println(" - Slave");

}

}

void setup() {

SerialBT.enableSSP(); //在begin之前调用

SerialBT.onConfirmRequest(BTConfirmRequestCallback);

SerialBT.onAuthComplete(BTAuthCompleteCallback);

Serial.begin(115200);

SerialBT.onData(RecvData); //注册接收回调函数

SerialBT.begin("ESP32Fish"); //蓝牙的名字叫ESP32Fish, 从机模式

Serial.println("The device started, now you can pair it with bluetooth!");

}

void loop() {

}

上传程序后,删除之前的配对信息,然后重新配对,就会提示输入code码。

在这里插入图片描述

4.ESP32作为主机介绍

4.1.主机常用函数

函数名称 介绍
BTScanResults* discover(int timeout=0x30*1280) 阻塞方式进行蓝牙设备搜索,未到达超时时间,程序会在该函数上一直等待,形成阻塞
bool discoverAsync(BTAdvertisedDeviceCb cb, int timeout=0x30*1280) 事件方式(非阻塞)进行蓝牙设备搜索;cb:回调函数;timeout:超时时间,搜索多少ms
void discoverAsyncStop() 取消由discoverAsync启动的蓝牙搜索,并清空回调函数
void discoverClear() 清空对象保存的搜索结果
BTScanResults* getScanResults() 获取蓝牙搜索结果
bool begin(String localName=String(), bool isMaster=false) 初始化蓝牙;localName:蓝牙的名称,如果为空的话,默认为ESP32;isMater:设置为主机,要设置为true
bool connect(String remoteName) 根据名字连接,速度慢
bool connect(uint8 t remoteAddress[], int channel=0, esp_spp_sec_t sec_mask=(ESP_SPP_SEC_ENCRYPT ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role=ESP_SPP_ROLE_MASTER) 根据MAC地址,速度快
bool connected(int timeout=0) 是否已成功连接
bool hasclient(void) 是否有设备已经连接

4.2.阻塞式搜索蓝牙示例

将下面的程序复制到ArduinoIDE,然后上传到ESP3上。打开窗口调试窗口,可以看到它如果搜索到蓝牙设备,则会点亮自身的D2 灯。

<code>#include "BluetoothSerial.h"

#include <esp_bt_device.h>

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

void setup() { -- -->

pinMode(LEDPIN, OUTPUT);

digitalWrite(LEDPIN,LOW);

Serial.begin(115200);

//初始化蓝牙,设置蓝牙名称和主机模式

if(!SerialBT.begin("ESP32Fish2", true))

{

Serial.println("BT begin failed.");

return;

}

delay(1500);

//阻塞式发现设备

auto ret = SerialBT.discover(10000);

if(ret == NULL)

{

Serial.println("discover failed");

return;

}

//如果搜到蓝牙设备就点亮esp32板子上D2led灯

digitalWrite(LEDPIN, HIGH);

//输出搜索到的设备名称

int count = ret->getCount();

Serial.printf("count: %d\r\n", count);

for(int i=0; i<count; i++)

{

auto dev = ret->getDevice(i);

if(dev)

{

auto name = dev->getName(); //name

auto rssi = dev->getRSSI(); //rssi

auto cod = dev->getCOD(); //cod

auto address = dev->getAddress();

Serial.printf("dev %d: %s [%s]\r\n", i, name.c_str(), address.toString().c_str());

}

}

}

void loop() {

// put your main code here, to run repeatedly:

}

4.3.非阻塞式搜索蓝牙示例

下面是非阻塞式搜索蓝牙示例,在示例中使用了AsyncTimer库,需要使用ArduinoIDE安装下这个库。

程序不会阻塞在搜索蓝牙过程中,在搜索过程中可以运行其他的任务。

#include "BluetoothSerial.h"

#include <AsyncTimer.h>

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

AsyncTimer t;

//获取设备信息

void Advertised(BTAdvertisedDevice* pDevice) {

auto name = pDevice->getName(); //name

auto rssi = pDevice->getRSSI(); //rssi

auto cod = pDevice->getCOD(); //cod

auto address = pDevice->getAddress();

Serial.printf("dev : %s [%s]\r\n", name.c_str(), address.toString().c_str());

}

void setup() {

pinMode(LEDPIN, OUTPUT);

digitalWrite(LEDPIN,LOW);

Serial.begin(115200);

//初始化蓝牙设备,设置名称和主机模式

if(!SerialBT.begin("ESP32Fish2", true))

{

Serial.println("BT begin failed.");

return;

}

delay(1500);

//非阻塞式搜索蓝牙设备信息

if (SerialBT.discoverAsync(Advertised)) {

Serial.println("find start...");

} else {

Serial.println("find error");

return;

}

//10s后取消搜索

t.setTimeout([]{

Serial.println("find stop...");

SerialBT.discoverAsyncStop();

}, 10000);

}

void loop() {

t.handle();

//这里可以在搜索的时候同时干其它事

}

4.4.非ssp模式双蓝牙收发送信息

下面的示例演示了主从机两个ESP32开发板通过蓝牙互相发送信息的过程,这个代码中省去了搜索蓝牙的代码如果需要这个功能,可以复制上面搜索蓝牙的代码。

1.从机模式

将代码上传到ESP32开发板,然后在调试窗口查看输出的蓝牙地址。

#include "BluetoothSerial.h"

#include <esp_bt_device.h>

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

//定义一个接收信息方法

void RecvData(const uint8_t *buffer, size_t size)

{

if(size > 0)

{

Serial.write(buffer, size); //打印出来

SerialBT.write(buffer, size);

SerialBT.println(" - Slave");

}

}

void setup() {

pinMode(LEDPIN, OUTPUT);

Serial.begin(115200);

SerialBT.onData(RecvData); //注册接收回调函数

SerialBT.begin("ESP32Fish1"); //蓝牙的名字叫ESP32Fish, 从机模式

auto address = esp_bt_dev_get_address(); //获取本机的蓝牙mac地址

if(address) //找到地址了

{

for(int i=0;i<6;i++)

{

Serial.printf("%02X", address[i]);

if(i<5)

Serial.print(":");

}

Serial.println();

}

Serial.println("The device started, now you can pair it with bluetooth!");

}

void loop()

{

//判断是否有设备连接

if(SerialBT.hasClient())

{

digitalWrite(LEDPIN, HIGH);

}

else

{

digitalWrite(LEDPIN, LOW);

}

delay(200);

}

2.主机模式

下面的程序需要将uint8_t clientAddress 蓝牙地址改为上面从机输出的蓝牙地址

然后将程序上传到另一个ESP32开发板。

#include "BluetoothSerial.h"

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

/*将这个蓝牙地址改为你的从机输出的蓝牙地址,这里省去了搜索蓝牙的功能,

因此需要手动配置从机的蓝牙地址进行连接。如果需要搜索蓝牙功能,

可以复制上面搜索蓝牙的代码。

*/

uint8_t clientAddress[] { 0xCC,0x7B, 0x5C, 0x24, 0xC1, 0x46};

// 蓝牙接收到的信息

void RecvData(const uint8_t *buffer, size_t size)

{

if(size > 0)

{

Serial.write(buffer, size); //打印出来

Serial.println();

}

}

void setup() {

Serial.begin(115200);

pinMode(LEDPIN, OUTPUT);

SerialBT.onData(RecvData); //注册接收回调函数

//初始化蓝牙设备,设置名称和主机模式

if(!SerialBT.begin("ESP32Fish2", true))

{

Serial.println("BT begin failed.");

return;

}

auto start = millis();

//指定名称连接

//if(!SerialBT.connect("ESP32Fish1"))

//根据搜索到的蓝牙地址连接

if(!SerialBT.connect(clientAddress))

{

Serial.println("connect failed");

return;

}

auto end = millis();

Serial.printf("Connect successed: %d \r\n", end-start);

}

void loop() {

//判断蓝牙设备是否已连接

if(SerialBT.connected())

{

digitalWrite(LEDPIN, HIGH);

auto sz = SerialBT.println("Hello");

Serial.println(sz);

}

else

{

digitalWrite(LEDPIN, LOW);

}

delay(500);

}

在主机和从机调试窗口可以看到他们连接成功后互相发送信息。

在这里插入图片描述

4.5.ssp模式双蓝牙收发送信息

1.从机模式

<code>#include "BluetoothSerial.h"

#include <esp_bt_device.h>

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

//认证请求回调

void BTConfirmRequestCallback(uint32_t numVal)

{ -- -->

//numVal是主机发来的识别码

Serial.printf("recv pin: %d \r\n", numVal);

SerialBT.confirmReply(true);

}

//认证结果回调函数

void BTAuthCompleteCallback(boolean success)

{

if (success)

Serial.println("Pairing success!!");

else

Serial.println("Pairing failed, rejected by user!!");

}

void RecvData(const uint8_t *buffer, size_t size)

{

if(size > 0)

{

Serial.write(buffer, size); //打印出来

SerialBT.write(buffer, size);

SerialBT.println(" - Slave");

}

}

void setup() {

pinMode(LEDPIN, OUTPUT);

SerialBT.enableSSP(); //在begin之前调用

SerialBT.onConfirmRequest(BTConfirmRequestCallback);

SerialBT.onAuthComplete(BTAuthCompleteCallback);

Serial.begin(115200);

SerialBT.onData(RecvData); //注册接收回调函数

SerialBT.begin("ESP32Fish1"); //蓝牙的名字叫ESP32Fish, 从机模式

auto address = esp_bt_dev_get_address(); //获取本机的蓝牙mac地址

if(address) //找到地址了

{

for(int i=0;i<6;i++)

{

Serial.printf("%02X", address[i]);

if(i<5)

Serial.print(":");

}

Serial.println();

}

Serial.println("The device started, now you can pair it with bluetooth!");

}

void loop()

{

if(SerialBT.hasClient())

{

digitalWrite(LEDPIN, HIGH);

}

else

{

digitalWrite(LEDPIN, LOW);

}

delay(200);

}

2.主机模式

#include "BluetoothSerial.h"

#include <AsyncTimer.h>

#define LEDPIN 2

BluetoothSerial SerialBT; //定义一个蓝牙对象

/*将这个蓝牙地址改为你的从机输出的蓝牙地址,这里省去了搜索蓝牙的功能,

因此需要手动配置从机的蓝牙地址进行连接。如果需要搜索蓝牙功能,

可以复制上面搜索蓝牙的代码。

*/

uint8_t clientAddress[] { 0xCC,0x7B, 0x5C, 0x24, 0xC1, 0x46};

AsyncTimer t;

//认证请求回调

void BTConfirmRequestCallback(uint32_t numVal)

{

Serial.print("发送code");

//numVal是主机发来的识别码

Serial.printf("recv pin: %d , (y|n) \r\n", numVal);

int num =0;

while(true)

{

if(Serial.available())

{

int ch = Serial.read();

if(ch=='Y' || ch=='y')

{

Serial.println("yes");

SerialBT.confirmReply(true);

break;

}

else if(ch=='N' || ch=='n')

{

Serial.println("no");

SerialBT.confirmReply(false);

break;

}

if(num++ >=20)

break;

}

delay(500);

}

}

//认证结果回调函数

void BTAuthCompleteCallback(boolean success)

{

if (success)

Serial.println("Pairing success!!");

else

Serial.println("Pairing failed, rejected by user!!");

}

//接收信息

void RecvData(const uint8_t *buffer, size_t size)

{

if(size > 0)

{

Serial.write(buffer, size); //打印出来

Serial.println();

}

}

void setup() {

Serial.begin(115200);

pinMode(LEDPIN, OUTPUT);

//启用SSP协议

SerialBT.enableSSP();

//设置配对认证请求回调函数

SerialBT.onConfirmRequest(BTConfirmRequestCallback);

//设置配对认证结果回调函数

SerialBT.onAuthComplete(BTAuthCompleteCallback);

SerialBT.onData(RecvData); //注册接收回调函数

if(!SerialBT.begin("ESP32Fish2", true))

{

Serial.println("BT begin failed.");

return;

}

auto start = millis();

if(!SerialBT.connect("ESP32Fish1"))

{

Serial.println("connect failed");

return;

}

auto end = millis();

Serial.printf("Connect successed: %d \r\n", end-start);

}

void loop() {

t.handle();

if(SerialBT.hasClient())

{

digitalWrite(LEDPIN, HIGH);

SerialBT.print("Hello");

}

else

{

digitalWrite(LEDPIN, LOW);

}

delay(2000);

}

打开调试窗口,会出现一个验证码,然后在主机的调试窗口发送 y 就可以连接,返送n就拒绝连接。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。