Zvukový výstup (MP3) na ESP32
- UPOZORNĚNÍ
- Na začátku roku 2024 bylo aktualizováno jádro ESP32 IDF, které je potřeba pro programování modulu ESP32 v prostředí Arduino IDE. Od verze 3.x tohoto jádra jsou některé funkce a příkazy zcela přepracovány! (například časovače a od nich odvozené volání přerušení…)
- V následujícím textu je použito několik knihoven od různých autorů, kteří se ke vzniklé situaci staví různými způsoby. Situace se tedy postupně odvíjí podle jejich přístupu. Čtěte proto pozorně poznámky u jednotlivých ukázek kódu.
V minulém článku Zvukový výstup (WAV) na ESP32 jsme se věnovali přehrávání zvukových WAV souborů na analogových výstupech (piny GPIO25 a GPIO26) modulu ESP32. Dnes na tento článek navážeme a ukážeme si, jak obdobným způsobem na modulu ESP32 přehrávat zvukové soubory formátu MP3.
Podobně jako v předešlém článku si připomeneme, že na analogové výstupy (pin GPIO25 a GPIO26) modulu ESP32 není dobré připojovat reproduktor přímo, ale je třeba je doplnit o audio zesilovač (viz obr. 1). My použijeme levný modul půlwattového zesilovače s obvodem LM386 (k zakoupení za 20 Kč např. u firmy Hadex).
| ESP32 | Audio zesilovač |
|---|---|
| VIN (5V) | VDD |
| GPIO25 (GPIO26) |
IN |
| GND | GND |
Obrázek č. 1 – Navržené připojení MONO audio zesilovače a reproduktoru k modulu ESP32
Přehrávání MP3 souborů
Soubor MP3 je zvukový soubor uložený v komprimovaném formátu zvuku, který používá kompresi zvuku „Layer 3“. Běžně se používá pro ukládání hudby a audioknih a jeho velikost je zhruba 1/10 velikosti souboru WAV. Kvalita a tím pádem i velikost souboru MP3 se do značné míry odvíjí od bitové rychlosti použité pro kompresi. Pro naše účely bude dobré, podobně jako u WAV souborů v předešlém článku, pomocí programu Audacity najít vhodné nastavení pro vzorkování a bitovou rychlost, abychom našli kompromis mezi velikostí souboru a kvalitou reprodukovaného zvuku.
Rychlé odkazy:
- Přehrávání MP3 souborů – knihovna ESP32-audioI2S (ESP32 jádro ver. 2.x.x)
- Přehrávání MP3 souborů – knihovna Arduino Audio Tools (ESP32 jádro ver. 3.x.x)
Přehrávání MP3 souborů na jádře ESP32 ver. 2 v Arduino IDE
Knihovna ESP32-audioI2S
- UPOZORNĚNÍ
- Níže uvedená knihovna
ESP32-audioI2Sje primárně určena pro přehrávání MP3 souborů přes zvukový výstup I2S. Do její verze 2.0.6 bylo možné tuto knihovnu použít i pro přehrávání MP3 souborů „klasicky“ přes analogový výstup modulu ESP32. Tak, jak je níže uvedeno… - Je chvályhodné, že autor již na konci roku 2023 zareagoval na vznik nového a zpětně nekompatibilního jádra ESP32 (ver. 3) prostředí Arduino IDE, leč v nové verzi této knihovny (V3.x.x) jsou již definitivně podporovány pouze výstupy I2S, které v našem případě nemůžeme použít!
- DŮLEŽITÉ UPOZORNĚNÍ!
- Chceme-li použít níže uvedený postup přehrávání MP3 souborů pomocí knihovny
ESP32-audioI2S, musíme provést následující dva kroky:
- Je třeba si z níže uvedeného odkazu stáhnout verzi knihovny
ESP32-audioI2Smaximálně verze 2.0.6! - Kvůli zpětné kompatibilitě knihovny verze 2.0.6 s předchozím ESP32-IDF musíte v manažeru desek prostředí Arduino IDE „downgradovat“ jádro modulu ESP32 na verzi 2.x.x.
Pro přehrávání MP3 souborů na analogovém výstupu modulu ESP32 použijeme knihovnu ESP32-audioI2S od vývojáře Schreibfaul1.
Odkaz na knihovnu ESP32-audioI2S ver. 2.0.6: https://
Odkaz na aktuální verzi knihovny: https://
Pozorný čtenář si možná všimne zkratky I2S v názvu knihovny a také si možná vzpomene na žlutý rámeček v minulém článku, ve kterém jsme se zmínili, že se u zvukových souborů nebudeme zabývat I2S digitálním audio výstupem. Tak jak to tedy je? Budeme na výstupu modulu ESP32 skutečně přehrávat zvuk analogový?
Odpověď zní: ANO, i když…
Výše zmíněná knihovna je sice primárně určena pro použití s I2S výstupy, ale je naštěstí možné nastavit (do verze 2.0.6!) pro výstup i interní DAC převodníky, tedy získat na pinech GPIO25 a GPIO26 analogový zvukový výstup.
- Poznámka:
I2S(Inter-IC Sound) na modulu ESP32 je specializované hardwarové rozhraní, které slouží k přenosu digitálního zvuku mezi ESP32 a jinými digitálními zvukovými zařízeními (např. převodníky DAC, mikrofony, zesilovače). Tento způsob přenosu umožňuje přenášet zvuk bez nutnosti analogového převodu uvnitř ESP32, což snižuje šum a rušení. Nejčastěji se používá s digitálními mikrofony (např. INMP441) nebo digitálními zesilovači (např. MAX98357A). ESP32 obsahuje dva I2S kontroléry, které podporují DMA. To znamená, že data mohou proudit mezi I2S a pamětí bez zatížení hlavního procesoru, což je klíčové pro plynulé přehrávání či nahrávání zvuku. Modul ESP32 je díky svým dvěma I2S rozhraním a výkonu ideální volbou pro projekty pracující s digitálním audiem.
A asi nás ještě bude zajímat ještě jedna informace, kterou jsme ale již nastínili dříve. Tato knihovna umožňuje přehrávat zvuk jako STEREO výstup, tedy jeden kanál na pinu GPIO25 a druhý na pinu GPIO26.
Jako ukázku přehrajeme soubor uložený v paměťové oblasti spravované souborovým systémem SPIFFS, která nám bude suplovat SD kartu.
Ukázkový kód:
#include "Audio.h"
#include "SPIFFS.h"
Audio audio(true, I2S_DAC_CHANNEL_BOTH_EN); // internal DAC = true, DAC enable both
void setup() {
Serial.begin(115200);
audio.setVolume(12); // 0...21
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
audio.connecttoFS(SPIFFS,"/mp3-soubor.mp3");
}
void loop() {
audio.loop();
}
Důležitým bodem programu je vytvoření objektu Audio, ve kterém nastavuje prvním parametrem (true), zda bude pro výstup použit vnitřní DAC (digitálně analogový převodník) pro analogový hudební výstup. Druhý parametr udává, zda budou přehrávány oba kanály.
I2S_DAC_– povolit pravý kanál vestavěného převodníku, mapuje se na kanál DAC 1, což jeCHANNEL_ RIGHT_EN GPIO25I2S_DAC_– povolit levý kanál vestavěného převodníku, mapuje se na kanál DAC 2, což jeCHANNEL_ LEFT_EN GPIO26I2S_DAC_– povolit oba kanály vestavěného převodníku (pinyCHANNEL_ BOTH_EN GPIO25aGPIO26)
Připojení MP3 audio souboru se provádí příkazem audio.connecttoFS, kde prvním parametrem je zadaný souborový systém, např. SD karta, zde je však nastaven paměťový prostor SPIFFS. Druhý parametr je cesta ke konkrétnímu souboru.
Pochopitelně nesmíme zapomenout do paměti modulu ESP32 nahrát příslušný MP3 soubor – nejlépe pomocí doplňku ESP32FS a jím přidané možnosti ESP32 Sketch Data Upload. Více se o využívání části paměti modulu ESP32 jako určité SD karty či „harddisku“ najdete v článku Kam se soubory? Aneb SPIFFS v ESP32 nebo v novějším Kam (zase) se soubory? Aneb LittleFS….
V hlavní smyčce je pak opakovaně voláno cyklické přehrávání zvoleného MP3 souboru. Pochopitelně knihovna ESP32-audioI2S umí mnohem více. Určitě ji doporučujeme k bližšímu průzkumu. Přehrávání MP3 souborů, které zabírají mnohem méně místa než obrovské WAV soubory, je určitě pro vývoj zvukových aplikací s modulem ESP32 jistě zajímavou možností.
Obrázek č. 2 – připojení audio zesilovače a reproduktoru k modulu ESP32 pro test přehrávání MP3
Možná se Vám zdá, že jsme přehrávání souborů pomocí knihovny ESP32-audioI2S trochu „ošidili“ – a máte pravdu! Původní článek jsme opravdu ořezali, jak to jen šlo. Důvodem je, její určitá současná „nefunkčnost“. Jak bylo zmíněno, knihovna v současnosti funguje (ale dobře!) za poměrně specifických podmínek. Stále ale počítáme s tím, že se autor ke knihovně vrátí a upgraduje ji na současné jádro ESP32 verze 3. Výše uvedenou ukázku zde tedy necháváme nejen z jakési nostalgie, ale i naděje do budoucna.
Přehrávání MP3 souborů na jádře ESP32 ver. 3 v Arduino IDE
Pojďme se podívat na řešení postavené na základní knihovně, která je spustitelná v současné konfiguraci prostředí Arduino IDE – Arduino IDE 2 s nainstalovaným doplňkem (jádrem) pro modul ESP32 verze 3.x.x.

Knihovna Arduino Audio Tools
V další části článku budeme nadále pracovat s knihovnou Arduino Audio Tools autor: Phil Schatzmann. Tato knihovna umožňuje poměrně široké možnosti práce s audio soubory a zřejmě se jí ještě v některém z dalších článků budeme věnovat. Kupříkladu by mohla být moderní alternativou pro přehrávání WAV souborů, ale dnes se zaměříme na přehrávání souborů MP3.
Pro přehrávání MP3 souborů musíme tuto základní knihovnu doplnit o MP3 dekodér, který možnosti knihovny rozšíří. Pro následující ukázky tedy potřebujeme stáhnout a do prostředí Arduino IDE nainstalovat následující knihovny (Instalujeme je ze stažených ZIP souborů).
- Základní soubory knihovny
arduino-audio-toolsstáhneme z GitHub autora: https://github. .com/ pschatzmann/ arduino- audio- tools - Rozšíření pro dekódování MP3 souborů
arduino-libhelixrovněž stáhneme z GitHub autora: https://github. .com/ pschatzmann/ arduino- libhelix
To by nám pro základní ukázku mělo stačit.
Přehrání MP3 souboru uloženého přímo v paměti mikrokontroleru
První ukázka nám bude trochu připomínat přehrávání WAV souboru, který jsme pomocí externího datového souboru SoundData.h vložili rovnou do kódu programu. Zde to bude obdobné, jen tento doplňkový soubor bude obsahovat data MP3 souboru. Ale podíváme se nejdříve na základní kód našeho přehrávače a pak si k němu připravíme příslušný datový soubor.
Pokud nechceme kopírovat níže uvedený kód programu do svého prostředí Arduino IDE, můžeme jej po nainstalování knihovny Arduino Audio Tools najít mezi příklady knihovny. Přejdeme do Soubor → Příklady → audio-tools → examples-stream → streams-memory_mp3-analog a měli bychom vidět následující kód:
/**
* @file stream-memory_mp3-analog.ino
* @author Phil Schatzmann
* @brief decode MP3 stream and output as out signal
* @version 0.1
* @date 2021-01-24
*
* @copyright Copyright (c) 2021
*/
// install https://github.com/pschatzmann/arduino-libhelix.git
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "BabyElephantWalk60_mp3.h"
MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len);
AnalogAudioStream analog; // Analog output
EncodedAudioStream out(&analog, new MP3DecoderHelix()); // output to decoder
StreamCopy copier(out, mp3); // copy in to out
void setup(){
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
analog.begin(analog.defaultConfig(TX_MODE));
// begin processing
out.begin();
}
void loop(){
if (mp3) {
copier.copy();
} else {
auto info = out.decoder().audioInfo();
LOGI("The audio rate from the mp3 file is %d", info.sample_rate);
LOGI("The channels from the mp3 file is %d", info.channels);
out.end();
stop();
}
}
V úvodní části programu vidíme import použitých knihoven:
AudioTools.h→ základ celé knihovny pro práci se zvukemCodecMP3Helix.h→ obsahuje MP3 dekodér (Helix)Baby→ pole bajtů s MP3 souborem (uložené v programu)Elephant Walk60_ mp3.h
MP3 soubor uložen například na SD kartě, ale je pomocí souboru BabyElephantWalk60_mp3.h přímo zkompilovaný v kódu.
Další část kódu vytváří „potrubí“ pro tok dat z MP3 souboru na DAC výstup, konkrétně:
MemoryStream mp3(
Tato část bere data z paměti a vlastně se chová jako vstupní stream – pole BabyElephantWalk60_mp3 je pole MP3 dat, BabyElephantWalk60_mp3_len je představuje počet dat v daném poli.
Analog – definuje výstup zvuku přes DAC, typicky na piny GPIO25/26.
Encoded – kombinuje dekodér (MP3DecoderHelix) a výstup (analog). Tady se nastavuje převod MP3 na PCM a odeslání na DAC (dekódování MP3 v reálném čase)
StreamCopy copier(out, mp3) – kopíruje data mp3 (vstup) → out (dekódování + výstup)
V části setup() se podívejme na:
Audio – umožňuje logování stavu a zpráv na sériovou linku (to asi v reálném použití vypneme).
Z hlediska přehrávání MP3 souboru nás spíše zajímá:
analog., což je nastavení analogového výstupu do režimu „transmit“ (TX) a příkaz out. připraví MP3 dekodér na zpracování.
V hlavní smyčce je testován objekt mp3, který vrací hodnotu true, pokud je „ve streamu ještě něco ke čtení“. Jestliže je stále co číst provede se příkaz copier.copy(), který přečte kus MP3 dat, dekóduje je a pošle na DAC. To se opakuje stále dokola, aby bylo přehrávání plynulé.
Jakmile se mp3 „vyprázdní“ vypíší se informace, jako: sample rate (např. 44100 Hz), počet kanálů (mono/stereo) a zastaví se přehrávání:
out.end(); – zastaví dekodér, ukončí zpracování MP3, uvolní interní buffery
Soubor Baby
Nyní se podíváme na soubor obsahující obsah MP3 souboru a především si ukážeme, jak jej naplnit jiným obsahem.
Výchozí soubor vypadá následovně: (zkráceno)
// converted with xxd
const unsigned char BabyElephantWalk60_mp3[] = {
0xff, 0xf3, 0x80, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x58, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x08,
0xfb, 0x00, 0x07, 0x41, 0x71, 0x00, 0x00, 0x01, 0x03, 0x06, 0x08, 0x0b,
0x0e, 0x10, 0x13, 0x16, 0x19, 0x1c, 0x1e, 0x21, 0x24, 0x26, 0x29, 0x2b,
0x2e, 0x31, 0x34, 0x36, 0x3a, 0x3c, 0x3f, 0x42, 0x44, 0x46, 0x49, 0x4c,
0x4e, 0x50, 0x53, 0x56, 0x59, 0x5c, 0x5f, 0x62, 0x64, 0x67, 0x6a, 0x6c,
0x6f, 0x71, 0x74, 0x76, 0x78, 0x7a, 0x7d, 0x80, 0x82, 0x85, 0x88, 0x8a,
0x8c, 0x8f, 0x92, 0x94, 0x97, 0x9a, 0x9d, 0x9e, 0xa1, 0xa4, 0xa7, 0xaa,
0xac, 0xaf, 0xb1, 0xb3, 0xb6, 0xb8, 0xbb, 0xbd, 0xc0, 0xc3, 0xc5, 0xc7,
0xca, 0xcd, 0xcf, 0xd2, 0xd5, 0xd8, 0xd9, 0xdc, 0xdf, 0xe1, 0xe4, 0xe7,
0xe9, 0xec, 0xee, 0xf1, 0xf3, 0xf6, 0xf8, 0xfb, 0xfd, 0x00, 0x00, 0x00,
0x50, 0x4c, 0x41, 0x4d, 0x45, 0x33, 0x2e, 0x31, 0x30, 0x30, 0x04, 0x6e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x08, 0x24, 0x02,
0x88, 0x21, 0x00, 0x01, 0xe0, 0x00, 0x07, 0x41, 0x71, 0x7f, 0xc5, 0xc5,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xf3, 0x10, 0xc4, 0x00, 0x00, 0x00, 0x03,
0x48, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x41, 0x4d, 0x45, 0x55, 0x4c, 0x41,
0x4d, 0x45, 0x33, 0x2e, 0x31, 0x30, 0xff, 0xf3, 0x10, 0xc4, 0x0d, 0x00,
0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x00, 0x30, 0x55, 0x55, 0x55, 0x55,
0x4c, 0x41, 0x4d, 0x45, 0x33, 0x2e, 0x31, 0x30, 0xff, 0xf3, 0x10, 0xc4,
0x1a, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x00, 0x30, 0x55, 0x55,
0x55, 0x55, 0x4c, 0x41, 0x4d, 0x45, 0x33, 0x2e, 0x31, 0x30, 0xff, 0xf3,
0x10, 0xc4, 0x27, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x00, 0x30,
0x55, 0x55, 0x55, 0x55, 0x4c, 0x41, 0x4d, 0x45, 0x33, 0x2e, 0x31, 0x30,
atd...
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55
};
unsigned int BabyElephantWalk60_mp3_len = 475505;
Podobný formát souboru jsme již viděli v článku Zvukový výstup (WAV) na ESP32, kde jsme obdobným způsobem zapisovali WAV soubor pro knihovnu XT_DAC_Audio. Nyní budeme postupovat obdobně, jen s tím rozdílem, že pomocí editoru HxD převedeme do tohoto zápisu soubor formátu MP3.
Ukážeme si to na konkrétním příkladu.
- Začneme stažením editoru HxD, což je freewarový hexadecimální editor, který lze stáhnout ze stránek: https://mh-nexus.de/en/hxd/. Stáhneme instalační soubor, editor nainstalujeme a spustíme.
- V editoru vložíme MP3 soubor volbou:
File→Open…(viz obr. 2)
Obrázek č. 3 – Otevření souboru MP3 v editoru HxD
- Vyberte všechna data (
 CTRL + A neboEdit→Select all), poté zvolímeEdit→Copy as→C) – viz obr. 4. Nyní máte ve schránce nakopírovaná data ve formátu, který můžete vložit do libovolného textového editoru, v našem případě do Arduino IDE.
Obrázek č. 4 – Zkopírování MP3 souboru ve formátu C do schránky
- V prostředí Arduino IDE přejděte na kartu
Baby, případně si vytvoříme novou a vložíme sem (Elephant Walk60_ mp3.h  CTRL + V ) data ze schránky. - Pokud jsme se rozhodli přepsat původní data souboru
Baby, označíme si vše kromě poslední řádky (Elephant Walk60_ mp3.h unsigned int Baby), kterou ještě budeme potřebovat.Elephant Walk60_ mp3_ len = 475505; - Vidíme, že nově vložená data mají trochu jinou strukturu – srovnejme předchozí ukázku dat souboru
Babys obrázkem č. 5.Elephant Walk60_ mp3.h
Obrázek č. 5 – Vložený MP3 souboru do Arduino IDE
- Právě vloženou proměnnou
rawDatapřejmenujeme naBaby. Číslo z hranatých závorek (v našem případě 22876) proměnnéElephant Walk60_ mp3[] rawData, které určuje počet dat přepíšeme do proměnnéBaby, kterou jsme si zachovali na konci souboru.Elephant Walk60_ mp3_ len - V tuto chvíli jsme do ukázkového kódu „propašovali“ data svého souboru. Po přeložení a spuštění kódu by se na výstupu audio zesilovače měla ozvat naše zvolená melodie, či zvuk.
Nyní se podíváme, jak kód funguje, abychom příště nemuseli data podstrkovat do ukázkového programu.
Asi je jasné, že potřebná data MP3 souboru jsou uložena v souboru Baby, který připojujeme na začátku kódu programu přehrávače:
#include "BabyElephantWalk60_mp3.h"
Další informací týkající se dat zvoleného MP3 souboru je nastavení datového proudu (streamu) na pole dat:
MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len);
kde první parametr je odkaz na pole dat a druhý počet dat v tomto poli. To jsou právě ty dvě proměnné, které jsme při překopírování našich dat do ukázkového programu museli naplnit našimi daty. Pokud budeme chtít připojit soubor s daty, který se bude jmenovat jinak, případně bude obsahovat jinou proměnnou, musíme tyto proměnné změnit právě zde. Následující část kódu ukazuje, jak se připojil soubor Test_mp3.h obsahující pole Test_mp3 a počet dat je uložen v proměnné Test_mp3_len.
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "Test_mp3.h"
MemoryStream mp3(Test_mp3, Test_mp3_len);
atd.
Pokud jsme pochopili, jak vložit data MP3 souboru, můžeme se podívat na další část programu:
AnalogAudioStream analog; // Analog output
EncodedAudioStream out(&analog, new MP3DecoderHelix()); // output to decoder
StreamCopy copier(out, mp3);
První řádka vytváří objekt analog, který odpovídá analogovému výstupu, tedy odesílání dat na vestavěné DAC převodníky modulu ESP32 – levý a pravý stereo kanál jsou v analogové (napěťové) podobě na pinech GPIO25 a GPIO26. Druhá řádka vytváří objekt out, který na výstup analog připojuje zvolený MP3 dekodér. Data nakopírovaná do vstupního bufferu objektu out budou dekódována z formátu MP3 na čistý analogový proud, který bude odesílán na DAC analogového výstupu modulu ESP32. Kopírování dat ze vstupního mp3 pole na výstup out má na starosti objekt copier – viz třetí řádka kódu.
Objekty analog a out jsou aktivovány v počáteční sekci setup.
analog.begin(analog.defaultConfig(TX_MODE));
out.begin();
Ve funkci setup je též nastaven výstupní log knihovny AudioTools na sériový port.
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
V okně sériového monitoru pak uvidíme, jak probíhá kopírování dat, a na závěr se zde vypíší informace o přenesených datech souboru.
Jelikož se data dají nakopírovat poměrně rychle, přehrávání zvuku na výstupu má svou rychlost a velikost bufferu je omezená, není možné vše udělat najednou. Je třeba data do bufferu dodávat postupně tak, jak je zvuk přehráván – a to po dobu, dokud jsou v poli mp3 dat stále dostupná data. Z tohoto důvodu je v hlavní nekonečné smyčce volán kopírovací objekt copier.copy(). Ale je do doby, dokud jsou v mp3 dostupná data:
if (mp3) {
copier.copy();
V případě, když nejsou kopírována data na výstup nastává větev podmínky else:, ve které jsou vypsány informace o odeslaných datech MP3 souboru. A přehrávání je zastaveno:
auto info = out.decoder().audioInfo();
LOGI("The audio rate from the mp3 file is %d", info.sample_rate);
LOGI("The channels from the mp3 file is %d", info.channels);
out.end();
stop();
Výstup dat na sériový port při přehrávání i na závěr po konci skladby vidíme na obrázku č. 6
Obrázek č. 6 – Výstup průběžných informací o přehrávání MP3 souboru na sériovém monitoru prostředí Arduino IDE
Pochopitelně, pokud máme k pinům GPIO 25 a 26 připojený audio zesilovač, měli bychom na jeho výstupu slyšet požadovaný zvuk – což je asi to nejdůležitější, oč nám v tuto chvíli šlo. Určitě je důležité si zkusit pohrát s parametry MP3 souboru, jako je kupříkladu nastavení frekvence vzorkování nebo kanály výstupu. Pro některé parametry je výstup poměrně zašuměný, jindy se ozývá nepříjemné praskání. Pokud se nám ale podaří nastavit správnou kombinaci všech možných parametrů MP3 souboru je zvuk vcelku slušný, i když neočekávejme žádné Hi-Fi! Také je třeba si uvědomit, že vnitřní paměť modulu ESP32 je omezená. Na internetu je několik zapojení, kde je knihovna Arduino Audio Tools využívána k přehrávání souborů z SD karty připojené k modulu ESP32.
V následující části článku si ukážeme, jak přehrát MP3 soubor z vnitřní paměti, která bude spravované souborovým systémem LittleFS.
Přehrání MP3 souboru z LittleFS
Abychom k modulu ESP32 nemuseli připojovat SD kartu a zároveň nemuseli vkládat MP3 soubor jako součást programu, tedy vždy při změně audio souboru nemuseli znova kompilovat kód programu, využijeme část paměti modulu ESP32, která může k takovému účelu sloužit. Jak pracovat s touto částí paměti jsme si již říkali v článku Kam se soubory? Aneb SPIFFS v ESP32, kde jsem popisovali starší souborový systém SPIFFS, nebo v novějším článku Kam (zase) se soubory? Aneb LittleFS…. Pro naše účely nyní použijeme souborový systém LittleFS a to nejen proto, že je modernější, ale také proto, že ve starší části tohoto článku (nahoře) jsme používali systém SPIFFS. Tak ať vidíme, jaký je v nich rozdíl z hlediska použití.
Nastavení jak knihovny Arduino Audio Tools, tak souborového systému LittleFS vychází z jejich standardního použití, takže si rovnou ukážeme kód programu a následně si jej vysvětlíme.
#include "AudioTools.h"
#include "AudioTools/Disk/AudioSourceLittleFS.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include <LittleFS.h>
const char *startFilePath = "/";
const char* ext = "mp3";
// LittleFS source
AudioSourceLittleFS source(startFilePath, ext);
AnalogAudioStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);
void printMetaData(MetaDataType type, const char* str, int len){
Serial.print("==> ");
Serial.print(toStr(type));
Serial.print(": ");
Serial.println(str);
}
void setup() {
Serial.begin(115200);
// AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
if(!LittleFS.begin(true)) { // Inicializace souboroveho systemu LittleFS, true = pri chybe se pokusi FS zformatovat
Serial.println("Chyba: LittleFS nenacteno.");
while(true); // bez FS nema smysl pokracovat
}
Serial.print("LittleFS total: "); // Vypis informaci o velikosti a zaplneni LittleFS
Serial.print(LittleFS.totalBytes());
Serial.print(" bytes, used: ");
Serial.print(LittleFS.usedBytes());
Serial.println(" bytes");
delay(1000);
File root = LittleFS.open("/");
File file = root.openNextFile();
while(file){
Serial.println(file.name());
file = root.openNextFile();
}
delay(2000);
// setup audio output
auto cfg = out.defaultConfig();
out.begin(cfg);
// setup player
player.setMetadataCallback(printMetaData);
player.begin();
}
void loop() {
player.copy();
}
Oproti předchozímu kódu nám v sekci připojených knihoven přibyla řádka:
#include "AudioTools/Disk/AudioSourceLittleFS.h"
Tato knihovna nám umožní načítat přímo MP3 soubor ze souborového systému LittleFS, nemusíme tedy řešit načítání dat a nějaké jejich ukládání do pole dat pro zpracování v dekodéru.
Další „nově“ připojená knihovna je knihovna pro LittleFS (ale tu známe z článku o systému LittleFS)
#include <LittleFS.h>
Následuje část, která propojuje LittleFS jako zdroj dat a budoucí audio stream:
// LittleFS source
AudioSourceLittleFS source(startFilePath, ext);
kde proměnné start a ext specifikují cestu a příponu souborů, které budou přehrávány. V našem případě to budou dva ukázkové MP3 soubory, které ještě před spuštěním programu nakopírujeme do paměťového místa spravovaného souborovým systémem LittleFS.
const char *startFilePath = "/";
const char* ext = "mp3";
Nastavujeme cestu "/", tedy kořenový adresář a soubory hledáme dle přípony "mp3".
V další sekci programu:
AnalogAudioStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);
kde o nastavení audio výstupu na analogové (DAC) výstupy modul ESP32, nastavení mp3 dekodéru a vytvoření objektu player. Objekt player má tři vstupní parametry: source – zdroj dat (v našem případě soubor na LittleFS), out – výstup dekódovaných dat (v našem případě interní DAC modulu ESP32) a decoder – dekodér mp3 souboru. Objekt player je určitou složitější strukturou, ale zatím pro nás jen určitou pohodlnější volbou pro nastavení audiostreamu, než bylo předešlé použití objektu copier. Ale jak uvidíme dále i objekt player budeme muset pravidelně volat ve smyčce loop(), aby docházelo ke stálému toku dat.
Tento program je oproti předešlému doplněn procedurou printMetaData(), která vypíše na sériový port metadata načteného MP3 souboru.
void printMetaData(MetaDataType type, const char* str, int len){
Serial.print("==> ");
Serial.print(toStr(type));
Serial.print(": ");
Serial.println(str);
}
Procedura print je nevolá přímo, ale jako návratová funkce objektu player (zde začínáme vidět, rozdíl mezi player a pouhým copier). Nastavení print jako návratové funkce objektu player vidíme v úvodní funkci setup.
player.setMetadataCallback(printMetaData);
Zvláštní část programu zabírají příkazy ohledně souborového systému LittleFS. Jde o jeho samotné nastavení:
if (!LittleFS.begin(true)) {
Serial.println("Chyba: LittleFS nenacteno.");
while(true);
}
Výpis základních informací o velikosti a obsazeném místě:
Serial.print("LittleFS total: ");
Serial.print(LittleFS.totalBytes());
Serial.print(" bytes, used: ");
Serial.print(LittleFS.usedBytes());
Serial.println(" bytes");
Nebo výpisu souborů v kořenovém adresáři:
File root = LittleFS.open("/");
File file = root.openNextFile();
while(file){
Serial.println(file.name());
file = root.openNextFile();
}
V závěru úvodní funkce setup je nastavení výstupu a inicializace přehrávače:
auto cfg = out.defaultConfig();
out.begin(cfg);
player.begin();
V nekonečné smyčce loop se v tomto případě pravidelně volá jen metoda copy objektu přehrávače player. Za zmínku ještě stojí zakomentování (tedy vypnutí) logování audio knihovny do sériového monitoru.
// AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
Nahrání MP3 souborů do LittleFS
Než se pustíme do testování našeho kódu, nesmíme zapomenout nahrát potřebné MP3 soubory do paměti. Také si musíme vzpomenout, že standardní paměťový prostor pro soubory souborového systému LittleFS je pouhých asi 1,5 MB, což v tuto chvíli není moc!
Pro upload MP3 souborů využijeme doplněk LittleFS Uploader, což je nástroj pro Arduino IDE, který umožňuje snadno nahrávat soubory do souborového systému LittleFS na modulech ESP32. Díky němu můžeme do flash paměti mikrokontroléru přenést soubory, aniž bychom museli soubory zapisovat přímo z programu.
Jelikož jsme se celé problematice LittleFS již věnovali v jiném článku, již zde instalaci tohoto doplňku nebudeme popisovat. Jen si zopakujeme, jak pomocí doplňku LittleFS Uploader nahrát MP3 soubor do modulu ESP32.
Chceme-li nahrát soubory do souborového systému LittleFS na modulu ESP32, postupujeme podle následujících kroků.
- Po vytvoření našeho programu v prostředí Arduino IDE jej uložíme, aby se vytvořila pracovní složka projektu.
- Vzniklou složku projektu otevřeme pomocí nabídky:
Projekt→Zobrazit složku projektu(viz obr. 7).
Obrázek č. 7 – Otevření složky projektu
- V pracovní složce projektu vytvoříme podsložku s názvem:
data(malými písmeny) (viz obr. 8).
Obrázek č. 8 – Vytvoření složky data ve složce projektu
- Do složky
datazkopírujeme MP3 soubor, který chceme nahrát do modulu ESP32. - Vrátíme se do prostředí Arduino IDE, kde (pokud již máme nainstalovaný plugin
LittleFS Uploader) stiskneme klávesy:
 Ctrl + Shift + P 
Tím otevřeme okno příkazů a v seznamu vybereme příkaz:Upload LittleFS to Pico/(viz obr. 9)ESP8266/ ESP32
Obrázek č. 9 – Upload souborů ze složky data do modulu ESP32.
- Po spuštění příkazu vyčkáme několik sekund, než dojde k nahrání MP3 ze složky
data) do modulu ESP32. Po úspěšném dokončení operace se zobrazí zpráva: (viz obr. 10)ⓘ LittleFS upload completed!
Obrázek č. 10 – Hláška v okně prostředí Arduino IDE o úspěšném nahrání souboru do souborového systému ESP32.
Tím byl soubor úspěšně nahrán do souborového systému modulu ESP32.
Zkouška programu
U tohoto příkladu si těžko ukážeme funkčnost, tu si musí každý vyzkoušet sám a sám posoudit, zda to, co slyší, je to, co očekával. V tomto článku se budeme muset spokojit jen s obrázkem textového výstupu v okně sériového monitoru prostředí Arduino IDE.
Obrázek č. 11 – Zobrazené informace o systému LittleFS a právě přehrávaném MP3 souboru.
Na obrázku č. 11 vidíme, jak program nejdříve (červený rámeček) inicioval souborový systém LittleFS a vypsal informace o jeho celkové velikosti a obsazenosti. Dále je zde vypsán seznam MP3 souborů, které jsou do paměti modulu ESP32 nahrány a které budou postupně přehrávány (modrý rámeček). Poslední (zelený) rámeček nás informuje o metadatech uložených v právě přehrávaném MP3 souboru – zde je to album, interpret, titul skladby a její žánr.
Závěr
V dnešním článku jsme si ukázali hned několik možností, jak docílit přehrávání zvukových MP3 souborů modulem ESP32. Pro rozsáhlejší zvukové soubory se MP3 soubory díky své kompresi jeví jako dobrá volba. Pokud chceme, aby modul ESP32 vydával realističtější zvukové efekty nebo dokonce promluvil, jistě tuto možnost oceníme.
Viděli jsme, že v principu máme dvě možnosti a to buď přímým začleněním MP3 obsahu do kódu programu, nebo přehrávání z paměťového média. V tom druhém případě jsme si to ilustrovali jen jako přehrávání z vnitřní paměti modulu ESP32, ale na stránkách autora použité knihovny je ukázkový program i pro přehrávání z připojené SD karty – z hlediska kódu jde jen o jiné nastavení vstupního zdroje pro audiostream. Takže to již necháme na samostatné práci bastlíře, který se k této cestě ve svém projektu přikloní.
V souvislosti s možnostmi modulu ESP32, jako je kupříkladu Bluetooth, nás při příležitosti přehrávání MP3 souborů napadá, což takhle zkusit přehrávat MP3 soubory přes BT reproduktor? Nejenže bychom si ušetřili „dráteničení“ při připojování audio zesilovače, ale možná bychom se opět naučili něco nového.