ESP32入門 通信機能が標準搭載されたマイコン・ボード (5) Wi-Fi経由でブラウザにグラフを表示するサンプル・プログラムを動かす
今回確認するサンプル・プログラムは次に示すように、ESP32のWeb Serverの中にあるAdvencedWebServerを取り上げます。
コンパイルして、ボードにプログラムを書き込み、シリアルモニタを起動します。すると次に示すようにSSID、IPアドレス、各機能のスタートが表示されます。
Webブラウザに確認されたIPアドレスを、次のようにURL欄にセットしてPCからアクセスします。
このURLはESP32-WROOM-32Dがサーバとして設定されたアドレスなので、Webブラウザからの問い合わせに次のように答えてきます。
この応答を行うプログラムは、次のような部分から出来上がっています。
●ヘッダ・ファイルの読み込み変数などの定義
最初にコメントでプログラムのライセンスの記述があります。その後WiFi.hなどのヘッダ・ファイルの読み込みを#include指令で指定しています。そのあとSSID、PASSWORD、LEDポートの値が定義され、WebServerのインスタンスserver(80)を定義しています。
●関数HandleRoot()
PCなどのクライアントのブラウザからの問い合わせに対して、サーバとなるESP32-WROOM-32Dの応答の処理を定義しています。
ブラウザに表示される見出し、経過時間の表示を行い、その後に表示されるグラフを指定しています。この関数は、setup()関数内でクライアントからの要求に応じて、/(root)がこの関数で処理されるものとしてサーバに設定されます。
●関数 handleNotFound()
クライアントなどからの要求が正しくなく、正しい応答ができない場合の処理を定義しています。
●関数setup()
プログラムが開始されると、最初に一度だけこの関数が呼び出され実行されます。LEDポート、シリアル通信の初期設定をおこない、Wi-Fiの接続を行い、それらの結果をシリアルモニタに表示します。Wi-Fiに接続し割り振られたIPアドレスもシリアルモニタに表示されます。そのIPアドレスでクライアントからブラウザでアクセスします。
その後、HandleRoot、drawGraphなどの処理をサーバで対応できるようにして、サーバをスタートします。
●関数loop()
プログラムの本体の部分で、実行中はこの処理が無限に繰り返されることになります。実際はクライアントからの問い合わせに従いsetup()の処理で設定された応答を返します。問い合わせがない場合は、ひたすら問い合わせを待ちます。
●関数drawGraph()
クライアントからの問い合わせに対して、ブラウザにグラフ表示の処理を行うために記述してある関数です。SVG(Scalable Vector Graphics)と呼ばれる方法でグラフが記述されています。グラフの線を引くために使用しているlineなどはHTMLの参考書には記述はありません。次のページを参考に確認しました。
SVG 1.1 仕様 (第2版) 日本語訳 https://triple-underscore.github.io/SVG11/index.html#minitoc |
●今回のサンプル・プログラム
// Copyright (c) 2015, Majenko Technologies
#include <WiFi.h> // 必要なライブラリを利用できるようにヘッダ・ファイルを読み込む
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
// Wi-Fiに接続するためのSSIDとパスワ-ドを定義する
const char *ssid = "Buffalo-G-2C6E";
const char *password = "xxxxxxx";
// Webサーバのオブジェクトをserver()として設定する
WebServer server(80);
// LEDを接続するデジタルI/Oの番号をledに設定する
const int led = 13;
// クライアントからアクセスされたときの応答を設定するhandleRoot()関数を
// ここで定義している
void handleRoot(){
digitalWrite(led, 1); //LEDの接続されているポートを1(HIGH)にする
char temp[400]; // 応答を格納する文字列を設定
int sec = millis() / 1000; // 経過時間を秒に変換しsecに設定
int min = sec / 60; // 経過時間を分に変換しminに設定
int hr = min / 60; // 経過時間を時間に変換しhrに設定
// このsnprintf(バッファ、最大値、書式、出力リスト)関数は文字列のバッファtempに最大400文字、”<html>から<html>“に示す書式や見出しなどと見出しに、次の出力リストを組み合わせてバッファにセットする
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\ // Webの表示を5秒ごとに更新指定
<title>ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\ // ここまではヘッダ部でWebには表示されない
<body>\
<h1>Hello from ESP32!</h1>\ // ページの見出し
<p>Uptime: %02d:%02d:%02d</p>\ // 経過時間の表示書式を設定
<img src=\"/test.svg\" />\ // グラフのイメージを表示設定
</body>\
</html>",
hr, min % 60, sec % 60 // 出力の時分秒の設定
);
server.send(200, "text/html", temp); // okのコードとhtmlの書式でメッセージを送信
digitalWrite(led, 0); // LED出力を0に設定
}
void handleNotFound() { // この関数はokじゃないときの処理
digitalWrite(led, 1);
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);
digitalWrite(led, 0);
}
// 初期設定の処理、プログラムの開始時に一度だけ処理
void setup(void) {
pinMode(led, OUTPUT); // LEDの接続ポートを出力に設定
digitalWrite(led, 0); // LEDの接続ポートの出力を0
Serial.begin(115200); // シリアルポートを115200bpsに設定
WiFi.mode(WIFI_STA); // ネットワークに存在するルータに接続するWi-Fi stationの設定
WiFi.begin(ssid, password); // Wi-Fiに接続
Serial.println("");
// Wi-Fiの接続を待つ
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
} // Wi-Fiと接続されるとこのループを抜ける
Serial.println("");
Serial.print("Connected to "); // モニタにWi-Fiとの接続を表示
Serial.println(ssid);
Serial.print("IP address: "); // DHCPサーバからふられたIPアドレスをモニタに表示
Serial.println(WiFi.localIP()); // 開発ボードのIPアドレスを表示
// mDNSはmulticast DNSのことで、DNSサーバを用いることなく
// マルチキャスト転送でネットワーク内のデバイスに問い合わせ行い
// 該当のデバイスからもマルチキャスト転送で応答を得て名前解決を行う手法
// hostnameがesp32であればesp32.localでアクセスできる
if (mDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/test.svg", drawGraph);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop(void) { // 繰り返し行う処理
server.handleClient(); // クライアントからの要求に応じて処理を行う
}
void drawGraph() { // グラフ表示の設定
String out = "";
char temp[100];
// svg xmlnsに続いて表示されているのはアドレスではなく名前空間を示している。
// グラフの表示領域を決めfill=rgb(250, 230, 210)で塗りつぶしの色をきめている。
// y軸の値はランダム関数で得られた値を130で除算した余りをセットする。
// yからy2のラインを引く。新しく得られたy2の値はyにセットし、次に進む。
// x軸は10から10刻みで380まで描画する
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
// <g から /g> の間で lineで直線を引いて
out += "<g stroke=\"black\">\n";
int y = rand() % 130; // グラフのyの初期値
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130 // 次のyの値算出;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
// %dは10進数表示の書式
out += temp;
y = y2;
}
out += "</g>\n</svg>\n"; // 文字列outにグラフの書式、データがセット
server.send(200, "image/svg+xml", out);
}
●グラフ部分の格納アドレスを確認する
表示されたグラフ部分をマウスの右ボタンでクリックします。
新しいタブで画像を選択すると画像の部分が新しいタブで開きます。
アドレスが192.168.1.66/test.svgと表示されています。IPアドレスはサーバのアドレスで続く/ test.svgは、
server.on("/test.svg", drawGraph); |
の処理で、drawGraph関数がserver.onの処理で設定された場所が表示されました。
●バックグラウンドの色を変えてみる
配色のRの部分を250から50に変更し赤の部分を減少してみました。
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(50, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n"; |
次に示すように緑色に変わりました。
(2019/1/31 V1.0)
<神崎康宏>
(※)mDNSの機能を利用してPCからhttp://esp32.local/でWebサーバにアクセスできます。ただし、ping esp32.local を実行すると100msぐらいとレスポンスが遅いです。chromeブラウザでは検索結果画面になって、上記のWebページは見れません。EdgeブラウザではURLにhttp://esp32.local/を入れると、上記のページを表示します。
mDNSでは、電源を入れるたびにDHCPによって振られるIPアドレスが変更されても、同じ「名前.local」でアクセスできるのが特徴です。もともとAppleがBonjourという呼び方で実装し、Linuxではavahiが相当します。Windows 10になりmDNSが標準実装され、現在は、OSが異なってもどのサービスが動いていても、ローカル・ネットワーク上で「名前.local」が利用できます。