用手机 App 轻轻一点,就能远程控制开发板上的 LED,这就是本文要实现的 BLE 小项目。基于 Wemos D1 R32(ESP32)和 Arduino IDE,整个过程不到 120 行核心代码,非常适合物联网入门。

目录

  1. 前言
  2. 软硬件准备
  3. 项目需求与 BLE 服务设计
  4. 编写代码(高电平点亮 LED)
  5. 烧录与调试
  6. 手机 App 操作指南
  7. 扩展与总结

1. 前言

Wemos D1 R32 是一款基于 ESP32 的高性价比开发板,板载一颗 LED(通常接在 GPIO2),非常适合作为 BLE 学习的第一个目标设备。
本文会带你实现:

  • 手机通过 BLE 连接开发板
  • 发送 0x01 点亮 LED,发送 0x00 熄灭 LED
  • 高电平点亮、低电平熄灭(适配某些自定义板或特殊要求)
  • 读取设备信息(剩余堆栈大小)
  • 通过串口监视器观察 BLE 事件和调试信息

最终效果:手机 App 上按一下,LED 亮;再按一下,LED 灭;顺便还能知道 ESP32 还有多少内存可用。


在这里插入图片描述

2. 软硬件准备

硬件 说明
Wemos D1 R32 开发板 或任何 ESP32 开发板,需确认板载 LED 引脚(本文用 GPIO2)
USB 数据线 供电与下载程序
安卓手机 需支持 BLE(绝大部分手机支持)
软件 版本
Arduino IDE 1.8.19 及以上
ESP32 开发板包 2.0.14 或更高(通过开发板管理器安装)
BLE 调试助手 推荐 nRF ConnectBLE 调试助手(应用商店下载)

安装 ESP32 支持:
Arduino IDE → 文件 → 首选项 → 附加开发板管理器网址 → 添加 https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
然后工具 → 开发板 → 开发板管理器 → 搜索 esp32 → 安装。

选择开发板:Wemos D1 R32ESP32 Dev Module,串口波特率推荐 115200


3. 项目需求与 BLE 服务设计

功能需求

  1. 上电后广播 BLE,设备名为 Wemos D1 R32 LED
  2. 提供 LED 控制特征:写入 0x01 点亮,0x00 熄灭
  3. 提供信息读取特征:返回 "Wemos D1 R32 | Free heap: xxxx bytes"
  4. 断开后自动重广播,可被重新连接
  5. 串口监视器打印连接、断开、控制指令等信息

BLE 服务定义

项目 UUID
Service 4fafc201-1fb5-459e-8fcc-c5c9c331914b
Control Characteristic beb5483e-36e1-4688-b7f5-ea07361b26a8(Write)
Info Characteristic beb5483e-36e1-4688-b7f5-ea07361b26a9(Read)

4. 编写代码(高电平点亮 LED)

代码分为几个部分:

  • 引入 BLE 库
  • 定义引脚和 UUID
  • 实现连接回调、控制回调、信息回调
  • 创建服务和特征,启动广播

特别说明: 原 Wemos D1 R32 板载 LED 通常是低电平点亮(即 digitalWrite(LED, LOW) 亮,HIGH 灭)。但本文要求 高电平点亮、低电平熄灭,因此代码中 digitalWrite(LED_PIN, HIGH) 点亮,LOW 熄灭。

完整代码

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

#define LED_PIN 2                      // 板载 LED 引脚
#define DEVICE_NAME "Wemos D1 R32 LED"
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CONTROL_CHAR_UUID   "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define INFO_CHAR_UUID      "beb5483e-36e1-4688-b7f5-ea07361b26a9"

BLECharacteristic *pControlChar;
BLECharacteristic *pInfoChar;
bool deviceConnected = false;

// ========== 连接状态回调 ==========
class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) override {
    deviceConnected = true;
    Serial.println(">> 手机已连接");
  }
  void onDisconnect(BLEServer* pServer) override {
    deviceConnected = false;
    Serial.println(">> 设备已断开,重新广播");
    pServer->startAdvertising();
  }
};

// ========== LED 控制回调(高电平点亮)==========
class ControlCallbacks : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    String value = pChar->getValue();
    if (value.length() > 0) {
      uint8_t cmd = value[0];
      if (cmd == 0x01) {
        digitalWrite(LED_PIN, HIGH);    // 高电平点亮
        Serial.println("LED 点亮 (高电平)");
      } else if (cmd == 0x00) {
        digitalWrite(LED_PIN, LOW);     // 低电平熄灭
        Serial.println("LED 熄灭");
      } else {
        Serial.printf("未知指令: 0x%02X\n", cmd);
      }
    }
  }
};

// ========== 信息读取回调 ==========
class InfoCallbacks : public BLECharacteristicCallbacks {
  void onRead(BLECharacteristic *pChar) override {
    String info = "Wemos D1 R32 | Free heap: ";
    info += String(ESP.getFreeHeap());
    info += " bytes";
    pChar->setValue(info.c_str());
    Serial.println(">> 读取设备信息");
  }
};

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);   // 初始熄灭

  // 初始化 BLE
  BLEDevice::init(DEVICE_NAME);
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);

  // 控制特征
  pControlChar = pService->createCharacteristic(
    CONTROL_CHAR_UUID,
    BLECharacteristic::PROPERTY_WRITE
  );
  pControlChar->setCallbacks(new ControlCallbacks());

  // 信息特征
  pInfoChar = pService->createCharacteristic(
    INFO_CHAR_UUID,
    BLECharacteristic::PROPERTY_READ
  );
  pInfoChar->setCallbacks(new InfoCallbacks());
  pInfoChar->setValue("Wemos D1 R32 | BLE Ready");

  // 启动服务
  pService->start();
  // 开始广播
  pServer->getAdvertising()->start();

  Serial.println("BLE 广播中,设备名: " + String(DEVICE_NAME));
  Serial.println("等待手机连接...");
}

void loop() {
  delay(10);   // 无需复杂任务
}

代码要点

  • ControlCallbacks:当手机向控制特征写入数据时触发,取出第一个字节判断,0x01 执行 HIGH 点亮,0x00 执行 LOW 熄灭。
  • InfoCallbacks:每次手机读取信息特征时,动态生成包含当前空闲堆大小的字符串并返回。
  • 串口输出:所有关键动作(连接、断开、点亮、熄灭、读取)都会打印到串口,方便调试。

5. 烧录与调试

  1. 将 Wemos D1 R32 用 USB 连接电脑,在 Arduino IDE 中选择正确的端口和开发板。
  2. 点击 上传,等待编译完成并烧录。
  3. 打开 串口监视器(工具 → 串口监视器),波特率 115200
  4. 如果看到 BLE 广播中...,说明程序运行正常。

6. 手机 App 操作指南

BLE 调试助手(或 nRF Connect)为例:

6.1 扫描并连接

  • 打开 App,点击 扫描(SCAN)。
  • 在列表中找到 Wemos D1 R32 LED,点击 连接

6.2 控制 LED 亮灭

  • 连接成功后,进入服务页面,找到服务 UUID 4fafc...914b
  • 下面有两个特征,点击 beb5483e...26a8(控制特征)。
  • 点击 写入(Write),输入格式选 Hex,发送 01 → LED 亮,发送 00 → LED 灭。

注意:如果 App 默认是 ASCII 文本,请务必切换到 Hex 模式,否则会发送字符 '0' '1'(0x30 0x31)而非 0x01。

6.3 读取设备信息

  • 点击 beb5483e...26a9(信息特征)。
  • 点击 读取(Read),下方会显示类似 Wemos D1 R32 | Free heap: 192340 bytes 的字符串。

6.4 观察串口监视器

当手机连接、控制、读取时,串口监视器会输出相应日志,例如:

>> 手机已连接
LED 点亮 (高电平)
LED 熄灭
>> 读取设备信息
>> 设备已断开,重新广播

在这里插入图片描述

7. 扩展与总结

可以做什么改进?

  • 增加 RTC 或传感器:将温度、湿度等数据通过信息特征返回。
  • 添加安全机制:启用 BLE 配对与绑定,防止他人控制。
  • 切换为通知模式:让设备主动上报 LED 状态变化。

总结

本文带你完整实现了一个基于 ESP32 的 BLE 遥控开关,核心知识点包括:

  • ESP32 的 BLE 服务、特征创建
  • 特征回调(onWrite / onRead)
  • 高电平点亮的 LED 驱动
  • 手机调试助手的正确使用方法

即便是物联网新手,按照步骤操作,也能在 10 分钟内让手机遥控开发板上的 LED。希望这篇博客能成为你探索 BLE 应用的起点。


老徐,2026/05/21 小满
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐