库完全指南:SPP 透传、配对模式与源码级解析)
Arduino ESP32 蓝牙串口BluetoothSerial库完全指南SPP 透传、配对模式与源码级解析【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32导读BluetoothSerial 是 arduino-esp32 核心库中基于经典蓝牙Classic BluetoothSerial Port ProfileSPP实现的串口透传库它让 ESP32 可以在不依赖硬件 UART 的情况下与手机、电脑、另一块 ESP32 乃至任意带蓝牙串口模块的 MCU 建立双向透明数据传输通道。本文以该库的官方 READMElibraries/BluetoothSerial/README.md为骨架结合 BluetoothSerial.cpp 的源码实现与全部示例工程系统讲解库的三种典型应用场景、三种配对Pairing方案的选择与配置、SSP 能力参数的语义以及 Legacy 固定 PIN 配对在 IDF 组件模式下的开启方法。读完本文你将能够独立完成 ESP32 蓝牙串口项目从烧录、配对到联调的完整流程并理解其底层事件驱动与队列机制。注意该库在头文件中被标记为[[deprecated(BluetoothSerial wont be supported in version 4.0.0 by default)]]见 BluetoothSerial.h即 4.0.0 版本默认将不再提供支持当前文章内容以本仓库 3.3.11 版本见 library.properties为准。一、库定位什么是蓝牙串口SPPBluetoothSerial 是一个与 ArduinoStream类兼容的串口库BluetoothSerial类直接继承自StreamBluetoothSerial.h因此它提供了available()、read()、write()、peek()、flush()等与硬件串口完全一致的标准接口。对使用者而言向SerialBT.write()写入的数据会经蓝牙 SPP 连接发送到远端设备远端发来的数据则可以通过SerialBT.read()读取与读写Serial几乎没有区别。从源码可以看到其内部实现依赖 ESP-IDF 的经典蓝牙协议栈底层事件回调esp_spp_cb与esp_bt_gap_cb分别在 BluetoothSerial.cpp 与 BluetoothSerial.cpp 中实现覆盖 SPP 初始化、连接建立/关闭、数据到达、拥塞控制、认证完成、PIN 请求、SSP 确认与密钥请求等事件收发数据采用 FreeRTOS 队列解耦接收队列_spp_rx_queue容量为 512 字节发送队列_spp_tx_queue容量为 32 个包指针BluetoothSerial.cpp发送路径由独立任务_spp_tx_task负责单次最大发送缓冲SPP_TX_MAX为 330 字节超出部分自动拆包BluetoothSerial.cppSPP 服务以回调模式ESP_SPP_MODE_CB初始化服务名默认为ESP32SPPBluetoothSerial.cpp。此外库对编译环境有明确要求所有官方示例开头都包含两组预处理检查例如 SerialToSerialBT.ino#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) #error Bluetooth is not enabled! Please run make menuconfig to and enable it #endif #if !defined(CONFIG_BT_SPP_ENABLED) #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip. #endif即需要在 sdkconfig 中开启CONFIG_BT_ENABLED、CONFIG_BLUEDROID_ENABLED与CONFIG_BT_SPP_ENABLEDArduino 默认配置已满足。同时该库仅对支持经典蓝牙的 ESP32 芯片生效——头文件与源文件都在最外层用#if SOC_BT_SUPPORTED defined(CONFIG_BT_ENABLED) defined(CONFIG_BLUEDROID_ENABLED)做了保护BluetoothSerial.h像仅支持 BLE 的芯片在编译期就会被排除。二、三个典型使用场景README 给出了三种基础用法手机、另一块 ESP32、第三方蓝牙串口模块。它们覆盖了绝大多数实际项目形态。2.1 与手机通信蓝牙终端 App流程如下在手机上安装任意一款蓝牙串口终端 AppREADME 推荐了 Android 端的 Serial Bluetooth Terminal 与 iOS 端的 HM10 Bluetooth Serial Lite向 ESP32 烧录官方示例固件如 SerialToSerialBT.ino打开手机蓝牙扫描并配对 ESP32 设备打开终端 App 连接该设备此时手机 App 与 ESP32 之间即建立起双向串口透传通道。作为从机Slave侧的最简示例SerialToSerialBT.ino 的核心逻辑如下#include Arduino.h #include BluetoothSerial.h String device_name ESP32-BT-Slave; BluetoothSerial SerialBT; void setup() { Serial.begin(115200); SerialBT.begin(device_name); // 设置蓝牙设备名并启动 SPP 服务 } void loop() { if (Serial.available()) { SerialBT.write(Serial.read()); // 硬件串口 → 蓝牙 } if (SerialBT.available()) { Serial.write(SerialBT.read()); // 蓝牙 → 硬件串口 } delay(20); }begin(String localName, bool isMaster false, bool disableBLE false)是库的启动入口BluetoothSerial.cpplocalName蓝牙广播的设备名默认值为ESP32构造函数中设置见 BluetoothSerial.cppisMaster置true表示本机作为主设备去主动连接其他从机disableBLE若项目不用 BLE可置true以经典蓝牙单模式BT_MODE_CLASSIC_BT启动释放约 10 kB 额外 RAM源码注释见 BluetoothSerial.cpp。2.2 两块 ESP32 互连主从模式README 推荐的组合是一块 ESP32 烧录 SerialToSerialBTM.ino 作为主设备Master另一块烧录 SerialToSerialBT.ino 作为从设备Slave两块板子开箱即可自动配对连接。示例具有可扩展性从源码注释可知主设备角色ESP_SPP_ROLE_MASTER最多可同时管理 7 个从机连接BluetoothSerial.cpp因此该方案可以扩展为一主多从拓扑。主设备端的连接方式有两种SerialToSerialBTM.ino#define USE_NAME // 注释掉此行则改用 MAC 地址连接 #ifdef USE_NAME String slaveName ESP32-BT-Slave; // 从机的蓝牙名称 #else String MACadd AA:BB:CC:11:22:33; uint8_t address[6] {0xAA, 0xBB, 0xCC, 0x11, 0x22, 0x33}; // 从机的 MAC 地址 #endif void setup() { Serial.begin(115200); SerialBT.begin(ESP32-BT-Master, true); // 以主设备模式启动 #ifdef USE_NAME connected SerialBT.connect(slaveName); // 按名称连接需先解析名称→地址 #else connected SerialBT.connect(address); // 按 MAC 地址连接 #endif }关于两种连接方式源码与示例注释给出了重要经验connect(uint8_t remoteAddress[], ...)按 MAC 地址直连速度快最多约 10 秒connect(String remoteName)需要先执行查询Inquiry把名称解析为地址较慢最多约 30 秒但允许连接同名设备中的任意一个示例最后展示了disconnect()后调用无参connect()重连的用法它会复用上次解析到的地址或名称BluetoothSerial.cpp。2.3 与第三方蓝牙串口模块通信当另一侧是 HC-05/HM-10 这类第三方串口蓝牙模块时需要先研读该模块的手册以确定其从机/主机模式与波特率等参数。但无论模块厂商如何与模块相连的那一侧仍可复用上述两个官方示例中的 Master 或 Slave 代码作为桥接的一端接入系统。三、配对Pairing方案总览README 明确区分了三种配对路径两种简单方案有/无 SSP可在常规 Arduino 环境直接使用一种困难方案Legacy 固定 PIN 配对必须以 IDF 组件方式编译并关闭CONFIG_BT_SSP_ENABLED才能使用。3.1 重要版本变更自3.0.0 版本起本库默认不再支持 Legacy 配对即使用 4 位固定 PIN 的传统配对方式。这意味着直接用 Arduino 方式烧录时手机/电脑与 ESP32 的配对将走 SSP 流程若你的应用强依赖固定 PIN 配对例如某些老式上位机只支持输入 0000/1234 这类 PIN必须按下文第三节的方法以 IDF 组件方式编译。3.2 无 SSP自动认证不推荐用于安全敏感场景不启用 SSP 时ESP32 会对任何配对尝试自动完成认证因此 README 明确警告若关注安全性不应使用此方式。官方示例 SerialToSerialBT.ino 与 SerialToSerialBTM.ino 默认即采用此方式方便快速上手验证链路。3.3 启用 SSPSecure Simple PairingSSP 提供安全连接官方示例 SerialToSerialBT_SSP.ino 完整演示了该方案。SSP 的控制接口如下BluetoothSerial.henableSSP()无参版本向后兼容等价于同时开启输入与输出能力enableSSP(bool inputCapability, bool outputCapability)带参版本可精确控制认证方式disableSSP()关闭 SSP。两个带参enableSSP的完整语义在源码注释中有详细描述BluetoothSerial.cppinputCapabilityESP32 设备是否具备输入手段串口终端、键盘等outputCapabilityESP32 设备是否具备输出手段串口终端、显示屏等。两者的组合决定了 SSP 的认证行为同时映射到 ESP-IDF 的 IO 能力IOCAP参数BluetoothSerial.cppinputCapabilityoutputCapability认证行为底层 IOCAP需要实现的回调truetrue两端各自显示随机数字用户核对一致后在两端分别确认ESP_BT_IO_CAP_IODisplay with promptonConfirmRequest()confirmReply()falsefalse仅由对端无 PIN 认证ESP_BT_IO_CAP_NONE无falsetrue仅由对端无 PIN 认证ESP_BT_IO_CAP_OUTDisplayOnly无truefalseESP32 端需要用户输入对端显示的 passkey 来认证ESP_BT_IO_CAP_INInput onlyonKeyRequest()respondPasskey()3.3.1 使用 SSP 的强制约束README 强调enableSSP()/disableSSP()必须在begin()之前调用如果在begin()之后调用则必须先end()再begin()重启驱动设置才会生效。这与源码实现一致——_enableSSP标志只会在_init_bt()初始化阶段被读取并写入 IOCAP 参数中途修改不会影响已初始化的协议栈。另外无参enableSSP()在驱动已就绪时还会打印提示并直接返回BluetoothSerial.cpp。3.3.2 配对回调的三件套SerialToSerialBT_SSP.ino 演示了完整的三个回调配合方式onConfirmRequest()当两端都需要人工确认InputOutput 场景时触发回调参数是 ESP32 端显示的随机数字在回调中提示用户核对后调用confirmReply(true)接受配对或confirmReply(false)拒绝。底层实现为esp_bt_gap_ssp_confirm_replyBluetoothSerial.cpponKeyRequest()当需要对端输入 passkeyInput-only 场景时触发回调中读取用户输入后调用respondPasskey(passkey)应答底层实现为esp_bt_gap_ssp_passkey_replyBluetoothSerial.cpponAuthComplete()配对完成后触发参数success表示认证是否成功可用于在配对完成前阻塞数据转发示例的loop()即依赖该标志位。示例中BTConfirmRequestCallback的处理值得一提它用%06 PRIu32格式打印 PIN避免 PIN 以 0 开头时被%lu忽略前导零等待用户在串口输入Y/y后调用confirmReply(true)。若想跳过人工确认可取消#define AUTO_PAIR的注释直接自动应答。3.4 Legacy 配对固定 PINIDF 组件方式当业务必须使用固定 PIN 的 Legacy 配对时唯一的途径是把 Arduino 以 IDF 组件ESP-IDF Component方式集成进工程然后关闭CONFIG_BT_SSP_ENABLED。README 给出的完整步骤如下按 Arduino as an IDF Component 的官方文档完成环境搭建可参考 docs/en/esp-idf_component.rst运行idf.py menuconfig导航到Component Config - Bluetooth - Bluedroid - [ ] Secure Simple Pairing并取消勾选在同一个 menuconfig 中修改分区方案Partition Table - Partition Table - (X) Single Factory app (large), no OTA保存并退出 menuconfig执行idf.py monitor flashREADME 原文如此等价于idf.py flash monitor的合写即编译烧录并打开串口监视器。仓库中提供了配套示例 SerialToSerialBT_Legacy.ino其关键调用为SerialBT.begin(deviceName); SerialBT.setPin(1234, 4); // 必须在 begin() 之后调用3.4.1 setPin 的字符与长度陷阱README 特别强调了一个极易踩坑的细节为手机和电脑设置 PIN 时必须传字符串字符数组而不是数字SerialBT.setPin(1234, 4); // 正确字符串形式 SerialBT.setPin(1234, 4); // 错误手机/电脑无法正确处理数字形式原因在于setPin底层调用的是esp_bt_gap_set_pin(ESP_BT_PIN_TYPE_FIXED, _pin_code_len, _pin_code)PIN 以字节数组形式交给协议栈BluetoothSerial.cpp。当对端也是嵌入式设备如另一块 MCU且双方约定以数字存储时数字形式也可以用但手机和电脑操作系统以字符处理 PIN必须使用字符形式。此外setPin对长度有硬性校验PIN 长度必须为 116 字节否则会打印错误日志并返回falseBluetoothSerial.cpp。在 Legacy 场景下底层通过ESP_BT_GAP_PIN_REQ_EVT事件触发esp_bt_gap_pin_reply应答BluetoothSerial.cpp且当对端要求 16 位 PIN 而本地 PIN 不足 16 字节时会拒绝配对。四、进阶 API扫描、发现、配对管理与连接状态除了 README 重点讲解的配对部分该库还提供了一批实用的进阶接口声明见 BluetoothSerial.h按源码逐一说明。4.1 设备发现DiscoveryBTScanResults *discover(int timeoutMs)同步扫描timeoutMs取值范围必须在MIN_INQ_TIME约 1.28 s与MAX_INQ_TIME约 61.44 s之间INQ_TIME单位为 1280 ms越界会直接返回nullptrBluetoothSerial.cppbool discoverAsync(BTAdvertisedDeviceCb cb, int timeoutMs)异步扫描每发现一个新设备就回调一次注意 README 之外的约束——若已设置远程名称或地址则无法异步扫描BluetoothSerial.cppdiscoverAsyncStop()/discoverClear()/getScanResults()停止扫描、清空结果、获取结果集。DiscoverConnect.ino 是完整范例异步扫描 10 秒后停止遍历结果用getChannels()查询每个设备上提供的 SPP 服务SDP 记录然后连接第一个提供 SPP 服务的设备并按其频道号建立连接。4.2 主动连接与安全参数connect()系列有多个重载最完整的版本允许显式指定安全掩码与角色BluetoothSerial.cppbool connect(uint8_t remoteAddress[], int channel 0, esp_spp_sec_t sec_mask (ESP_SPP_SEC_ENCRYPT | ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role ESP_SPP_ROLE_MASTER);channel指定 SPP 频道号0表示先做 SDP 发现自动探测sec_mask安全级别常用ESP_SPP_SEC_ENCRYPT | ESP_SPP_SEC_AUTHENTICATE加密认证或ESP_SPP_SEC_NONEDiscoverConnect 示例的注释提示当远端要求RequireAuthentication时应使用前者否则可用后者roleESP_SPP_ROLE_MASTER主可连接多个从机或ESP_SPP_ROLE_SLAVE从只能与一个主机建立连接。连接状态相关connected(int timeout 0)阻塞等待连接结果超时 0 表示不等待见 BluetoothSerial.cppisClosed()判断连接是否已断开或连接尝试是否失败disconnect()主动断开并等待最多 10 秒hasClient()判断当前是否存在活动的 SPP 客户端句柄。4.3 配对设备Bonding管理getNumberOfBondedDevices()/getBondedDevices(dev_num, dev_list)查询已配对设备数量与列表deleteBondedDevice(remoteAddress)/deleteAllBondedDevices()删除单个或全部配对记录。deleteAllBondedDevices的源码实现会先查询数量、分配esp_bd_addr_t数组、再逐个调用esp_bt_gap_remove_bond_deviceBluetoothSerial.cpp在多个官方示例中都有注释提示SerialBT.deleteAllBondedDevices()必须在begin()之后调用用于清除历史配对缓存、方便重复测试。4.4 本地 MAC 地址与远程名称getBtAddress(uint8_t *mac)/getBtAddressObject()/getBtAddressString()以字节数组、BTAddress对象、字符串AA:BB:CC:DD:EE:FF大写格式三种形式获取本机蓝牙 MAC完整用法见 GetLocalMAC.inorequestRemoteName(address)readRemoteName(buf)异步请求远端设备名称并在后台存储随后读取invalidateRemoteName()用于使缓存失效BluetoothSerial.cpp。4.5 数据回调与内存释放onData(BluetoothSerialDataCb cb)注册数据到达回调注册后数据将直接交给回调处理而不是进入接收队列BluetoothSerial.cpp适合需要低延迟逐包处理的场景register_callback(esp_spp_cb_t callback)注册自定义的原始 SPP 事件回调库内置回调会在处理完后追加调用它BluetoothSerial.cppmemrelease()释放经典蓝牙控制器占用的额外约 30 kB RAM调用后需要复位才能重新启用经典蓝牙源码注释见 BluetoothSerial.cpp。五、官方示例速查表仓库libraries/BluetoothSerial/examples/下共 8 个示例全部带 CI 配置ci.yml可按需选用示例用途关键 APISerialToSerialBT从机模式串口透传无 SSP 自动认证begin(name)、读写透传SerialToSerialBTM主机模式按名称/MAC 连接从机begin(name, true)、connect()、disconnect()SerialToSerialBT_SSP带 SSP 认证的从机透传enableSSP()、onConfirmRequest、onKeyRequest、onAuthCompleteSerialToSerialBT_LegacyLegacy 固定 PIN 配对需 IDF 组件 关 SSPsetPin(1234, 4)DiscoverConnect异步扫描 SDP 查频道 自动连接discoverAsync、getChannels、connect(addr, ch, sec, role)bt_classic_device_discovery经典蓝牙设备发现discover()bt_remove_paired_devices删除配对设备deleteAllBondedDevices等GetLocalMAC获取本机蓝牙 MAC三种格式getBtAddress*系列六、常见问题与排查建议编译报错 Bluetooth is not enabled说明 sdkconfig 未开启CONFIG_BT_ENABLED/CONFIG_BLUEDROID_ENABLEDArduino 默认配置通常已开启若自定义过配置需检查编译报错 Serial Port Profile ... not enabled缺少CONFIG_BT_SPP_ENABLEDSPP 仅在经典蓝牙芯片上可用SSP 设置不生效确认enableSSP()在begin()之前调用否则需end()begin()重启驱动README 明确要求手机配对失败或无法输入 PIN确认用的是字符形式setPin(1234, 4)而非数字且 Legacy PIN 方案必须按 IDF 组件方式编译并关闭CONFIG_BT_SSP_ENABLED纯 Arduino 方式下默认走 SSP主设备连不上手机/电脑手机、电脑通常是主设备角色ESP32 以主机模式begin(name, true)连接它们会出现能配对但串口不通的现象——SerialToSerialBTM.ino 的注释明确警告不要尝试用主机模式连接同为 Master 的手机或电脑连接速度差异按 MAC 直连约 10 秒上限快于按名称连接约 30 秒上限后者需要先完成名称解析日志级别设为 Info 可观察到解析过程重复配对测试示例普遍提供SerialBT.deleteAllBondedDevices()注释行在begin()之后取消注释即可清空历史配对避免旧缓存干扰新连接。总结BluetoothSerial 库以Stream兼容接口封装了 ESP-IDF 经典蓝牙 SPP 全链路让 ESP32 的蓝牙串口开发与普通硬件串口一样简单。本篇文章从 README 的三类应用场景与三种配对方案出发结合 BluetoothSerial.cpp 源码与 8 个官方示例完整覆盖了 SSP 能力参数、Legacy 固定 PIN 的 IDF 组件开启步骤、setPin字符陷阱以及发现、连接、配对管理和 MAC 查询等进阶 API。无论你是做手机调试工具、双机透传还是与第三方模块对接都可以直接复用本文给出的示例代码与参数说明快速落地同时请留意 3.0.0 之后的 Legacy 配对策略变化与 4.0.0 的弃用计划合理规划工程方案。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考