You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()函数中完成的,避开了系统启动阶段的资源冲突。

修复步骤

  1. 将蓝牙初始化逻辑从构造函数移到单独的init()方法中
  2. 在setup()函数中完成系统初始化后,再调用蓝牙初始化
  3. 修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 11:29:57