多台 ESP32 BLE 设备使用相同的 UUID(服务+特征),但不同设备名称(bleName)时是否相互干扰

[复制链接]
 楼主| keer_zu 发表于 2025-6-4 16:24 | 显示全部楼层 |阅读模式

答案是 会干扰,因为 BLE 的发现和连接机制不仅依赖设备名,还依赖广播数据和服务 UUID。以下是详细分析和解决方案:


1. 问题原因分析

(1) 相同 UUID 的冲突

  • 所有设备广播相同的 SERVICE_UUID: 当 Python 端扫描设备时,会看到多个设备都广播 00001101-0000-1000-8000-00805F9B34FB,客户端可能无法区分它们,导致随机连接或连接失败。
  • 特征 UUID 也相同: 即使连接成功,如果 Python 端同时尝试读写多台设备,数据可能发送到错误的设备(因为特征 UUID 完全一致)。

(2) 设备名称(bleName)的作用有限

  • bleName 仅在扫描时显示,但 BLE 连接实际依赖的是设备的 MAC 地址和广播数据
  • 如果 Python 端未明确绑定目标设备的 MAC 地址,可能会错误地连接到同名设备(尤其在广播缓存干扰时)。

(3) 经典蓝牙 vs. BLE 的区别

  • 你使用的 UUID 00001101-...经典蓝牙 SPP 的标准 UUID,但在 BLE 模式下,它只是一个普通 UUID,没有特殊作用。
  • 经典蓝牙 SPP 的 UUID 不能直接用于 BLE,因为两者的协议栈完全不同。

2. 解决方案

(1) 为每台设备生成唯一的服务 UUID

修改代码,让每台设备使用 不同的服务 UUID(特征 UUID 可以相同):

// 设备1
#define SERVICE_UUID "00001101-0000-1000-8000-00805F9B34F1" 

// 设备2
#define SERVICE_UUID "00001101-0000-1000-8000-00805F9B34F2"

// 设备3
#define SERVICE_UUID "00001101-0000-1000-8000-00805F9B34F3"

优点

  • Python 端可以明确选择连接特定设备(通过不同的 SERVICE_UUID)。
  • 完全避免 UUID 冲突。

(2) 动态生成 UUID(推荐)

在设备启动时,基于 MAC 地址生成唯一 UUID:

#include "BLEAddress.h"

String generateUniqueUUID() {
  String mac = BLEDevice::getAddress().toString().c_str();
  mac.replace(":", "");
  return "0000" + mac.substring(0, 4) + "-0000-1000-8000-00805F9B34FB";
}

// 在 setup() 中:
String SERVICE_UUID = generateUniqueUUID();

优点

  • 完全自动化,无需手动配置。
  • 保证每台设备的 UUID 唯一。

(3) Python 端明确指定目标设备

在 Python 代码中,先扫描设备,再通过 设备名或 MAC 地址 过滤目标设备:

import asyncio
from bleak import BleakScanner, BleakClient

async def connect_to_device(device_name):
    devices = await BleakScanner.discover()
    for d in devices:
        if d.name == device_name:
            client = BleakClient(d.address)
            await client.connect()
            return client
    raise Exception("Device not found")

# 使用示例
client = await connect_to_device("ESP32_Device1")

优点

  • 直接绑定物理设备,避免 UUID 冲突问题。

(4) 避免使用经典蓝牙的 SPP UUID

如果确实需要 BLE 通信,建议使用 随机生成的 UUID,而非经典蓝牙的固定 UUID。例如:

// 随机生成(符合 UUID v4 规范)
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" 
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"

(注:这是 Nordic UART Service 的 UUID,常用于 BLE 串口透传)


3. 现场问题排查步骤

如果多台设备仍然异常,按以下步骤检查:

  1. 确认 Python 端是否正确过滤设备
    • 打印扫描到的所有设备,检查 bleName 和 MAC 地址是否唯一。
  2. 检查 ESP32 的广播数据
    • 确保 pAdvertising->addServiceUUID(SERVICE_UUID) 使用的是动态 UUID。
  3. 验证单设备连接稳定性
    • 如果单台设备偶尔断开,可能是电源或信号干扰问题。
  4. 监听 BLE 广播包
    • 使用手机 App(如 nRF Connect)扫描,确认每台设备的 UUID 和名称是否唯一。

4. 总结

问题根源 解决方案
所有设备 UUID 相同 为每台设备生成不同的服务 UUID
Python 端未绑定具体设备 通过设备名或 MAC 地址过滤
使用经典蓝牙 UUID 于 BLE 改用随机生成的 BLE UUID
广播冲突 动态生成 UUID(基于 MAC 地址)

最终建议

  • 优先使用动态 UUID 生成(方案 2),确保唯一性。
  • Python 端严格绑定设备 MAC 地址(方案 3),避免误连接。
  • 如果仍有问题,提供具体的错误日志(如连接超时、数据错乱等),可以进一步分析。
 楼主| keer_zu 发表于 2025-6-4 18:41 | 显示全部楼层
代码:
  1. #include "protocol.h"

  2. // 全局变量
  3. BLEServer* pServer = nullptr;
  4. BLECharacteristic* pRxCharacteristic = nullptr;
  5. String receivedData;
  6. message_para_t g_message_para;
  7. int g_current_score = 0;

  8. // 回调类实现
  9. class MyServerCallbacks : public BLEServerCallbacks {
  10.     void onConnect(BLEServer* pServer) override {
  11.         Serial.println("设备已连接");
  12.     }

  13.     void onDisconnect(BLEServer* pServer) override {
  14.         Serial.println("设备已断开");
  15.         BLEDevice::startAdvertising();
  16.         Serial.println("重新开始广播...");
  17.     }
  18. };

  19. class RxCharacteristicCallbacks : public BLECharacteristicCallbacks {
  20.     void onWrite(BLECharacteristic* pCharacteristic) override {
  21.         String rxValue = pCharacteristic->getValue().c_str(); // 修正类型转换
  22.         if (rxValue.length() > 0) {
  23.             receivedData += rxValue;
  24.             Serial.print("收到数据: ");
  25.             Serial.println(rxValue);
  26.         }
  27.     }
  28. };

  29. void processReceivedData(String data) {
  30.     data.trim();
  31.    
  32.     // 使用ArduinoJson解析
  33.     StaticJsonDocument<256> doc; // 改用StaticJsonDocument避免动态内存分配
  34.     DeserializationError error = deserializeJson(doc, data);
  35.    
  36.     if (error) {
  37.         Serial.print("JSON解析错误: ");
  38.         Serial.println(error.c_str());
  39.         return;
  40.     }

  41.     // 提取数据
  42.     const char* status = doc["status"];
  43.     int score = doc["score"];

  44.     // 验证数据
  45.     if (status == nullptr || score < 0 || score > 10) {
  46.         Serial.println("无效数据");
  47.         return;
  48.     }

  49.     // 状态处理
  50.     if (g_message_para.status != status) {
  51.         g_message_para.status = status;
  52.         
  53.         if (strcmp(status, "start") == 0) {
  54.             Serial.println("疗愈开始");
  55.             stopAllEffects();
  56.             startFastChase();
  57.         }
  58.         else if (strcmp(status, "listening") == 0) {
  59.             stopAllEffects();
  60.             startClockwiseChase();
  61.         }
  62.         else if (strcmp(status, "thinking") == 0) {
  63.             stopAllEffects();
  64.             startFastBreathing();
  65.         }
  66.         else if (strcmp(status, "speaking") == 0) {
  67.             stopAllEffects();
  68.             startAnticlockwiseChase();
  69.         }
  70.         else if (strcmp(status, "stop") == 0) {
  71.             Serial.println("疗愈停止");
  72.             stopAllEffects();
  73.             startSlowBreathing();
  74.         }
  75.     }

  76.     // 评分处理
  77.     if (g_message_para.score != score) {
  78.         g_message_para.score = score;
  79.         on_new_score();
  80.     }

  81.     Serial.printf("状态更新: %s, 评分: %d\n", status, score);
  82. }

  83. void blue_setup() {
  84.     Serial.begin(115200);
  85.    
  86.     // 生成设备SN
  87.     uint64_t chipid = ESP.getEfuseMac();
  88.     char sn[5];
  89.     snprintf(sn, sizeof(sn), "%04X", (uint16_t)(chipid >> 32));
  90.     Serial.printf("设备SN: %s\n", sn);

  91.     // 生成UUID
  92.     char service_uuid[37];
  93.     char characteristic_uuid[37];
  94.     snprintf(service_uuid, sizeof(service_uuid), "6E400001-B5A3-F393-E0A9-E50E24DC%s", sn);
  95.     snprintf(characteristic_uuid, sizeof(characteristic_uuid), "6E400002-B5A3-F393-E0A9-E50E24DC%s", sn);

  96.     // 初始化BLE
  97.     BLEDevice::init(String("BW-LED-") + sn);
  98.     pServer = BLEDevice::createServer();
  99.     pServer->setCallbacks(new MyServerCallbacks());

  100.     // 创建服务
  101.     BLEService* pService = pServer->createService(service_uuid);

  102.     // 创建特征
  103.     pRxCharacteristic = pService->createCharacteristic(
  104.         characteristic_uuid,
  105.         BLECharacteristic::PROPERTY_WRITE
  106.     );
  107.     pRxCharacteristic->setCallbacks(new RxCharacteristicCallbacks());

  108.     // 启动服务
  109.     pService->start();

  110.     // 开始广播
  111.     BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
  112.     pAdvertising->addServiceUUID(service_uuid);
  113.     pAdvertising->setScanResponse(true);
  114.     BLEDevice::startAdvertising();

  115.     Serial.println("BLE初始化完成");
  116.     Serial.printf("服务UUID: %s\n", service_uuid);
  117.     Serial.printf("特征UUID: %s\n", characteristic_uuid);
  118. }

  119. void data_process() {
  120.     if (receivedData.length() > 0 && receivedData.endsWith("\r\n")) {
  121.         processReceivedData(receivedData);
  122.         receivedData = "";
  123.     }
  124. }

  125. void update_outer_ring() {
  126.     if (g_message_para.status == "start") {
  127.         updateFastChase();
  128.     }
  129.     else if (g_message_para.status == "listening") {
  130.         updateClockwiseChase();
  131.     }
  132.     else if (g_message_para.status == "thinking") {
  133.         updateFastBreathing();
  134.     }
  135.     else if (g_message_para.status == "speaking") {
  136.         updateAnticlockwiseChase();
  137.     }
  138.     else if (g_message_para.status == "stop") {
  139.         updateSlowBreathing();
  140.     }
  141. }


您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1478

主题

12912

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部