ESP32を使って無線でGPIOを操作する

# 前回のあらすじ

　前回はESP32の開発環境を作り、そこからLEDを光らせるだけの単純なプログラムを動作させた。
　今回は、これぞ**ESP32**と言えるようなことをやっていきたい。具体的には、ESP32をWiFiに接続させ、そこからサーバーとして機能させ、アクセスに応じてLEDを点灯/消灯を行うものを作っていく。

# 使用したもの
　今回も、前回に引き続きLED等を使用していく。
* PC(Windows11)
* ESP32-DevkitC
* ブレッドボード 1個
* ジャンパワイヤ 2本
* 赤色LED 1個
* USB-AとmicroUSB-B端子のケーブル
　また、今回はWiFiを使用するため、WiFiのアクセスポイントを作ることができるデバイスが必要である。(WiFiルータ、AndroidスマホのWiFiテザリングなど)
　今回はWindowsの「モバイルホットスポット」機能を使用した。

# 配線
　今回は2色LEDではなく単色のLEDを使用した。ESP32のGPIOは33番ポートを使用する。
![かいろ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/05c44d3d-ab47-7bf6-8263-7c4832a7e649.png)
![cercuit.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/2fcaaa6b-a2c9-a124-b7c8-ba3ec2e1f09d.jpeg)

# Part1 : ESP32をWiFiに接続させるだけのプログラム
　初めに、ESP32をWiFiにつなげるだけのプログラムで動作をチェックする。
　Arduinoには、WiFi.hという便利なクラスライブラリがあるので、それを使用する。

```ruby:espwifi01.ino
#include <WiFi.h>
const char* ssid = "HAREKAZE";               //  アクセスポイントのSSID
const char* password = "yokosuka467";        //  アクセスポイントのパスワード
                                             //  以下最初に行われる処理
void setup() {
  Serial.begin(115200);                      //  PCとのシリアル通信を開始(WiFiとは関係なし)
  WiFi.begin(ssid, password);                //  アクセスポイントに接続
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);                              //  接続完了するまでwhileで無限ループして待つ
  }
  Serial.println("Connected");               //  シリアルモニタに"Connected"を表示(WiFiとは関係なし)
}
                                             //  以下ループ処理(何もしない)
void loop(){
}
```

　このプログラムは、処理に入る前に定数としてアクセスポイントのSSIDとパスワードを定義しておき、setupの処理の時に、WiFi.beginと後ろのwhileループでWiFiに接続する。この時、シリアルモニタに「Connected」と表示するものである。
アクセスポイントを持ったWindowsPC側の設定画面からも、ESP32がWiFiに接続されていることがわかる。
![result01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/271ff2b7-4cd2-0cf9-3871-ada2efaaa10e.png)
![result02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/b2dd800f-ef70-93d8-1557-546da354c150.png)


# Part2 : ESP32をWiFiに接続させ、接続時にLEDを1回点滅させるプログラム
```ruby:espwifi02.ino
#include <WiFi.h>
const char* ssid = "HAREKAZE";               //  アクセスポイントのSSID
const char* password = "yokosuka467";        //  アクセスポイントのパスワード
const int led = 33;                          //  定数ledを33と定義

void setup() {                               //  以下最初に行われる処理
  pinMode(led, OUTPUT);                      //  ledの指すGPIO(33番)を出力ピンに設定
  Serial.begin(115200);                      //  PCとのシリアル通信を開始
  WiFi.begin(ssid, password);                //  アクセスポイントに接続
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);                              //  接続完了するまでwhileで無限ループして待つ
  }
  Serial.println("Connected");               //  シリアルモニタに"Connected"を表示
  digitalWrite(led, HIGH);                   //  ledをHIGHに設定
  delay(200);                                //  200ミリ秒待つ
  digitalWrite(led, LOW);                    //  ledをLOWに設定
}
                                             //  以下ループ処理(何もしない)
void loop() {
}
```
　これは先ほどのプログラムの延長で、GPIOに関する設定と、接続時にLEDが200ミリ秒だけ光るようにしたものである。シリアルモニタに"Connected"と表示されると同時に、LEDが光ることが確認できる。


# Part3 : ESP32をWebサーバとして動作させ、接続されたときにLEDを点滅させるプログラム
　今度はESP32をWebサーバとして動作させる。これも便利なWebServerというクラスライブラリがあるので、これを使用する。
```ruby:espwifi03.ino
#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "HAREKAZE";
const char* password = "yokosuka467";
const int led = 33;
WebServer server(80);                //  クラス"WebServer"のオブジェクトserverを宣言、この際、コンストラクタに引数80を渡す。

void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println("Connected");
  Serial.print("IP=");               //  シリアルモニタに"IP="を表示
  Serial.println(WiFi.localIP());    //  シリアルモニタに上行の"IP="に続けて自身のIPアドレスを表示
  server.on("/", lamp);              //  IPアドレス直下にアクセスがあった時に行う処理"lamp"を定義
  server.begin();                    //  Webサーバを開始
}

void loop() {
  server.handleClient();             //  setup処理内で定義した情報に従ってクライアントからのリクエストを処理
}

void lamp() {                        //  アクセスされたときの処理を定義
  server.send(200, "text/plain", "LED FLASH");  //  http通信が成功したことを送り、"LED FLASH"と表示
  digitalWrite(led,HIGH);
  delay(200);
  digitalWrite(led,LOW);
}

```

　これを実行すると、ESP32がWiFiに接続し、サーバとして機能する。シリアルモニタに表示されたIPアドレスをChromeなどのWebブラウザで開くと、"LED FLASH"という文字が書かれたページが開き、LEDが1回点滅する。
![result03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/93aff5c0-c9c9-3e49-b3c5-db785410c14e.png)
![result04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/e0964f28-c565-c948-40c0-50c5299a2f77.png)


### WebServer server(80); について
　「クラス"WebServer"のオブジェクトserverを宣言、この際、コンストラクタに引数80を渡す。」と記述したが、わからない人も多いと思われる。まず、**オブジェクト**というのは、変数(メンバ変数という)や関数(メンバ関数という)の集合体で、例えば今の例だと、オブジェクト"server"には、"on"や"begin"というメンバ関数が含まれている。また、**クラス**というのはオブジェクトの雛形であり、いわば変数の「型」のようなものである。つまり、"WebServer server(80);"というのは、**WebServer型の変数serverを定義している**と解釈できる。しかしここでserverの後ろに"(80)"というものがあり、意味が分からない。実はクラス(雛形)を元にオブジェクトを生成する際、**コンストラクタ**という特殊な関数が最初に呼び出され、生成されるオブジェクトの初期化を行うのだが、このときオブジェクト名の後ろの()内の数字が引数として使用されるのである。
　ちなみにこの80という数字は、HTTP通信をするにあたってのポート番号である。コンストラクタが初期化してくれることによって、私たちがオブジェクトのメンバ変数にわざわざ数字を代入する必要がなくなるのである。

### server.on("/",lamp); について
　この部分で、ESP32にアクセスされたときの場所と処理について記述している。"/"となっている部分はIPアドレス直下を意味する。

# Part4 : ESP32をWebサーバとして動作させ、Webページ内のボタンで点灯/消灯を操作できるプログラム
　HTML書いたことなかったので記述が怪しい部分があるかもしれない。
```ruby:espwifi04.ino
#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "HAREKAZE";
const char* password = "yokosuka467";
const int led = 33;
WebServer server(80);

void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println("Connected");
  Serial.print("IP=");
  Serial.println(WiFi.localIP());
  server.on("/", Index);                       //  IPアドレス直下にアクセスされたときの処理"Index"を定義
  server.on("/LED", LampSW);                   //  "IPアドレス/LED"にアクセスされたときの処理"LampSW"を定義
  server.onNotFound(NotFound);                 //  存在しない場所にアクセスされたときの処理"NotFound"を定義
  server.begin();
}

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

void Index() {
  server.send(200, "text/plain", "Hello,work");
}

void NotFound() {
  server.send(200, "text/plain", "Sorry, page was not found in this server.");
}

void LampSW() {
  String LampSwitch;                           //  String型の変数"LampSwitch"を定義
  LampSwitch = "<!DOCTYPE html>";              //  以下しばらく"LampSwitch"内にHTMLを記述していく
  LampSwitch += "<html lang=\"ja\">";
  LampSwitch += "<head>";
  LampSwitch += "<meta charset=\"utf - 8\" />";
  LampSwitch += "<title>LED_SW</title>";
  LampSwitch += "</head>";
  LampSwitch += "<body>";
  LampSwitch += "<p>LED</p>";
  LampSwitch += "<button type=\"button\" >";
  LampSwitch += "<a href=\"/LED\" >";
  LampSwitch += "Switch</a >";
  LampSwitch += "</body>";
  server.send(200, "text/html", LampSwitch);  //  HTMLのテキスト"LampSwitch"を送信
  digitalWrite(led, !digitalRead(led));       //  ledピンの出力を現在と反転
  Serial.println("Switch pushed!");
}
```

　これを実行し、WebブラウザでESP32のIPアドレスを入力すると"Hello,Work."と表示される。また、"IPアドレス/LED"にアクセスすると、LEDが点灯し、ページ内のボタンを押すと、LEDの表示が反転する。
大まかなプログラムはPart3とあまり変わっていないが、アクセスされたときの処理でHTMLを表示させ、ボタンなどの機能が使用できるようになっている。また、存在しないページにアクセスされたときの処理が追加されている。
![result05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2386155/633a2b3e-67ed-4193-82b0-2e7d5ef73bb2.png)


# まとめ
　今回はESP32でWiFiにアクセスし、サーバとしてWebページを表示させることができた。
　HTMLについて今まで書いてこなかったこともあり、日本語表示に失敗したりしたが、何とかそれっぽいものができてよかった。

# 参考文献
  藤本壱「ESP32&Arduino 電子工作 プログラミング入門」(技術評論社 2020年)
  [WebServerライブラリリファレンス](https://garretlab.web.fc2.com/arduino/esp32/reference/libraries/WebServer/)
  [Arduino日本語リファレンス](http://www.musashinodenpa.com/arduino/ref/)
  [WIFI環境でESP32を使ったサーバーを立ててみよう！](https://qiita.com/northVil/items/f5cfee1cfd38ece59bb5)