Hello Server(ボタンを追加)

今日はホームページのボタンを追加して、LEDのオンオフを行いたいと思うのですが、その前にサーバのアドレスをホスト名でアクセツ出来る様に変更します。消去した、”MDNS.begin()を追加します。


#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>

const char* ssid = "xxxxx";
const char* password = "yyyyy";

WebServer server(80);

const int led = 2;

void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "hello from esp8266!");
  digitalWrite(led, 0);
}

void setup(void) {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.print("Connected to ");
  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");
}

void loop(void) {
  server.handleClient();
}

ハイライトの部分が追加箇所です。MDNS.begin(“esp32″)でホスト名を”esp32″に設定しています。ブラウザのアドレスを指定する箇所にIPアドレスを入れる代わりに、”esp32.local” と入力してサーバにアクセス可能になります。スケッチをコンパイルして実行して下さい。

IPアドレスを入力した時と同じ結果になりました。

本題に入ります

作成するサーバはこんな感じ

  • ホスト名(esp32.local)でサーバにアクセスすると”ON”ボタンの左が表示される。この時点ではESP32上のLEDは消灯している。
  • ”ON”ボタンをクリックすると”OFF”ボタンの右が表示される。この時点でLEDは点灯する。
  • 引き続きボタンを押すと、”ON”、”OFF”の動作を繰り返す。

例えば、”ON”ボタンが表示されている時のHTLMは、


<!DOCTYPE html> <html>
<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>LED Control</title>
<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
.button {display: block;width: 60px;background-color: #3498db;border: none;color: white;padding: 13px 30px;
text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}
.button-on {background-color: #3498db;}
.button-on:active {background-color: #2980b9;}
.button-off {background-color: #34495e;}
.button-off:active {background-color: #2c3e50;}
</style>
</head>
<body>
<h2>Hello ESP32</h2>
<a class="button button-on" href="/led_on">ON</a>
</body>
</html>

ボタンが”OFF”の時は、15行目を変更するのみでその他は同じです。つまり、ボタンを押される度に15行目を書き換えて、LEDを”オン”、”オフ”する機能をスケッチに埋め込めば良いのです。こんな感じに埋め込みました。


#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>

const char* ssid = "XXXXXX";
const char* password = "YYYYYY";

WebServer server(80);

#define LED_pin 2
bool LED_status = LOW;

String index_HD = 
            "<!DOCTYPE html> <html>\n" 
            "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n" 
            "<title>LED Control</title>\n" 
            "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"
            ".button {display: block;width: 60px;background-color: #3498db;border: none;color: white;padding: 13px 30px;\n"
                "text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"
            ".button-on {background-color: #3498db;}\n"
            ".button-on:active {background-color: #2980b9;}\n"
            ".button-off {background-color: #34495e;}\n"
            ".button-off:active {background-color: #2c3e50;}\n"
            "</style>\n"
            "</head>\n"
            "<body>\n"
            "<h2>Hello ESP32</h2>\n";

void setup() {
  Serial.begin(115200);
  pinMode(LED_pin, OUTPUT);
  digitalWrite(LED_pin, 0);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.on("/led_on", handle_led_on);
  server.on("/led_off", handle_led_off);
  
  server.begin();
}

void loop() {
  server.handleClient();
}

void handleRoot() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(LED_status)); 
}

void handle_led_on() {
  LED_status = HIGH;
  digitalWrite(LED_pin, HIGH);
  server.send(200, "text/html", SendHTML(true)); 
}

void handle_led_off() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(false)); 
}

String SendHTML(uint8_t led_stat){
  String ptr;

  ptr = index_HD;
  if(led_stat)
    ptr +="<a class=\"button button-off\" href=\"/led_off\">OFF</a>\n";
  else
    ptr +="<a class=\"button button-on\" href=\"/led_on\">ON</a>\n";

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

WebServerクラスのインスタンス、”server(80)”の内部関数、”server.send(a,b,c)”ですが、これがクライアントにデータを送信しています。各引数ですが、

  • a: HTTPステータスコード。
    • クライアントの要求に対してサーバが問題無く対応出来た場合、”200”
    • 要求されたデータが無い場合、”404”
  • b: コンテンツタイプ。
    • 今回はテキストなので、”text/html”を指定。
  • c: コンテンツ。
    • クライアントに送るデータ(HTML)をここに入れて送る

今回の場合は、server.send(200, “text/html”,prt);とし、クライアントに送りたいHTMLの文字列を、prtに代入しているだけです。

  • 13から27行:String index_HD =
    • 今回のサーバはクライアントに送るHTMLは、ボタン表示の部分を除いて他は同じです。その共通部分をここに宣言しています。
  • 54から56行:ここで、処理関数を定義しています。
    • クライアントからのクリエに処理関数を対応させます。
    • 例えばクライアントから、”esp32.local/led_off”と要求が有った場合
      • クリエは”/led_off”。
      • この”led_off”に対応する関数を、server.on(“/led_off”, handle_led_off);と定義。
      • void handle_led_off()は、実際の操作を行う関数
    • esp32.local/ ー> server.on(“/”, handleRoot);
    • esp32.local/led_on ー> server.on(“/led_on”, handle_led_on);
    • esp32.local/led_off ー> server.on(“/led_off”, handle_led_off);
  • 66行:void handleRoot()
    • ルートをアクセスされた時の処理。LED_statusを”Low”にしLEDを消灯する。
    • SendHTML(LED_status)でクライアントに送るHTMLを作成。その後、send()でクライアントに送信。
  • 72行:void handle_led_on()
    • LEDをオンさせる関数。
    • ”OFF”ボタンが押されると実行する。
  • 78行:void handle_led_off()
    • LEDをオフさせる関数。
    • ”ON”ボタンが押されると実行する。
  • 84行:String SendHTML(uint8_t led_stat)
    • ここでクライアントに送信されるHTMLが作られる。
    • 89行から91行で状態に応じてボタン表示部を入れ替える。
    • 戻り値は、完成したHTML。

スケッチの概要はこんな感じ。LEDとボタンのオンオフについてもちょっと説明すると

  1. クライアントがルートにアクセス。
    1. handleRoot()が実行される。
      1. LED_statusがLowに、LEDがOFFになる。
      2. 引数がLow(=0)でSendHTML()が実行。
      3. SendHTML()のボタン判断箇所で、led_stat=0なので、
        1. “ON”ボタンが選ばれる。
        2. ボタンは押されると、”/led_on”をサーバにリクエスト
      4. この時点で、LEDは、オフ。 ボタンは、”ON”表示となる。
  2. ”ON”と表示されているボタンを押す。
    1. “/led_on”がリクエストされる。
    2. handle_led_on()を実行
      1. LED_statusがHighに、LEDがONになる。
      2. 引数がHigh(=1)でSendHTML()が実行。
      3. SendHTML()のボタン判断箇所で、led_stat=1なので、
        1. “OFF”ボタンが選ばれる。
        2. ボタンは押されると、”/led_off”をサーバにリクエスト
      4. この時点で、LEDは、オン。 ボタンは、”OFF”表示となる
  3. ”OFF”と表示されているボタンを押す。
    1. “/led_off”がリクエストされる。
    2. handle_led_off()を実行
      1. LED_statusがLowに、LEDがOFFになる。
      2. 引数がLow(=0)でSendHTML()が実行。
      3. SendHTML()のボタン判断箇所で、led_stat=0なので、
        1. “ON”ボタンが選ばれる。
        2. ボタンは押されると、”/led_on”をサーバにリクエスト
      4. この時点で、LEDは、オフ。 ボタンは、”ON”表示となる。

ボタン1つの設定になんだか大掛かりな気がしますが、でもこれでボタン1つでLEDがオンオフ出来る様になりました。もちろんパソコンでもスマホでもタブレットでも。