-1

I have a problem with my code for a music box that has the following features. Using two neokey1x4 I can switch between folders on the SD card and press play/pause, stop, next song, previous song. Then .mp3 on the SD card should be selected and played. In each folder (01, 02, 03, etc.) there are .mp3 according to the following scheme: 001.mp3, 002.mp3, etc. I use a function to count the songs in the folder. The two cores of the µC are used to separate the buttons from the music playback. Either only the first song can be played, no more songs can be found or the µC freezes. In the meantime I have inserted a lot of serial.prints to check where the problem could be. I also checked different SD cards (FAT32), replaced the µC or the vs1053. In a stripped down project everything works fine. Maybe one of you has an idea? Thanks a lot!

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
#include <Adafruit_seesaw.h>
#include <Adafruit_NeoKey_1x4.h>

// Variables volatile int setvolume = 20; int lastsetvolume = 0; volatile int currentFolder = 0; volatile int currentSong = 0; volatile bool isPlaying = false; volatile bool folderChanged = false; volatile bool songChanged = false; int cachedLastSongIndex[16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};

// VS1053 #define VS1053_RESET -1 #define VS1053_CS 8 #define VS1053_DCS 10 #define CARDCS 7 #define VS1053_DREQ 9

Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

// Rotary Encoder Adafruit_seesaw ss; #define SEESAW_ADDR 0x36 #define SS_SWITCH 24 #define ENCODER_RESOLUTION 4 int32_t lastEncoderPosition = 0; bool lastEncoderButtonState = HIGH;

// Neokeys Adafruit_NeoKey_1x4 controlNeokey; Adafruit_NeoKey_1x4 folderNeokey; #define CONTROL_NEOKEY_ADDR 0x30 #define FOLDER_NEOKEY_ADDR 0x31

const uint32_t FOLDER_COLORS[] = {0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00};

uint8_t lastControlButtons = 0; uint8_t lastFolderButtons = 0;

void setup() { Serial.begin(115200); while(!Serial); if (!musicPlayer.begin()) { Serial.println(F("VS1053 not found")); while (1); } musicPlayer.setVolume(setvolume, setvolume); musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);

if (!SD.begin(CARDCS)) { Serial.println(F("SD-Card not found")); while (1); } delay(1000); // time for init sd-card }

void setup1() { if (!ss.begin(SEESAW_ADDR)) { Serial.println(F("seesaw not found")); while(1); } ss.pinMode(SS_SWITCH, INPUT_PULLUP); ss.setEncoderPosition(setvolume);

if (!controlNeokey.begin(CONTROL_NEOKEY_ADDR)) { Serial.println("Control NeoKey not found"); while(1); }

if (!folderNeokey.begin(FOLDER_NEOKEY_ADDR)) { Serial.println("Folder NeoKey not found"); while(1); }

updateFolderLEDs(); }

void loop() {

updateVolume();

if (folderChanged) { stopPlayback(); currentSong = 0; folderChanged = false; }

if (songChanged) { stopPlayback(); if (playSong(currentSong)) { Serial.print(F("Changed to song: ")); Serial.println(currentSong + 1); // +1 for readable numbering isPlaying = true; } else { Serial.println(F("Failed to change song")); isPlaying = false; } songChanged = false; }

if (isPlaying && musicPlayer.stopped()) { playNextSong();

}

if (isPlaying) { // Feed the buffer multiple times for (int i = 0; i < 8; i++) { if (musicPlayer.readyForData()) { musicPlayer.feedBuffer(); } else { break; } } }

}

void loop1() { handleRotaryEncoder(); handleControlButtons(); handleFolderButtons(); }

void updateVolume() { if (setvolume != lastsetvolume) { musicPlayer.setVolume(setvolume, setvolume); lastsetvolume = setvolume; Serial.println(setvolume, DEC); } }

void handleRotaryEncoder() { int32_t encoderPosition = ss.getEncoderPosition();

if (encoderPosition != lastEncoderPosition) { if (encoderPosition > lastEncoderPosition) { setvolume = min(50, setvolume + 1); Serial.println(F("leiser")); } else { setvolume = max(0, setvolume - 1); Serial.println(F("lauter")); } lastEncoderPosition = encoderPosition; }

bool currentEncoderButtonState = ss.digitalRead(SS_SWITCH); if (currentEncoderButtonState != lastEncoderButtonState) { if (currentEncoderButtonState == LOW) { int newFolderGroup = ((currentFolder / 4) + 1) % 4; currentFolder = newFolderGroup * 4; updateFolderLEDs(); folderChanged = true; Serial.print(F("Switched to folder group: ")); Serial.println(newFolderGroup + 1); } lastEncoderButtonState = currentEncoderButtonState; }

}

void handleControlButtons() { uint8_t buttons = controlNeokey.read(); uint8_t changed = buttons ^ lastControlButtons;

for (uint8_t i = 0; i < 4; i++) { if (changed & (1 << i)) { if (buttons & (1 << i)) { switch (i) { case 0: togglePlayPause(); controlNeokey.pixels.setPixelColor(i, isPlaying ? 0xFF0000 : 0); break; case 1: stopPlayback(); controlNeokey.pixels.setPixelColor(i, musicPlayer.stopped() ? 0xFF0000 : 0); break; case 2: playNextSong(); controlNeokey.pixels.setPixelColor(i, 0xFF0000); break; case 3: playPreviousSong(); controlNeokey.pixels.setPixelColor(i, 0xFF0000); break; } controlNeokey.pixels.show();

    // debouncing
    delay(50);
  } else {
    controlNeokey.pixels.setPixelColor(i, 0);
    controlNeokey.pixels.show();
  }
}

}
lastControlButtons = buttons; }

void handleFolderButtons() { uint8_t buttons = folderNeokey.read(); uint8_t changed = buttons ^ lastFolderButtons;

for (uint8_t i = 0; i < 4; i++) { if (changed & (1 << i)) { if (buttons & (1 << i)) { int selectedFolder = (currentFolder / 4) * 4 + i; selectFolder(selectedFolder); updateFolderLEDs(); folderChanged = true; } } }
lastFolderButtons = buttons; }

void togglePlayPause() { if (isPlaying) { musicPlayer.pausePlaying(true); Serial.println(F("Paused")); } else { if (musicPlayer.stopped()) { if (playSong(currentSong)) { Serial.println(F("Started playing")); } else { Serial.println(F("Failed to start playing")); isPlaying = false; return; } } else { musicPlayer.pausePlaying(false); Serial.println(F("Resumed")); } } isPlaying = !isPlaying; }

void stopPlayback() { musicPlayer.stopPlaying(); isPlaying = false; currentSong = 0; Serial.print(F("stop playing")); }

void playNextSong() { checkSDCard(); int lastSongIndex = getLastSongIndex(); int attempts = 0; bool success = false;

while (attempts <= lastSongIndex && !success) { currentSong++; if (currentSong > lastSongIndex) { currentSong = 0; }

if (playSong(currentSong)) {
  success = true;
  Serial.print(F(&quot;Playing next song: &quot;));
  Serial.print(currentSong + 1);  // +1 for readable numbering
  Serial.print(F(&quot; of &quot;));
  Serial.println(lastSongIndex + 1);
} else {
  Serial.print(F(&quot;Failed to play song &quot;));
  Serial.println(currentSong + 1);
}
attempts++;

}

if (!success) { Serial.println(F("Failed to play any song in the folder")); isPlaying = false; } }

void playPreviousSong() { int lastSongIndex = getLastSongIndex(); int attempts = 0; bool success = false;

while (attempts <= lastSongIndex && !success) { if (currentSong > 0) { currentSong--; } else { currentSong = lastSongIndex; }

if (playSong(currentSong)) {
  success = true;
  Serial.print(F(&quot;Playing previous song: &quot;));
  Serial.print(currentSong + 1);  // +1 for readable numbering
  Serial.print(F(&quot; of &quot;));
  Serial.println(lastSongIndex + 1);
} else {
  Serial.print(F(&quot;Failed to play song &quot;));
  Serial.println(currentSong + 1);
}
attempts++;

}

if (!success) { Serial.println(F("Failed to play any song in the folder")); isPlaying = false; } }

int getLastSongIndex() { int lastIndex = 0; char filename[20];

Serial.println(F("Entering getLastSongIndex()")); Serial.print(F("Current folder: ")); Serial.println(currentFolder + 1);

while (true) { snprintf(filename, sizeof(filename), "/%02d/%03d.mp3", currentFolder + 1, lastIndex + 1); Serial.print(F("Checking file: ")); Serial.println(filename);

if (!SD.exists(filename)) {
  Serial.println(F(&quot;File not found, breaking loop&quot;));
  break;
}

Serial.println(F(&quot;File found, incrementing lastIndex&quot;));
lastIndex++;

if (lastIndex &gt;= 999) {
  Serial.println(F(&quot;Warning: More than 999 songs in folder, stopping search&quot;));
  break;
}

}

if (lastIndex == 0) { Serial.print(F("No songs found in folder ")); Serial.println(currentFolder + 1); } else { Serial.print(F("Last song index in folder ")); Serial.print(currentFolder + 1); Serial.print(F(": ")); Serial.println(lastIndex); }

Serial.print(F("Returning lastIndex: ")); Serial.println(lastIndex > 0 ? lastIndex - 1 : 0); return lastIndex > 0 ? lastIndex - 1 : 0; }

bool playSong(int songIndex) { char filename[20]; snprintf(filename, sizeof(filename), "/%02d/%03d.mp3", currentFolder + 1, songIndex + 1);

Serial.println(F("Entering playSong()")); Serial.print(F("Attempting to play file: ")); Serial.println(filename);

if (SD.exists(filename)) { Serial.println(F("File exists, attempting to start playback")); if (musicPlayer.startPlayingFile(filename)) { Serial.print(F("Now playing: ")); Serial.println(filename); isPlaying = true; return true; } else { Serial.print(F("Failed to start playing: ")); Serial.println(filename); Serial.println(F("VS1053 may be having issues or file may be corrupted")); return false; } } else { Serial.print(F("File not found: ")); Serial.println(filename); Serial.println(F("Check SD card contents and file naming")); return false; } }

void selectFolder(int folder) { if (currentFolder != folder) { currentFolder = folder; cachedLastSongIndex[currentFolder] = -1; // Reset cache for new folder Serial.print(F("Current Folder: ")); Serial.println(currentFolder + 1); folderChanged = true; } }

void updateFolderLEDs() { uint32_t color = FOLDER_COLORS[currentFolder / 4]; for (uint8_t i = 0; i < 4; i++) { if (i == currentFolder % 4) { folderNeokey.pixels.setPixelColor(i, 0xFFFFFF); // White for selected folder } else { folderNeokey.pixels.setPixelColor(i, color); } } folderNeokey.pixels.show(); }

void checkSDCard() { if (!SD.begin(CARDCS)) { Serial.println(F("SD-Card not found, new init")); delay(100); if (!SD.begin(CARDCS)) { Serial.println(F("SD-Card-Init fails")); } } }

My Serial Monitor:

 - 19:41:21.474 -> Entering playSong()
 - 19:41:21.474 -> Attempting to play file: /01/001.mp3
 - 19:41:21.474 -> File exists, attempting to start playback
 - 19:41:21.528 -> Now playing: /01/001.mp3
 - 19:41:21.528 -> Started playing
 - 19:41:28.365 -> Entering getLastSongIndex()
 - 19:41:28.365 -> Current folder: 1
 - 19:41:28.365 -> Checking file: /01/001.mp3
 - 19:41:28.365 -> File not found, breaking loop
 - 19:41:28.365 -> No songs found in folder 1
 - 19:41:28.365 -> Returning lastIndex: 0
 - 19:41:28.365 -> Entering playSong()
 - 19:41:28.365 -> Attempting to play file: /01/001.mp3
 - 19:41:28.662 -> File not found: /01/001.mp3
 - 19:41:28.662 -> Check SD card contents and file naming
 - 19:41:28.662 -> Failed to play song 1
 - 19:41:28.662 -> Failed to play any song in the folder

The first Song can be played and sometimes the second, but then it cannot found any song anymore.


i have rewritten the complete sketch to single core and tested it with an arduino zero in debugging mode. basically everything works, but there seems to be a problem with the sd card access when playing an .mp3. basically the songs in the folder cannot be counted while playing an .mp3. at the same time i tried this again in dual core mode and the same problem occurs there. so for now i assume that it has nothing to do with the programming of the two cores. i am thinking about outsourcing the incrementing of the songs to the setup. as soon as i have a solution (both single core and dual core) i will post it here.

Juraj
  • 18,264
  • 4
  • 31
  • 49
mweisbr
  • 1
  • 1

1 Answers1

0

For RP2040 when running multi-core, each core has its own stack, I think you thought both cores are sharing the same stack. When the Pico is running in single core mode, core 0 has the full 8KB of stack space available to it. When using multicore setup1/loop1 the 8KB is split into two 4K stacks, one per core.

For Pico when running multi-core, it is more like running FreeRTOS, each core need to signalling the other core the share resource status through a FIFO mechanism, see the document for Communication between Cores.

By default the second core isn't doing anything. Here is an example on how to start the second core.

This Pico-MultiCore.ino demonstrates on how to temporarily stop and resume another core as well as on how to communicate with each other using the APIs described in the Communication between Cores link above.

hcheung
  • 1,933
  • 8
  • 15