M5StackのマイコンESP32はWiFi機能を持っており、M5StackをWiFiに接続してWebサーバーとして動かすことでPCやスマホのWebブラウザからM5Stackをリモートコントロールすることができます。
Webサーバーの仕組み
PCやスマホがWebページを表示するとき、WebブラウザとWebサーバーはHTTPプロトコルでリクエスト/レスポンス型の通信をしています。クライアントであるWebブラウザはURLなどのリクエスト(URI)をWebサーバーに送信し、Webサーバーがリクエストに応じたWebページの内容などをレスポンスとして返送します。Webブラウザは返送された内容を解読してブラウザ上に表示しています。
HTTPリクエスト
HTTPリクエストメッセージの中身は、HTTPリクエストメソッド、リクエスト、HTTPバージョンです。WebブラウザにURLを打ち込むとHTTPリクエストに変換されて送信されます。
HTTPメッセージの内容は実際にWebブラウザで確認できます。Chromeの場合はメニューボタンから「ディペロッパーツール」を開き、「ネットワーク」にあるタブの「ヘッダ」を選択するとメッセージの中身を確認できます。今回作成したプログラムのM5Stack CORE2とのHTTPメッセージは以下のようになりました。

HTTPリクエストメソッドにはGET、POSTなどがありますが、サーバーからWebページの内容を取得する場合は「GET」になります。
リクエストはWebブラウザのアドレスバーに打ち込む内容になります。例えば、M5Stack CORE2にアクセスする場合は、URL「http://192.168.1.25」です。
また、URLにはパラメーターを付加することができます。例えば、ECサイトで服の色など特定の条件で検索するときに使われます。このURLパラメータはURLの後に「?」クエスチョンマークで区切って付加されます。複数のパラメータがある場合は、複数のパラメータを「&」で区切って設定します。
例えば、2つのパラメーターを設定する場合、1つ目のパラメーター名はcolor、値はwhite、2つ目のパラメーターはsizeで値はMとすると、「http://192.168.1.25/sub1?color=white&size=M」のようになります。
プロトコルバージョンはHTTP/1.1となっています。ユーザーはブラウザにURLを打ち込むだけで自動的にHTTPリクエストが送信されます。
URLにポート番号が省略されていますが、Webサーバー(HTTP)のポート番号は80になります。
HTTPレスポンス
Webサーバーが返信する内容、HTTPバージョン、HTTPステータスコード、コンテンツタイプ、ブラウザ表示内容などです。
HTTPステータスコードは、例えば「200 OK」は正常に処理された、「404 Not Found」は指定のリソース(ファイル)が存在しない、などがあります。
コンテンツタイプは、リソースの種類を表すMIMEメディアタイプで、例えば、テキストなら「text/plain」、HTMLなら「text/HTML」、画像なら「image/jpeg」、音声なら「audio/mpeg」、映像なら「video/mpeg」・・・です。
今回はお試しとしてクライアントであるWebブラウザからM5Stack CORE2へのリクエストに対しM5Stackがレスポンスとしてテキストを送り返してブラウザに表示されることを確認しました。
ESP32のWebServerクラスのメンバ関数を使う
M5StackではESP32のWebServerクラスのメンバ関数を使ってWebサーバーを実現できます。ヘッダWebServer.hで定義されています。今回のお試しプログラムではWebServerクラスのインスタンスserverを生成しています。
- server.begin(void)・・・Webサーバー開始
- server.on(URI, ハンドル関数名) ・・・URI(URL)にアクセスがあった場合にハンドル関数を呼び出す。
- server.handleClient(void)・・・loop()の中に置き、クライアントからリクエストがあったら、setup()の中に設定した所定のserver.on()関数が実行される。
- server.send(code, content_type, content)・・・codeにHTTPステータスコード、content_typeにMIMEタイプ、contentにブラウザに表示する本体を設定して送信する。
- server.onNotFound(ハンドル関数名)・・・リクエストされたリソースが存在しない場合にハンドル関数を呼び出す。
- server.uri()・・・リクエストされたURI(URL)を返す。
- server.method()・・・リクエストされたHTTPリクエストメソッドを返す。
- server.args()・・・リクエストに設定されたパラメータの総数を返す。リクエストのURL後に「?」マークを付けて付加情報をパラメーターとして送信できるが、そのパラメーターの総数を返す。
- server.argName(int i)・・・args()で取得した全パラメーターのうち、取得したいパラメータ番号を引数に設定すると、そのパラメータ名(文字列)が返される。
- server.arg(int i)・・・引数で指定したパラメーター番号の値が返される。
Webサーバーお試しプログラム作成
ArduinoIDEのサンプルスケッチ「HelloServer.ino」を参考にしています。ArduinoIDEのファイル>スケッチ例>WebServer>HelloServerにあります。WiFi接続の部分はこちらの以前作成したプログラムを流用しています。
#include <M5Unified.h>
#include <WiFi.h>
#include <WebServer.h>
const char *ssid = "**************"; //WiFi SSID
const char *password = "*************"; //WIFi パスワード
IPAddress ip; //IP address
WebServer server(80); //WebServerクラスのインスタンス生成(HTTPのポート番号)
void handleRoot() {
server.send(200, "text/plain", "Response from M5Stack CORE2. \n...Root");
}
void handleSub1() {
server.send(200, "text/plain", "Response from M5Stack CORE2. \n...Sub1");
}
void handleSub2() {
server.send(200, "text/plain", "Response from M5Stack CORE2. \n...Sub2");
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.fillScreen(TFT_WHITE);
M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
M5.Display.setTextSize(2);
M5.Display.print("\n*** Test Web Server ***\n\n");
M5.Display.print("Connecting");
WiFi.mode(WIFI_STA); //ステーションモード。M5Stackを子機として接続
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Display.print(".");
}
M5.Display.print("\n\nWiFi connected!\n\n");
M5.Display.print("IP address: ");
ip = WiFi.localIP(); //IP address取得
M5.Display.print(ip); //IP address表示
server.on("/", handleRoot); //IPアドレスのルートにアクセスされたとき
server.on("/sub1", handleSub1); //Sub1にアクセスされたとき
server.on("/sub2", handleSub2); //Sub2にアクセスされたとき
server.onNotFound(handleNotFound); //存在しないリソースにアクセスされたとき
server.begin(); //Webサーバー開始
}
void loop(void) {
server.handleClient(); //クライアントのリクエスト待ち
}setup()関数内でWiFiとWebサーバーの設定をします。loop()関数ではWebブラウザからのアクセスを待ちます。
M5StackのIPアドレスのルート「/」にアクセスされた場合、ユーザー関数handleRoot()を呼び出します。この関数の中のsend()関数により、HTTPステータスコード=200(正常に処理された)、MIMEタイプ=text/plain、送信テキスト内容=\n…Rootを返送します。
ルートの下の/sub1または/sub2ディレクトリにアクセスした場合も同様にそれぞれのユーザーハンドル関数handleSub1()、handleSub2()に記述した処理を実行します。
「ルート」、「/sub1」、「/sub2」以外のURLは設定しておらず、これ以外のURLにアクセスした場合はonNotFound()関数が呼び出され、ハンドル関数handleNotFound()が実行されます。このユーザー関数はWebサーバーからリクエストされたURI、HTTPリクエストメソッド、パラメータを表示させるようになっています。
設定したURLと実際に存在しないURLにアクセスした時の結果をそれぞれ次項に示します。
Webブラウザからのアクセス結果
Webブラウザ(Chrome)にM5StackのIPアドレス打ち込む(http://192.168.1.25)と、ブラウザには下のように表示されました。server.send()に設定したテキスト文が表示されています。

次にroot下に設定したsub1(http://192.168.1.25/sub1)を打ち込むと下のように表示されました。こちらもserver.send()に設定したテキスト文が表示されています。

存在しないURL(http://192.168.1.25/sub3)を打ち込むと下のように表示されました。Arguments(URLパラメータ)は何も設定していないので0が返されています。

まとめ
基本的なWebサーバーの知識を身に付ければ、M5StackをWebブラウザからリモートコントロールするプログラムを作成できるようになります。プログラムの構成やアクセス手順はサンプルスケッチが参考になります。
サーバーとの通信はESP32のWebServerクラスのメンバ関数を使うことでプロトコルに関するところで苦労することはなさそうです。
今回はM5StackのIPアドレスにアクセスしてテキストを送り返すだけの簡単なプログラムでしたが今後はもう少し実用的なプログラムを作成しようと思います。

