今回はラジオ局の選局機能の実装です。内容は、
- プルダウンリストを追加してラジオ局を選択。
- サーバにラジオ局名、リンク先等を記録した専用ファイルを作成
- ファイル処理用に、SPIFFSを導入。
- MP3とAACの2フォーマットに対応。
- 記録データを元に、MP3、AACを切り替える。
- MP3,AACどちらも使用出来るAudioGeneratorを使用
例によって、前回からの修正でスケッチを書いていますが、今回は修正部分が多いです。
#include <Arduino.h>
#include <WiFi.h>
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioGeneratorAAC.h"
#include "AudioOutputI2S.h"
#include <WebServer.h>
#include <ESPmDNS.h>
#include "SPIFFS.h"
// Enter your WiFi setup here:
const char *SSID = "XXXXXX";
const char *PASSWORD = "YYYYYY";
AudioGenerator *decoder = NULL;
AudioFileSourceICYStream *file;
AudioFileSourceBuffer *buff;
AudioOutputI2S *out;
// Controll flg
static int8_t flg[10];
#define play 0
#define volume 1
#define total 2
#define now 3
WebServer server(80);
String index_Dt =
"<!DOCTYPE html>\n<html>\n<head>\n"
"<title>ESP32 WebRadio</title>\n</head>\n"
"<body>\n<center>\n<h2>ESP32 WebRadio</h2>\n"
"<form id='cmd_vol' method='get'>\n"
"<input type='range' min='0' max='100' step='1' name='3' onchange='showValue()' id='s_value' value='50'>\n"
"<span id='p_value'>50</span><br><br>\n"
"</form>\n"
"<form id='pull_list' method='get'>\n"
"<select name='5' id='pull_item' onchange='getItem()'>\n";
String index_Dt1 =
"</select><br><br>\n"
"</form>\n"
"<form method='get'>\n"
"<button type='submit' name='1' id='b_play' style='background:#90ee90'>PLAY</button>\n"
"<button type='submit' name='2' style='background:#90ee90'>STOP</button>\n"
"<button type='submit' name='4' id='b_mute' style='background:#90ee90'>MUTE</button>\n"
"</form>\n"
"<script>\n"
"function showValue() {\n"
"target = document.getElementById('cmd_vol');\n"
"target.submit();\n}\n"
"function getItem() {\n"
"target = document.getElementById('pull_list');\n"
"target.submit();\n}\n";
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("Connecting to WiFi");
WiFi.disconnect();
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASSWORD);
// Try forever
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());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.begin();
Serial.println("HTTP server started");
out = new AudioOutputI2S();
//Initialize File System
SPIFFS.begin();
Serial.println("File System Initialized"); //Initialize File System
flg[play]=0;
flg[volume]=30;
flg[total]=6;
flg[now]=1;
out->SetGain((float)flg[volume]/100.0);
}
void loop()
{
if(flg[play])
{
if (decoder->isRunning()) {
if (!decoder->loop()) decoder->stop();
}
else {
Serial.print("decoder done\n");
StopPlaying();
delay(500);
radio_play(flg[now]);
}
}
server.handleClient();
}
void handleRoot()
{
String cmd;
int a;
cmd=server.argName(0);
switch(cmd.toInt())
{
case 1: // Play
if(flg[play] == 0)
if(flg[total] != flg[now])
radio_play(flg[now]);
break;
case 2: // Stop
StopPlaying();
break;
case 3: // Volume
cmd=server.arg("3");
flg[volume]=cmd.toInt();
out->SetGain((float)flg[volume]/100.0);
break;
case 4: // Mute
flg[volume] *= -1;
a=flg[volume];
if(flg[volume] < 0) a=0;
out->SetGain(((float)a)/100.0);
break;
case 5: // List
cmd = server.arg("5");
flg[now] = cmd.toInt();
break;
}
load_index();
}
void load_index()
{
int a;
File dataFile;
String line,line1,stbuf;
//Pull Down List
flg[total] = 0;
a = flg[now];
dataFile = SPIFFS.open("/title.txt", FILE_READ);
stbuf="";
while(dataFile.available())
{
line = dataFile.readStringUntil('\n');
stbuf += "<option value='" + String(flg[total]+1) + "'";
a -= 1;
if(a) stbuf += ">";
else { stbuf += " selected>"; line1=line; }
if(line != "<--- end --->") stbuf = stbuf + String(flg[total]+1) + ": ";
stbuf = stbuf + line + "</option>\n";
flg[total] += 1;
}
dataFile.close();
stbuf = index_Dt + stbuf + index_Dt1;
stbuf += ("document.getElementById('s_value').value='" + String(abs((int)flg[volume])) + "'\n");
stbuf += ("document.getElementById('p_value').innerHTML='" + String(abs((int)flg[volume])) + "'\n");
if(flg[play])
stbuf += ("document.getElementById('b_play').style.backgroundColor='#ff6347'\n");
if(flg[volume] < 0)
stbuf += ("document.getElementById('b_mute').style.backgroundColor='#2020ff'\n");
stbuf += "</script>\n</center>\n</body>\n</html>";
server.send(200, "text/html",stbuf);
}
void radio_play(int no)
{
File l_file;
int a;
String link_data;
l_file = SPIFFS.open("/link_list.txt", FILE_READ);
for(a=0; a<no; a ++) link_data=l_file.readStringUntil('\n');
l_file.close();
file = new AudioFileSourceICYStream(link_data.substring(2).c_str());
buff = new AudioFileSourceBuffer(file, 2048*16);
if(link_data.charAt(0) == '1') decoder = (AudioGenerator*) new AudioGeneratorAAC();
else decoder = (AudioGenerator*) new AudioGeneratorMP3();
decoder->begin(buff, out);
flg[play]=1;
}
void StopPlaying()
{
if (decoder) { decoder->stop(); delete decoder; decoder = NULL; }
if (buff) { buff->close(); delete buff; buff = NULL; }
if (file) { file->close(); delete file; file = NULL; }
flg[play] = 0;
}
- 6行:#include “AudioGeneratorAAC.h” AAC対応のGenerator
- 11行:#include “SPIFFS.h” SPIFFS用ヘッダー
- 17行:AudioGenerator *decoder = NULL; MP3,AAC対応のGenerator宣言
- 23行:static int8_t flg[10];
- 今回はサーバーの状態を保持するパラメターが幾つか必要だったので、パラメターを配列にまとめました。
- 例えば、再生中 の状態はflg[0]で保持します。再生=1。停止=0です。
- 24行目以降でパラメータの格納場所を宣言しています。再生は、flg[0]->flg[play] (play=0)
- 44行:String index_Dt1 =
- 前回は、HTMLをひとまとまりで扱っていました。今回はボリュームとボタンの間にプルダウンリストを入れたかったので2つに分けました。
- HTMLは index_Dt + プルダウンリスト + index_Dt2 となります。
- 59から61行:Scriptでプルダウンリストが変更された時サーバーに変更内容を送信する様に設定しています。
- 97行:SPIFFS.begin(); SPIFFSの開始
- 100から104行:各パラメータの初期設定
- flg[play]=0; 再生無し 停止
- flg[volume]=30; 音量30
- flg[total]=6; 登録曲数+1 今回は5局登録している。
- flg[now]=1; 現在選択されているラジオ局の番号
- out->SetGain((float)flg[volume]/100.0); ボリューム設定
- 116から118行:止まったらもう一度再生する様にしました。
- 134から136行:再生をradio_play(flg[now]);関数で実行
- 140行:停止をStopPlaying();関数で実行。
- 156から159行:プルダウンリストが変更されるとここに来ます。リストの値をflg[now](今選択されている局番を保持)に代入しています。
- 166から204行:ここでHTMLのコードを作っています。
- 176から190行:ここで、タイトル保存ファイル、”title.txt”から各局のタイトルを読み込み、リストを作成しています。
- 192行:リスト作成後、index_Dt +リスト+index_Dt2 でHTMLを作成
- 以降: ボリュームの表示とボタンの処理を上記にHTMLに足してクライアントに送信
- 206から225行:ここが再生部分
- URLが登録されたファイル、”link_list.txt”を開く。
- 213行:現在選択されている局順番を元に局にURLを探す。
- 例えば現在、3番目の局が選択されていれば、”link_list.txt”の3番目のURLが目的のURL
- 216行:局のURLを指定
- “link_list.txt”に登録してあるデータは、0,http://199.180.75.118:80/streamの様に先頭にMP3かAACかを区別する為に0または1を付けています。
- よって、局のURLは3文字目から始まります。
- 219から220行:MP3からAACか判断してどちらかのGeneratorを作成
- AudioGenerator *decoder = NULL; と宣言すると、MP3型でもAAC型でも対応出来る様です。
- 222行:再生開始
- 227行以降: 停止処理。
- 前回で、mp3と有った箇所が、decoderに変わっています。
- これで、MP3 AAC両方い対応します。
今回は、タイトルを保持した、”title.txt” と URLを保持した、”link_list.txt”の2つのファイルを使用します。
“title.txt”
title.txt
Jazz Radio
CLASSIC
J-POP
SomaFM
antena
<--- end --->
“link_list.txt”
link_list.txt
0,http://199.180.75.118:80/stream
0,http://144.217.49.251:80/stream1
0,http://158.69.38.195:20278/stream
0,http://ice1.somafm.com/secretagent-128-mp3
1,http://107.191.33.98:8232/stream/1
--- end ---
- タイトルとURLが一対一になっています。
- URLの先頭には、フォーマット区別用に、MP3なら”0”をAACなら”1”を付けています。
スケッチのコンパイル。その後でデータのアップロードを行って実行して下さい。Webブラウザを上げて、”esp32.local”と入力すると、こんな感じの画面が表示されます。
ページのソースは
ラジオ局は5局登録して有ります。リストから選んで再生してみて下さい。
次回はラジオ局の編集(登録、削除等)について実装します。