本帖最后由 dql2016 于 2021-8-29 16:36 编辑
ESP32-E集成了低功耗蓝牙功能,蓝牙设备分为中央设备和外围设备,例如连接了蓝牙鼠标、蓝牙键盘的电脑,其中蓝牙鼠标和蓝牙键盘就属于外围设备,而电脑则属于中央设备。本帖测试的就是esp32作为外围设备(提供GATT服务 )的例子, esp32作为从机使用相当于一台服务器,中央设备(手机app)可以从其中读取数据或是向它写入数据 , 这种方式十分常见。
蓝牙设备作为外网设备,提供GATT服务,可以看做是一个服务器(Server),一个服务器包含了一个或多个服务(Service),每个服务由一个UUID【128位通用唯一识别码 Universally Unique Iden tifier】来标识,每个服务中含有一个或多个特征(Characteristic),每个特征由一个UUID来标识,每个特征中包含一个值(Value),另外还包含零个或多个描述(Descriptor),每个描述由一个UUID来标识。
使用Arduino IDE开发esp32-e的蓝牙应用,主要有以下几个流程:创建Server,设置客户端设备接入时和断开连接回调函数,需要注意的是有设备接入后Advertising广播会被停止,所以要在设备断开连接时重新开启广播;
创建Service并启动;
创建Characteristic;
设置Characteristic中读写回调函数,Characteristic中的Value除了可以由中央设备主动读写访问外还可以由外围设备主动推送给中央设备。外围设备向中央设备推送数据有两种方法: notify 和 indicate , 其中notify 是非阻塞的,不会确认客户端是否接收到, indicate 是阻塞的,会确认客户端响应。中央设备接不接收这些消息由中央设备自己决定。
蓝牙里面使用Advertising广播自身,是客户端可以发现自己。这个Advertising广播的内容可以包含很多东西,比如字节的设备类型是什么、里面的服务都有什么UUID、自定义的数据(如蓝牙温度传感器向周围广播测量的温度值)等等。
- #include
- #include
- #include
- #include
- #include
- #define NUMPIXELS 1
- Adafruit_NeoPixel pixels(NUMPIXELS, 5, NEO_GRB + NEO_KHZ800);
- // See the following for generating UUIDs: https://www.uuidgenerator.net/
- #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
- #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
- BLEServer* pServer = NULL;
- BLECharacteristic* pCharacteristic = NULL;
- bool deviceConnected = false;
- bool oldDeviceConnected = false;
- uint32_t notifyValue = 0;
- class MyServerCallbacks: public BLEServerCallbacks
- {
-
- void onConnect(BLEServer* pServer)
- {
- deviceConnected = true;
- pixels.clear();
- pixels.setPixelColor(0, 0, 100, 0);
- pixels.show();
- Serial.println("****device Connected****");
- //pServer->startAdvertising(); //有客户端连接后广播会停止,这里再次打开使其它设备可以搜索到
- };
- void onDisconnect(BLEServer* pServer)
- {
- deviceConnected = false;
- //pixels.clear();
- //pixels.setPixelColor(0, 0, 0, 50);
- //pixels.show();
- Serial.println("****device DisConnected****");
- }
- };
- class MyCallbacks: public BLECharacteristicCallbacks
- {
- void onWrite(BLECharacteristic *pCharacteristic)
- {
- std::string value = pCharacteristic->getValue();
- if (value.length() > 0) {
- Serial.println("*********");
- Serial.print("New value: ");
- for (int i = 0; i < value.length(); i++)
- Serial.print(value[i]);
- Serial.println();
- Serial.println("*********");
- if (value == "ledon")
- digitalWrite(LED_BUILTIN, HIGH);
- else if (value == "ledoff")
- digitalWrite(LED_BUILTIN, LOW);
- }
- }
- void onRead(BLECharacteristic *pCharacteristic)
- {
- pCharacteristic->setValue("temp is 30");
- }
- void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code)
- {
- if (s == SUCCESS_INDICATE)
- {
- Serial.print("SUCCESS_INDICATE");
- }
- if (s == ERROR_INDICATE_DISABLED)
- {
- Serial.print("ERROR_INDICATE_DISABLED");
- }
- if (s == ERROR_INDICATE_TIMEOUT)
- {
- Serial.print("ERROR_INDICATE_TIMEOUT");
- }
- if (s == ERROR_INDICATE_FAILURE)
- {
- Serial.print("ERROR_INDICATE_FAILURE");
- }
- if (s == SUCCESS_INDICATE)
- {
- Serial.print("SUCCESS_INDICATE");
- }
- if (s == SUCCESS_NOTIFY)
- {
- Serial.print("SUCCESS_NOTIFY");
- }
- Serial.println("====");
- Serial.print("code: ");
- Serial.print(code, HEX);
- Serial.println();
- Serial.println("====");
- //pCharacteristic->indicate();
- }
- void onNotify(BLECharacteristic* pCharacteristic)
- {
- /*std::string value = pCharacteristic->getValue();
- if (value.length() > 0) {
- Serial.println("****onNotify s*****");
- Serial.print("Notify value: ");
- for (int i = 0; i < value.length(); i++)
- Serial.print(value[i]);
- Serial.println();
- Serial.println("****onNotify e*****");
- }*/
- }
- };
- void setup()
- {
- Serial.begin(115200);
- pixels.begin();
- pixels.setBrightness(100);
- pixels.clear();
- pixels.setPixelColor(0, 0, 150, 0);
- pixels.show();
- pinMode(LED_BUILTIN, OUTPUT);
- Serial.println("1- Download and install an BLE scanner app in your phone");
- Serial.println("2- Scan for BLE devices in the app");
- Serial.println("3- Connect to MyESP32");
- Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
- Serial.println("5- See the magic =)");
- BLEDevice::init("MyESP32");
- pServer = BLEDevice::createServer();
- pServer->setCallbacks(new MyServerCallbacks());
- BLEService *pService = pServer->createService(SERVICE_UUID);
- pCharacteristic = pService->createCharacteristic(
- CHARACTERISTIC_UUID,
- BLECharacteristic::PROPERTY_READ |
- BLECharacteristic::PROPERTY_WRITE |
- BLECharacteristic::PROPERTY_NOTIFY |
- BLECharacteristic::PROPERTY_INDICATE
- );
- pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
- pCharacteristic->setCallbacks(new MyCallbacks());
- pCharacteristic->setIndicateProperty(true);
- pCharacteristic->setNotifyProperty(true);
- pCharacteristic->addDescriptor(new BLE2902());//16-bit UUID 0x2902 的Descriptor提供了客户端控制服务器是否使能 notify 和 indicate 的功能
- pCharacteristic->setValue("Hello World");//设置该Characteristic的Value值 如果客户端连上设备后没有任何写入的情况下第一次读取到的数据应该是这里设置的值
- pService->start();
- BLEAdvertising *pAdvertising = pServer->getAdvertising();
- pAdvertising->addServiceUUID(SERVICE_UUID);
- pAdvertising->setScanResponse(false);
- pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
- pAdvertising->start();
- Serial.println("Waiting a client connection to notify...");
- }
- unsigned long previousMillis;
- unsigned long lastMillis;
- bool s;
- void loop()
- {
- // notify changed value
- if (deviceConnected)
- {
- unsigned long currentMillis = millis();
- if ((currentMillis - previousMillis) > 1000)
- {
- pCharacteristic->setValue((uint8_t*)¬ifyValue, 4);
- pCharacteristic->notify();
- notifyValue++;
- previousMillis = currentMillis;
- }
- }
- else
- {
- if (( millis() - lastMillis) > 500)
- {
- s=!s;
- if(s)
- {
- pixels.clear();
- pixels.setPixelColor(0, 0, 0, 0);
- pixels.show();
- }
- else
- {
- pixels.clear();
- pixels.setPixelColor(0, 100, 0, 0);
- pixels.show();
- }
- lastMillis = millis();
- }
- }
- // disconnecting
- if (!deviceConnected && oldDeviceConnected)
- {
- delay(500); // give the bluetooth stack the chance to get things ready
- pServer->startAdvertising(); // restart advertising
- Serial.println("start advertising");
- oldDeviceConnected = deviceConnected;
- }
- // connecting
- if (deviceConnected && !oldDeviceConnected)
- {
- // do stuff here on connecting
- oldDeviceConnected = deviceConnected;
- }
- }
复制代码
示例代码,创建了一个具有读、写、通知、指示权限的特征值,没有设备连接时,则RGB灯以红色500ms周期闪烁,有设备连接后RGB灯绿色常亮,收到手机app发送的"ledon"字符串后开启板载绿色led,收到手机app发送的"ledoff"字符串后关闭板载绿色led,手机app上打开通知后可以接收到通知数据。
串口打印日志如下:
手机app录屏如下:
Screenrecorder-2021-08-29-14-02-15-452
|