BLE低功耗蓝牙技术详解:从理论到实践
BLE低功耗蓝牙技术详解:从理论到实践
在物联网和嵌入式开发领域,低功耗蓝牙(BLE)技术因其低功耗、短距离通信等特点而被广泛应用。本文将从BLE的基本概念出发,介绍其在游戏手柄与单片机连接中的应用,并通过具体代码示例展示如何使用ESP32单片机读取游戏手柄数据。
BLE简介
BLE(Bluetooth Low Energy),也称为蓝牙低功耗,是一种无线个人区域网络技术,用于短距离数据交换。它在经典蓝牙的基础上进行了优化,专注于降低能耗,同时仍然提供足够的通信性能。
它具有非常低的电量消耗,并且可以在非常短的时间内进行连接和数据传输。不过也有很多缺点,包括通讯距离短,适合近距离的无线交互。
BLE蓝牙通讯特点
BLE通讯主要由广播(Advertising)和连接(Connection)两部分组成。广播是BLE设备宣布其存在并传递少量数据的一种方式,主要有Advertising Packets(包含设备的基本信息,如设备名称、服务UUID等)和Scan Response Packets(设备在接收到扫描请求时返回的额外信息)。
广播包结构包括Preamble(1字节,固定为0xAA),Access Address(4字节,广播信道的地址,固定为0x8E89BED6),PDU(可变长度,包含实际的数据),和CRC(3字节,用于校验数据完整性)。连接建立后,数据通过连接事件(Connection Events)进行传输,每个连接事件包括主设备(Central)和从设备(Peripheral)之间的一次完整的数据交换。连接事件结构包括Header(包含包类型和长度信息),LL Data(链路层数据,包括控制包或用户数据),和MIC(用于确保数据完整性和防止篡改)。
数据通过预定义的服务和特性(Characteristic)进行传输,每个特性都有一个唯一的UUID(通用唯一标识符)。服务是一个逻辑功能单元,包含多个特性,例如心率服务包含测量值和传感器位置等特性;特性是一个具体的数据单元,包括一个值和可选的描述符(提供关于特性值的额外信息,如格式、范围等)。
如果比较难明白的话,我们直接用手机的BLE调试助手连接我的手柄查看具体的。
一个BLE蓝牙设备会向外广播信息,而我们就需要知道特定的服务UUID和特性UUID来获取信息,例如这里我知道我需要的服务UUID如下。
特性UUID如下,这样子我就可以接收到设备发出的信息了,这些信息包括了手柄的摇杆,各个按键状态信息等等。
单片机连接手柄
我们首先需要一款能够支持BLE的单片机或者模块,这里选择使用ESP32,它可以连接BLE设备并且可以对信息处理。以下是具体的代码实现:
#include "BLEDevice.h"
#include <Wire.h>
BLEClient* pClient;
BLERemoteService* pRemoteService;
BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAddress targetAddress("a4:c1:38:91:43:57"); // 替换为目标设备的MAC地址
int Lx,Ly;
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pClient) {
Serial.println("Connected to the target device.");
}
void onDisconnect(BLEClient* pClient) {
Serial.println("Disconnected from the target device.");
}
};
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE client...");
BLEDevice::init("");
// 创建客户端
pClient = BLEDevice::createClient();
pClient->setClientCallbacks(new MyClientCallback());
// 尝试连接目标设备
if (pClient->connect(targetAddress)) {
Serial.println("Connected to device.");
} else {
Serial.println("Failed to connect.");
return;
}
// 获取远程服务
pRemoteService = pClient->getService("00001812-0000-1000-8000-00805f9b34fb"); // 替换为目标设备的服务UUID
if (pRemoteService == nullptr) {
Serial.print("Failed to find service UUID: ");
Serial.println("SERVICE_UUID");
pClient->disconnect();
return;
}
Serial.println("Found the service.");
// 获取远程特性
pRemoteCharacteristic = pRemoteService->getCharacteristic("00002a4d-0000-1000-8000-00805f9b34fb"); // 替换为目标设备的特性UUID
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find characteristic UUID: ");
Serial.println("CHARACTERISTIC_UUID");
pClient->disconnect();
return;
}
Serial.println("Found the characteristic.");
// 读取特性值
if (pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
Serial.print("Characteristic value: ");
Serial.println(value.c_str());
}
// 设置特性通知
if (pRemoteCharacteristic->canNotify()) {
pRemoteCharacteristic->registerForNotify(notifyCallback);
}
}
void loop() {
}
// 通知回调函数
void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Lx = pData[0];
Ly = pData[1];
Serial.print("x:"+String(Lx));
Serial.println(",y:"+String(Ly));
}
这段代码展示了如何使用ESP32连接BLE设备并读取其数据。通过设置服务UUID和特性UUID,可以获取游戏手柄的摇杆位置和按键状态等信息。