ESP32 Arduino学习篇 (三) 蓝牙

news2024/12/28 5:25:03

简介:

1.1蓝牙分类

经典蓝牙我们一般说的是BT,低功耗蓝牙我们一般说成BLE。当设备支持蓝牙4.0时,还得进一步确认设备是支持BT单模、BLE单模还是BT和BLE都支持的双模。

  • 低功耗蓝牙 (BLE): 支持蓝牙协议4.0或更高的模块。主打低功耗,多用于物联网类型。

  • 经典蓝牙( BT): 指支持蓝牙协议在4.0以下的模块。主打短距离数据高速传输,多用于蓝牙耳机等。

  • 经典蓝牙可再细分为:传统蓝牙和高速蓝牙。

  • 传统蓝牙: 2004年推出,蓝牙2.0/2.1协议。

  • 高速蓝牙: 2009年推出,蓝牙3.0协议,速率提高到约24Mbps,是传统蓝牙模块的八倍。

  • 双模蓝牙: 即兼容BLE和BT,如手机,使用分时机制来达到同时与低功耗蓝牙和经典蓝牙设备通信。

1.2蓝牙技术

蓝牙协议包括两种技术:Basic Rate(BR)Low Energy(LE)。

Basic Rate又包括可选的EDR(Enhanced Data Rate) 技术,以及 交替使用的(Alternate)的MAC(Media Access Control)层和PHY层扩展(简称AMP)。、

在蓝牙4.0及后面规格中,SIG定义了四种蓝牙技术:BR,EDR,AMP和LE ,由于LE是2010年才提出的,比较新,所以人们把之前的BR/EDR/AMP技术称之为经典蓝牙。

  • 经典蓝牙( BT):

  • BR(Basic Rate): 蓝牙基础速率技术。

  • EDR(Enhanced Data Rate) : 蓝牙增强速率技术。

  • AMP (Alternate MAC/PHYs): 蓝牙核心系统的次要控制器,可切换的媒体访问控制器(Media Access Controller)和物理层(Physical Layer)。

  • 低功耗蓝牙 (BLE):

  • LE(Low Energy): 蓝牙低功耗技术。

注意:
EDR 是在 BR 技术基础上升级,所以两者可以同时使用。但是AMP 技术是使用的802.11(WIFI)规范,所以和原有的技术差异过大,所以BR/EDR和AMP只能二选一进行使用。
LE技术相比BR技术,差异非常大,可以说就是两种不同的技术。经典蓝牙和低功耗蓝牙两者物理层调制解调方式是不一样的,所以低功耗蓝牙设备和经典蓝牙设备两者之间是不能相互通信的。

而我们的esp32自带蓝牙模块可以让我们进行蓝牙连接并进行数据交换

相关API:

一、经典蓝牙BT使用方法

经典蓝牙API

  • BluetoothSerial SerialBT :创建一个蓝牙串口,命名为SerialBT

  • SerialBT.begin() :开启蓝牙

  • SerialBT.available() :返回蓝牙串口缓冲区中当前剩余的字符个数。

  • SerialBT.print() :蓝牙串口发送的是字符,

  • SerialBT.write() :蓝牙串口发送的字节.

  • Serial.read() :返回蓝牙串口接收的字符

程序示例代码:

//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Evandro Copercini - 2018
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial

#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

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());//将串口收到的数据,再通过蓝牙串口转发出去
    Serial.println("由SerialBT打印");
  }
  if (SerialBT.available()) {//将蓝牙串口收到的数据,再通过串口把信息发回给电脑
    Serial.write(SerialBT.read());
     Serial.println("由Serial打印");
  }
  delay(20);
}

二、低功耗蓝牙BLE使用方法

简介:

BLE GATT协议

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

下图为BLE的基本结构,需要记清楚

UUID:ble的服务和characteristic是通过UUID来进行识别的。创建uuid可以使用这么一个网站:

Online UUID Generator Tool

notify:如果这个主机的一个特征值characteristic发生改变,就可以通过notify来告诉我们

创建 BLE 服务器代码流程:

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

  • 2,创建BLE服务。

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

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

  • 5,启动服务。

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

低功耗蓝牙API:

创建一个BLE设备

    BLEDevice::init(ble_name);

创建一个BLE服务

    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks()); //设置回调
    BLEService *pService = pServer->createService(SERVICE_UUID);

创建一个BLE特征

pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
pRxCharacteristic->setCallbacks(new MyCallbacks()); //设置回调

这里创建了一个特征值,类型是通知。

在后面使用createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE)将rx收集到的信息写入,通过MyCallbacks再打印出来

为了实现"串口",我们在这个服务下添加了两个特征值, 一个是TX. 一个是RX.另外还需注意三个uuid因该是相对应的值。

开始服务和广播

    pService->start();                  // 开始服务
    pServer->getAdvertising()->start(); // 开始广播
    Serial.println(" 等待一个客户端连接,且发送通知... ");

代码示例

/*
    Video: https://www.youtube.com/watch?v=oCMOYS71NIU
    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
    Ported to Arduino ESP32 by Evandro Copercini

   Create a BLE server that, once we receive a connection, will send periodic notifications.
   创建一个BLE服务器,一旦我们收到连接,将会周期性发送通知

   T使用步骤:
   1. 创建一个 BLE Server
   2. 创建一个 BLE Service
   3. 创建一个 BLE Characteristic
   4. 创建一个 BLE Descriptor
   5. 开始服务
   6. 开始广播


*/
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "common.h"

uint8_t txValue = 0;                         //后面需要发送的值
BLEServer *pServer = NULL;                   //BLEServer指针 pServer
BLECharacteristic *pTxCharacteristic = NULL; //BLECharacteristic指针 pTxCharacteristic
bool deviceConnected = false;                //本次连接状态
bool oldDeviceConnected = false;             //上次连接状态d
// See the following for generating UUIDs: https://www.uuidgenerator.net/
#define SERVICE_UUID "12a59900-17cc-11ec-9621-0242ac130002" // UART service UUID
#define CHARACTERISTIC_UUID_RX "12a59e0a-17cc-11ec-9621-0242ac130002"
#define CHARACTERISTIC_UUID_TX "12a5a148-17cc-11ec-9621-0242ac130002"

class MyServerCallbacks : public BLEServerCallbacks
{
    void onConnect(BLEServer *pServer)
    {
        deviceConnected = true;
    };

    void onDisconnect(BLEServer *pServer)
    {
        deviceConnected = false;
    }
};

class MyCallbacks : public BLECharacteristicCallbacks
{
    void onWrite(BLECharacteristic *pCharacteristic)
    {
        std::string rxValue = pCharacteristic->getValue(); //接收信息

        if (rxValue.length() > 0)
        { //向串口输出收到的值
            Serial.print("RX: ");
            for (int i = 0; i < rxValue.length(); i++)
                Serial.print(rxValue[i]);
            Serial.println();
        }
    }
};

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

    // 创建一个 BLE 设备
    BLEDevice::init("BAKUMAN");//在这里面是ble的名称

    // 创建一个 BLE 服务
    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks()); //设置回调
    BLEService *pService = pServer->createService(SERVICE_UUID);

    // 创建一个 BLE 特征
    pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
    pTxCharacteristic->addDescriptor(new BLE2902());
    BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
    pRxCharacteristic->setCallbacks(new MyCallbacks()); //设置回调

    pService->start();                  // 开始服务
    pServer->getAdvertising()->start(); // 开始广播
    Serial.println(" 等待一个客户端连接,且发送通知... ");
}

void loop()
{
    // deviceConnected 已连接
    if (deviceConnected)
    {
        pTxCharacteristic->setValue(&txValue, 1); // 设置要发送的值为1
        pTxCharacteristic->notify();              // 广播
        txValue++;                                // 指针数值自加1
        delay(2000);                              // 如果有太多包要发送,蓝牙会堵塞
    }

    // disconnecting  断开连接
    if (!deviceConnected && oldDeviceConnected)
    {
        delay(500);                  // 留时间给蓝牙缓冲
        pServer->startAdvertising(); // 重新广播
        Serial.println(" 开始广播 ");
        oldDeviceConnected = deviceConnected;
    }

    // connecting  正在连接
    if (deviceConnected && !oldDeviceConnected)
    {
        // do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
}

在loop函数中我们的蓝牙连接会对应三种状态:

这里讲解一下当设备连接时的情况:

if (deviceConnected){
        pTxCharacteristic->setValue(&txValue, 1); // 设置要发送的值为1
        pTxCharacteristic->notify();              // 广播
        txValue++;                                // 指针数值自加1
        delay(2000);                              // 如果有太多包要发送,蓝牙会堵塞
    }

这里面测试的是不停发送数据,每发一次数值加一。 此时:

class MyServerCallbacks : public BLEServerCallbacks
{
    void onConnect(BLEServer *pServer)
    {
        deviceConnected = true;
    };

    void onDisconnect(BLEServer *pServer)
    {
        deviceConnected = false;
    }
};

通过这个回调可以说清楚这个蓝牙到底有没有连接,如果连接了通过

pServer->setCallbacks(new MyServerCallbacks());

这个MyServerCallbacks回调就可以返回连接状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手上有了这些工具,明天争取6点下班!

一个优秀的开发者&#xff0c;一定是会利用各种工具来提升自己的开发效率。这段时间&#xff0c;我开始分享优质提升开发效率的工具&#xff0c;集成效率工具&#xff0c;开源集成网站等等&#xff0c;朋友们一直在催更&#xff0c;我知道你很急&#xff0c;但是你先别急&#…

最短路径算法刷题笔记

Dijkstra最短路算法 带负权则无法处理&#xff0c;不能使用Dijkstra算法 Dijkstra算法以 点 出发。D——点从剩下的点里的最短路dis最小的出发 SPFA单源最短路算法 算是bellman-ford算法 对于稀疏图来说&#xff0c;比Dijkstra算法快 SPFA算法可以用于有负权图有负环则不行 …

文档扫描OCR识别_积累

opecncv和python的积累cv2.resize()功能&#xff1a;对图像尺寸调整为指定大小dst cv2.resize(src, dsize, interpolationNone)各参数释义&#xff1a;scr&#xff1a; 输入图像dst: 输出图像dsize&#xff1a; 输出图像的尺寸&#xff0c;为元组格式(x, y)&#xff0c;(300, …

MySQL基础(2)—— 初识MySQL

文章目录客户端 - 服务器架构MySQL的可执行文件1、在命令行解释器中运行可执行文件2、服务器程序和客户端程序2.1 服务器程序 mysqld2.2 客户端程序 mysql注意事项1、客户端 - 服务器连接注意事项2、SQL语句使用注意事项客户端 - 服务器架构 MySQL当中存在客户端和服务器&…

不知道该学那一个语言?一文带你了解三门语言

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python。正在学习c 主页&#xff1a;阿玥的小东东 目录 粉丝留言&#xff0c;回答问题 1.首先&#xff0c;初步了解 2.再明确自己要拿编程做什么 3.详细介绍以及建议 4.劝告 5.总结 1.如果你是一个初学者 2.你是个老手 …

vulnhub之COFFEE ADDICTS: 1

1.信息收集 输入arp-scan 192.168.239.0/24发现192.168.239.168主机存活。 使用nmap对目标主机192.168.239.168进行端口收集,&#xff0c;发现存活端口&#xff1a;22、80。 访问http://192.168.239.168/&#xff0c;提示将coffeeaddicts.thm加入到/etc/hosts中 加入之后访…

docker 安装启动异常

问题现象1 yum install docker -y #安装docker后&#xff0c;在启动docker时出现 排查过程 查看系统日志下面软件运行日志messages日志&#xff0c;出现如下错误 在docker的配置文件种有异常的文件符号存在&#xff0c;那么接下来就百度一下正常的docker的配置文件是如何的…

【HBase——陌陌海量存储案例】6. 预分区表

6.5 预分区表 默认创建表的方式&#xff0c;则HBase顺序写入可能会受到RegionServer热点的影响。对行键进行加盐可以解决热点问题。在HBase中&#xff0c;可以使用两种方式&#xff1a; 1.ROWKEY预分区 2.加盐指定数量分区 6.5.1 ROWKEY预分区 按照用户ID来分区&#xff0c;一…

Docker 入门详解!新手也能看懂!

接下来通过4方面来初步了解docker docker介绍docker架构docker镜像操作docker容器操作 一、docker介绍 容器技术 计算机的世界中&#xff0c;容器拥有一段漫长且传奇的历史。容器与管理程序虚拟化 &#xff08;hypervisor virtualization&#xff0c;HV&#xff09;有所不同…

postman断言使用总结

一、断言的概念一个完整的接口测试包括&#xff1a;请求——>获取响应结果——>断言断言&#xff1a;实际结果与预期结果的校验二、断言的作用通过实际结果与预期结果的对比&#xff0c;来判断接口测试用例是否通过&#xff1b;对比结果一致则通过&#xff0c;对比结果不…

webgl绘制图形API——drawArrays、drawElements

文章目录前言gl.drawArrays()——按顶点绘制可绘制基本类型绘制矩形和圆形gl.drawElements()——按索引绘制使用规范绘制矩形总结前言 gl.drawArrays()作为webgl中常用的函数图形绘制方法&#xff0c;可以在浏览器按照指定的模式绘制图形&#xff0c;与之相对的gl.drawElement…

字节跳动测试岗面试挂在2面,复盘后,我总结了失败原因,决定再战一次...

先说下我基本情况&#xff0c;本科不是计算机专业&#xff0c;现在是学通信&#xff0c;然后做图像处理&#xff0c;可能面试官看我不是科班出身没有问太多计算机相关的问题&#xff0c;因为第一次找工作&#xff0c;字节的游戏专场又是最早开始的&#xff0c;就投递了&#xf…

Java图形化界面---JSplitPane和JTabbedPane

目录 一、JSplitPane &#xff08;1&#xff09;JSplitPane的介绍 &#xff08;2&#xff09;JSplitPane的使用步骤 &#xff08;3&#xff09;案例 二、JTabbedPane &#xff08;1&#xff09;JTabbedPane的介绍 &#xff08;2&#xff09;JTabbedPane的使用步骤 &…

CSS样式基础内容6

目录 定位 为什么需要定位&#xff1f; 定位的四种方式 静态定位 相对定位 绝对定位&#xff08;脱标&#xff09; 子绝父相 固定定位&#xff08;脱标&#xff09; 固定定位---固定到版心右侧 粘性定位 定位的叠放顺序z-index 绝对定位盒子水平垂直居中 定位特殊特…

C#/WPF入门到多项目实战开发教程2——

登录界面 布局 右上角退出按钮 编辑图标和名称 用户名设置 布局 用户名框设置 密码框设置&#xff0c;使用模板进行设置 验证码框设置&#xff0c;使用模板进行设置 登录按钮 创建登录模板按钮 绑定模板 失败提醒 第三方登录设置 使用字体图标去替代一般图标&#xff0c;字体…

QThread的应用——在线程里面更新QProgressBar进度条

在线程里面更新QProgressBar进度条 编写QT软件的时候&#xff0c;经常会遇到点击某个按钮&#xff0c;进行一个比较耗时的计算。为了在计算过程中&#xff0c;软件界面就继续响应用户的点击&#xff0c;不会有卡死的感觉&#xff0c;一般会将这个耗时的计算放在另外一个线程里…

“打家劫舍”系列总结,偷不偷这个房间呢?(Java实现)

目录 前言 一、打家劫舍 ——>房子是线性的 1.1、dp定义 1.2、递推公式 1.3、初始化 1.4、遍历顺序 1.5、解题代码 二、打家劫舍II ——>房子是环型 2.1、分析 2.2、解题代码 三、打家劫舍III ——>房子是树形 3.1、dp含义 3.2、递推公式 3.3、解题代码…

【Trusted Firmware-A 移植】

Trusted Firmware-A 移植Trusted Firmware-A 简介步骤一、解压标准 tf-a 源码包,进入 tf-a 源码目录下&#xff1a;二、将 ST 官方补丁文件打到 tf-a 源码中&#xff1a;三、配置交叉编译工具链四、添加设备树文件五、编译源码六、固件烧写结果Trusted Firmware-A 简介 嵌入式…

Magisk工具使用指南

对于一般玩机用户,Magisk官方提供的发布版本即可满足要求,但对于高级开发者来说这远远不够,我们不仅仅是满足于使用,更要学会定制面具,最好的能完全理解面具的核心架构以便于自己也能写出来一套和面具差不多的工具,这才是我们研究面具最根本的原因所在。因为做移动安全,…

edp 基本信号-参数及命令

基本信号及概念通信双方及通道下图为edp规范中的总体框图。通过此框图我们可以了解到&#xff1a;交互的双方&#xff1a;1&#xff09;source device 即 显卡的显示控制部分2) sink device 即 显示屏 双方间的通道&#xff1a;1&#xff09; HPD 即热插拔&#xff0c;通过sink…