WebRadio w/I2S -07 . ラジオ局の登録、削除、追加

今回は、ラジオ局の登録、削除、変更を行う機能を追加します。今回の主な変更箇所は、

  • ラジオ局の登録、削除、変更用に、”Entry”というボタンを追加。
  • 選局用のプルダウンリストとボリュームの順序を変更

です。例によって前回から修正で説明します。


#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='pull_list' method='get'>\n"
                  "<select name='5' id='pull_item' onchange='getItem()'>\n";
                  
String index_Dt1 =
                  "</select><br><br>\n"
                  "</form>\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>\n"
                  "</form><br><br>\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><br><br>\n"
                  "<button type='submit' name='6' style='background:#90ee90'>Entry</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,fl;

    fl=1;
    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;

      case 6:   // Entry Sheet
                entry_sheet();
                fl=0;
                break;

      case 7:   //Edit(0) / ADD(1) / delete(2) OK
     
                cmd = server.arg("7");
                
                a=1;
                if(flg[total] == flg[now]) 
                  if(cmd.charAt(0) != '1') a=0;

                if(a) make_file(flg[now],cmd);
                break;

    }

  if(fl) load_index();
  else server.send(204, "text/plane",""); 
}

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; 
}

void entry_sheet()
{
  File dataFile,dataFile1;
  String line,line1,html_buf;
  int a;

    dataFile = SPIFFS.open("/entry.txt", FILE_READ);
    html_buf="";
    while(dataFile.available())
    {
      html_buf += dataFile.readStringUntil('\n');
      html_buf += "\n"; 
    }
    dataFile.close();

    dataFile = SPIFFS.open("/title.txt", FILE_READ);
    dataFile1 = SPIFFS.open("/link_list.txt", FILE_READ);
    for(a=0; a<flg[now]; a++)
    {
      line = dataFile.readStringUntil('\n');
      line1 = dataFile1.readStringUntil('\n');  
    }
    dataFile.close(); dataFile1.close();
                
    html_buf += "document.getElementById('m_title').value='" + line + "';\n"
                "document.getElementById('m_link').value='" + line1.substring(2) + "';\n"
                "var mp_acc = document.getElementsByName( 'a2' ) ;\n";

    if(line1.charAt(0) == '1') html_buf += "mp_acc[1].checked = true; \n";        
                            
    html_buf += "</script>\n</body>\n</html>\n";

    server.send(200, "text/html", html_buf);
}

void make_file(int no, String line)
{
    File t,t1,l,l1;
    String d_data,l_data,s_data[3];
    int a,b;

        s_data[0]=line.charAt(0);
                
        b=2; a=2;
        while(a)
         {
           while(line.charAt(b) != ',') b ++;
           a --; b ++;
         }
        s_data[1]=line.substring(2,b-1);
        s_data[2]=line.substring(b);

        a = s_data[0].toInt();
        t = SPIFFS.open("/title.txt",FILE_READ); t1 = SPIFFS.open("/title.tmp",FILE_WRITE);
        l = SPIFFS.open("/link_list.txt",FILE_READ); l1 = SPIFFS.open("/link_list.tmp",FILE_WRITE);
        while(t.available())
        {
          d_data = t.readStringUntil('\n'); l_data = l.readStringUntil('\n');
          no --;
          switch(a)
          {
            case 0: //Edit
                    if(no == 0)
                    { 
                      t1.print(s_data[2] + "\n"); 
                      l1.print(s_data[1] + "\n"); 
                    }
                    else 
                    { 
                      t1.print(d_data + "\n"); 
                      l1.print(l_data + "\n"); 
                    }
                    break;

            case 1: // Add
                    if(no == 0) 
                    { 
                      t1.print(s_data[2] + "\n"); 
                      l1.print(s_data[1] + "\n"); 
                    }
                    t1.print(d_data+"\n"); l1.print(l_data + "\n");
                    break;

            case 2: // Delete
                    if(no != 0) 
                    { 
                      t1.print(d_data + "\n"); 
                      l1.print(l_data + "\n"); 
                    }
                    break;
          }
        }
        t.close(); t1.close(); l.close(); l1.close();

        SPIFFS.remove("/title.txt");
        SPIFFS.remove("/link_list.txt");
        SPIFFS.rename("/title.tmp","/title.txt");
        SPIFFS.rename("/link_list.tmp","/link_list.txt");

        if(s_data[0] == "1") flg[total] ++; 
        if(s_data[0] == "2") flg[total] --; 
       
}
  • 38行:String index_Dt1 =
    • プルダウンリストの位置を変更した為。
  • 162行: Entryボタンを押すと、ここが実行されます。
    • 163行:entry_sheet(); 実際の処理を行う関数
  • 167行:ここで選局関係のファイル、”title.txt”と”link_list.txt”の2つのファイルを書き換えます。
  • 180から181行:以前は全ての処理後に基本の管理ページを表示していました。今回からラジオ局編集用の画面が追加されたので、両者を分けて表示する必要が有ります。ここで両者を切り替えています。
  • 254から287行:編集用の画面を表示して値を入力を待ちます。
    • 260から267行:編集画面用HTMLの大半は、”entry.txt”に保存されています。それの読み込み。以下で不足分を追加。
    • 269から279行:現在選択されているタイトルとURLを表示
    • 282行:MP3とAACの判別
    • 286行:画面のHTMLをクライアントに送信
  • 289から356行:ここで、”title.txt”と”link_list.txt”の2つファイルが編集されます。

このスケッチは、”entry.txt”,”title.txt”,”link_list.txt”の3つのファイルを使用します。スケッチの下に、”data”フォルダーを作りこれら3つのファイルを保存して下さい。

“entry.txt”

entry.txt
<!DOCTYPE html>
<html>
<head>
<title>Internet Radio</title>
</head>

<body>
<center>
<h2>--- Entry Sheet ---</h2>
<form name="cmd">
<input type="radio" name="a1" value="e" checked> Edit  
<input type="radio" name="a1" value="a" > Add  
<input type="radio" name="a1" value="d" > Delete  
</form>	    

<form name="add">
<br>Title
<input type="text" maxlength="50" id="m_title"
style="width:200px; overflow:auto; " /> <br>
URL 
<input type="text" maxlength="250" id="m_link"
style="width:200px; overflow:auto; '"/> 
</form>	
<br>
<form name="M_A">
<input type="radio" name="a2" value="m" checked> MP3  
<input type="radio" name="a2" value="c" > AAC  
</form>	    
<br>
<form method="get">
<button type="submit" name="7" value="123" onclick="onButtonOk()" id="add_ok">OK</button>
<button type="submit" name="99" value="aaa" >Quit</button>
</form>

</center>
<script>
function onButtonOk() {
cmd="0,";
if(document.cmd.a1[1].checked) cmd="1,";
if(document.cmd.a1[2].checked) cmd="2,";
cmd1="0,";
if(document.M_A.a2[1].checked) cmd1="1,";
document.getElementById("add_ok").value =
cmd + cmd1 + document.getElementById("m_link").value + "," + document.getElementById("m_title").value;
}

”title.txt”

title.txt
Jazz Radio
CLASSIC
J-POP
SomaFM
antena
<--- end --->

“link_list.txt”

link.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  ---

先ずはコンパイル。続いてアップロード。実行して下さい。ブラウザを上げて、”esp32.local”と入力すると、

このページのソースは

<!DOCTYPE html>
<html>
<head>
<title>ESP32 WebRadio</title>
</head>
<body>
<center>
<h2>ESP32 WebRadio</h2>
<form id='pull_list' method='get'>
<select name='5' id='pull_item' onchange='getItem()'>
<option value='1' selected>1: Jazz Radio</option>
<option value='2'>2: CLASSIC</option>
<option value='3'>3: J-POP</option>
<option value='4'>4: SomaFM</option>
<option value='5'>5: antena</option>
<option value='6'><--- end ---></option>
</select><br><br>
</form>
<form id='cmd_vol' method='get'>
<input type='range' min='0' max='100' step='1' name='3' onchange='showValue()' id='s_value' value='50'>
<span id='p_value'>50</span><br>
</form><br><br>
<form method='get'>
<button type='submit' name='1' id='b_play' style='background:#90ee90'>PLAY</button>
<button type='submit' name='2' style='background:#90ee90'>STOP</button>
<button type='submit' name='4' id='b_mute' style='background:#90ee90'>MUTE</button><br><br>
<button type='submit' name='6' style='background:#90ee90'>Entry</button>
</form>
<script>
function showValue() {
target = document.getElementById('cmd_vol');
target.submit();
}
function getItem() {
target = document.getElementById('pull_list');
target.submit();
}
document.getElementById('s_value').value='30'
document.getElementById('p_value').innerHTML='30'
</script>
</center>
</body>
</html>

ここで、”Entry”ボタンを押すと、編集画面が表示されます。

簡単な説明。

  • ラジオ管理画面で選択したラジオ局に対して編集します。
  • 上記の場合、”1”の局が選択されていたので、そのタイトルとURL、フォーマットが編集画面に表示されます。
  • Edit
    • 選択した局に対して変更します。
    • Title、URLを入力。フォーマット(MP3かAAC)を選んで
    • OKならOKボタン。編集をキャンセルするならQuitボタンを押す
  • Add
    • 現在選択されている局に上に追加されます。
    • 例では、”1”の局が選択されているので、この局が新たに”1”となり、現在”1”の局は”2”となります。
    • この追加方法ではリストの最後に新たに追加する事が出来まえん。そこで、ラジオのリストの最後に、”<−−− end −−−>を追加しています。リストの最後に新たに登録する場合、この項目を選んで追加します
    • 必要項目を入力して、OKまたはQuitボタン
  • Delete
    • 選択されている局を削除します。
    • OKを押せば削除。Quitでキャンセル。

このページのソースは

<!DOCTYPE html>
<html>
<head>
<title>Internet Radio</title>
</head>

<body>
<center>
<h2>--- Entry Sheet ---</h2>
<form name="cmd">
<input type="radio" name="a1" value="e" checked> Edit  
<input type="radio" name="a1" value="a" > Add  
<input type="radio" name="a1" value="d" > Delete  
</form>	    

<form name="add">
<br>Title
<input type="text" maxlength="50" id="m_title"
style="width:200px; overflow:auto; " /> <br>
URL 
<input type="text" maxlength="250" id="m_link"
style="width:200px; overflow:auto; '"/> 
</form>	
<br>
<form name="M_A">
<input type="radio" name="a2" value="m" checked> MP3  
<input type="radio" name="a2" value="c" > AAC  
</form>	    
<br>
<form method="get">
<button type="submit" name="7" value="123" onclick="onButtonOk()" id="add_ok">OK</button>
<button type="submit" name="99" value="aaa" >Quit</button>
</form>

</center>
<script>
function onButtonOk() {
cmd="0,";
if(document.cmd.a1[1].checked) cmd="1,";
if(document.cmd.a1[2].checked) cmd="2,";
cmd1="0,";
if(document.M_A.a2[1].checked) cmd1="1,";
document.getElementById("add_ok").value =
cmd + cmd1 + document.getElementById("m_link").value + "," + document.getElementById("m_title").value;
}

document.getElementById('m_title').value='Jazz Radio';
document.getElementById('m_link').value='http://199.180.75.118:80/stream';
var mp_acc = document.getElementsByName( 'a2' ) ;
</script>
</body>
</html>

これでラジオ局に変更、追加、削除が出来る様になりました。次回は電源オン時に前回の状態で再生を始める(リトーリーブ)機能を追加します。