今回はESP32にHTTPサーバを上げてRadiko Playerを作って見ました。こんな感じです。
機能の説明
- サーバのIPアドレスを固定
- スマホでアクセスする時にDNSが使えないのでサーバのIPアドレスを固定しています。
- ボリューム調整用のスライドバー
- 最小:0 最大:100
- ラジオ局選択用プルダウンメニュー
- ここでラジオ局を選択します。
- 右の図の様にラジオ局が表示され選択出来ます。
- 選択と同時に開始します。
- 開始ボタン
- 消音(Mute)ボタン
- 停止ボタン
- 状態保存ボタン
- このボタンで状態を保存すれば次回ESP32が起動状態が再現されます。
- 保存される状態は
- ボリューム
- ラジオ局
- 自動開始ボタン
- オンの場合、ESP32の起動と同時にラジオの再生が開始。
- サーバにアクセスしなくてもラジオが聞ける。
プログラムの説明
今回製作したプログラムは下記の5つ
- radiko_02.ino 本体
- radiko.css サーバ用HTMLファイル
- radiko.js サーバ用HTMLファイル
- state.txt 状態保存ファイル
- favicon.ico ファビコンファイル
radiko_02.ino(本体)の説明
これまで説明してきた通りRadikoの再生は
- 第一認証
- 第二認証
- エリアコードとラジオ局一覧
- Play List取得
- Chunk List 取得
- AACファイルの再生
を実行する事になります。このプログラムは
- ESP32が起動時(電源がオン)
- 上記の1,2,3を実行してラジオ局リストを取得
- それを元にプルダウンリストを作成します。
- ラジオ局の再生
- 起動時に1,2,3を既に行っているので上記4からの実行で良さそうですが
- 起動してから時間が経つと認証が切れる様なので、毎回1,2,4,5,6の順で実行
- 3は飛ばしても問題無い様です)
サーバ本体のHTMLは ”radiko_02.ino” に組み込まれています。サーバの状態に合わせてクライアントにデータを送信しています。
radiko_02.ino
#include <Arduino.h>
#include <WiFi.h>
#include <string.h>
#include "mbedtls/base64.h"
#include <HTTPClient.h>
#include <WebServer.h>
#include "SPIFFS.h"
#include <SPI.h>
//For VS1053b PIN
#define XCS 17
#define XDCS 22
#define DREQ 39
#define XRESET 21
#define Buf_busy 32
#define Dummy 25
#define CLK 18
#define MISO 19
#define MOSI 23
//VS1053 register
#define VLSI_MODE 0x00
#define VLSI_VOL 0x0B
#define Run_LED 2
#define AAC_HEADER 0x49
#define TIME_OFFSET 40
#define BUFF_MAX 73728
// Enter your WiFi setup here:
const char *SSID = "XXXXXXXX";
const char *PASSWORD = "YYYYYYYYY";
IPAddress ip(192, 168, 3, 250); // IP Address
IPAddress gateway(192,168, 3, 1); // Gateway Address
IPAddress dns(192,168, 3, 1); // Gateway Address
IPAddress subnet(255, 255, 255, 0); // Subnet Address
WebServer _server(80);
String _index_top =
"<!DOCTYPE html>\n<html>\n<head>\n<meta charset='utf-8'>\n<meta name='viewport' content='width=device-width,initial-scale=1'>\n"
"<link rel='stylesheet' type='text/css' href='/radiko.css' >\n<title>Radiko</title>\n</head>\n"
"<body>\n<center>\n<div class='b_frame'>\n<br>\n<div class='t_font'><u>Radiko 1.0</u></div>\n<br>\n"
"<form method='get' id='abc'>\n<input name='1' id='123' style='display:none'>\n</form>\n"
"<form oninput='op.value=ab.value'>\n<span class='v_font'>Volume</span><br>\n"
"<input style='width:220px;' type='range' value='";
int _buff_pt, _play_pt, _aac_no;
byte _st_selected, _station_MAX, _flg_mute, _flg_auto, _flg_play, _volume;
byte _sound_buff[BUFF_MAX];
String _auth_token, _chunk_url, _base_aac_url;
String _station_ID[25][2];
void setup()
{
String str;
//----------------- part0 ------------------------------------------------------------------------------
Serial.begin(115200);
delay(500);
pinMode(Run_LED,OUTPUT);
digitalWrite(Run_LED, LOW);
WiFi.disconnect();
WiFi.softAPdisconnect(true);
delay(500);
WiFi.mode(WIFI_STA);
WiFi.config(ip, gateway, subnet, dns);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
Serial.println("...Connecting to WiFi");
delay(1000);
}
Serial.println("Connected");
Serial.println(SSID);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//----------------- HTTP Server & SPIFFS ----------------------------------------------------------------------
_server.on("/", handleRoot);
_server.onNotFound(handleWebRequests);
_server.begin();
Serial.println("HTTP server started");
SPIFFS.begin();
Serial.println("SPIFFS started");
Init_vs1053();
Serial.print("Init vs1053: ");
Serial.println(VLSIReadReg(VLSI_MODE),HEX);
Read_state();
_flg_mute = 0;
VLSI_SetVolume(_volume);
Radiko(1, " ");
if(_st_selected > (_station_MAX - 1)) _st_selected = 0;
digitalWrite(Run_LED, HIGH);
if(_flg_auto) Radiko_play(_st_selected);
}
void loop()
{
int a;
_server.handleClient();
if(_flg_play)
{
if(_buff_pt > _play_pt) a = _buff_pt - _play_pt;
else a = BUFF_MAX - _play_pt + _buff_pt;
if( a < 4096 * 9) Ring_buffer();
}
}
void handleRoot()
{
String cmd,str;
int a,flg;
flg = 1;
cmd = _server.argName(0);
switch(cmd.toInt())
{
case 1: // Play, Mute, Auto, Save
cmd = _server.arg("1");
str = cmd.substring(2);
a = str.toInt();
flg = 0;
switch(cmd.charAt(0))
{
case '1': // volume data
_volume = a;
VLSI_SetVolume(_volume);
break;
case '2': // station No
_st_selected = a;
Radiko_stop();
delay(50);
Radiko_play(_st_selected);
break;
case '3': // play
if(!_flg_play) Radiko_play(_st_selected);
break;
case '4': // mute
_flg_mute = !_flg_mute;
a = _volume;
if(_flg_mute) a = 5;
VLSI_SetVolume(a);
break;
case '5': // auto flg
_flg_auto = !_flg_auto;
break;
case '6': // Save
Save_state();
break;
case '7': // Unlock 1st load
if(_flg_play) attachInterrupt(DREQ, Send_vs1053, RISING);
Send_vs1053();
break;
}
break;
case 2: // Stop
Radiko_stop();
break;
}
if(flg)
{
str = Make_index(str);
if(_flg_play) detachInterrupt(DREQ);
_server.send(200, "text/html",str);
}
else _server.send(204, "text/html","");
}
void Radiko_play(int ch_no)
{
digitalWrite(Run_LED, LOW);
_flg_play = 1;
Radiko(0, _station_ID[_st_selected][0]);
Get_aac_offset();
_buff_pt = 0;
Ring_buffer();
_play_pt = 0;
attachInterrupt(DREQ, Send_vs1053, RISING);
Send_vs1053();
digitalWrite(Run_LED, HIGH);
}
void Radiko_stop()
{
if(_flg_play) detachInterrupt(DREQ);
_flg_play = 0;
}
void Radiko(byte flg, String ID)
{
char * auth_key = "bcd151073c03b352e1ef2fd66c32209da9ca0afa" ;
HTTPClient http;
int key_length, offset;
char base64_encoded[32],token_p[32];
size_t base64_len;
const char *headerKeys[] = {"X-Radiko-AuthToken", "X-Radiko-KeyLength", "X-Radiko-KeyOffset"};
String str;
int a,b,c;
WiFiClient * stream;
//----------------- part1 ------------------------------------------------------------------------------
http.begin("https://radiko.jp/v2/api/auth1");
http.addHeader("User-Agent", "esp32/4");
http.addHeader("Accept", "*/*");
http.addHeader("X-Radiko-App", "pc_html5");
http.addHeader("X-Radiko-App-Version", "0.0.1");
http.addHeader("X-Radiko-User", "dummy_user");
http.addHeader("X-Radiko-Device", "pc");
http.collectHeaders(headerKeys, 3);
Serial.println("part1 = " + String(http.GET()));
digitalWrite(Run_LED, HIGH);
offset = String(http.header(headerKeys[2])).toInt();
key_length = String(http.header(headerKeys[1])).toInt();
_auth_token = http.header(headerKeys[0]);
http.end();
token_p[0] = '\0';
strncpy(token_p, &auth_key[offset], key_length);
token_p[key_length] = '\0';
mbedtls_base64_encode((unsigned char *) base64_encoded, sizeof(base64_encoded),&base64_len, (unsigned char *) token_p, strlen(token_p));
//----------------- part2 ------------------------------------------------------------------------------
http.begin("https://radiko.jp/v2/api/auth2");
http.addHeader("X-Radiko-AuthToken", _auth_token);
http.addHeader("X-Radiko-Partialkey", base64_encoded);
http.addHeader("X-Radiko-User", "dummy_user");
http.addHeader("X-Radiko-Device", "pc");
Serial.println("part2 = " + String(http.GET()));
digitalWrite(Run_LED, LOW);
str = http.getString();
str = str.substring(0,str.indexOf(","));
http.end();
if(flg)
{
//----------------- part3 ------------------------------------------------------------------------------
// http.begin("https://radiko.jp/v2/station/list/[ID].xml");
str = "https://radiko.jp/v2/station/list/" + str + ".xml";
http.begin(str);
http.addHeader("X-Radiko-AuthToken", _auth_token);
Serial.println("part3 = " + String(http.GET()));
digitalWrite(Run_LED, HIGH);
b = http.getSize();
c = 0;
stream = http.getStreamPtr();
while(b)
{
while(!(a = stream->available())) NOP();
stream->readBytes(&_sound_buff[c], a);
b -= a;
c += a;
}
_sound_buff[c] = 0;
str = String((char *)_sound_buff);
b = 0;
while((a = str.indexOf("<id>")) != -1)
{
a += 4;
str = &str[a];
a = str.indexOf("</id>");
_station_ID[b][0] = str.substring(0,a);
a = a + 16;
str = &str[a];
a = str.indexOf("</name>");
_station_ID[b][1] = str.substring(0,a);
b ++;
}
_station_MAX = b;
for(a = 0; a < b; a ++)
Serial.println("No." + String(a) +" ID: " + _station_ID[a][0] + " , name: " + _station_ID[a][1]);
Serial.println("station_MAX = " + String(b));
}
else
{
//----------------- part4 ------------------------------------------------------------------------------
str = "https://f-radiko.smartstream.ne.jp/" + ID + "/_definst_/simul-stream.stream/playlist.m3u8";
http.begin(str);
http.addHeader("X-Radiko-AuthToken", _auth_token);
Serial.println("part4 = " + String(http.GET()));
digitalWrite(Run_LED, HIGH);
str = http.getString();
_chunk_url = &str[str.indexOf("https")];
}
http.end();
}
void Get_aac_offset()
{
int a,b;
HTTPClient http;
String str;
http.begin(_chunk_url);
http.addHeader("X-Radiko-AuthToken", _auth_token);
Serial.println("part5 = " + String(http.GET()));
digitalWrite(Run_LED, LOW);
str = http.getString();
a = str.indexOf("https");
str = &str[a];
a = str.indexOf(".aac");
_base_aac_url =str.substring(0,a);
a =_base_aac_url.length();
while(_base_aac_url[a] != '_') a --;
str = &_base_aac_url[a + 1];
_base_aac_url =_base_aac_url.substring(0,a + 1);
_aac_no = str.toInt();
http.end();
a = TIME_OFFSET;
b = 1;
while(b)
{
a --;
str =_base_aac_url + String(_aac_no + a) + ".aac";
http.begin(str);
http.addHeader("X-Radiko-AuthToken", _auth_token);
if(http.GET() == 200) b = 0;
http.end();
if(a == 1) b = 0;
}
a --;
Serial.println("TIME OFFSET: " + String(a));
_aac_no += a;
}
void Ring_buffer()
{
int a, aac_size, avail;
byte d_buff[0x50];
String str;
HTTPClient http;
WiFiClient * stream;
str =_base_aac_url + String(_aac_no) + ".aac";
http.begin(str);
http.addHeader("X-Radiko-AuthToken", _auth_token);
if(http.GET() != 200)
{
http.end();
Get_aac_offset();
str =_base_aac_url + String(_aac_no) + ".aac";
http.begin(str);
http.addHeader("X-Radiko-AuthToken", _auth_token);
http.GET();
}
Serial.println(str);
aac_size = http.getSize();
stream = http.getStreamPtr();
avail = stream->available();
aac_size -= AAC_HEADER;
avail -= AAC_HEADER;
stream->readBytes(d_buff, AAC_HEADER);
while(aac_size)
{
aac_size -= avail;
a = _buff_pt + avail;
if(a > BUFF_MAX - 1)
{
stream->readBytes(&_sound_buff[_buff_pt], BUFF_MAX - _buff_pt);
avail = a - BUFF_MAX;
_buff_pt = 0;
}
stream->readBytes(&_sound_buff[_buff_pt], avail);
_buff_pt += avail;
if(aac_size)
while(!(avail = stream->available())) NOP();
}
_aac_no ++;
http.end();
}
String Make_index(String str)
{
int a;
str = String(_volume);
str += "' id='vol' name='ab' onchange='set_data(1)'>\n<output name='op' class='v_font'>";
str += String(_volume);
str += "</output>\n<br>\n<form>\n<select id='_title' class='m_font' onchange='set_data(2)'>\n";
for(a = 0; a < _station_MAX; a ++)
{
str += "<option value='";
str += (String(a) + "'");
if(a == _st_selected) str += " selected>";
else str += ">";
str += _station_ID[a][1];
str += "</option>\n";
}
str += "</select>\n</form>\n<br>\n<form method='get'>\n<button type='button' id='_play' onclick='set_data(3)' ";
if(_flg_play) str += "style ='background: red;'";
str += ">Play</button>\n<button type='button' id='_mute' onclick='set_data(4)' ";
if(_flg_mute) str += "style ='background: red;'";
str += ">Mute</button>\n<button type='submit' name='2' >Stop</button>\n<br>\n<span class='v_font' style ='margin-left:5%;'>Auto Start :</span>\n";
str += "<input type='radio' id='_auto' style ='margin-left:5%;' onclick='set_data(5)'";
if(_flg_auto) str += " checked ";
str += "/>\n<button type='button' style ='margin-left:16%;' onclick='set_data(6)'";
str += ">Save</button>\n</form>\n</div>\n</center>\n<script src='./radiko.js'></script>\n</body>\n</html>";
str = _index_top + str;
return str;
}
void handleWebRequests()
{
String dataType = "text/plain";
String path;
File dataFile;
path = _server.uri();
if(path.endsWith(".txt")) dataType = "text/plain";
else if(path.endsWith(".html")) dataType = "text/html";
else if(path.endsWith(".css")) dataType = "text/css";
else if(path.endsWith(".js")) dataType = "application/javascript";
else if(path.endsWith(".mp3")) dataType = "audio/mp3";
else if(path.endsWith(".aac")) dataType = "audio/aac";
else if(path.endsWith(".ico")) dataType = "image/html";
dataFile = SPIFFS.open(path.c_str(), "r");
_server.streamFile(dataFile, dataType);
dataFile.close();
delay(5);
}
void Send_vs1053()
{
digitalWrite(XDCS,LOW);
while(digitalRead(DREQ))
{
SPI.write(_sound_buff[_play_pt]);
_play_pt ++;
if(_play_pt == BUFF_MAX) _play_pt = 0;
}
digitalWrite(XDCS,HIGH);
}
void VLSI_SetVolume(byte v_data)
{
float a;
uint16_t vol_data;
a = v_data * 2.5;
vol_data = a;
vol_data = 250 - vol_data;
vol_data |= (vol_data << 8);
VLSIWriteReg(VLSI_VOL, vol_data);
}
uint16_t VLSIReadReg(byte vAddress)
{
uint16_t wValue;
while(!digitalRead(DREQ)) NOP();
digitalWrite(XCS,LOW);
SPI.write(0x03); // Read
SPI.write(vAddress); // Read
((byte*)&wValue)[1] = SPI.transfer(0xFF); // 16 bit high byte
((byte*)&wValue)[0] = SPI.transfer(0xFF); // 16 bit low byte
digitalWrite(XCS,HIGH);
return wValue;
}
void VLSIWriteReg(byte vAddress, uint16_t wValue)
{
while(!digitalRead(DREQ)) NOP();
digitalWrite(XCS,LOW);
SPI.write(0x02); // Write
SPI.write(vAddress); // Register address
SPI.write(((byte*)&wValue)[1]); // 16 bit write high byte
SPI.write(((byte*)&wValue)[0]); // 16 bit write low byte
digitalWrite(XCS,HIGH);
}
void Init_vs1053()
{
pinMode(XCS, OUTPUT);
digitalWrite(XCS,HIGH);
pinMode(XDCS, OUTPUT);
digitalWrite(XDCS,HIGH);
pinMode(Buf_busy, OUTPUT);
digitalWrite(Buf_busy,HIGH);
pinMode(XRESET, OUTPUT);
digitalWrite(XRESET,HIGH);
pinMode(DREQ,INPUT);
// Reset VS1053
digitalWrite(XRESET,LOW);
delay(500);
digitalWrite(XRESET,HIGH);
delay(500);
// SPI
SPI.begin(CLK, MISO, MOSI, Dummy);
VLSIWriteReg(VLSI_MODE, 0x800);
}
void Save_state()
{
String str;
File dataFile;
str = String(_volume) + "\n" + String(_st_selected) +"\n" + String(_flg_auto) + "\n";
dataFile = SPIFFS.open("/state.txt", "w");
dataFile.print(str);
dataFile.close();
}
void Read_state()
{
String str;
File dataFile;
dataFile = SPIFFS.open("/state.txt", "r");
str = dataFile.readStringUntil('\n');
_volume = str.toInt();
str = dataFile.readStringUntil('\n');
_st_selected = str.toInt();
str = dataFile.readStringUntil('\n');
_flg_auto = str.toInt();
dataFile.close();
}
- 34,35行: 自宅ルーターの情報を入力下さい。
- 37,40行: サーバのIPアドレスを固定する為に必要です。
- 73行: ここでサーバのIPアドレスを固定
- 97行: ここで前回の状態を読み込んでいます。ボリュームとラジオ局、自動開始のパラメータが読み込まれます
- 101行: ここでラジオ局のリストを作成しています。
- 106行: 自動再生がオンならここで再生を開始します。
- 124行: HP画面の操作はここで行います。
- 185行: Make_index(str)関数でクライアントに送るデータを作成しています。
- 403行: Make_index(str)関数本体
- ここで、状態に合わせてHPのHTMLを作成しています
- 最後に43行のString _index_topを付け足してサーバHTML作成完了
- 403行: Make_index(str)関数本体
- 192行: HPのPlayボタンをクリックするとここが実行されます。
- 196行: Radiko(0, _station_ID[_st_selected][0]); ここで1,2,4まで実行されます。
- 197行: Get_aac_offset(); ここでAACファイルのURLを取得します。
- 199行: AACファイルのデータをバッファーにダウンロード
- 201,202行: そのデータをVS1053に転送して再生開始
以下は、radiko.js、radiko.cssです。
radiko.js
// var url = "http://raspberrypi.local:8080/?80=0";
function set_data(a)
{
var str;
var _color;
str = a + ",";
switch(Number(a))
{
case 1: // Volume
str += document.getElementsByName("ab")[0].value;
break;
case 2: // Station no
case 3: // play
document.getElementById('_play').style.background = "#ff3034";
if(a == '2') str += document.getElementById('_title').value;
else str += "0";
break;
case 4: // mute
_color = "red";
if(document.getElementById('_mute').style.background == "red") _color = "#228b22";
document.getElementById('_mute').style.background = _color;
str += "0";
break;
case 5: // Auto flg
_auto = !_auto;
if(_auto) document.getElementById('_auto').checked = true;
else document.getElementById('_auto').checked = false;
case 6: // Save Parameter
case 7: // Send Comp
str += "0";
break;
}
document.getElementById('123').value = str;
document.getElementById('abc').submit();
}
var _auto = document.getElementById('_auto').checked;
set_data(7);
history.pushState(null,null,'/');
radiko.css
@charset "UTF-8";
.t_font {
font-size: 40px;
font-weight: bold;
font-style: italic;
color: #0ff;
}
.m_font {
width: 290px;
background: khaki;
border-radius: 10px;
font-size: 26px;
font-weight: bold;
font-style: italic;
color: mediumblue;
text-align: center;
}
.v_font {
font-size: 20px;
font-weight: bold;
font-style: italic;
color: #0ff;
}
.e_font {
font-size: 18px;
font-weight: bold;
color: #0ff;
}
.b_frame {
width: 320px;
background: #363636;
border-radius: 50px;
border-style: ridge;
border-width: 5px 30px;
border-color: sienna;
}
button {
display: inline;
margin: 10px;
line-height: 30px;
cursor: pointer;
color: #fff;
background: #228b22;
border-radius: 20px;
font-size: 20px;
width: 80px;
font-weight: bold;
font-style: italic;
}
button:active{
background: red;
}
button:disabled{
filter:brightness(0.5);
cursor:not-allowed;
}
option {
text-align: center;
margin: 10px;
color: #fff;
margin-left:16%;
background: #228b22;
border-radius: 20px;
font-size: 20px;
font-weight: bold;
font-style: italic;
}
select {
text-align: center;
margin: 10px;
background-color: red;
color: #fff;
background: #228b22;
border-radius: 20px;
font-size: 40px;
}
state.txtは以下の様になっています。
state.txt
70
7
0
上から
- 70
- 音量の値
- 7
- ラジオ局。東京エリアでは、J-WAVEになります。
- 地域が違う場合その地域の7の局になります
- その地域に7が無い場合、一番最初の局になります
- 0
- 自動再生の有無。
- 1:自動再生有り 0:自動再生無し
となります。radiko.js、radiko.css、state.txt、favicon.icoの4つファイルはSPIFFSで予めESP32にUploadして下さい。
プログラム実行時シリアルモニタには以下の様に表示されます。起動直後
起動直後
IP address: 192.168.3.250
HTTP server started
SPIFFS started
Init vs1053: 800
part1 = 200
part2 = 200
part3 = 200
No.0 ID: TBS , name: TBSラジオ
No.1 ID: QRR , name: 文化放送
No.2 ID: LFR , name: ニッポン放送
No.3 ID: RN1 , name: ラジオNIKKEI第1
No.4 ID: RN2 , name: ラジオNIKKEI第2
No.5 ID: INT , name: interfm
No.6 ID: FMT , name: TOKYO FM
No.7 ID: FMJ , name: J-WAVE
No.8 ID: JORF , name: ラジオ日本
No.9 ID: BAYFM78 , name: BAYFM78
No.10 ID: NACK5 , name: NACK5
No.11 ID: YFM , name: FMヨコハマ
No.12 ID: IBS , name: LuckyFM 茨城放送
No.13 ID: HOUSOU-DAIGAKU , name: 放送大学
No.14 ID: JOAK , name: NHKラジオ第1(東京)
No.15 ID: JOAK-FM , name: NHK-FM(東京)
station_MAX = 16
- 1行: サーバのIPアドレスの表示
- 4行: VS1053の初期化
- 5行: Part1正常完了
- 6行: Part2正常完了
- 7行: Part3正常完了
- 8行以降: 取得したラジオ局一覧
PCのブラウザを上げてURL欄に”192.168.3.250”と入力すると以下の画面が表示されます。
ここで、”Play”ボタンをクリックすると
Play後
part1 = 200
part2 = 200
part4 = 200
part5 = 200
TIME OFFSET: 37
https://f-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream/media-uq19ty0ur_w1215646049_708269.aac
https://f-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream/media-uq19ty0ur_w1215646049_708270.aac
https://f-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream/media-uq19ty0ur_w1215646049_708271.aac
https://f-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream/media-uq19ty0ur_w1215646049_708272.aac
- 1から4行: Part1,2,4,5と実行
- 5行: ここでAACファイルのURLを時間遅れを考慮して取得しています。
- 6行以降: ダウンロードされたAACをファイルのURLを表示しています。
問題点
再生中時々音が飛びます。これは前回から起こっている現象ですが、以前原因が分かりません。
最後に
これで本来の目的である、”ArduinoでRadikoを聞く”が実現しました(若干の問題は有りますが)。かなり満足です。
ところでこのVS1053はSDカードを持っています。ダウンロードしたAACファイルを保存すればラジオの録音が可能になります。次はそのあたりを調べたいと思います。
今回製作したファイル