David KOCHONI
Published © MIT

ESP32-8048S050 Interactive Media Player

A fully standalone multimedia kiosk built with the ESP32-8048S050, LovyanGFX, and ESP32-audioI2S no OS, no Linux, just bare metal Arduino.

IntermediateWork in progress9
ESP32-8048S050 Interactive Media Player

Things used in this project

Hardware components

ESP32-8048S050
×1
Speaker: 3W, 4 ohms
Speaker: 3W, 4 ohms
×1

Software apps and online services

VS Code
Microsoft VS Code
PlatformIO IDE
PlatformIO IDE

Story

Read more

Custom parts and enclosures

esp32-8048s050_specifications-en_EV994bnBsJ.pdf

Schematics

Guide

Shématic

Code

main.cpp

C/C++
#include <Arduino.h>
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <FS.h>
#include <SD_MMC.h>
#include "Audio.h"

// ============================================
// GPIO I2S vers MAX98357
//   VRIFIER sur le schma de votre carte
// ============================================
#define I2S_BCLK  0
#define I2S_WS    45
#define I2S_DOUT  17

// ============================================
// Configuration cran ESP32-8048S050
// ============================================
class LGFX : public lgfx::LGFX_Device {

  lgfx::Panel_RGB   _panel_instance;
  lgfx::Bus_RGB     _bus_instance;
  lgfx::Touch_GT911 _touch_instance;

public:
  LGFX(void) {
    { // Bus RGB
      auto cfg = _bus_instance.config();
      cfg.panel = &_panel_instance;
      cfg.pin_d0  = GPIO_NUM_8;  cfg.pin_d1  = GPIO_NUM_3;
      cfg.pin_d2  = GPIO_NUM_46; cfg.pin_d3  = GPIO_NUM_9;
      cfg.pin_d4  = GPIO_NUM_1;  cfg.pin_d5  = GPIO_NUM_5;
      cfg.pin_d6  = GPIO_NUM_6;  cfg.pin_d7  = GPIO_NUM_7;
      cfg.pin_d8  = GPIO_NUM_15; cfg.pin_d9  = GPIO_NUM_16;
      cfg.pin_d10 = GPIO_NUM_4;  cfg.pin_d11 = GPIO_NUM_45;
      cfg.pin_d12 = GPIO_NUM_48; cfg.pin_d13 = GPIO_NUM_47;
      cfg.pin_d14 = GPIO_NUM_21; cfg.pin_d15 = GPIO_NUM_14;
      cfg.pin_henable = GPIO_NUM_40;
      cfg.pin_vsync   = GPIO_NUM_41;
      cfg.pin_hsync   = GPIO_NUM_39;
      cfg.pin_pclk    = GPIO_NUM_42;
      cfg.freq_write  = 15000000;
      cfg.hsync_polarity    = 0; cfg.hsync_front_porch = 8;
      cfg.hsync_pulse_width = 4; cfg.hsync_back_porch  = 16;
      cfg.vsync_polarity    = 0; cfg.vsync_front_porch = 4;
      cfg.vsync_pulse_width = 4; cfg.vsync_back_porch  = 4;
      cfg.pclk_active_neg   = 1; cfg.de_idle_high      = 0;
      cfg.pclk_idle_high    = 0;
      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }
    { // Panel
      auto cfg = _panel_instance.config();
      cfg.memory_width  = 800; cfg.memory_height = 480;
      cfg.panel_width   = 800; cfg.panel_height  = 480;
      cfg.offset_x = 0;        cfg.offset_y = 0;
      _panel_instance.config(cfg);
    }
    { // PSRAM
      auto cfg = _panel_instance.config_detail();
      cfg.use_psram = 1;
      _panel_instance.config_detail(cfg);
    }
    { // Tactile GT911
      auto cfg = _touch_instance.config();
      cfg.x_min = 0;   cfg.x_max = 799;
      cfg.y_min = 0;   cfg.y_max = 479;
      cfg.pin_int  = GPIO_NUM_NC;
      cfg.pin_rst  = GPIO_NUM_38;
      cfg.bus_shared = false;
      cfg.offset_rotation = 0;
      cfg.i2c_port = 0;
      cfg.i2c_addr = 0x5D;
      cfg.pin_sda  = GPIO_NUM_19;
      cfg.pin_scl  = GPIO_NUM_20;
      cfg.freq     = 400000;
      _touch_instance.config(cfg);
      _panel_instance.setTouch(&_touch_instance);
    }
    setPanel(&_panel_instance);
  }
};

LGFX   display;
Audio  audio;

// ============================================
// tats de l'application
// ============================================
enum Etat { ETAT_LOGO, ETAT_LECTURE };
Etat etatActuel = ETAT_LOGO;

// ============================================
// Dfinition des 3 boutons
// Position et taille sur l'cran 800x480
// Ajustez X, Y, W, H selon votre mise en page
// ============================================
struct Bouton {
  int x, y, w, h;
  const char* labelImg;
  const char* labelAudio;
  const char* texte;
  uint32_t couleur;
};

// 3 boutons rpartis horizontalement, centrs verticalement
// Zone boutons : bas de l'cran, y=340, hauteur=100
// Modifiez ces valeurs selon la mise en page souhaite
Bouton boutons[3] = {
  { 60,  340, 200, 100, "/bmg1.bmp", "/BE-C.mp3", "BE-CALAVI", 0x1E90FF },
  { 300, 340, 200, 100, "/bmg3.bmp", "/BE-F.mp3", "BE-FSS", 0x32CD32 },
  { 540, 340, 200, 100, "/bmg4.bmp", "/BE-G.mp3", "BE-GODOMEY", 0xFF6347 }
};

bool audioEnCours = false;

// ============================================
// Charger et afficher un BMP depuis la SD
// ============================================
void afficherBMP(const char* chemin) {
  File f = SD_MMC.open(chemin, FILE_READ);
  if (!f) {
    Serial.println("Impossible d'ouvrir : " + String(chemin));
    display.fillScreen(TFT_BLACK);
    display.setTextColor(TFT_RED);
    display.setTextSize(2);
    display.setCursor(20, 220);
    display.printf("Image introuvable :\n%s", chemin);
    return;
  }
  size_t taille = f.size();
  uint8_t* buf = (uint8_t*) ps_malloc(taille);
  if (!buf) {
    Serial.println("Erreur allocation PSRAM");
    f.close();
    return;
  }
  f.read(buf, taille);
  f.close();
  display.drawBmp(buf, taille, 0, 0);
  free(buf);
  Serial.println("Image affiche : " + String(chemin));
}

// ============================================
// Dessiner un bouton  l'cran
// ============================================
void dessinerBouton(const Bouton& b, bool surbrillance) {
  uint32_t coulFond = surbrillance ? TFT_WHITE : b.couleur;
  uint32_t coulTexte = surbrillance ? b.couleur : TFT_WHITE;

  // Rectangle arrondi avec bordure
  display.fillRoundRect(b.x, b.y, b.w, b.h, 16, coulFond);
  display.drawRoundRect(b.x, b.y, b.w, b.h, 16, TFT_WHITE);

  // Texte centr dans le bouton
  display.setTextColor(coulTexte);
  display.setTextSize(2);
  int tw = strlen(b.texte) * 12; // approximation largeur texte
  int tx = b.x + (b.w - tw) / 2;
  int ty = b.y + (b.h - 16) / 2;
  display.setCursor(tx, ty);
  display.print(b.texte);
}

// ============================================
// Afficher l'cran logo avec les 3 boutons
// ============================================
void afficherLogo() {
  afficherBMP("/logo.bmp");
  for (int i = 0; i < 3; i++) {
    dessinerBouton(boutons[i], false);
  }
  Serial.println("cran logo affich");
}

// ============================================
// Lancer la lecture d'un mdia (image + audio)
// ============================================
void lancerMedia(int index) {
  Serial.printf("Lancement mdia %d : %s + %s\n",
                index, boutons[index].labelImg, boutons[index].labelAudio);

  // Feedback visuel : bouton en surbrillance
  dessinerBouton(boutons[index], true);
  delay(150);

  // Afficher l'image
  afficherBMP(boutons[index].labelImg);

  // Lancer l'audio
  audio.stopSong();
  bool ok = audio.connecttoFS(SD_MMC, boutons[index].labelAudio);
  if (!ok) {
    Serial.println("Erreur : fichier audio introuvable");
    display.setTextColor(TFT_RED);
    display.setTextSize(2);
    display.setCursor(20, 440);
    display.printf("Audio introuvable : %s", boutons[index].labelAudio);
  } else {
    audioEnCours = true;
    Serial.println("Audio lanc");
  }

  etatActuel = ETAT_LECTURE;
}

// ============================================
// Vrifier si un appui touche un bouton
// ============================================
int boutonTouche(int tx, int ty) {
  for (int i = 0; i < 3; i++) {
    if (tx >= boutons[i].x && tx <= boutons[i].x + boutons[i].w &&
        ty >= boutons[i].y && ty <= boutons[i].y + boutons[i].h) {
      return i;
    }
  }
  return -1;
}

// ============================================
// Callback fin de lecture audio
// ============================================
void audio_eof_mp3(const char* info) {
  Serial.printf("Fin audio : %s\n", info);
  audioEnCours = false;
}

void audio_info(const char* info) {
  Serial.printf("Audio info : %s\n", info);
}

// ============================================
// Setup
// ============================================
void setup() {
  Serial.begin(115200);
  delay(500);

  // Rtroclairage
  //pinMode(2, OUTPUT);
  //digitalWrite(2, HIGH);
  ledcSetup(0, 20000, 8);   // canal 0, 20 kHz, 8 bits
  ledcAttachPin(2, 0);
  ledcWrite(0, 255);  

  // Init cran
  display.init();
  display.setRotation(0);
  display.fillScreen(TFT_BLACK);
  display.setTextColor(TFT_WHITE);
  display.setTextSize(2);
  display.setCursor(20, 220);
  display.println("Initialisation...");

  // Init SD_MMC en mode 1-bit
  SD_MMC.setPins(12, 11, 13);
  if (!SD_MMC.begin("/sdcard", true)) {
    Serial.println("Erreur SD_MMC !");
    display.fillScreen(TFT_BLACK);
    display.setTextColor(TFT_RED);
    display.setTextSize(2);
    display.setCursor(20, 220);
    display.println("Erreur : carte SD non trouvee !");
    return;
  }
  Serial.println("SD OK");

  // Init audio I2S
  //  GPIO  vrifier sur le schma de votre carte
  audio.setPinout(I2S_BCLK, I2S_WS, I2S_DOUT);
  audio.setVolume(1); // 0  21

  // Afficher l'cran logo
  afficherLogo();
  etatActuel = ETAT_LOGO;
}

// ============================================
// Loop
// ============================================
void loop() {

  // Toujours appeler audio.loop() pour alimenter le dcodeur
  audio.loop();

  // --- TAT LECTURE : surveiller la fin de l'audio ---
  if (etatActuel == ETAT_LECTURE) {
    if (!audioEnCours && !audio.isRunning()) {
      Serial.println("Audio termin  retour au logo");
      delay(500); // petite pause avant retour
      afficherLogo();
      etatActuel = ETAT_LOGO;
    }
    // Pas de dtection tactile pendant la lecture
    return;
  }

  // --- TAT LOGO : dtecter les appuis sur les boutons ---
  if (etatActuel == ETAT_LOGO) {
    lgfx::touch_point_t tp;
    if (display.getTouch(&tp)) {
      Serial.printf("Touche dtecte : x=%d y=%d\n", tp.x, tp.y);

      int idx = boutonTouche(tp.x, tp.y);
      if (idx >= 0) {
        lancerMedia(idx);
      }

      // Anti-rebond simple : attendre relchement
      while (display.getTouch(&tp)) {
        delay(10);
      }
    }
  }

  delay(5);
}

platformio.ini

C/C++
[env:esp32-s3-devkitc-1]
platform = espressif32 @ ^6.9.0
board = esp32-s3-devkitc-1
framework = arduino

board_build.arduino.memory_type = qio_opi
board_upload.flash_size = 8MB
board_build.partitions = default_8MB.csv

build_flags =
    -DBOARD_HAS_PSRAM
    -DARDUINO_USB_MODE=1
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DCONFIG_SPIRAM_BOOT_INIT
    -DLV_CONF_SKIP=1
    -DLV_COLOR_DEPTH=16
    -DLV_COLOR_16_SWAP=0
    -DLV_MEM_SIZE="(64*1024)"
    -DLV_TICK_CUSTOM=1
    -DLV_TICK_CUSTOM_INCLUDE=\"Arduino.h\"
    -DLV_TICK_CUSTOM_SYS_TIME_EXPR="millis()"
    -DLV_FONT_MONTSERRAT_14=1
    -DLV_FONT_MONTSERRAT_16=1
    -DLV_FONT_MONTSERRAT_20=1
    -DLV_USE_BTN=1
    -DLV_USE_LABEL=1

board_build.f_cpu = 240000000L
board_build.f_flash = 80000000L
board_build.flash_mode = qio

lib_deps =
    lovyan03/LovyanGFX @ ^1.1.16
    lvgl/lvgl @ ^8.3.0
    esphome/ESP32-audioI2S @ ^2.0.7

   

Credits

David KOCHONI
1 project • 0 followers

Comments