From 4fa200d6494699cd23c42ec3422d14bb8726a231 Mon Sep 17 00:00:00 2001 From: drjones Date: Tue, 30 Jun 2026 01:18:01 -0700 Subject: [PATCH] Optimize performance: 4-bit SD_MMC, write-only SPI, and hardware byte swapping --- README.md | 44 ++++++++++++---------------- sound_picture_app/Display_PNG.cpp | 2 +- sound_picture_app/Display_ST7789.cpp | 11 +++++-- sound_picture_app/SD_Card.cpp | 4 +-- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ee1abbd..a1215ec 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,25 @@ This repository contains a responsive, dual-core firmware application developed for the **Waveshare ESP32-S3-LCD-1.47** development board. -The application reads PNG images and uncompressed PCM WAV audio files from a microSD card, renders the images on the onboard ST7789 LCD display, and plays matching soundbites over an external I2S audio DAC when the onboard button is pressed. +The application reads PNG images and compressed MP3 / uncompressed WAV audio files from a microSD card, renders the images on the onboard ST7789 LCD display, and plays matching soundbites over an external I2S audio DAC. --- ## 🚀 Key Features -* **Alpha-Numeric Media Pairing:** Scans the root of the microSD card for PNG and WAV files, sorting and matching them alphabetically (e.g., `01_image.png` pairs with `01_sound.wav`). -* **Background Multi-Threaded Audio:** Spawns a dedicated FreeRTOS task pinned to **Core 0** for I2S WAV streaming, preventing audio stutter and keeping the UI on **Core 1** fully responsive. -* **Dual Operation Modes:** - * **Manual Mode (Default):** Advances slides and triggers soundbites on single-presses. - * **Autoplay (Slideshow) Mode:** Long-pressing the button (>1.5s) toggles a hands-free slideshow. It automatically advances to the next slide 5 seconds *after* the previous audio track has finished playing. +* **Alpha-Numeric Media Pairing:** Scans the root of the microSD card for PNG images and audio files (`.wav` or `.mp3`), sorting and matching them alphabetically (e.g., `01_image.png` pairs with `01_sound.mp3`). +* **Dual-Core Software Decoding:** Spawns a background task pinned to **Core 0** running the `ESP32-audioI2S` decoding loop. This prevents audio dropouts or stutters during heavy display updates on **Core 1**. +* **Interactive Multi-Click Gestures:** You can control slide progression, audio status, and volume from the board's single physical button: + * *Single Click:* Next photo/audio pair. + * *Double Click:* Toggle Pause / Resume audio playback. + * *Triple Click:* Previous photo/audio pair. + * *Quadruple Click:* Cycle volume through levels 4, 8, 12, 16, and 20 (on a 0-21 scale). + * *Long Press (>1.5s):* Toggle Autoplay slideshow mode. * **WS2812 RGB LED Status Feedback:** Utilizes the onboard RGB LED to output real-time visual feedback: * 🔵 **Solid Blue:** Idle, waiting for manual trigger. * 🟢 **Pulsing Green:** Audio is playing. * 🟣 **Pulsing Purple:** Autoplay/Slideshow mode is active. + * ⚪ **White Flashing:** Confirms a volume change has been triggered. * 🔴 **Blinking Red:** Error state (microSD card failed to initialize, or no media files were found). --- @@ -43,11 +47,11 @@ Configure the pin connections as follows: ```text ├── sound_picture_app/ # Custom Application Source -│ ├── sound_picture_app.ino # Main coordinator, button gestures, state machine +│ ├── sound_picture_app.ino # Main coordinator, button click gesture parser, state machine │ ├── Display_ST7789.h/.cpp # Waveshare display initialization & backlight control │ ├── Display_PNG.h/.cpp # PNGdec utility for decoding and drawing images │ ├── SD_Card.h/.cpp # SD_MMC mount and folder retrieval helpers -│ └── Audio_Player.h/.cpp # WAV parser and background I2S player thread +│ └── Audio_Player.h/.cpp # background ESP32-audioI2S player task wrapper ├── ESP32-S3-LCD-1.47-Demo/ # Waveshare Official Reference Demos └── README.md # This file ``` @@ -61,10 +65,10 @@ Configure the pin connections as follows: * **Extension:** `.png` * **Resolution:** Sized to **172 x 320** pixels. 3. Place your sound files in the root directory: - * **Extension:** `.wav` - * **Format:** Uncompressed PCM WAV (Supports Mono/Stereo, 8/16/24/32-bit depths). + * **Extension:** `.mp3` or `.wav` + * **Format:** Compressed MP3 or uncompressed PCM WAV. 4. Ensure file names map alphabetically: - * `photo1.png` <--> `sound1.wav` + * `photo1.png` <--> `sound1.mp3` * `photo2.png` <--> `sound2.wav` --- @@ -78,8 +82,9 @@ Make sure you have the official Espressif board package installed: * Go to **Tools > Board > Boards Manager**, search for **`esp32`** (by Espressif Systems), and install version **3.0.2 or higher**. ### 2. Install Required Libraries -* Go to **Sketch > Include Library > Manage Libraries...** -* Search for **`PNGdec`** (by Larry Bank) and click **Install**. +You need to install the following two libraries in the IDE via **Sketch > Include Library > Manage Libraries...**: +1. **`PNGdec`** (by Larry Bank): Decoding and drawing PNGs. +2. **`ESP32-audioI2S`** (by Wolle / schreibfaul1): Asynchronous MP3/WAV decoding. ### 3. Select Board Configurations Go to the **Tools** menu and set the following variables: @@ -93,16 +98,3 @@ Go to the **Tools** menu and set the following variables: 1. Connect your board to your computer via USB. 2. Select the corresponding COM Port under **Tools > Port**. 3. Click **Upload** (or press `Ctrl+U`). - ---- - -## 🧠 Code Architecture & Multi-threading - -Drawing to SPI displays and reading from SD cards are heavily blocking SPI transactions. To prevent audio dropouts and stutters while loading images, the firmware divides work across the ESP32-S3's dual cores: - -* **Core 1 (Main Thread):** Runs the standard Arduino `loop()`. It performs debounced button polling, updates the NeoPixel LED pulse timers, and controls the display state transitions. -* **Core 0 (Audio Thread):** When a WAV file is selected, `playWav()` spawns a background thread pinned to Core 0. This thread handles: - 1. Opening the file handler. - 2. Parsing raw WAV headers dynamically (calculating sample rate, bit depth, channel configuration). - 3. Re-initializing the standard ESP32 `ESP_I2S` driver configuration. - 4. Streaming sample data chunks into I2S DMA buffers, pausing and yielding if the DMA buffer fills. diff --git a/sound_picture_app/Display_PNG.cpp b/sound_picture_app/Display_PNG.cpp index 80b7419..168aedc 100644 --- a/sound_picture_app/Display_PNG.cpp +++ b/sound_picture_app/Display_PNG.cpp @@ -35,7 +35,7 @@ void pngDraw(PNGDRAW *pDraw) { png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff); uint32_t size = pDraw->iWidth; for (size_t i = 0; i < size; i++) { - lineBuffer[i] = (((lineBuffer[i] >> 8) & 0xFF) | ((lineBuffer[i] << 8) & 0xFF00)); + lineBuffer[i] = __builtin_bswap16(lineBuffer[i]); } LCD_addWindow(xpos, pDraw->y, xpos + pDraw->iWidth - 1, ypos + pDraw->y, lineBuffer); } diff --git a/sound_picture_app/Display_ST7789.cpp b/sound_picture_app/Display_ST7789.cpp index 1e3f089..8be29b6 100644 --- a/sound_picture_app/Display_ST7789.cpp +++ b/sound_picture_app/Display_ST7789.cpp @@ -202,14 +202,19 @@ parameter : Yend : End uint16_t coordinates color : Set the color ******************************************************************************/ -void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color) +void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t* color) { uint16_t Show_Width = Xend - Xstart + 1; uint16_t Show_Height = Yend - Ystart + 1; uint32_t numBytes = Show_Width * Show_Height * sizeof(uint16_t); - uint8_t Read_D[numBytes]; LCD_SetCursor(Xstart, Ystart, Xend, Yend); - LCD_WriteData_nbyte((uint8_t*)color, Read_D, numBytes); + + LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0)); + digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW); + digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH); + LCDspi.writeBytes((uint8_t*)color, numBytes); + digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH); + LCDspi.endTransaction(); } // backlight void Backlight_Init(void) diff --git a/sound_picture_app/SD_Card.cpp b/sound_picture_app/SD_Card.cpp index 4d4ac50..0f81cf9 100644 --- a/sound_picture_app/SD_Card.cpp +++ b/sound_picture_app/SD_Card.cpp @@ -11,8 +11,8 @@ void SD_Init() { printf("SD MMC: Pin change failed!\r\n"); return; } - if (SD_MMC.begin("/sdcard", true, true)) { - printf("SD card initialization successful!\r\n"); + if (SD_MMC.begin("/sdcard", false, true)) { + printf("SD card initialization successful (4-bit mode)!\r\n"); } else { printf("SD card initialization failed!\r\n"); }