如何通过BLE实现ESP32与跨平台应用的双向数据收发?
嘿,这个需求我太熟了!之前帮好几个朋友做过ESP32 BLE和跨平台APP通信的项目,核心就是基于BLE的GATT(通用属性配置文件)来实现双向数据收发。下面我把ESP32端和跨平台应用端的实现步骤、代码示例都给你理清楚,照着来就能跑通。
一、ESP32端:搭建BLE服务与收发逻辑
我们用Arduino框架的官方ESP32 BLE库来实现,不用自己造轮子,核心是定义一个GATT服务和特征值,通过回调处理接收,用notify方法主动发送数据。
#include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> // 自定义UUID,两端必须完全一致(大小写、字符都不能错) #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; // 处理设备连接/断开的回调 class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; Serial.println("✅ 应用已连接"); }; void onDisconnect(BLEServer* pServer) { deviceConnected = false; Serial.println("❌ 应用已断开"); // 断开后重新广播,方便应用重连 pServer->startAdvertising(); } }; // 处理应用发来的数据(ESP32接收数据的核心) class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string rxValue = pCharacteristic->getValue(); if (rxValue.length() > 0) { Serial.print("📥 收到应用数据: "); for (char c : rxValue) Serial.print(c); Serial.println(); } } }; void setup() { Serial.begin(115200); // 初始化BLE设备,设置广播名称 BLEDevice::init("ESP32_BLE_Module"); // 创建BLE服务器实例 pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // 创建自定义GATT服务 BLEService *pService = pServer->createService(SERVICE_UUID); // 创建特征值:配置读写+通知权限(要发数据必须开NOTIFY) pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY ); // 添加BLE2902描述符,让应用能启用通知功能(必须加) pCharacteristic->addDescriptor(new BLE2902()); pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); // 启动服务 pService->start(); // 开始广播,让应用能搜索到设备 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); BLEDevice::startAdvertising(); Serial.println("🔍 等待应用连接..."); } void loop() { // 模拟ESP32主动给应用发数据(比如每隔2秒发一次心跳) if (deviceConnected) { static unsigned long lastSendTime = 0; if (millis() - lastSendTime > 2000) { String txData = "ESP32心跳: " + String(millis()/1000) + "s"; pCharacteristic->setValue(txData.c_str()); pCharacteristic->notify(); // 主动推送数据给应用 Serial.println("📤 已发送数据: " + txData); lastSendTime = millis(); } } }
二、跨平台应用端:以Flutter为例(支持iOS/Android)
Flutter是跨平台应用的首选框架,我们用flutter_blue_plus插件来实现BLE扫描、连接和数据收发。
第一步:添加依赖
在pubspec.yaml里加入:
dependencies: flutter_blue_plus: ^1.13.3
第二步:核心代码实现
import 'package:flutter/material.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'ESP32 BLE通信', theme: ThemeData(primarySwatch: Colors.blue), home: const BleCommPage(), ); } } class BleCommPage extends StatefulWidget { const BleCommPage({super.key}); @override State<BleCommPage> createState() => _BleCommPageState(); } class _BleCommPageState extends State<BleCommPage> { final FlutterBluePlus _flutterBlue = FlutterBluePlus.instance; List<BluetoothDevice> _devices = []; BluetoothDevice? _connectedDevice; BluetoothCharacteristic? _targetChar; // 扫描BLE设备 void _scanDevices() { _flutterBlue.startScan(timeout: const Duration(seconds: 5)); _flutterBlue.scanResults.listen((results) { for (var result in results) { if (!_devices.contains(result.device)) { setState(() => _devices.add(result.device)); } } }); } // 连接设备并找到目标特征值 void _connectDevice(BluetoothDevice device) async { try { await device.connect(); setState(() => _connectedDevice = device); // 发现设备的GATT服务 List<BluetoothService> services = await device.discoverServices(); for (var service in services) { // 匹配ESP32端定义的服务UUID if (service.uuid.toString() == "4fafc201-1fb5-459e-8fcc-c5c9c331914b") { for (var char in service.characteristics) { // 匹配特征值UUID if (char.uuid.toString() == "beb5483e-36e1-4688-b7f5-ea07361b26a8") { setState(() => _targetChar = char); // 启用通知,监听ESP32发来的数据 await char.setNotifyValue(true); char.value.listen((bytes) { String receivedData = String.fromCharCodes(bytes); _showSnackBar("收到ESP32数据: $receivedData"); }); break; } } break; } } _showSnackBar("✅ 连接成功"); } catch (e) { _showSnackBar("❌ 连接失败: $e"); } } // 给ESP32发送数据 void _sendData(String data) async { if (_targetChar == null) { _showSnackBar("⚠️ 未连接设备或未找到特征值"); return; } try { await _targetChar!.write(data.codeUnits); _showSnackBar("📤 已发送数据: $data"); } catch (e) { _showSnackBar("❌ 发送失败: $e"); } } void _showSnackBar(String msg) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); } @override void initState() { super.initState(); _scanDevices(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("ESP32 BLE通信")), body: Column( children: [ // 扫描到的设备列表 Expanded( child: ListView.builder( itemCount: _devices.length, itemBuilder: (context, index) { var device = _devices[index]; return ListTile( title: Text(device.name.isNotEmpty ? device.name : "未知设备"), subtitle: Text(device.id.toString()), onTap: () => _connectDevice(device), ); }, ), ), // 发送数据输入框(连接后显示) if (_connectedDevice != null) Padding( padding: const EdgeInsets.all(16.0), child: TextField( decoration: const InputDecoration(hintText: "输入要发送的数据"), onSubmitted: _sendData, ), ), ], ), ); } }
三、必看的关键注意事项
- UUID完全一致:ESP32和应用端的服务、特征值UUID必须完全匹配,大小写、字符都不能错。
- 特征值权限配置:要实现双向通信,特征值必须同时开启
PROPERTY_WRITE(应用发数据)和PROPERTY_NOTIFY(ESP32主动推数据),还要加BLE2902描述符。 - 跨平台权限配置:
- iOS:在
Info.plist中添加NSBluetoothAlwaysUsageDescription和NSBluetoothPeripheralUsageDescription权限说明。 - Android:在
AndroidManifest.xml中添加BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION(Android 12+还要加BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。
- iOS:在
- 数据格式:BLE传输的是字节数组,字符串要转成字节再发送,接收时再转回字符串;如果传输二进制数据(比如传感器数值),要注意字节序(小端/大端)。
内容的提问来源于stack exchange,提问作者Rounak Datta




