以前、ESP32によるインターネットラジオを製作したが、これはDACをI2Sで接続し、スピーカーから音を出す物だった。ESP32はWiFiとBluetoothを搭載しているので、それらを同時に使えば、ESP32でインターネットラジオを受信し、それをA2DPでBluetoothスピーカーに送って音を出すことができるはずである(スマホで普通にやってること)。事例がないかを調べて見た。
GithubにESP32を色々な方式のプレイヤーにするコードの実装例があった。その中に、SDカードモジュールを使い、A2DPで接続したBluetoothスピーカーから音を出すプレイヤーと、インターネットラジオを受信し、I2Sで接続したDACから音を出すプレイヤーの例を見つけた。これらを組み合わせれば、所期の目的を達成できそうだと思った。
しかしながら、結論からいうと、WiFiとA2DPを同時に動作させることはできなかった。A2DPを接続するとWiFiが繋がらない。WiFiを接続するとA2DPの接続でリセットエラーになる等、(このケースでは)同時には動作しなかった。
以下は試行錯誤の報告である。実装コード(ライブラリ)は洗練されていて、各種のプレーヤーが簡単に実装できるようになっており、最初の2例は動作を確認したので参考になると思う。
インターネットラジオを受信し、I2Sで接続したDACから音を出すプレイヤー
以下にスケッチを示す。上記の実装例からは、ボリューム(音量)や接続先のURLを変更するコードは省略している。ライブラリは、pschatzmannさんのライブラリを、ZIP形式でダウンロードし、ArduinoIDEのライブラリ管理からインストールする。「*wifi」と「*password」の部分は、利用しているWiFiのものを指定する。DACはUDA1334かPCM5102のモジュールが使える。ESP32との接続方法は、上記のリンク先に記載されている。
なお、ArduinoIDEは1.8.9。ボードマネージャからインストールするESP32ライブラリはEspressif Systemsの2.0.10である(古いとコンパイルエラーになる)。
下記のスケッチ中の、「AudioPlayer player(source, i2s, decoder)」の組み合わせを変えることで、色々なプレーヤーを実装できる。
#include "AudioTools.h"
#include "AudioCodecs/CodecMP3Helix.h"
const char *urls[] = {
"http://stream.srg-ssr.ch/m/rsj/mp3_128"
};
const char *wifi = "your_ssid";
const char *password = "your_password";
URLStream urlStream(wifi, password);
AudioSourceURL source(urlStream, urls, "audio/mp3");
I2SStream i2s;
MP3DecoderHelix decoder;
AudioPlayer player(source, i2s, decoder);
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Info);
// setup output
auto cfg = i2s.defaultConfig(TX_MODE);
i2s.begin(cfg);
// setup player
player.begin();
}
void loop() {
player.copy();
}
A2DPで接続したBluetoothスピーカーから音を出すプレイヤー
以下にスケッチと実験セットを示す。SDカードモジュール(写真ではタテになっている基板。Amazonから200円程度で入手できる)の接続方法は、上記の実装例に記載されている。mp3の楽曲をSDカードにコピーしてセットしておく。複数の曲をコピーした場合は一通り演奏後に停止する(その後は一旦電源を落とさないと「ファイル無」のエラーになる)。スケッチ中の「cfg.name」に、Bluetoothスピーカーのデバイス名を設定する。
なお、SDカードモジュールが手元に無い場合は、ESP32のSPIFFS領域にMP3のファイルを予めコピーしておく方法(プラグインの設定が必要。なお、このプラグインはArduinoIDEのVer2.x.xでは使用できないので注意)で実験できる(実装例はこちら、コピーするデータあり)。
#include "AudioTools.h"
#include "AudioLibs/AudioA2DP.h"
#include "AudioLibs/AudioSourceSDFAT.h"
#include "AudioCodecs/CodecMP3Helix.h"
const char *startFilePath="/";
const char* ext="mp3";
AudioSourceSDFAT source(startFilePath, ext); // , PIN_AUDIO_KIT_SD_CARD_CS);
A2DPStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
// setup output - We send the test signal via A2DP - so we conect to a Bluetooth Speaker
auto cfg = out.defaultConfig(TX_MODE);
cfg.name = "bbbbbbbbbb"; // set the device here. Otherwise the next available device is used for output
cfg.auto_reconnect = true; // if this is use we just quickly connect to the last device ignoring cfg.name
out.begin(cfg);
// setup player
player.setVolume(0.7);
player.begin();
}
void loop() {
player.copy();
}
インターネットラジオを受信し、A2DPで接続したBluetoothスピーカーから音を出すプレイヤー(動作せず)
目的のプレイヤーである。実装例にはないが、上の2つを組み合わせれば(入力:インターネットラジオ、出力:Bluetoothスピーカー)実現できるはずである。実装例からの類推で作成したスケッチは以下の通り。
実行したところ、「E (17464) wifi_init_default: netstack cb reg failed with 257」というようなエラーとrebootを繰り返し、動作はしなかった。なお、以下のスケッチをコンパイルすると、プログラムサイズが1.7MBになるので、ArduinoIDEからPartition Schemeのサイズを「No OTA 2MB APP/ 2MB SPIFFS」に変更した。
#include "AudioTools.h"
#include "AudioLibs/AudioA2DP.h"
#include "AudioCodecs/CodecMP3Helix.h"
const char *urls[] = {
"http://cast1.torontocast.com:2170/stream"
};
const char *wifi = "your_ssid";
const char *password = "your_password";
URLStream urlStream(wifi, password);
AudioSourceURL source(urlStream, urls, "audio/mp3");
A2DPStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
// setup output - We send the test signal via A2DP - so we conect to a Bluetooth Speaker
auto cfg = out.defaultConfig(TX_MODE);
cfg.name = "bbbbbbbbbb"; // set the device here. Otherwise the next available device is used for output
cfg.auto_reconnect = true; // if this is use we just quickly connect to the last device ignoring cfg.name
out.begin(cfg);
// setup player
player.setVolume(0.9);
player.begin();
}
void loop() {
player.copy();
}