ESP32封装BluetoothSerial类后反复触发RTCWDT_RTC_RESET重启
ESP32 BluetoothSerial封装后触发RTC看门狗重启问题排查
问题背景
使用arduino-esp32的BluetoothSerial库时,官方示例可正常运行,但将功能封装为自定义C++类后,设备触发RTCWDT_RTC_RESET反复重启,蓝牙功能完全失效。
官方可运行示例代码
//This example code is in the Public Domain (or CC0 licensed, at your option.) //By Evandro Copercini - 2018 // //This example creates a bridge between Serial and Classical Bluetooth (SPP) //and also demonstrate that SerialBT have the same functionalities of a normal Serial #include "BluetoothSerial.h" //#define USE_PIN // Uncomment this to use PIN during pairing. The pin is specified on the line below const char *pin = "1234"; // Change this to more secure PIN. String device_name = "ESP32-BT-Slave"; #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 Bluetooth not available or not enabled. It is only available for the ESP32 chip. #endif BluetoothSerial SerialBT; void setup() { Serial.begin(115200); SerialBT.begin(device_name); //Bluetooth device name Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str()); //Serial.printf("The device with name \"%s\" and MAC address %s is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str(), SerialBT.getMacString()); // Use this after the MAC method is implemented #ifdef USE_PIN SerialBT.setPin(pin); Serial.println("Using PIN"); #endif } void loop() { if (Serial.available()) { SerialBT.write(Serial.read()); } if (SerialBT.available()) { Serial.write(SerialBT.read()); } delay(20); }
自定义模块化代码
bluetooth.h
/*library link: https://github.com/espressif/arduino-esp32/tree/master/libraries/BluetoothSerial*/ #include "BluetoothSerial.h" class Bluetooth { private: /*this is object used to access bluetooth utils*/ BluetoothSerial SerialBT; public: /*this is the name of the esp32 when being searched for*/ const String device_name = "ESP32-Carcotech"; /*this is the Pin number for connection with ESP for legacy devices*/ const char *pin = "1234"; /*constructor*/ Bluetooth(); /*destructor*/ ~Bluetooth(); /** * @brief used to receive a string from the bluetooth * @param dataLen: is the length of the expected data to receive from the bluetooth module * @param timeOutMS: is the timeOut to wait for if not all data received : put 0 to wait infinitely if didn't till recevie same dataLen */ String getData(int dataLen, uint32_t timeOutMS); /** * @brief used to send data over the bluetooth module * @param data: this is the data to send */ void sendData(String data); };
bluetooth.cpp
#include "bluetooth.h" Bluetooth::Bluetooth() { // initialization of the bluetooth SerialBT.begin(device_name); // telling the bluetooth to use that pin for connection SerialBT.setPin(pin); } String Bluetooth::getData(int dataLen, uint32_t timeOutMS) { // this is a counter for how many data we received int receivedCounter = 0; // this is the buffer to receive in String data; // setting the timeOut //SerialBT.setTimeout(timeOutMS); // check if we should wait indefinitely or not if(timeOutMS == 0) { while(SerialBT.available()) { if(receivedCounter == dataLen) break; data += SerialBT.read(); receivedCounter++; } } else { while(SerialBT.available()) { int temp = SerialBT.read(); if(receivedCounter == dataLen || temp == -1 ) break; data += SerialBT.read(); receivedCounter++; } } return data + "\0"; } void Bluetooth::sendData(String data) { SerialBT.write((const uint8_t *)data.c_str(), strlen(data.c_str())); } Bluetooth::~Bluetooth() { }
main.cpp
#include <Arduino.h> #include "bluetooth.h" Bluetooth bluetooth; void setup() { // put your setup code here, to run once: Serial.begin(115200); while(!Serial); } void loop() { // put your main code here, to run repeatedly: /*Serial.println(bluetooth.getData(10, 0)); delay(1000); bluetooth.sendData("A");*/ }
错误现象
设备反复输出重启日志:
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 load:0x3fff0018,len:4 load:0x3fff001c,len:1044 load:0x40078000,len:10124 load:0x40080400,len:5828 entry 0x400806a8
已尝试的解决方法
将PlatformIO的espressif32平台版本更新至3.5.0,配置如下:
[env:esp32dev] platform = espressif32@3.5.0 board = esp32dev framework = arduino monitor_speed = 115200
但问题未解决。
问题原因与修复方案
核心问题
自定义类中全局Bluetooth对象的构造函数在系统初始化前执行,而SerialBT.begin()调用依赖ESP32底层蓝牙硬件初始化,此时系统尚未完成启动,直接调用会导致硬件资源访问异常,触发看门狗重启。官方示例中,蓝牙初始化是在setup()函数中完成的,避开了系统启动阶段的资源冲突。
修复步骤
- 将蓝牙初始化逻辑从构造函数移到单独的
init()方法中 - 在
setup()函数中完成系统初始化后,再调用蓝牙初始化 - 修复
getData()方法中的数据读取错误(原代码中存在重复读取导致数据丢失的问题)
修复后的代码
修改后的bluetooth.h
#include "BluetoothSerial.h" class Bluetooth { private: BluetoothSerial SerialBT; public: const String device_name = "ESP32-Carcotech"; const char *pin = "1234"; Bluetooth(); ~Bluetooth(); // 新增初始化方法 void init(); String getData(int dataLen, uint32_t timeOutMS); void sendData(String data); };
修改后的bluetooth.cpp
#include "bluetooth.h" Bluetooth::Bluetooth() { // 构造函数不做硬件初始化 } void Bluetooth::init() { SerialBT.begin(device_name); SerialBT.setPin(pin); } String Bluetooth::getData(int dataLen, uint32_t timeOutMS) { int receivedCounter = 0; String data; SerialBT.setTimeout(timeOutMS); if(timeOutMS == 0) { while(receivedCounter < dataLen) { if(SerialBT.available()) { data += (char)SerialBT.read(); receivedCounter++; } } } else { while(receivedCounter < dataLen) { int temp = SerialBT.read(); if(temp == -1) // 超时退出 break; data += (char)temp; receivedCounter++; } } return data; } void Bluetooth::sendData(String data) { SerialBT.write((const uint8_t *)data.c_str(), data.length()); } Bluetooth::~Bluetooth() { }
修改后的main.cpp
#include <Arduino.h> #include "bluetooth.h" Bluetooth bluetooth; void setup() { Serial.begin(115200); while(!Serial); // 系统初始化完成后再初始化蓝牙 bluetooth.init(); Serial.println("Bluetooth module initialized"); } void loop() { String received = bluetooth.getData(10, 5000); if(!received.isEmpty()) { Serial.print("Received data: "); Serial.println(received); bluetooth.sendData("Echo: " + received); } delay(100); }
额外注意事项
- ESP32的硬件相关操作必须在
setup()函数中执行,全局对象构造函数的执行时机早于系统启动,此时硬件资源未准备就绪 - 确保
BluetoothSerial::setTimeout()正确设置,否则超时逻辑无法正常工作 - 避免在循环中长时间阻塞,必要时添加
yield()或短延时喂看门狗
内容的提问来源于stack exchange,提问作者abdo Salm
相关产品推荐
相关产品推荐

