跳转至

蓝牙通信

ESP32 同时支持经典蓝牙(Classic Bluetooth)和低功耗蓝牙(BLE 4.2),可以与手机、电脑和其他蓝牙设备进行数据交换。


一、蓝牙类型对比

特性 经典蓝牙(Classic BT) 低功耗蓝牙(BLE)
传输速率 最高 3 Mbps 最高 1 Mbps
功耗 较高 极低
连接方式 点对点,持续连接 可广播、扫描、连接
适用场景 音频传输、大量数据传输 传感器数据、可穿戴、IoT
配对 需要配对 可选配对
手机兼容性 iOS 不支持 SPP iOS + Android 都支持
ESP32 API BluetoothSerial BLEDevice
graph TB
    BT[ESP32 蓝牙] --> C[经典蓝牙<br>Classic BT]
    BT --> B[低功耗蓝牙<br>BLE]

    C --> C1[SPP 串口透传]
    C --> C2[A2DP 音频]

    B --> B1[GATT Server<br>ESP32 作为外围设备]
    B --> B2[GATT Client<br>ESP32 连接其他 BLE 设备]
    B --> B3[广播 Beacon]

选择建议

  • 与 Android 手机做串口通信 → 经典蓝牙 SPP(最简单)
  • 与 iOS 手机通信必须用 BLE(iOS 不支持 SPP)
  • 低功耗传感器 → BLE
  • 音频传输 → 经典蓝牙 A2DP

二、经典蓝牙 SPP(串口透传)

SPP(Serial Port Profile)让蓝牙就像一个无线串口一样使用,非常适合调试和简单数据传输。

基本使用

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

void setup() {
    Serial.begin(115200);
    SerialBT.begin("ESP32-Robot");  // 蓝牙设备名称
    Serial.println("蓝牙已启动,等待连接...");
}

void loop() {
    // USB 串口 → 蓝牙
    if (Serial.available()) {
        SerialBT.write(Serial.read());
    }

    // 蓝牙 → USB 串口
    if (SerialBT.available()) {
        Serial.write(SerialBT.read());
    }

    delay(10);
}

Android APP

推荐使用 Serial Bluetooth Terminal(Google Play 搜索),连接 ESP32 后即可收发数据。

蓝牙遥控小车

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

const int MOTOR_L1 = 16, MOTOR_L2 = 17;  // 左电机
const int MOTOR_R1 = 18, MOTOR_R2 = 19;  // 右电机

void setup() {
    Serial.begin(115200);
    SerialBT.begin("ESP32-Car");

    pinMode(MOTOR_L1, OUTPUT); pinMode(MOTOR_L2, OUTPUT);
    pinMode(MOTOR_R1, OUTPUT); pinMode(MOTOR_R2, OUTPUT);
    stopMotors();
}

void loop() {
    if (SerialBT.available()) {
        char cmd = SerialBT.read();
        switch (cmd) {
            case 'F': forward();  break;
            case 'B': backward(); break;
            case 'L': turnLeft(); break;
            case 'R': turnRight();break;
            case 'S': stopMotors(); break;
        }
        SerialBT.printf("执行命令: %c\n", cmd);
    }
}

void forward() {
    digitalWrite(MOTOR_L1, HIGH); digitalWrite(MOTOR_L2, LOW);
    digitalWrite(MOTOR_R1, HIGH); digitalWrite(MOTOR_R2, LOW);
}

void backward() {
    digitalWrite(MOTOR_L1, LOW); digitalWrite(MOTOR_L2, HIGH);
    digitalWrite(MOTOR_R1, LOW); digitalWrite(MOTOR_R2, HIGH);
}

void turnLeft() {
    digitalWrite(MOTOR_L1, LOW);  digitalWrite(MOTOR_L2, HIGH);
    digitalWrite(MOTOR_R1, HIGH); digitalWrite(MOTOR_R2, LOW);
}

void turnRight() {
    digitalWrite(MOTOR_L1, HIGH); digitalWrite(MOTOR_L2, LOW);
    digitalWrite(MOTOR_R1, LOW);  digitalWrite(MOTOR_R2, HIGH);
}

void stopMotors() {
    digitalWrite(MOTOR_L1, LOW); digitalWrite(MOTOR_L2, LOW);
    digitalWrite(MOTOR_R1, LOW); digitalWrite(MOTOR_R2, LOW);
}

蓝牙事件回调

void btCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
    if (event == ESP_SPP_SRV_OPEN_EVT) {
        Serial.println("蓝牙客户端已连接");
    }
    if (event == ESP_SPP_CLOSE_EVT) {
        Serial.println("蓝牙客户端已断开");
    }
}

void setup() {
    SerialBT.register_callback(btCallback);
    SerialBT.begin("ESP32-Device");
}

三、BLE 基础概念

GATT 架构

BLE 通信基于 GATT(Generic Attribute Profile) 协议:

graph TB
    S[BLE Server<br>ESP32] --> SV1[Service 1<br>环境传感器<br>UUID: 0x181A]
    S --> SV2[Service 2<br>LED 控制<br>UUID: 自定义]

    SV1 --> C1[Characteristic<br>温度<br>UUID: 0x2A6E<br>Read + Notify]
    SV1 --> C2[Characteristic<br>湿度<br>UUID: 0x2A6F<br>Read + Notify]

    SV2 --> C3[Characteristic<br>LED 状态<br>UUID: 自定义<br>Read + Write]
概念 说明 类比
Server 提供数据的设备 服务器
Client 请求数据的设备 客户端(手机)
Service 一组相关功能的集合 数据库中的表
Characteristic 具体的数据点 表中的字段
UUID 唯一标识符 ID
Descriptor 特征值的元信息 字段说明

Characteristic 属性

属性 说明
Read 客户端可以读取数据
Write 客户端可以写入数据
Notify 服务端主动推送数据(不需要确认)
Indicate 服务端主动推送数据(需要确认)

四、BLE Server(ESP32 作为服务端)

创建 BLE 服务端

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

// 自定义 UUID(使用在线生成器生成)
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHAR_TEMP_UUID      "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHAR_LED_UUID        "1c95d5e3-d8f7-413a-bf3d-7a2e5d7be87e"

BLEServer* pServer = NULL;
BLECharacteristic* pTempChar = NULL;
BLECharacteristic* pLedChar = NULL;
bool deviceConnected = false;

// 连接回调
class MyServerCallbacks : public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
        deviceConnected = true;
        Serial.println("BLE 客户端已连接");
    }
    void onDisconnect(BLEServer* pServer) {
        deviceConnected = false;
        Serial.println("BLE 客户端已断开");
        pServer->startAdvertising();  // 重新开始广播
    }
};

// 写入回调
class LedCallbacks : public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic* pCharacteristic) {
        String value = pCharacteristic->getValue();
        if (value.length() > 0) {
            Serial.printf("收到 LED 指令: %s\n", value.c_str());
            if (value == "1" || value == "on") {
                digitalWrite(2, HIGH);
            } else {
                digitalWrite(2, LOW);
            }
        }
    }
};

void setup() {
    Serial.begin(115200);
    pinMode(2, OUTPUT);

    // 初始化 BLE
    BLEDevice::init("ESP32-BLE-Sensor");
    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks());

    // 创建 Service
    BLEService* pService = pServer->createService(SERVICE_UUID);

    // 创建温度 Characteristic(Read + Notify)
    pTempChar = pService->createCharacteristic(
        CHAR_TEMP_UUID,
        BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
    );
    pTempChar->addDescriptor(new BLE2902());  // 启用 Notify

    // 创建 LED Characteristic(Read + Write)
    pLedChar = pService->createCharacteristic(
        CHAR_LED_UUID,
        BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
    );
    pLedChar->setCallbacks(new LedCallbacks());

    // 启动服务和广播
    pService->start();
    BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->start();

    Serial.println("BLE 服务端已启动,等待连接...");
}

void loop() {
    if (deviceConnected) {
        // 定期发送温度数据
        float temp = 25.0 + random(0, 100) / 10.0;
        char tempStr[8];
        snprintf(tempStr, sizeof(tempStr), "%.1f", temp);
        pTempChar->setValue(tempStr);
        pTempChar->notify();  // 推送通知

        Serial.printf("发送温度: %s°C\n", tempStr);
    }
    delay(2000);
}

手机端连接

推荐 APP

  • nRF Connect(Nordic 官方,功能最全)
  • LightBlue(iOS 推荐)
  • BLE Scanner

连接后:

  1. 找到 ESP32-BLE-Sensor → 连接
  2. 展开 Service → 找到 Characteristic
  3. 读取温度值(按 Read 按钮)
  4. 订阅通知(按 Notify 按钮,自动接收推送)
  5. 写入 LED 控制(按 Write,输入 "1" 或 "0")

五、BLE Client(ESP32 作为客户端)

ESP32 也可以主动连接其他 BLE 设备(如小米手环、BLE 传感器节点):

#include <BLEDevice.h>

static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");

static BLEAdvertisedDevice* targetDevice;
static bool doConnect = false;
static bool connected = false;
static BLERemoteCharacteristic* pRemoteChar;

// 扫描回调
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
        if (advertisedDevice.haveServiceUUID() &&
            advertisedDevice.isAdvertisingService(serviceUUID)) {
            Serial.printf("找到目标设备: %s\n", 
                         advertisedDevice.toString().c_str());
            targetDevice = new BLEAdvertisedDevice(advertisedDevice);
            doConnect = true;
            BLEDevice::getScan()->stop();
        }
    }
};

// Notify 回调
static void notifyCallback(
    BLERemoteCharacteristic* pBLERemoteChar,
    uint8_t* pData, size_t length, bool isNotify) {
    Serial.printf("收到通知: %.*s\n", length, (char*)pData);
}

bool connectToServer() {
    BLEClient* pClient = BLEDevice::createClient();
    pClient->connect(targetDevice);

    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) return false;

    pRemoteChar = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteChar == nullptr) return false;

    if (pRemoteChar->canNotify()) {
        pRemoteChar->registerForNotify(notifyCallback);
    }

    connected = true;
    return true;
}

void setup() {
    Serial.begin(115200);
    BLEDevice::init("ESP32-Client");

    BLEScan* pBLEScan = BLEDevice::getScan();
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(true);
    pBLEScan->start(10);  // 扫描 10 秒
}

void loop() {
    if (doConnect) {
        if (connectToServer()) {
            Serial.println("连接成功!");
        }
        doConnect = false;
    }

    if (connected && pRemoteChar->canRead()) {
        String value = pRemoteChar->readValue();
        Serial.printf("读取值: %s\n", value.c_str());
    }

    delay(2000);
}

六、BLE Beacon(广播信标)

BLE Beacon 不需要建立连接,只是周期性广播数据,适合室内定位、信息推送等场景:

#include <BLEDevice.h>
#include <BLEBeacon.h>

void setup() {
    Serial.begin(115200);
    BLEDevice::init("ESP32-Beacon");

    BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();

    BLEBeacon beacon;
    beacon.setManufacturerId(0x4C00);  // Apple iBeacon
    beacon.setProximityUUID(BLEUUID("12345678-1234-1234-1234-123456789ABC"));
    beacon.setMajor(1);
    beacon.setMinor(100);
    beacon.setSignalPower(-59);

    BLEAdvertisementData advData;
    advData.setFlags(0x06);

    std::string strServiceData = "";
    strServiceData += (char)26;    // 长度
    strServiceData += (char)0xFF;  // 厂商自定义
    strServiceData += beacon.getData();
    advData.addData(strServiceData);

    pAdvertising->setAdvertisementData(advData);
    pAdvertising->start();

    Serial.println("iBeacon 广播已启动");
}

void loop() {
    delay(1000);
}

七、蓝牙与 WiFi 共存

ESP32 的 WiFi 和蓝牙共用同一个 2.4GHz 射频模块,需要注意:

共存注意事项

  • WiFi + BLE 可以共存,但性能会互相影响
  • WiFi + 经典蓝牙共存时,吞吐量明显下降
  • 蓝牙和 WiFi 通过==时分复用==共享天线
  • 高负载 WiFi 通信时,BLE 的延迟会增加
  • 内存占用:WiFi ≈ 80KB,BLE ≈ 60KB,同时开启需要更多内存
// WiFi + BLE 同时使用
#include <WiFi.h>
#include <BLEDevice.h>

void setup() {
    // 先初始化 WiFi
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) delay(500);

    // 再初始化 BLE
    BLEDevice::init("ESP32-Dual");
    // ... 创建 BLE 服务 ...
}

八、常用函数速查

BluetoothSerial(经典蓝牙)

函数 功能
SerialBT.begin(name) 启动蓝牙,设置设备名
SerialBT.available() 可读字节数
SerialBT.read() 读取一字节
SerialBT.write(data) 写入数据
SerialBT.print(data) 格式化输出
SerialBT.connected() 是否已连接
SerialBT.end() 关闭蓝牙

BLE

函数 功能
BLEDevice::init(name) 初始化 BLE
createServer() 创建 Server
createService(uuid) 创建 Service
createCharacteristic(uuid, props) 创建 Characteristic
setValue(value) 设置特征值
getValue() 获取特征值
notify() 发送通知
startAdvertising() 开始广播

九、常见问题

经典蓝牙和 BLE 怎么选?

场景 选择
与 Android 手机简单通信 经典蓝牙 SPP
与 iOS 手机通信 BLE(唯一选择)
低功耗传感器节点 BLE
大量数据持续传输 经典蓝牙
室内定位信标 BLE Beacon
同时兼容 Android + iOS BLE

BLE 最多能传多少数据?

  • 单次通知/读写最大 MTU 默认 23 字节(20 字节有效载荷)
  • 可以协商更大 MTU(最大 512 字节):pServer->updatePeerMTU(pServer->getConnId(), 512)
  • 大量数据需要分包传输

手机搜不到 ESP32 蓝牙怎么办?

  • 经典蓝牙:在手机蓝牙设置中搜索,确认 ESP32 代码中 begin() 已调用
  • BLE:普通蓝牙搜索看不到 BLE 设备,需要使用 nRF Connect 等 BLE 扫描 APP
  • 确认 ESP32 已开始广播(startAdvertising()
  • 某些 Android 版本需要打开==位置权限==才能扫描 BLE

ESP32-C3 / ESP32-S2 支持蓝牙吗?

  • ESP32-S2不支持任何蓝牙
  • ESP32-C3:仅支持 BLE 5.0
  • ESP32-S3:仅支持 BLE 5.0
  • 只有原版 ESP32 同时支持经典蓝牙和 BLE