BLEESP32 Arduino学习篇:玩转蓝牙BLE

简介:

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回调就可以返回连接状态。

    物联沃分享整理
    物联沃-IOTWORD物联网 » BLEESP32 Arduino学习篇:玩转蓝牙BLE

    发表评论