ESP32 for Arduino 经典蓝牙学习笔记教程

一、学习目标

  1. 熟练掌握 BluetoothSerial 库的使用方法,能够独立编写基于 ESP32 经典蓝牙通信的程序。
  2. 完成实操任务:通过蓝牙控制四位数码管显示相应数字,实现手机与 ESP32 之间的无线数据交互及外设控制 。

二、蓝牙基础概念

(一)定义与特点

蓝牙是一种短距离(通常有效通信距离约 10 米 )的无线电通信技术 。它就像一个“无线桥梁”,能让手机、平板、电脑、无线耳机等众多设备之间轻松实现无线信息传输 。咱们的 ESP32 开发板很厉害,支持经典蓝牙低功耗蓝牙两种模式,本次课程重点聚焦经典蓝牙模块的使用,用它来完成数据传输和设备控制的小项目 。

(二)模块类型

  • 内置经典蓝牙和低功耗蓝牙的 ESP32 开发板:开发板本身就集成了蓝牙功能,不用额外外接模块,方便又省事,直接就能用代码开启蓝牙通信啦 。
  • HC - 06 外置蓝牙模块:这是一个单独的蓝牙模块,如果有的开发板没有内置蓝牙,或者想扩展蓝牙功能,就可以通过接线的方式把它和开发板连接起来使用,不过本次课程用不到它,咱们直接用 ESP32 内置的经典蓝牙 。

(三)拓扑图理解

咱们可以把 ESP32 想象成一个“信息中转站” :

  • 一方面,它能通过 USB 线和电脑进行有线连接,利用串口通信(TX 发送数据、RX 接收数据 ),把电脑发过来的数据接收住,或者把自己这边的数据传给电脑 。
  • 另一方面,它又能和手机等设备建立无线蓝牙连接,这样手机发送的信息能传到 ESP32,ESP32 也能把信息发给手机,实现无线的数据传输和交互 。就像下图展示的这样(结合课程里的拓扑图理解 ),ESP32 在中间,搭建起电脑和手机之间的沟通桥梁,当然也能和其他蓝牙设备互动哟 。

三、BluetoothSerial 库详解

(一)库的作用

BluetoothSerial 库就像是为咱们操作 ESP32 经典蓝牙通信量身定制的“工具包” 。它的作用和大家之前可能用过的 Serial 库很像,不过 Serial 库主要是操作硬件串口(比如 USB 串口 ),而 BluetoothSerial 库专门用来实现蓝牙终端的通讯 。使用的时候,只要导入 BluetoothSerial.h 这个头文件,就能方便地创建蓝牙实例,进行数据的发送和接收啦 。

(二)函数详细说明

1. 创建蓝牙实例

BluetoothSerial 实例名称;
  • 功能:这一步就像是在代码里“造”出一个专门负责蓝牙通信的“小助手” 。后面所有和蓝牙收发数据、设置蓝牙参数相关的操作,都要靠这个“小助手”来完成 。比如咱们可以写 BluetoothSerial SerialBT; ,这样就创建了一个叫 SerialBT 的蓝牙实例,后续用 SerialBT 就能操作蓝牙啦 。

2. begin(Name) 函数

实例名称.begin(Name);
  • 功能:初始化 ESP32 的蓝牙功能,并且给咱们的蓝牙设备设置一个名称,这样在手机等设备搜索蓝牙的时候,就能看到这个名字啦 。
  • 返回值:返回一个布尔类型的值,如果返回 true ,说明初始化和设备名称设置成功;要是返回 false ,就是失败啦,可能得检查一下代码或者硬件有没有问题 。
  • 参数 Name:这是一个字符串类型的参数,就是咱们给蓝牙设备起的名字,比如 SerialBT.begin("ESP32test"); ,这样手机搜索蓝牙时,就能找到名为 ESP32test 的设备 。

3. pinCode(password) 函数

实例名称.pinCode(password);
  • 功能:给 ESP32 蓝牙设置配对密码 。有些设备在进行蓝牙配对的时候,需要输入密码才能连接上,用这个函数就可以设置密码,增加蓝牙连接的安全性 。
  • 返回值:布尔类型,true 表示密码设置成功,false 表示失败 。
  • 参数 password:得是一个 1 - 16 位的字符串密码 。比如 SerialBT.pinCode("23456"); ,不过这个函数在咱们一些简单项目里可以根据需求选择是否启用,如果只是自己测试玩,不设置密码也能连接上 。

4. available() 函数

实例名称.available();
  • 功能:查看蓝牙接收缓冲区里有多少个字节的数据 。就好比去快递站看有没有自己的快递,有多少个 ,这样咱们就知道有没有数据可以读取啦 。
  • 返回值:整数类型,返回的数值就是接收缓冲区中的字节数量 。

5. write(data) 函数

实例名称.write(data);
  • 功能:通过蓝牙发送单个数据 。不管是发送字符、数字还是其他简单数据,都可以用这个函数 。
  • 参数 data:就是咱们要发送出去的数据 。比如想给手机发送字符 'A' ,就可以写 SerialBT.write('A');

6. read() 函数

实例名称.read();
  • 功能:从蓝牙接收缓冲区里读取一个字节的数据,而且每读取一个字节,这个字节就会从缓冲区里被“移除” ,保证下次读取的是新数据 。
  • 返回值:返回进入缓冲区的第一个字节的数据 。比如缓冲区里有数据 '1''2' ,第一次调用 read() 会返回 '1' ,第二次返回 '2'

四、代码示例及详细解析

(一)蓝牙串口透传示例(基础版 )

这个示例实现的是电脑和蓝牙设备(比如手机 )之间的数据“透传”,简单说就是电脑通过 USB 发给 ESP32 的数据,ESP32 能通过蓝牙转发给手机;手机通过蓝牙发给 ESP32 的数据,ESP32 也能通过 USB 转发给电脑,就像一个“数据桥梁” 。

// 引入蓝牙串口通信库,有了它才能使用ESP32的蓝牙串口功能
#include "BluetoothSerial.h" 
// 创建蓝牙串口对象,取名叫SerialBT,后面用它操作蓝牙通信
BluetoothSerial SerialBT; 

void setup() { 
  // 初始化硬件串口(也就是USB串口),设置波特率为115200。波特率就像约定好的“语言速度”,这样电脑串口监视器才能正确显示ESP32发过来的信息
  Serial.begin(115200); 
  // 初始化蓝牙串口,给蓝牙设备起名叫“ESP32test”,这样手机搜索蓝牙时能看到这个名字
  SerialBT.begin("ESP32test"); 
  // 这里可以设置蓝牙配对密码,比如SerialBT.pinCode("23456"); ,如果不需要密码配对,就像现在这样注释掉(//表示注释,代码不会执行这行)
  //SerialBT.pinCode("23456");  
  // 通过USB串口打印提示信息,告诉我们设备启动啦,可以去配对蓝牙啦
  Serial.println("The device started, now you can pair it with bluetooth!"); 
}

void loop() { 
  // 检查USB串口有没有数据输入(也就是电脑通过USB给ESP32发数据了没)
  if (Serial.available()) { 
    // 如果有数据,就把USB串口的数据读出来,再通过蓝牙串口发送出去,这样手机就能收到电脑发的数据啦
    SerialBT.write(Serial.read()); 
  }
  // 检查蓝牙串口有没有数据输入(也就是手机等蓝牙设备给ESP32发数据了没)
  if (SerialBT.available()) { 
    // 如果有数据,就把蓝牙串口的数据读出来,再通过USB串口发送出去,这样电脑就能收到手机发的数据啦
    Serial.write(SerialBT.read()); 
  }
  // 延迟20毫秒,别让ESP32一直高频检测数据,不然会占用太多资源,设备可能会变卡
  delay(20); 
}

代码运行流程

  1. setup 阶段:先引入蓝牙库,创建蓝牙实例 SerialBT 。然后初始化 USB 串口和蓝牙串口,给蓝牙设备起名字,最后打印提示信息,告诉我们可以去配对蓝牙啦 。
  2. loop 阶段:不断循环检查 USB 串口和蓝牙串口有没有数据 。如果 USB 串口有数据(电脑发的 ),就转发给蓝牙设备;如果蓝牙串口有数据(手机发的 ),就转发给电脑 。每次循环结束延迟 20 毫秒,让设备喘口气 。

(二)蓝牙控制引脚示例(拓展版 )

在基础透传的功能上,咱们再加个好玩的功能:用手机通过蓝牙发送指令,控制 ESP32 某个引脚的电平高低,进而控制外接在这个引脚上的设备,比如让 LED 灯亮或者灭 。

// 引入蓝牙串口通信库,开启ESP32蓝牙功能的钥匙
#include "BluetoothSerial.h" 
// 创建蓝牙串口对象SerialBT,专门负责蓝牙通信操作
BluetoothSerial SerialBT; 

void setup() { 
  // 初始化USB串口,波特率115200,用于和电脑串口监视器通信
  Serial.begin(115200); 
  // 初始化蓝牙串口,设置蓝牙设备名称为“ESP32test”
  SerialBT.begin("ESP32test"); 
  // 可选的蓝牙配对密码设置,这里注释掉了,需要的话取消注释并设置密码
  //SerialBT.pinCode("23456");  
  // 打印提示信息,告知设备已启动可配对
  Serial.println("The device started, now you can pair it with bluetooth!"); 
  // 设置引脚25为输出模式,这样我们就能通过控制这个引脚的电平,来控制接在上面的设备(比如LED)
  pinMode(25, OUTPUT); 
}

void loop() { 
  // 定义一个字符变量t,用来存从蓝牙串口读到的数据
  char t = ' ';
  // 检查USB串口是否有电脑发送的数据
  if (Serial.available()) { 
    // 有数据的话,把USB串口的数据通过蓝牙发出去
    SerialBT.write(Serial.read()); 
  }
  // 检查蓝牙串口是否有手机等设备发送的数据
  if (SerialBT.available()) { 
    // 读取蓝牙串口的数据,存到变量t里
    t = SerialBT.read(); 
    // 把读到的数据再通过USB串口发回给电脑,这样我们在串口监视器能看到手机发了啥
    Serial.write(t); 
    // 根据收到的数据控制引脚25的电平:收到'1'就把引脚设为高电平(比如让LED亮);收到'0'就设为低电平(让LED灭)
    if (t == '1') { 
      digitalWrite(25, 1); 
    } else if (t == '0') { 
      digitalWrite(25, 0); 
    }
  }
  // 延迟20毫秒,避免频繁检测数据,让ESP32运行更稳定
  delay(20); 
}

代码运行流程

  1. setup 阶段:除了基础版里的初始化操作,还多了一步 pinMode(25, OUTPUT); ,把引脚 25 设置成输出模式,为控制外接设备做准备 。
  2. loop 阶段:同样先检查 USB 串口和蓝牙串口的数据 。如果蓝牙串口收到数据,不仅把数据转发给电脑,还会判断数据是 '1' 还是 '0' ,然后控制引脚 25 的电平,实现对比如 LED 灯这样外设的控制 。比如手机发 '1' ,引脚 25 变高电平,LED 亮;发 '0' ,引脚变低电平,LED 灭 。

五、手机端操作场景(以 Arduino bluetooth controller APP 为例 )

(一)APP 连接流程

  1. 打开手机上的 Arduino bluetooth controller APP ,它会自动搜索周围的蓝牙设备 。
  2. 找到名为 ESP32test(就是咱们在代码里给蓝牙设备设置的名字 )的设备,点击它进行连接 。
  3. 连接的时候,如果之前设置了配对密码(比如 23456 ),就输入密码;要是没设置,直接就能连接上 。连接成功后,会出现几个模式选项,咱们选择 Terminal mode(终端模式 ),这个模式就像一个“聊天窗口”,可以直接发送字符数据 。

(二)发送指令控制

Terminal mode 下,咱们就可以给 ESP32 发指令啦 :

  • 发送字符 '1' ,ESP32 收到后,会把引脚 25 设为高电平,如果引脚 25 接了 LED 灯,灯就会亮起来 。
  • 发送字符 '0' ,ESP32 收到后,把引脚 25 设为低电平,LED 灯就会熄灭 。
  • 也可以发送其他字符,不过咱们代码里只对 '1''0' 做了处理,要是想实现更多功能,就可以在代码里继续扩展判断逻辑哟 。

1 条评论

  • @ 2025-7-29 20:04:40

    以下是基于之前内容,对“蓝牙控制四位数码管显示相应数字”实操任务的完整代码及讲解补充,结合数码管驱动逻辑,让整个学习笔记更完整,手把手教你实现功能:


    ESP32 for Arduino 经典蓝牙学习笔记教程(含四位数码管控制实操)

    六、实操任务:蓝牙控制四位数码管显示相应数字

    (一)任务分析

    要实现**“手机蓝牙发指令 → ESP32 收数据 → 控制四位数码管显示对应数字”**,需结合两部分知识:

    1. 蓝牙通信:用 BluetoothSerial 库实现手机与 ESP32 无线数据传输。
    2. 数码管驱动:通过 GPIO 或移位寄存器(如 74HC595)控制数码管显示,需处理**段选(显示哪个数字)位选(哪个数码管亮)**逻辑。

    (二)硬件准备

    • ESP32 开发板
    • 四位共阴/共阳数码管(以共阴数码管 + 74HC595 移位寄存器为例,简化 GPIO 占用)
    • 手机(安装 Arduino bluetooth controller APP 或类似蓝牙串口工具)

    (三)接线说明(74HC595 + 四位共阴数码管)

    ESP32 引脚 74HC595 引脚 功能
    D2 DS(SER) 串行数据输入
    D3 SHCP(SRCLK) 移位寄存器时钟
    D4 STCP(RCLK) 存储寄存器时钟
    5V VCC 电源
    GND 接地

    数码管位选引脚(控制哪一位亮)接 74HC595 输出(如 Q0~Q3 对应数码管第 1~4 位)。

    (四)完整代码实现

    #include "BluetoothSerial.h"  // 引入蓝牙串口库
    BluetoothSerial SerialBT;     // 创建蓝牙实例
    
    // 74HC595 移位寄存器引脚定义
    const int dataPin  = 2;  // DS(SER):串行数据输入
    const int clockPin = 3;  // SHCP(SRCLK):移位时钟
    const int latchPin = 4;  // STCP(RCLK):存储时钟
    
    // 共阴数码管段选编码(0~9 + 熄灭)
    // 格式:bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 → 对应数码管 a~g + 小数点(dp)
    byte numCodes[] = {
      0b00111111, // 0(a~f亮)
      0b00000110, // 1(b~c亮)
      0b01011011, // 2(a,b,g,e,d亮)
      0b01001111, // 3(a,b,g,c,d亮)
      0b01100110, // 4(f,g,b,c亮)
      0b01101101, // 5(a,f,g,c,d亮)
      0b01111101, // 6(a,f,g,e,d,c亮)
      0b00000111, // 7(a,b,c亮)
      0b01111111, // 8(全亮)
      0b01101111, // 9(a,b,c,d,f,g亮)
      0b00000000  // 熄灭
    };
    
    // 数码管显示缓存(四位,初始全灭)
    byte displayBuffer[4] = {10, 10, 10, 10}; 
    
    void setup() {
      Serial.begin(115200);        // 初始化USB串口(用于调试)
      SerialBT.begin("ESP32test"); // 初始化蓝牙,设备名设为“ESP32test”
      
      // 初始化74HC595引脚为输出模式
      pinMode(dataPin,  OUTPUT);
      pinMode(clockPin, OUTPUT);
      pinMode(latchPin, OUTPUT);
      
      Serial.println("蓝牙已启动,搜索‘ESP32test’并连接,进入Terminal模式发4位数字(如1234)");
    }
    
    void loop() {
      // 1. 处理蓝牙接收(手机发数据)
      if (SerialBT.available()) {
        String cmd = SerialBT.readStringUntil('\n'); // 读取蓝牙指令(换行结束)
        cmd.trim(); // 去掉首尾空格/换行
        
        // 只处理4位数字指令(如“1234”)
        if (cmd.length() == 4 && cmd.isDigit()) { 
          // 将字符转为数字,存入显示缓存
          for (int i = 0; i < 4; i++) {
            int num = cmd.charAt(i) - '0'; // 字符转数字(如 '1'→1)
            displayBuffer[i] = num;        // 存入缓存
          }
          SerialBT.println("已接收:" + cmd + ",数码管将更新显示");
        } else {
          SerialBT.println("指令无效!请发4位数字(如1234)");
        }
      }
    
      // 2. 刷新数码管显示(循环扫描四位)
      static int digitIndex = 0; // 当前扫描的数码管位(0~3)
      // 选择第 digitIndex 位数码管(通过74HC595输出位选信号)
      byte bitMask = 1 << digitIndex; // 位选掩码(如第0位:0001,第1位:0010...)
      
      // 发送段选码(要显示的数字)和位选码到74HC595
      shiftOut(dataPin, clockPin, MSBFIRST, numCodes[displayBuffer[digitIndex]]); // 段选
      shiftOut(dataPin, clockPin, MSBFIRST, bitMask);                             // 位选
      
      digitalWrite(latchPin, HIGH); // 锁存数据,让数码管显示
      digitalWrite(latchPin, LOW);
      
      digitIndex = (digitIndex + 1) % 4; // 切换到下一位(循环0~3)
      delay(1); // 扫描间隔(太快会闪烁,太慢会有残影)
    }
    
    // 移位输出函数(简化版,74HC595需要先移段位选、再移位段选)
    void shiftOut(int dataPin, int clockPin, int bitOrder, byte value) {
      for (int i = 0; i < 8; i++) {
        if (bitOrder == MSBFIRST) {
          digitalWrite(dataPin, (value & (1 << (7 - i))) ? HIGH : LOW);
        } else {
          digitalWrite(dataPin, (value & (1 << i)) ? HIGH : LOW);
        }
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);
      }
    }
    

    (五)代码逐行解析

    1. 库与变量定义

      • #include "BluetoothSerial.h":引入蓝牙串口库。
      • numCodes[]:共阴数码管的段选编码(0~9 对应亮灯逻辑)。
      • displayBuffer[]:缓存四位数码管要显示的数字(初始设为 10,对应“熄灭”)。
    2. setup() 初始化

      • 初始化 USB 串口(调试用)、蓝牙(设备名 ESP32test),并设置 74HC595 引脚为输出。
    3. loop() 主逻辑

      • 蓝牙接收
        • SerialBT.available() 检查是否有蓝牙数据,readStringUntil('\n') 读取手机发送的指令(以换行结尾)。
        • 校验指令长度(4 位)和内容(全数字),合法则存入 displayBuffer
      • 数码管扫描显示
        • 利用动态扫描原理,循环点亮四位数码管的某一位,人眼因“视觉暂留”看到完整数字。
        • shiftOut() 函数:将段选码(数字)和位选码(哪一位亮)通过 74HC595 串行输出,latchPin 锁存后显示。

    (六)手机端操作

    1. 打开 Arduino bluetooth controller APP → 搜索 ESP32test 并连接 → 选择 Terminal mode(终端模式)。
    2. 发送4 位数字指令(如 1234),ESP32 接收后会刷新数码管显示。

    (七)常见问题排查

    • 数码管不亮
      • 检查接线(74HC595 电源、数据线是否接对)。
      • 确认数码管类型(共阴/共阳),若为共阳,需修改 numCodes 编码(取反)。
    • 蓝牙连不上
      • 确保 ESP32 已启动(看串口监视器提示),手机蓝牙权限已开启。
      • 若需配对密码,在 setup() 中添加 SerialBT.pinCode("1234");(密码设为 1234)。

    通过以上步骤,你就能实现**“手机蓝牙发指令 → 数码管动态显示”**的完整功能啦!后续还能扩展:

    • 支持更多指令(如清屏、闪烁)。
    • 改用直接 GPIO 驱动数码管(无需 74HC595,需占用更多引脚)。

    如果是共阳数码管,只需修改 numCodes 为取反值(如 0b11000000 对应 0),其他逻辑不变~

    • 1