I have an issue that I cannot resolved.
The setup :
- ESP32S3
- SDCard
On loading; the ESP32 load a content.json in the first folder.
It reads, based on the json, a preview track mp3 (it is a Text To Speech mp3 generated by a script that says the Title of the track because my child is too young too read for now).
Then, if pushing the play button, it plays the mp3 that is corresponding of the selection (always based on the json).
The issue starts here : I don't know why but it seems that if I next to the next track, it cannot read mp3 anymore.
I can see in console that the path is good but there is not sound while the first mp3 has no issue.
Any idea ?
EDIT : I know that when the ESP32 is loading for the first time, it will not play the preview track, I don't have made that cade for now.
Code: Untitled.cpp Select all
#include "Arduino.h"
#include "Audio.h"
#include "FS.h"
#include "SPI.h"
#include "SD.h"
#include <Wire.h>
#include <vector>
#include <lvgl.h>
#include <ui.h>
#include <Arduino_GFX_Library.h>
#include <esp_wifi.h>
#include "esp_bt.h"
#include <ArduinoJson.h>
#define TFT_BL 2
#define SD_SCK 12 // CLK
#define SD_MISO 13 // D0
#define SD_MOSI 11 // CMD
#define SD_CS 10 // CLK
#define I2S_DOUT 17
#define I2S_BCLK 0
#define I2S_LRC 18
#define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
#if defined(DISPLAY_DEV_KIT)
Arduino_GFX *gfx = create_default_Arduino_GFX();
#else /* !defined(DISPLAY_DEV_KIT) */
Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
GFX_NOT_DEFINED /* CS */, GFX_NOT_DEFINED /* SCK */, GFX_NOT_DEFINED /* SDA */,
40 /* DE */, 41 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
45 /* R0 */, 48 /* R1 */, 47 /* R2 */, 21 /* R3 */, 14 /* R4 */,
5 /* G0 */, 6 /* G1 */, 7 /* G2 */, 15 /* G3 */, 16 /* G4 */, 4 /* G5 */,
8 /* B0 */, 3 /* B1 */, 46 /* B2 */, 9 /* B3 */, 1 /* B4 */
);
// option 1:
// ST7262 IPS LCD 800x480
Arduino_RPi_DPI_RGBPanel *gfx = new Arduino_RPi_DPI_RGBPanel(
bus,
800 /* width */, 0 /* hsync_polarity */, 8 /* hsync_front_porch */, 4 /* hsync_pulse_width */, 8 /* hsync_back_porch */,
480 /* height */, 0 /* vsync_polarity */, 8 /* vsync_front_porch */, 4 /* vsync_pulse_width */, 8 /* vsync_back_porch */,
1 /* pclk_active_neg */, 13000000 /* prefer_speed */, true /* auto_flush */);
#endif /* !defined(DISPLAY_DEV_KIT) */
// End of Arduino_GFX setting
// screen configuration
static uint32_t screenWidth;
static uint32_t screenHeight;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;
// Touch Configuration
#include "touch.h"
Audio audio;
// Variables for navigation
int currentFolderIndex = 0;
int currentTrackIndex = 0;
std::vector<String> folders;
// Adjust function declarations
void playCurrentTrack(_lv_event_t *event = NULL);
void playPreviewTrack(_lv_event_t *event = NULL);
void playNextTrack(_lv_event_t *event = NULL);
void playPreviousTrack(_lv_event_t *event = NULL);
// ESP32 pins initialization
void pin_init()
{
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
}
// SD Card initialization
void sd_init()
{
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
SPI.setFrequency(400000);
if (!SD.begin(SD_CS, SPI))
{
Serial.println("Card Mount Failed");
while (1)
;
}
else
{
Serial.println("SD OK");
}
}
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
#if (LV_COLOR_16_SWAP != 0)
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif
lv_disp_flush_ready(disp);
}
// Touchpad
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
if (touch_has_signal())
{
if (touch_touched())
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touch_last_x;
data->point.y = touch_last_y;
}
else if (touch_released())
{
data->state = LV_INDEV_STATE_REL;
}
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
// Functions with Files & Folders
void goToPreviousFolder(lv_event_t *event)
{
navigateFolder(-1);
}
void goToNextFolder(lv_event_t *event)
{
navigateFolder(1);
}
void loadFolders()
{
folders.clear(); // Clear existing folders
File root = SD.open("/");
while (File entry = root.openNextFile())
{
if (entry.isDirectory())
{
String folderName = entry.name();
if (folderName != "System Volume Information")
{
folders.push_back(folderName);
}
}
entry.close();
}
root.close();
// Sort folders alphabetically
std::sort(folders.begin(), folders.end());
}
void navigateFolder(int direction)
{
if (folders.empty())
{
Serial.println("No valid folders found");
return;
}
currentFolderIndex = (currentFolderIndex + direction + folders.size()) % folders.size();
currentTrackIndex = 0; // Reset track index when changing folders
//playCurrentTrack();
playPreviewTrack();
}
void playNextTrack(_lv_event_t *event)
{
// Stop the current audio playback
audio.stopSong();
currentTrackIndex++;
//playCurrentTrack();
playPreviewTrack();
}
void playPreviousTrack(_lv_event_t *event)
{
// Stop the current audio playback
audio.stopSong();
currentTrackIndex--;
if (currentTrackIndex < 0)
{
currentTrackIndex = 0;
}
//playCurrentTrack();
playPreviewTrack();
}
void playCurrentTrack(_lv_event_t *event)
{
if (folders.empty())
{
Serial.println("No valid folders found");
return;
}
String selectedFolder = folders[currentFolderIndex];
Serial.println(selectedFolder);
File jsonFile = SD.open("/" + selectedFolder + "/content.json");
if (jsonFile)
{
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, jsonFile);
if (error)
{
Serial.print("Failed to parse content.json. Error: ");
Serial.println(error.c_str());
// Print the raw JSON content for debugging
jsonFile.seek(0);
while (jsonFile.available())
{
Serial.write(jsonFile.read());
}
jsonFile.close();
return;
}
JsonArray tracks = doc["AlbumTracks"];
int totalTracks = tracks.size();
if (currentTrackIndex >= totalTracks)
{
currentTrackIndex = 0; // Wrap around to the first track
}
else if (currentTrackIndex < 0)
{
currentTrackIndex = totalTracks - 1; // Wrap around to the last track
}
const char *trackPath = tracks[currentTrackIndex]["TrackFullPath"];
const char *artist = tracks[currentTrackIndex]["TrackArtist"];
const char *title = tracks[currentTrackIndex]["TrackName"];
playMP3(trackPath);
updateLabels(artist, title);
jsonFile.close();
}
else
{
Serial.println("Failed to open content.json");
}
}
void playPreviewTrack(_lv_event_t *event)
{
if (folders.empty())
{
Serial.println("No valid folders found");
return;
}
String selectedFolder = folders[currentFolderIndex];
Serial.println(selectedFolder);
File jsonFile = SD.open("/" + selectedFolder + "/content.json");
if (jsonFile)
{
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, jsonFile);
if (error)
{
Serial.print("Failed to parse content.json. Error: ");
Serial.println(error.c_str());
// Print the raw JSON content for debugging
jsonFile.seek(0);
while (jsonFile.available())
{
Serial.write(jsonFile.read());
}
jsonFile.close();
return;
}
JsonArray tracks = doc["AlbumTracks"];
int totalTracks = tracks.size();
if (currentTrackIndex >= totalTracks)
{
currentTrackIndex = 0; // Wrap around to the first track
}
else if (currentTrackIndex < 0)
{
currentTrackIndex = totalTracks - 1; // Wrap around to the last track
}
const char *trackPath = tracks[currentTrackIndex]["TrackPreview"];
const char *artist = tracks[currentTrackIndex]["TrackArtist"];
const char *title = tracks[currentTrackIndex]["TrackName"];
playMP3(trackPath);
updateLabels(artist, title);
jsonFile.close();
}
else
{
Serial.println("Failed to open content.json");
}
}
// Playing audio
void playMP3(const String &mp3FilePath)
{
const char *mp3Path = mp3FilePath.c_str();
audio.connecttoSD(mp3Path);
}
void changeVolume(lv_event_t *event) {
lv_event_code_t code = lv_event_get_code(event);
if (code == LV_EVENT_VALUE_CHANGED) {
int arcValue = lv_arc_get_value(ui_arcVolume);
int maxArcValue = 100;
int maxSound = 10;
// Map arc value (0-100) to sound volume (0-14)
int volume = (arcValue * maxSound) / maxArcValue;
audio.setVolume(volume);
}
}
void stopSong(lv_event_t *event) {
audio.stopSong();
}
void tooglePlayPause(lv_event_t *event) {
audio.pauseResume();
}
void updateLabels(const char *artist, const char *title)
{
lv_label_set_text(ui_labelAlbum, artist);
lv_label_set_text(ui_labelTitle, title);
}
// Setup
void setup()
{
// Disable Wi-Fi
esp_err_t results = esp_wifi_stop();
// Disable Bluetooth
esp_bt_controller_disable();
esp_bt_controller_deinit();
// Serial console for debug
Serial.begin(115200);
Serial.println("Starting...");
// Hardware initialization
pin_init();
sd_init();
lv_init();
// Init Display
gfx->begin();
gfx->fillScreen(RED);
delay(500);
gfx->fillScreen(GREEN);
delay(500);
gfx->fillScreen(BLUE);
delay(500);
gfx->fillScreen(BLACK);
delay(500);
lv_init();
delay(10);
// Touch screen initialization
touch_init();
screenWidth = gfx->width();
screenHeight = gfx->height();
#ifdef ESP32
disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 4, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#else
disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 4);
#endif
if (!disp_draw_buf)
{
Serial.println("LVGL disp_draw_buf allocate failed!");
}
else
{
lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, screenWidth * screenHeight / 4);
/* Initialize the display */
lv_disp_drv_init(&disp_drv);
/* Change the following line to your display resolution */
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/* Initialize the (dummy) input device driver */
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
// Loading interface
ui_init();
// Setup functions on LVGL items
lv_obj_add_event_cb(ui_btUp, goToPreviousFolder, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btDown, goToNextFolder, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btPlay, playCurrentTrack, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btPause, tooglePlayPause, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btStop, stopSong, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btNext, playNextTrack, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btPrevious, playPreviousTrack, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_arcVolume, changeVolume, LV_EVENT_VALUE_CHANGED, NULL);
// Setup audio
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(5);
// Initialize folders during setup
loadFolders();
}
}
// Loop
void loop()
{
// Audio loop
audio.loop();
lv_event_t lv_event;
// LVGL GUI processing
lv_timer_handler();
delay(2);
}