(9)新しい機能追加。(HTTP Serverその3)

今回は、”WiFi関係のパラメータの編集” と ”ラジオ局情報の編集” のコードの実装です。

WiFi関係のパラメータの編集

前回、ブラウザに”my-esp32.local”と入力すると、

が表示されました。ここで、”Set WiFi”ボタンを押して

上記を表示させ、WiFiの ”SSID” ”KEY_(Password)” の編集を行いFlashに保存するプログラムを書いて行きます。

例えば、このホームページは下記の様なhtmlファイルで表示する事が出来ます。

set_wifi.html

<!doctype html>
<html>
	<head>
		<meta charset='utf-8'>
		<meta name='viewport' content='width=device-width,initial-scale=1'>
		<link rel='stylesheet' type='text/css' href='./http_server.css' >
		<title>Radio Editor</title>
	</head>
	<body>
		<center>
			<div class='b_frame'>
			<div class='t_font'><u>Radio Editor</u></div><br>
			<form method='get'>
				<p><font color='#0ff'>SSID : </font><input name='10' type='text' style='font-size:18px;' size='20' maxlength='50' value='XXXXXXXX'></p>
				<p><font color='#0ff'>KEY_ : </font><input name='11' type='text' style='font-size:18px;' size='20' maxlength='50' value='YYYYYYYY'></p>
				<button class='m01_button' type='submit' > OK </button><button class='m01_button' type='submit' name='19' > Back </button><br>
			</form>
			</div>
		</center>
	</body>
</html>

このファイルをホームページ用htmlファイルとして保存すれば良さそうですが、14,15行のWiFi関係のデータ(SSIDとPassword)を表示している箇所に問題が有ります。問題はこれらのパラメータは予めFlashに保存されているのですが、ホームページを予めhtmlファイルとしてFlashに保存するとこれらの値をそれに反映出来ないという事です。

HTMLファイルをCで書く

この問題を解決する為にホームページのHTMLを本体プログラムに組み込みました。下記は上記のHTMLコードをクライアントに送信する関数 void wifi_edit() です。 

wifi_edit

void wifi_edit(httpd_req_t *req)
{
	char edit_top[] = "<form method='get'>\n<p><font color='#0ff'>SSID :"
					  " </font><input name='10' type='text' style='font-size:18px;' size='20' maxlength='50' value='";
	
	char edit_mid[] = "'></p>\n<p><font color='#0ff'>KEY_ : </font><input name='11' type='text' style='font-size:18px;' size='20' maxlength='50' value='";

	char edit_end[] = "'></p>\n<button class='m01_button' type='submit' > OK </button><button class='m01_button'"
					  " type='submit' name='19' > Back </button><br>\n</form>\n</div>\n</center>\n</body>\n</html>";

	httpd_resp_set_type(req, "text/html");
	httpd_resp_send_chunk(req, html_header, strlen(html_header));
	httpd_resp_send_chunk(req, edit_top, strlen(edit_top));
	httpd_resp_send_chunk(req, wifi_data[0], strlen(wifi_data[0]));
	httpd_resp_send_chunk(req, edit_mid, strlen(edit_mid));
	httpd_resp_send_chunk(req, wifi_data[1], strlen(wifi_data[1]));
	httpd_resp_send_chunk(req, edit_end, strlen(edit_end));
    httpd_resp_send_chunk(req, NULL, 0);
}

送信方法は、

  • HTMLファイルのWiFiのSSID表示直前までを、httpd_resp_send_chunk() 関数で送信
  • 14行:WiFiのSSIDのパラメータ部でFlashから読み込んだ値を送信
  • Password表示直前までを、httpd_resp_send_chunk() 関数で送信
  • 15行:WiFiのPasswordのパラメータ部でFlashから読み込んだ値を送信
  • その後は残りを送信

これで、Flashの値をHTMLに反映出来ました。

Radio infoボタンのコード

Radikoのプログラムはラジオ局を全部で24局登録出来るので1ページ12局、2ページに分けて編集するようにしました。メインメニューで、”Radio Info”ボタンを押すと先ず1ページ目

が表示されます。ここでは0から11までのラジオ局データが表示されます。編集したい番号の左のラジオボタンを選択後、下の”Edit”ボタンをクリックすると編集モードに入ります。 下記は0番を選択した”Edit”ボタンを押した場合です。

この画面で編集し、”OK”ボタンを押せばデータは変更されて、”Back”ボタンを押せばデータ変更されず上記のHPに戻ります。

2ページ目を編集したい時は、”Page:1″のラジオボタンをクリックします。

画面下の”Save”または”Back”ボタンを押すと各ボタン下記の処理を行った後メインメニューに戻ります

  • ”Save”ボタン: 変更したデータをFlashに保存する
  • ”Back”ボタン: 変更したデータをFlashに保存しない

AP接続

編集用のコードが出来たのでAP接続も試してみました。予想した通り接続をSTAからAP接続に変更してもコードは問題無く機能しました。

プログラムの構成

プロジェクトの構成は以下の通り。


- http_sample/
      - CMakeLists.txt
      - partitions_example.csv
      - sdkconfig.defaults
      - main/
                   - CMakeLists.txt
                   - main.c
      - components/
                   - spiffs_image/
                              - station.txt
                              - index.html
                              - http_server.css
                              - wifi.txt
                              - st_edit02.js
  • mainフォルダーの”Kconfig.projbuild”を削除し、componentsフォルダーに”wifi.txt”を追加
    • ”wifi.txt”はルーターのSSIDとPasswordを保持しています。
    • 今回は、AP接続でサーバーを上げて値を編集保存しますので、下記の値を書いて置きます。
      • SSID: XXXXXXXX
      • Password: YYYYYYYY
  • componentsフォルダー下の”Staion.txt”も局データを24個にしています。
    • 24個の内16個はRadikoの局としています。Radikoは関東圏のものです。
    • その他、MP3 AAC 形式の局、ブランク(16,17番)も登録しています。
  • componentsフォルダーに”st_edit02.js”を追加。
    • 編集画面で使用するJava Script Fileです。
  • mainフォルダーの”main.c”
    • 今回、大幅に変更しています。詳細は後で説明します。
wifi.txt

XXXXXXXX
YYYYYYYY
station.txt

1,0,0
1,1,0
1,2,0
1,3,0
1,4,0
1,5,5
1,6,6
1,7,0
1,8,0
1,9,0
1,10,0
1,11,0
1,12,0
1,13,0
1,14,0
1,15,0
0, , 
0, , 
2,Classic,http://yp.shoutcast.com/sbin/tunein-station.pls?id=22146
2,JAZZ1,http://yp.shoutcast.com/sbin/tunein-station.pls?id=1528122
2,TOP40,http://yp.shoutcast.com/sbin/tunein-station.pls?id=1914279
2,Piano,http://yp.shoutcast.com/sbin/tunein-station.pls?id=99513978
2,Dance,http://yp.shoutcast.com/sbin/tunein-station.pls?id=1817772
3,Antena,http://yp.shoutcast.com/sbin/tunein-station.pls?id=1796249

プログラムの説明

プログラム”main.c”に付いて簡単に説明します。

main.c

#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include "nvs_flash.h"
#include <esp_http_server.h>
#include "mdns.h"
#include "esp_spiffs.h"
#include "freertos/event_groups.h"

static const char *TAG = "Http_server01";

struct st_station_info {         //Station info
    int _deco;
    char _st_ID[20];
    char _st_URL[200];
};
//	_deco
//	0: No_Data
//	1: Radiko
//	2: MP3
//	3: AAC

struct st_station_info station_info[24];

const char* decode_buf[] = {"No_Data", "Radiko", "MP3", "ACC"};
const char* st_buf[] = {"TBS", "QRR", "LFR", "RN1", "RN2", "INT", "FMT", "FMJ", "JORF", "BAYFM78", "NACK5", "YFM", "IBS", "HOUSOU-D", "JOAK", "JOAK-FM"};

char wifi_data[2][55];

void read_wifi_data(void)
{
    FILE *fp = NULL;

	fp = fopen("/spiffs/wifi.txt", "r");
	fgets(wifi_data[0], 50, fp);
	fgets(wifi_data[1], 50, fp);
	fclose(fp);

	wifi_data[0][strlen(wifi_data[0])-1] = 0;
	wifi_data[1][strlen(wifi_data[1])-1] = 0;
}

#define SCRATCH_BUFSIZE  2000
esp_err_t send_file(httpd_req_t *req, char* f_name)
{
    FILE *fp = NULL;
    char send_buf[SCRATCH_BUFSIZE];
    size_t buf_size;

	fp = fopen(f_name, "r");
    do {
        buf_size = fread(send_buf, 1, SCRATCH_BUFSIZE, fp);
        if (buf_size > 0) 
        {
            if (httpd_resp_send_chunk(req, send_buf, buf_size) != ESP_OK) 
            {
                fclose(fp);
                ESP_LOGE(TAG, "File sending failed!");
                httpd_resp_sendstr_chunk(req, NULL);
                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
               	return ESP_FAIL;
           	}
        }
    } while (buf_size != 0);
    fclose(fp);
    httpd_resp_send_chunk(req, NULL, 0);
               
    return ESP_OK;
}

//---------------	HTML Header    --------------------------------------------------------------------------
const char html_header[] =
	"<!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='./http_server.css' >\n<title>Radio Editor</title>\n</head>\n<body>\n<center>\n"
	"<div class='b_frame'>\n<div class='t_font'><u>Radio Editor</u></div><br>\n";

//---------------	Station Edit02    --------------------------------------------------------------------------
int station_page = 0;	
int edit_no = 0;
void station_edit02(httpd_req_t *req)
{
	char buf[1224];
	char c_buf[10];
	int a;

	httpd_resp_set_type(req, "text/html");
	httpd_resp_send_chunk(req, html_header, strlen(html_header));

	strcpy(buf, "<form method='get' style='font-size:22px;color:#0ff'>\nEdit No:<span>");
	sprintf(c_buf, "%02d", edit_no); strcat(buf,c_buf);
	strcat(buf,"</span>\n<div style='font-size:16px;'><br>\nDecode:\n<select name='40' style='text-align:center;' onchange='list_Change()' id='0'>\n");
	for(a = 0; a < 4; a++)
	{
		strcat(buf,"<option ");
		if( a == station_info[edit_no]._deco) strcat(buf, "selected");
		strcat(buf," value='");
		itoa(a,c_buf,10); strcat(buf,c_buf);
		strcat(buf,"'>");
		strcat(buf,decode_buf[a]);
		strcat(buf,"</option>\n");
	}	
	strcat(buf,"</select>\n&emsp;&emsp; Title:\n<select name='41' style='text-align:center;' id='1'>\n");
	for(a = 0; a < 16; a ++)
	{
		strcat(buf, "<option ");
		if(station_info[edit_no]._deco == 1)
			if( a == atoi(station_info[edit_no]._st_ID))
				strcat(buf, "selected");

		strcat(buf," value='");
		itoa(a,c_buf,10); strcat(buf,c_buf);
		strcat(buf,"'>");
		strcat(buf,st_buf[a]);
		strcat(buf,"</option>\n");
	}
	httpd_resp_send_chunk(req, buf, strlen(buf));

	strcpy(buf,"</select>\n<input type='text' size='6' maxlength='8' style='text-align:center;' hidden name='42' id='2' value='");
	strcat(buf,station_info[edit_no]._st_ID);
	strcat(buf,"'><br>\nURL:<input type='text' size='30' maxlength='200' name='43' id='3' value='");
	strcat(buf,station_info[edit_no]._st_URL);
	strcat(buf,"'><br><br>\n<button class='m02_button' type='submit' name='44'> OK </button> &emsp;&emsp;"
			 	"\n<button class='m02_button' type='submit' name='49'> Back </button><br>\n</div>\n</form>\n</div>\n</center>\n"
				"<script type='application/javascript' src='./st_edit02.js'></script>\n</body>\n</html>");
	httpd_resp_send_chunk(req, buf, strlen(buf));
    httpd_resp_send_chunk(req, NULL, 0);
}

//---------------	Station Edit    --------------------------------------------------------------------------
void station_edit(httpd_req_t *req)
{
	char buf[1224];
	char c_buf[10];
    int a,b,c,d;

	httpd_resp_set_type(req, "text/html");
	httpd_resp_send_chunk(req, html_header, strlen(html_header));

	strcpy(buf, "<div style='font-size:18px;color:#0ff'>\n<form id='1' method='get'>\n<input name='30' type='radio' value='0' onclick='click_Rad()'");
	if(station_page == 0) strcat(buf," checked");
	strcat(buf,"> Page:0 &emsp;&ensp;&emsp;\n<input name='30' type='radio' value='1' onclick='click_Rad()'");
	if(station_page) strcat(buf," checked");
	strcat(buf,"> Page:1\n</form>\n</dev>\n<div style='font-size:14px;color:#0ff'>\n<form id='2' method='get'>");

	httpd_resp_send_chunk(req, buf, strlen(buf));
	b = station_page * 12;
	c = 0;
	d = edit_no;
	if(station_page) d -= 12;
	for(a = b; a < b + 12; a ++)
	{
		strcpy(buf, "\n<input type='radio' name='31' ");
		if(d == c) strcat(buf,"checked ");
		strcat(buf,"value='");
		itoa(c,c_buf,10); strcat(buf,c_buf); c ++;
		strcat(buf,"'>\n<font >");
		sprintf(c_buf, "%02d", a); strcat(buf,c_buf);
		strcat(buf,":</font>\n<input type='text' size='2' maxlength='8' style='text-align: center;' readonly value='");
		strcat(buf,decode_buf[station_info[a]._deco]);
		strcat(buf,"'>\n<input type='text' size='3' maxlength='8' style='text-align: center;' readonly value='");
		switch(station_info[a]._deco)
		{
			case 0:	// No_Data
						strcat(buf," ");
						break;
						
			case 1:	//Radiko
						strcat(buf,st_buf[atoi(station_info[a]._st_ID)]);
						break;
						
			default :	// MP3 ACC
						strcat(buf, station_info[a]._st_ID); 
		}	

		strcat(buf, "'>\n<input type='text' size='15' maxlength='200' readonly value='");
		if(station_info[a]._deco < 2) strcat(buf," ");
		else strcat(buf, station_info[a]._st_URL);
		strcat(buf, "'><br>\n");
	
		httpd_resp_send_chunk(req, buf, strlen(buf));
	}	

	strcpy(buf,"</form>\n</dev>\n<form method='get' >\n<button class='m02_button' type='button' onclick='onBtn_Edit()'> Edit </button>"
        	    "\n<button class='m02_button' type='submit' name='32' > Save </button>"
        	    "\n<button class='m02_button' type='submit' name='39' > Back </button><br>\n</form>\n</div>\n</center>\n<script>\n"
        	    "function click_Rad(){ document.getElementById('1').submit(); } \n"
        	    "function onBtn_Edit(){ document.getElementById('2').submit(); }\n</script>\n</body>\n</html>");

	httpd_resp_send_chunk(req, buf, strlen(buf));
    httpd_resp_send_chunk(req, NULL, 0);
}

//---------------	WiFi Edit HP    --------------------------------------------------------------------------
void wifi_edit(httpd_req_t *req)
{
	char edit_top[] = "<form method='get'>\n<p><font color='#0ff'>SSID :"
					  " </font><input name='10' type='text' style='font-size:18px;' size='20' maxlength='50' value='";
	
	char edit_mid[] = "'></p>\n<p><font color='#0ff'>KEY_ : </font><input name='11' type='text' style='font-size:18px;' size='20' maxlength='50' value='";

	char edit_end[] = "'></p>\n<button class='m01_button' type='submit' > OK </button><button class='m01_button'"
					  " type='submit' name='19' > Back </button><br>\n</form>\n</div>\n</center>\n</body>\n</html>";

	httpd_resp_set_type(req, "text/html");
	httpd_resp_send_chunk(req, html_header, strlen(html_header));
	httpd_resp_send_chunk(req, edit_top, strlen(edit_top));
	httpd_resp_send_chunk(req, wifi_data[0], strlen(wifi_data[0]));
	httpd_resp_send_chunk(req, edit_mid, strlen(edit_mid));
	httpd_resp_send_chunk(req, wifi_data[1], strlen(wifi_data[1]));
	httpd_resp_send_chunk(req, edit_end, strlen(edit_end));
    httpd_resp_send_chunk(req, NULL, 0);
}

//---------------	Radio Editor HP   --------------------------------------------------------------------------
void radio_main(httpd_req_t *req)
{
    httpd_resp_set_type(req, "text/html");
    send_file(req, "/spiffs/index.html");
}

//--------------------------------------------------------------------------------------------------------------

void urldecode2(char *dst, const char *src)
{
	char a, b;
	
    while (*src) 
    {
	    if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) 
        {
	        if (a >= 'a') a -= 'a'-'A';
            if (a >= 'A') a -= ('A' - 10); else a -= '0';
            if (b >= 'a') b -= 'a'-'A';
            if (b >= 'A') b -= ('A' - 10); else b -= '0';
            *dst++ = 16*a+b;
            src+=3;
        } 
        else if (*src == '+') 
        {
	        *dst++ = ' ';
            src++;
        } 
        else  *dst++ = *src++;
    }
	*dst++ = '\0';
}

#define IS_FILE_EXT(filename, ext) \
    (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)
void down_load_file(httpd_req_t *req)
{
    char name_buf[100];

	if (IS_FILE_EXT(req->uri, ".html")) httpd_resp_set_type(req, "text/html");
	else if (IS_FILE_EXT(req->uri, ".css")) httpd_resp_set_type(req, "text/css");
	else if (IS_FILE_EXT(req->uri, ".js")) httpd_resp_set_type(req, "application/javascript");
	else if (IS_FILE_EXT(req->uri, ".jpeg")) httpd_resp_set_type(req, "image/jpeg");
	else if (IS_FILE_EXT(req->uri, ".txt")) httpd_resp_set_type(req, "text/plain");
	strcpy(name_buf,"/spiffs");
	strcat(name_buf,req->uri);
	send_file(req, name_buf);
}

int flg_state = 1;
char param[500];
/* An HTTP GET handler */
esp_err_t get_handler(httpd_req_t *req)
{
    FILE *fp = NULL;
    char* buf;
    char * cmd_buf;
    size_t buf_len;
	int flg_index;
	int a;

    buf_len = strlen(req->uri);
    flg_index = 1;
    if (buf_len > 1)
    {
   		if(strchr(req->uri, '?') != NULL)
   		{
        	buf = malloc(buf_len);
        	if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK)
        	{
       			cmd_buf = strchr(buf, '='); 
       			*cmd_buf  = 0;
				a = atoi(buf);
       			*cmd_buf  = '=';
				
            	switch(a)
            	{
            		case 1:		// Edit Wifi
		            			flg_state = 2;
            					break;
            					
            		case 2:		// Enter Radio Info
								station_page = 0;	
								edit_no = 0;
		            			flg_state = 3;
            					break;

 					// ----------------------- Wifi ID & KEY ----------------------------     
            		case 10:	// Main
            		
             					if(httpd_query_key_value(buf, "19", param, sizeof(param)) == ESP_ERR_NOT_FOUND)
             					{ 
	            					fp = fopen("/spiffs/wifi.txt", "w");
	            					httpd_query_key_value(buf, "10", param, sizeof(param)); 
	            					urldecode2(param, param);
	            					fprintf(fp, "%s\n", param); strcpy(wifi_data[0],param);

	            					httpd_query_key_value(buf, "11", param, sizeof(param)); 
	            					urldecode2(param, param);
	            					fprintf(fp, "%s\n", param); strcpy(wifi_data[1],param);
	    							fclose(fp);
	    						}

		            			flg_state = 1;
            					break;

 					// ----------------------- Staion Edit Main ----------------------------     
            		case 30:	// Page select
	            				httpd_query_key_value(buf, "30", param, sizeof(param)); 
								
								edit_no = 0; station_page = 0;
								if(param[0] == '1') 
								{
									edit_no = 12; 
									station_page = 1;
								}	
								
								flg_state = 3;
		            			break;
            		
             		case 31:	// Start edit 
								httpd_query_key_value(buf, "31", param, sizeof(param)); 
								edit_no = atoi(param);
								if(station_page) edit_no += 12;
								
		            			flg_state = 4;
		            			break;
            		
             		case 32:	// Save data 
	            				fp = fopen("/spiffs/station.txt", "w");
	            				for(a = 0; a < 24; a++)
	            				{
	            					itoa(station_info[a]._deco,param,10);
	            					strcat(param,",");
	            					strcat(param,station_info[a]._st_ID);
	            					strcat(param,",");
	            					strcat(param,station_info[a]._st_URL);
	            					fprintf(fp, "%s\n", param);
	            				}
    							fclose(fp);
		            			flg_state = 1;
		            			break;
            		
              		case 39:	// Back to Main Menu 
								station_page = edit_no = 0;
								
		            			flg_state = 1;
		            			break;
            		
 					// ----------------------- Staion Edit Sub----------------------------     
              		case 40:	// Back to Edit Main Menu  w/ OK
								
             					if(httpd_query_key_value(buf, "49", param, sizeof(param)) == ESP_ERR_NOT_FOUND)
             					{ 
		            				httpd_query_key_value(buf, "40", param, sizeof(param));
		            				station_info[edit_no]._deco = atoi(param);
		            				
		            				switch(station_info[edit_no]._deco)
		            				{
		            					case 0:	// No_Data
		            								strcpy(param," "); 
		            								break;
		            					case 1:	// Radiko
		            								httpd_query_key_value(buf, "41", param, sizeof(param));
		            								break;
		            					default :	// MP3 ACC
						            				httpd_query_key_value(buf, "42", param, sizeof(param));
						            				break;
						            }
		            				urldecode2(param, param);
		            				strcpy(station_info[edit_no]._st_ID, param);

		            				httpd_query_key_value(buf, "43", param, sizeof(param));
		            				urldecode2(param, param);
		            				strcpy(station_info[edit_no]._st_URL, param);
             					}
             					
		            			flg_state = 3;
		            			
		            			break;
            		
          			default	:	// Main Menu
		            			flg_state = 1;
            	}
        	}
        	free(buf);
   		}
   		else
   		{
   			down_load_file(req);
   			flg_index = 0;
   		}
    }

	if(flg_index)
	{
		switch(flg_state)
		{
			case 1:	radio_main(req);
					break;
			case 2:	wifi_edit(req);
					break;
			case 3:	station_edit(req);
					break;
			case 4:	station_edit02(req);
					break;
		}
	}

    return ESP_OK;
}

void read_station(void)
{
	char buf[330];
    FILE *fp = NULL;
    int a,b;

	fp = fopen("/spiffs/station.txt", "r");
	for(a = 0; a < 24; a ++)
	{
		fgets(buf, 300, fp);

		*(strchr(buf, ',')) = 0;
		station_info[a]._deco = atoi(buf);
		b = strlen(buf) + 1;

		*(strchr(&buf[b], ',')) = 0;
		strcpy(station_info[a]._st_ID, &buf[b]);
		b += (strlen(&buf[b]) + 1);
		
		strcpy(station_info[a]._st_URL, &buf[b]);
	}	
	fclose(fp);
}

void init_spiffs(void)
{
    esp_vfs_spiffs_conf_t conf = {
      .base_path = "/spiffs",
      .partition_label = NULL,
      .max_files = 5,
      .format_if_mount_failed = false
    };

    // Use settings defined above to initialize and mount SPIFFS filesystem.
    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
    esp_vfs_spiffs_register(&conf);

}

httpd_handle_t start_webserver(void)
{

    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.lru_purge_enable = true;
    config.uri_match_fn = httpd_uri_match_wildcard;

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) != ESP_OK) return NULL;

    // Set URI handlers
	httpd_uri_t hello = {
        .uri       = "/*",  // Match all URIs of type /path/to/file
    	.method    = HTTP_GET,
    	.handler   = get_handler,
    	.user_ctx  = "Hello World!"
	};
    ESP_LOGI(TAG, "Registering URI handlers");
    httpd_register_uri_handler(server, &hello);

   return server;
}

void stop_webserver(httpd_handle_t server)
{
    // Stop the httpd server
    httpd_stop(server);
}

void disconnect_handler(void* arg, esp_event_base_t event_base,
                               int32_t event_id, void* event_data)
{
    httpd_handle_t* server = (httpd_handle_t*) arg;
    if (*server) {
        ESP_LOGI(TAG, "Stopping webserver");
        stop_webserver(*server);
        *server = NULL;
    }
}

// FreeRTOS event group to signal when we are connected
static EventGroupHandle_t s_wifi_event_group;

// The event group allows multiple bits for each event, but we only care about two events:
// - we are connected to the AP with an IP
// - we failed to connect after the maximum amount of retries 
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1
#define EXAMPLE_ESP_MAXIMUM_RETRY  10

static int s_retry_num = 0;

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 
    {
        esp_wifi_connect();
    } 
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 
    {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) 
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } 
        else 
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } 
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
    {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

esp_err_t wifi_init_sta(void)
{
	int a;
	
    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    
    s_wifi_event_group = xEventGroupCreate();
    esp_netif_create_default_wifi_sta();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));
	
    wifi_config_t wifi_config = {
        .sta = {
//            .ssid = "xxxxxxxxxx",
//            .password = "yyyyyyyyyy",
	     	.threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    
//	write .ssid data
    a = -1;
    do {
     	a ++;
     	wifi_config.sta.ssid[a] = wifi_data[0][a];
    }while(wifi_data[0][a]) ;

//	write .password data
    a = -1;
    do {
     	a ++; 
     	wifi_config.sta.password[a] = wifi_data[1][a];
    }while(wifi_data[1][a]) ;

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

//  Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
//  number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above)
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

//  xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually	happened. 
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(s_wifi_event_group);
    if (bits & WIFI_CONNECTED_BIT) 
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",wifi_data[0], wifi_data[1]);
        return ESP_OK;         
    } 
    else if (bits & WIFI_FAIL_BIT) 
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",wifi_data[0], wifi_data[1]);
    } 
    else 
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
    return ESP_FAIL;
}

#define EXAMPLE_ESP_WIFI_SSID      "esp32_radiko_AP"
#define EXAMPLE_ESP_WIFI_PASS      "12345678"
#define EXAMPLE_ESP_WIFI_CHANNEL   11
#define EXAMPLE_MAX_STA_CONN       4

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

void wifi_init_softap(void)
{
    esp_netif_create_default_wifi_ap();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}

//========================================== Main ============================================================
void app_main(void)
{
    esp_err_t err;
    
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
	init_spiffs();
   	read_wifi_data();
    read_station();
/*
    err = wifi_init_sta();
    if(err) 
    {
        ESP_LOGE(TAG, "WiFi Init failed: %d\n", err);
        return;
    }
*/
   	wifi_init_softap();

   	start_webserver();
    
    //initialize mDNS service
    err = mdns_init();
    if (err) 
    {
        ESP_LOGE(TAG, "MDNS Init failed: %d\n", err);
        return;
    }

    //set hostname
    mdns_hostname_set("my-esp32");
    //set default instance
    mdns_instance_name_set("Jhon's ESP32 Thing");
}
  • プログラムは685行から開始します。
  • 693行: SPIFFSの初期化
  • 694行: WiFiデータの読み込み
    • 今回追加した機能です。
    • WiFiのSSIDとPasswordを保存したファイル”wifi.txt”からそれらを読み込みます。
  • 695行: ラジオデータの読み込み
    • ”station.txt”から局データを読み込みます。局データの数を24個に増やしています。
  • 697行: STA接続の初期化
    • 前回と同じ
    • ここではコメントアウトしています。
    • 下記のAT初期化部分をコメントアウトし、この部分を有効にするとSTA接続になります。
  • 704行: AT接続の初期化
    • 今回追加した部分。
    • 接続時のパラメターは
      • SSID: ”esp32_radiko_AP”
      • Password: ”12345678″
  • 706行: サーバーの開始
    • ここではGETの処理関数を定義しています。前回と同じ。
  • 268行: ここで GET の処理を行います。
    • 前回と同じく、HTMLの name の値を元に各ボタンの処理を行っています。
    • switch 各case
      • case 1,2:メインメニューの処理
      • case 10:WiFiパラメータの処理
      • その他:ラジオ局データの処理
  • 195行:WiFiパラメータ編集用関数
  • 131行:ラジオ局データ表示用関数
  • 79行:ラジオ局編集用関数

プログラムの実行

今回もESP32をターゲットにしているので、モニターから idf.py -p [USB Port] flash monitor でコンパイル実行出来ます。

プログラムが実行されたらスマホ等でAP接続を行います。

  • スマホでアクセスポイント ”esp32_radiko_AP” を検索。
  • 接続Passwordは、”12345678″
  • その後ブラウザのURL欄に、”my-esp32.local” と入力
  • サーバーのホームページが表示されたら、”Set WiFi”ボタンをクリック
  • WiFiパラメータ編集用HPが表示されます。
  • ここで接続したいルーターのSSIDとPasswordを入力します
  • ”OK” ボタンで 値がFlashに保存されます。
  • 最後は、”Radio Info”ボタンを押して局データを表示したものです。

本来なら保存したSSIDとPasswordでSTA接続出来る事を確認したいのですが、今回はSTA接続部をコメントアウトしている為このプログラムでは出来ません。

次回はradikoに組み込みます

サーバー側のプログラムが出来たので次回はこれをRadikoに組み込んで行きます。