You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过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中添加NSBluetoothAlwaysUsageDescriptionNSBluetoothPeripheralUsageDescription权限说明。
    • Android:在AndroidManifest.xml中添加BLUETOOTHBLUETOOTH_ADMINACCESS_FINE_LOCATION(Android 12+还要加BLUETOOTH_SCANBLUETOOTH_CONNECT)。
  • 数据格式:BLE传输的是字节数组,字符串要转成字节再发送,接收时再转回字符串;如果传输二进制数据(比如传感器数值),要注意字节序(小端/大端)。

内容的提问来源于stack exchange,提问作者Rounak Datta

火山引擎 最新活动