keer_zu 发表于 2025-6-4 16:24

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

答案是 **会干扰**,因为 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

代码:
#include "protocol.h"

// 全局变量
BLEServer* pServer = nullptr;
BLECharacteristic* pRxCharacteristic = nullptr;
String receivedData;
message_para_t g_message_para;
int g_current_score = 0;

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

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

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

void processReceivedData(String data) {
    data.trim();
   
    // 使用ArduinoJson解析
    StaticJsonDocument<256> doc; // 改用StaticJsonDocument避免动态内存分配
    DeserializationError error = deserializeJson(doc, data);
   
    if (error) {
      Serial.print("JSON解析错误: ");
      Serial.println(error.c_str());
      return;
    }

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

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

    // 状态处理
    if (g_message_para.status != status) {
      g_message_para.status = status;
      
      if (strcmp(status, "start") == 0) {
            Serial.println("疗愈开始");
            stopAllEffects();
            startFastChase();
      }
      else if (strcmp(status, "listening") == 0) {
            stopAllEffects();
            startClockwiseChase();
      }
      else if (strcmp(status, "thinking") == 0) {
            stopAllEffects();
            startFastBreathing();
      }
      else if (strcmp(status, "speaking") == 0) {
            stopAllEffects();
            startAnticlockwiseChase();
      }
      else if (strcmp(status, "stop") == 0) {
            Serial.println("疗愈停止");
            stopAllEffects();
            startSlowBreathing();
      }
    }

    // 评分处理
    if (g_message_para.score != score) {
      g_message_para.score = score;
      on_new_score();
    }

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

void blue_setup() {
    Serial.begin(115200);
   
    // 生成设备SN
    uint64_t chipid = ESP.getEfuseMac();
    char sn;
    snprintf(sn, sizeof(sn), "%04X", (uint16_t)(chipid >> 32));
    Serial.printf("设备SN: %s\n", sn);

    // 生成UUID
    char service_uuid;
    char characteristic_uuid;
    snprintf(service_uuid, sizeof(service_uuid), "6E400001-B5A3-F393-E0A9-E50E24DC%s", sn);
    snprintf(characteristic_uuid, sizeof(characteristic_uuid), "6E400002-B5A3-F393-E0A9-E50E24DC%s", sn);

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

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

    // 创建特征
    pRxCharacteristic = pService->createCharacteristic(
      characteristic_uuid,
      BLECharacteristic::PROPERTY_WRITE
    );
    pRxCharacteristic->setCallbacks(new RxCharacteristicCallbacks());

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

    // 开始广播
    BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(service_uuid);
    pAdvertising->setScanResponse(true);
    BLEDevice::startAdvertising();

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

void data_process() {
    if (receivedData.length() > 0 && receivedData.endsWith("\r\n")) {
      processReceivedData(receivedData);
      receivedData = "";
    }
}

void update_outer_ring() {
    if (g_message_para.status == "start") {
      updateFastChase();
    }
    else if (g_message_para.status == "listening") {
      updateClockwiseChase();
    }
    else if (g_message_para.status == "thinking") {
      updateFastBreathing();
    }
    else if (g_message_para.status == "speaking") {
      updateAnticlockwiseChase();
    }
    else if (g_message_para.status == "stop") {
      updateSlowBreathing();
    }
}

页: [1]
查看完整版本: 多台 ESP32 BLE 设备使用相同的 UUID(服务+特征),但不同设备名称(bleName)时是否相互干扰