【FireBeetle 2 ESP32 C6开发板】-2- BLE server创建
<div class='showpostmsg'><p>BLE的Server通常是提供多种Service的设备,每个Service底下包含了多个Characteristic,每个Characteristic的值通常都代表一定的意义。这种设备通常需要通过广播来告诉别的设备自己是谁,能提供什么样的数据,所以也成为“从设备”。</p><p> </p>
<p>BLE的Client通常Scan周围的BLE设备,然后发现了自己感兴趣的设备,并且匹配到了相应的Service UUId和Characteristic UUID后,即可获取上述BLE Server提供的数据。</p>
<p>下面的例子中:ESP32-C6是BLE SERVER,是从设备,把自己广播给周围的设备。</p>
<pre>
<code>#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
void BLEBegin(){
// Create the BLE Device
BLEDevice::init(/*BLE名称*/"UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
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());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}</code></pre>
<p>上述代码中,启动广播之前,把自己能提供的SERVICE UUID也广播出去。接下来是手机BLE助手上看一下。</p>
<p> </p>
<p> </p>
<p>通过BLE助手向CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" 写入数据:</p>
<p> </p>
<p> </p>
<p>完整代码如下:</p>
<pre>
<code>/*
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.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with"NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
*/
/* 该示例演示了蓝牙数据透传,烧录代码,打开串口监视器,打开手机的BLE调试助手
* 1.即可看见ESP32发送的数据--见APP使用图
* 2.通过BLE调试助手的输入框可向ESP32发送数据--见APP使用图
* 该示例由BLE_uart示例更改而来
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
//蓝牙连接/断开处理。当有连接/断开事件发生时自动触发
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { //当蓝牙连接时会执行该函数
Serial.println("蓝牙已连接");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {//当蓝牙断开连接时会执行该函数
Serial.println("蓝牙已断开");
deviceConnected = false;
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
}
};
/****************数据接收部分*************/
/****************************************/
//蓝牙接收数据处理。当收到数据时自动触发
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = (pCharacteristic->getValue()).c_str();//接收数据,并赋给rxValue
//if(rxValue == "ON"){Serial.println("开灯");} //判断接收的字符是否为"ON"
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++){
Serial.print(rxValue);
}
Serial.println();
Serial.println("*********");
}
}
};
/***************************************/
/****************************************/
void setup() {
Serial.begin(115200);
BLEBegin();//初始化蓝牙
}
void loop() {
/****************数据发送部分*************/
/****************************************/
if (deviceConnected) {//如果有蓝牙连接,就发送数据
pTxCharacteristic->setValue("Hello");//发送字符串
pTxCharacteristic->notify();
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
pTxCharacteristic->setValue("DFRobot");//发送字符串
pTxCharacteristic->notify();
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
}
/****************************************/
/****************************************/
}
void BLEBegin(){
// Create the BLE Device
BLEDevice::init(/*BLE名称*/"UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
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());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}</code></pre>
<p> </p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p>最后,通过BLE助手向CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" 写入数据,完美</p>
<p><span style="font-size:20px;"><u><span style="color:#9b59b6;"><strong>补充一下BLE CLIENT的创建:</strong></span></u></span></p>
<p> </p>
<p>C6作为BLE central设备,通过扫描来发现提供服务的设备。nrf52 usb dongle作为BLE peripheral设备,通过广播,把自己提供温湿度数据的服务给广播出去。</p>
<p>C6作为central设备的代码:</p>
<pre>
<code>#include "BLEDevice.h"
// The remote service of Xiaomi Temp and Humidity Sensor we wish to connect to.
static BLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a8");
// The characteristic of the remote service we are interested in.
// In this case, it's Xiaomi Temp and Humidity Sensor Version 2.
static BLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a8");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = true;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
for(uint8_t i = 0; i < length; i++){
Serial.println("pData[" + String(i) + "] = " + String(pData));
}
uint16_t TempVal = pData*256 + pData;
Serial.println("Temperature is: "+ String(TempVal/100) + "." + String(TempVal%100) + " degree");
uint8_t HumidityVal = pData;
Serial.println("Humidity is: " + String(pData) + "% ");
uint16_t BatteryVal = pData*256 + pData;
Serial.println("Battery Voltage is: "+ String(BatteryVal/100) + "." + String(BatteryVal%100) + " mV");
Serial.println();
}
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient*pClient= BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice);// if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
String value = pRemoteCharacteristic->readValue();
uint8_t* test = pRemoteCharacteristic->readRawData();
uint8_t i;
uint8_ttest_temp_val={0};
for(i = 0; i < 5; i++){
if(test != nullptr){
Serial.print("The characteristic value using readRawData was: ");
Serial.println(*test);
test_temp_val = *test;
}else{
Serial.println("pRemoteCharacteristic->readRawData() returns NULL PTR");
}
test++;
}
static uint16_t Test_TempVal = test_temp_val*256 + test_temp_val;
Serial.println("Test Temperature is: "+ String(Test_TempVal/100) + "." + String(Test_TempVal%100) + " degree");
static uint8_t Test_HumidityVal = test_temp_val;
Serial.println("Test Humidity is: " + String(test_temp_val) + "% ");
static uint16_t Test_BatteryVal = test_temp_val*256 + test_temp_val;
Serial.println("Test Battery Voltage is: "+ String(Test_BatteryVal/100) + "." + String(Test_BatteryVal%100) + " mV");
}
if(pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
* Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.println("*-------------------------------------------------------------------*");
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
Serial.println(advertisedDevice.getServiceUUID().toString().c_str());
Serial.println(advertisedDevice.getName().c_str());
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device.Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
} // End of setup.
// This is the Arduino main loop function.
void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect.Now we connect to it.Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
//String newValue = "Time since boot: " + String(millis()/1000);
//Serial.println("Time since boot: " + newValue);
// Set the characteristic's value to be the array of bytes that is actually a string.
//pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(5);// this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop
</code></pre>
<p> </p>
<p>nrf52 usb dongle作为BLE 外设的配置:</p>
<p>首先点击SERVER SETUP,添加SERVICE</p>
<p> </p>
<p></p>
<p>添加一个Characteristic:</p>
<p> </p>
<p> </p>
<p>使能了notify,需要添加CCCD即UUID为2902的Attribute。</p>
<p> </p>
<p> </p>
<p>点击“Apply to device”,重启usb dongle:</p>
<p> </p>
<p>回到首页,点击小齿轮,选择“Advertising setup”, 注意提供温湿度数据的服务的UUID已经被加入广播数据。</p>
<p> </p>
<p>点击“Start advertising”</p>
<p> </p>
<p> </p>
<p>串口输出:</p>
<p> </p>
<p> </p>
页:
[1]