ESP32のAudioライブラリにESP32-audioI2S-masterが有ります。今回はこれの説明。このライブラリを使用するとWebラジオが非常に簡単に出来ます。
まずはハード
CPUとそれが再生出来るCodecの表をWebで見つけました。この表よりESP32−S3であれば表の全てのCodecに対応しているのでCPUはESP32-S3を選択。

その他下記のモジュールが必要です。
- I2Sモジュール
- PCM5102 I2Sを使用。このボードは使用する前にパッドをショートする必要が有ります。
- SDカードソケット
- SDカードスロットソケットリーダーモジュールを使用。(電圧が3.3Vのソケット)
これらのモジュールを以下の様に配線しています。

音はPCM5102のジャックにイヤフォンをつないで聞いています。(アンプとスピーカ無し)
サンプルスケッチ
サンプルスケッチは、ファイルー>スケッチ例ー>ESP32-audioI2S-masterー>I2Saudio を使います。

このスケッチのピン配置を今回の回路に合わせて書き直し
I2Saudio.ino
//**********************************************************************************************************
//* audioI2S-- I2S audiodecoder for ESP32, *
//**********************************************************************************************************
//
// first release on 11/2018
// Version 3 , Jul.02/2020
//
//
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
//
#include "Arduino.h"
#include "WiFiMulti.h"
#include "Audio.h"
#include "SPI.h"
#include "SD.h"
#include "FS.h"
SPIClass sd_spi(HSPI);
// Digital I/O used
/*
#define SD_CS 5
#define SPI_MOSI 23
#define SPI_MISO 19
#define SPI_SCK 18
#define I2S_DOUT 25
#define I2S_BCLK 27
#define I2S_LRC 26
*/
#define SD_CS 7
#define SPI_MOSI 15
#define SPI_MISO 17
#define SPI_SCK 16
#define I2S_DOUT 5
#define I2S_BCLK 6
#define I2S_LRC 4
Audio audio;
WiFiMulti wifiMulti;
String ssid = "xxxxx";
String password = "xxxxx";
void setup() {
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
// SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
sd_spi.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
SPI.setFrequency(1000000);
Serial.begin(115200);
// SD.begin(SD_CS);
SD.begin(SD_CS, sd_spi);
WiFi.mode(WIFI_STA);
wifiMulti.addAP(ssid.c_str(), password.c_str());
wifiMulti.run();
if(WiFi.status() != WL_CONNECTED){
WiFi.disconnect(true);
wifiMulti.run();
}
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(12); // 0...21
// audio.connecttoFS(SD, "test.wav");
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
// audio.connecttohost("http://somafm.com/wma128/missioncontrol.asx"); // asx
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.aac"); // 128k aac
audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3"); // 128k mp3
}
void loop()
{
audio.loop();
if(Serial.available()){ // put streamURL in serial monitor
audio.stopSong();
String r=Serial.readString(); r.trim();
if(r.length()>5) audio.connecttohost(r.c_str());
log_i("free heap=%i", ESP.getFreeHeap());
}
}
// optional
void audio_info(const char *info){
Serial.print("info "); Serial.println(info);
}
void audio_id3data(const char *info){ //id3 metadata
Serial.print("id3data ");Serial.println(info);
}
void audio_eof_mp3(const char *info){ //end of file
Serial.print("eof_mp3 ");Serial.println(info);
}
void audio_showstation(const char *info){
Serial.print("station ");Serial.println(info);
}
void audio_showstreamtitle(const char *info){
Serial.print("streamtitle ");Serial.println(info);
}
void audio_bitrate(const char *info){
Serial.print("bitrate ");Serial.println(info);
}
void audio_commercial(const char *info){ //duration in sec
Serial.print("commercial ");Serial.println(info);
}
void audio_icyurl(const char *info){ //homepage
Serial.print("icyurl ");Serial.println(info);
}
void audio_lasthost(const char *info){ //stream URL played
Serial.print("lasthost ");Serial.println(info);
}
- 23行: SPIClass sd_spi(HSPI);
- 今回使用したESP32-S3はPSRAMインターフェイスとしてVSPIを使用していました。
- そこで、uSDカード用にHSPIを使用しています。
- 34から40行: ここでピンを合わせています。
- 53行: HSPIの初期化
- 59行: SDの初期化
Arduino IDEのツールでパラメータを下記の様に設定(Arduino 1.8.19)
- ボード: ESP32S3 Dev Module
- Partition Scheme: ”Huge APP(3MB No OTA/1MB SPIFFS)”
- PSRAM: ”OPI PSRAM”

これでコンパイル実行出来ました。実行するとシリアルモニタに下記の様に表示されました。
monitor
...Connecting to WiFi
Connected
info PSRAM found, inputBufferSize: 638965 bytes
info buffers freed, free Heap: 237500 bytes
info connect to: "www.wdr.de" on port 80 path "/wdrlive/media/einslive.m3u"
info Connection has been established in 303 ms, free Heap: 236868 bytes
info buffers freed, free Heap: 237324 bytes
info connect to: "wdr-1live-live.icecast.wdr.de" on port 80 path "/wdr/1live/live/mp3/128/stream.mp3?ar-distributor=ffa1"
info Connection has been established in 299 ms, free Heap: 237076 bytes
info redirect to new host "http://d141.rndfnk.com/ard/wdr/1live/live/mp3/128/stream.mp3?aggregator=app&cid=01FBRZTS1K1TCD4KA2YZ1ND8X3&sid=2xi7X6nlERLtPQFuAOK1Y45j7z9&token=Il5Y_JNOQky-zVbAY4vVc0RqnV5XwkOmz_PC9EGl5Ps&tvf=KVvNtm-sQxhkMTQxLnJuZGZuay5jb20"
info buffers freed, free Heap: 237324 bytes
info connect to: "d141.rndfnk.com" on port 80 path "/ard/wdr/1live/live/mp3/128/stream.mp3?aggregator=app&cid=01FBRZTS1K1TCD4KA2YZ1ND8X3&sid=2xi7X6nlERLtPQFuAOK1Y45j7z9&token=Il5Y_JNOQky-zVbAY4vVc0RqnV5XwkOmz_PC9EGl5Ps&tvf=KVvNtm-sQxhkMTQxLnJuZGZuay5jb20"
info Connection has been established in 279 ms, free Heap: 237076 bytes
bitrate 128000
station 1Live, Westdeutscher Rundfunk Koeln
icyurl https://www1.wdr.de/radio/1live/
info MP3Decoder has been initialized, free Heap: 235624 bytes , free stack 5320 DWORDs
lasthost http://d141.rndfnk.com/ard/wdr/1live/live/mp3/128/stream.mp3?aggregator=app&cid=01FBRZTS1K1TCD4KA2YZ1ND8X3&sid=2xi7X6nlERLtPQFuAOK1Y45j7z9&token=Il5Y_JNOQky-zVbAY4vVc0RqnV5XwkOmz_PC9EGl5Ps&tvf=KVvNtm-sQxhkMTQxLnJuZGZuay5jb20
info stream ready
info syncword found at pos 0
info MPEG-2.5, Layer I
info Channels: 2
info SampleRate: 48000
info BitsPerSample: 16
info BitRate: 128000
info StreamTitle='1LIVE'
streamtitle 1LIVE
これらは、loop() 以下に有る関数の出力の様です。これらの関数はスケッチで意図的に呼び出していません。CALL_BACK関数なのでしょうか。ちなみに89行以下をコメントアウトすると”info xxxxxxx”は表示されませんでした。
スケッチ内の以下のコメントを外して試してみました。全て繋がりました。これだけで立派なWebRadioです。
// audio.connecttoFS(SD, "test.wav");
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
// audio.connecttohost("http://somafm.com/wma128/missioncontrol.asx"); // asx
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.aac"); // 128k aac
audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3"); // 128k mp3
audio.connecttohost();とaudio.loop();が組になっていて、audio.connecttohost();で対象に接続。audio.loop();で処理(Decodeとバッファー管理)を行う様です。audio.connecttohost();で相手に接続しただけでは音声は出ませんでした。
関数の紹介
スケッチで使用した関数は以下の通り。
- audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
- I2Sのピンを設定します。
- audio.setVolume(12);
- ボリュウムの設定です。値は0から21まで(21が最大)指定出来ます。
- audio.connecttoFS(SD, “test.wav”);
- ファイルシステムとファイル名を指定して再生する。
- この例ではファイルシステムは、”SD”。ファイル名は、””test.wav”
- audio.connecttohost(“http://www.wdr.de/wdrlive/media/einslive.m3u”);
- サーバURLを指定してそのサーバに接続します。
- m3u, asx, aac, mp3形式に対応しています。
- audio.loop();
- ここで実際の処理が行われています。
- 一度この関数を呼び出すとある程度のデータをバッファーに読み込み、そのデータを処理する。
- バッファーにデータが有る間はDecodeされるのでこの関数を呼び出す必要は有りません。
- よってバッファーのデータが無くならない間隔でこの関数を繰り返し呼び出せば再生が続く事になります。
- Web Radioを製作する場合、下記の様になります。
- audio.loop()を呼び出す。
- Web Radioで必要な処理を行う。
- audio.loop()を呼び出す。
- audio_info(const char *info); 現在のステータス
- audio_id3data(const char *info); アーティスト、アルバム、バンドに関する情報
- audio_eof_mp3(const char *info); ファイル名
- audio_showstation(const char *info); 放送局名
- audio_showstreamtitle(const char *info); アーティスト、音楽トラックなどの情報
- audio_bitrate(const char *info); ビットレート
- audio_commercial(const char *info); 予想されるコマーシャルの長さ
- audio_icyurl(const char *info); 放送局にホームページ
- audio_lasthost(const char *info); 別のURLにリダイレクトされる場合、そのURL
その他にもこんな関数が有ります。
- setVolumeSteps(uint8_t steps);
- ボリューム値の範囲を設定する。
- setVolumeSteps(100); → 0から100で指定出来る。
- uint8_t getVolume()
- 現在のボリュームの値を取得
- setBalance(int8_t bal)
- 左または右のスピーカの減衰
- 指定範囲は、−16から16。
- −16で左のスピーカがMute。 16で右のスピーカがMute
- bool pauseResume(); PAUSEとRESUME。
- uint32_t Audio::stopSong(); 再生中止
- uint32_t getAudioCurrentTime(); 開始からの再生時間を秒単位で返します。
これらの関数を呼び出すだけで簡単にWebRadioが出来そうです。