sweg
sweg

Reputation: 9

ESP32-WROOM-32 Dev module crashing short time after start

I am making an A2DP Sink bluetooth audio reciever using the [https://github.com/pschatzmann/ESP32-A2DP/tree/main](ESP32-A2DP) library and Arduino IDE. When i start the program, connect to an audio source (pc or phone) and play music, after the first few seconds of playing my program crashes with this error:

E (24574) task_wdt:  - IDLE0 (CPU 0)
E (24574) task_wdt: Tasks currently running:
E (24574) task_wdt: CPU 0: BTC_TASK
E (24574) task_wdt: CPU 1: IDLE1
E (24574) task_wdt: Aborting.
E (24574) task_wdt: Print CPU 0 (current core) backtrace

Backtrace: 0x4000bfed:0x3ffd2940 0x40097f28:0x3ffd2950 0x4009596f:0x3ffd2970 0x400dacd5:0x3ffd29b0 0x400d334e:0x3ffd29f0 0x400d336f:0x3ffd2a20 0x401a0233:0x3ffd2a50 0x4019f9bd:0x3ffd2a70 0x401a01d9:0x3ffd2a90 0x400d3fd5:0x3ffd2ab0 0x4019fff9:0x3ffd2ad0 0x400d36df:0x3ffd2af0 0x40120071:0x3ffd2b10 0x4011873d:0x3ffd2b50 0x401186d9:0x3ffd2b70 0x40097caa:0x3ffd2ba0

this backtrace decoded is:

0x40097f28: vPortExitCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos\portmacro.h:568
0x4009596f: xQueueGenericSend at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel\queue.c:998
0x400dacd5: i2s_channel_write at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/i2s\i2s_common.c:1145
0x400d334e:  is in I2SClass::write(unsigned char*, unsigned int) (C:\Users\20vik\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7\libraries\ESP_I2S\src\ESP_I2S.cpp:831).
0x400d336f:  is in I2SClass::write(unsigned char) (C:\Users\20vik\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7\libraries\ESP_I2S\src\ESP_I2S.cpp:889).
0x401a0233:  is in Print::write(unsigned char const*, unsigned int) (C:\Users\20vik\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7\cores\esp32\Print.cpp:41).
0x4019f9bd:  is in BluetoothA2DPOutputPrint::write(unsigned char const*, unsigned int) (c:\Users\20vik\Documents\Arduino\libraries\ESP32-A2DP\src/BluetoothA2DPOutput.h:132).
0x401a01d9:  is in BluetoothA2DPSink::i2s_write_data(unsigned char const*, unsigned int) (c:\Users\20vik\Documents\Arduino\libraries\ESP32-A2DP\src\BluetoothA2DPSink.cpp:1242).
0x400d3fd5:  is in BluetoothA2DPSink::write_audio(unsigned char const*, unsigned int) (c:\Users\20vik\Documents\Arduino\libraries\ESP32-A2DP\src/BluetoothA2DPSink.h:533).
0x4019fff9:  is in BluetoothA2DPSink::audio_data_callback(unsigned char const*, unsigned long) (c:\Users\20vik\Documents\Arduino\libraries\ESP32-A2DP\src\BluetoothA2DPSink.cpp:1049).
0x400d36df:  is in ccall_audio_data_callback(uint8_t const*, uint32_t) (c:\Users\20vik\Documents\Arduino\libraries\ESP32-A2DP\src\BluetoothA2DPSink.cpp:1183).
0x40097caa: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa\port.c:162

This is my code:

#include <Wire.h>
#include <ESP_I2S.h>
#include <BluetoothA2DPSink.h>
#include <LiquidCrystal_I2C.h>
#include "DebugTools.h"


#define I2C_SCL 17
#define I2C_SDA 16

#define I2S_SCK 22
#define I2S_WS 25
#define I2S_SD 26

#define LCD_I2C_ADDRESS 0x27

I2SClass i2s;
BluetoothA2DPSink a2dp_sink(i2s);
//BluetoothA2DPSink a2dp_sink; //debug
LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, 20, 4);

String currentSongTitle;
String currentSongArtist;
String currentSongAlbum;

// FOR OPTIMISATION (FUTURE)
/*
struct AVRCP_MetadataEvent 
{
  uint8_t id;
  String value;
};
*/
//QueueHandle_t songMetadataQueue;

struct ScrollState 
{
  int scrollIndex = 0;
  unsigned long previousMillis = 0;
};


void setup() 
{
  Serial.begin(115200);

  Wire.setPins(I2C_SDA, I2C_SCL);
  lcd.init();                      
  lcd.backlight();

  a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST 
  | ESP_AVRC_MD_ATTR_ALBUM);
  //a2dp_sink.set_avrc_rn_track_change_callback([](uint8_t *id){
  //  currentSongTitle = "";
  //  currentSongArtist = "";
  //  currentSongAlbum = "";
  //  lcd.clear();
  //});
  a2dp_sink.set_avrc_metadata_callback([](uint8_t metadata_id, const uint8_t *metadata_value){
    dt::cb::print_avrc_metadata(metadata_id, metadata_value); 
    switch (metadata_id) 
    {
      case ESP_AVRC_MD_ATTR_TITLE:
        currentSongTitle = (const char*)metadata_value;
        break;
      case ESP_AVRC_MD_ATTR_ARTIST:
        currentSongArtist = (const char*)metadata_value;
        break;
      case ESP_AVRC_MD_ATTR_ALBUM:
        currentSongAlbum = (const char*)metadata_value;
        break;
      default:
        break;
    }
  });
  //a2dp_sink.set_stream_reader(dt::cb::read_data_stream, false); //debug
  i2s.setPins(I2S_SCK, I2S_WS, I2S_SD);
  i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH);
  a2dp_sink.start("AurioSystem");
}

ScrollState titleScrollState;
ScrollState artistScrollState;
ScrollState albumScrollState;

void loop() 
{
  if (a2dp_sink.is_connected())
  {
    printFormattedSongData();
  }
  else
  { 
    lcd.setCursor(0, 0);
    lcd.print("Not Connected");
  }
}

String repeatString(String text, int times) 
{
  String result = "";
  for (int i = 0; i < times; i++)
    result += text;
  return result;
}

void displayStaticAndScrollData(String dataText, int row, int displayWidth, int interval, ScrollState &state) 
{
  if (dataText.length() > displayWidth) 
  {
    dataText += "  ";
    unsigned long currentMillis = millis();
    if (currentMillis - state.previousMillis >= interval) 
    {
      state.previousMillis = currentMillis;

      lcd.setCursor(0, row);
      lcd.print(repeatString(" ", displayWidth));

      String displayText;
      if (state.scrollIndex + displayWidth <= dataText.length())
        displayText = dataText.substring(state.scrollIndex, state.scrollIndex + displayWidth);
      else
        displayText = dataText.substring(state.scrollIndex) + dataText.substring(0, (state.scrollIndex + displayWidth) % dataText.length());

      lcd.setCursor(0, row);
      lcd.print(displayText);

      state.scrollIndex = (state.scrollIndex + 1) % dataText.length();
    }
  } 
  else 
  {
    lcd.setCursor(0, row);
    lcd.print(dataText);
  }
}

void printFormattedSongData()
{
  displayStaticAndScrollData(currentSongTitle, 0, 20, 250, titleScrollState);
  displayStaticAndScrollData(currentSongArtist, 1, 20, 250, artistScrollState);
  displayStaticAndScrollData(currentSongAlbum, 2, 20, 250, albumScrollState);
}

I tried removing the i2s output functionality, and the program does not refresh the AVRCP metadata on song change, only when I pause it.

Upvotes: 1

Views: 88

Answers (3)

sweg
sweg

Reputation: 9

Increasing the watchdog timeout did not help, however I fixed it. Instead of BluetoothA2DPSink I used BluetoothA2DPSinkQueued which puts it in a separate task. There were other problems that occured only in debug, because the callbacks got too heavy and were somehow blocking the AVRCP data from coming in.

Upvotes: -1

0___________
0___________

Reputation: 67835

You starve other tasks, especially the idle loop where ESP32 IDF WDT is reset. You need to give back control to the scheduler.

In Arduino it can can be archived by calling delay(1) which behind the scenes will call vTaskDelay

yield() will most likely not work as your loop is run the the task having higher priority than the idle loop

Upvotes: 0

Jason
Jason

Reputation: 2671

Another solution beyond just increasing the length of the watchdog timer is to use state machines to split up the task and get the loop iterating faster.

void printFormattedSongData()
{
  static unsigned char state = 0;
  switch (state % 3) {
  case 0:
    displayStaticAndScrollData(currentSongTitle, 0, 20, 250, titleScrollState);
    break;
  case 1:
    displayStaticAndScrollData(currentSongArtist, 1, 20, 250, artistScrollState);
    break;
  case 2:
    displayStaticAndScrollData(currentSongAlbum, 2, 20, 250, albumScrollState);
    break;
  }
  
  state += 1;
}

This is a very common practice in the embedded world to get the main loop running faster. The faster your main loop, the more things you can put in the main loop, and the more things that don't have to happen in interrupt. The more interrupts required, the more complexity is being introduced by concurrency.

Upvotes: 0

Related Questions