この記事ではESP32をIoTデバイスとして使用し、家の電気のボタンや電子レンジのボタンなどの、あらゆる物理ボタンを押すことのできるスイッチボットを作成する方法について解説します。
市販されているスイッチボットは安いやつでも4000円くらいしますが、ESP32で自作すれば1500円程で作れます!
目次
ESP32-CAMについて
ESP32-CAM は Arduino と同じように Arduino IDE で開発を行うことができる開発ボードです。
wi-fi, BLE(Bluetooth low energy), カメラが搭載されています。
今回作成したもの
今回作成したものは↓こちらです。

ESP32のローカルIPアドレスに特定のURLパラメーターを付けてアクセスする(GETリクエストを行う)ことでサーボモーターを回し、以下のような物理スイッチをON、OFFします。

例えば、スマホから以下のようなURLにアクセスするとモータが回り、スイッチが押されます。
http://192.168.1.111/?angle=50&delay=500&このURLの?以降の文字がURLパラメーターと呼ばれるものです。
このURLパラメーターを変更することで、モーターを左右に回したり、LEDを付けたりなどの事前に設定した動作を行わせることができます。
また、URLパラメーターを付けていないローカルIPアドレス
http://192.168.1.111
にアクセスすると、以下のような画面が表示されます。このボタンを押すことでもESP32を制御できます。

さらにIFTTTと組み合わせることで、AlexaやGoogle Homeを使用して、声で操作することもできます。
必要なもの
- ESP32-CAM 
 (今回のメインである格安マイコン)

- USBシリアルコンバータ
 ( ESP32-CAM に書き込みを行うための機器)

- パソコン
手順
- ESP32-CAMのセットアップ
- コードの書き込み
- スイッチボット本体の作成
①ESP32-CAMのセットアップ
ESP32-CAMのセットアップについては以下の記事で紹介しています。
上記の記事を参考に、Arduino IDE でコードを書き込める状態まで行ってください。
②コードの書き込み
Arduino IDE のスケッチを開き、以下のコードを書き込んでください。
//アクセスするときはURLパラメーターの最後に&をつけること!
#include <WiFi.h>
// 使用するWi-Fiとそのパスワードに書き換えてください
const char* ssid     = "abc-2g";
const char* password = "pass";
// ポート80番を使用
WiFiServer server(80);
// HTTPリクエストを格納する変数
String header;
// 値の設定に使用する変数
String valueString = String(5);
String delay_valueString = String(500);
int pos1 = 0;
int pos2 = 0;
//pin
const int servo_pin = 16;
//サーボモーターの回転角
const int servo_left   = 26;
const int servo_center = 75;
const int servo_right  = 123;
void setup() {
  pinMode(4, OUTPUT);
  
  //ledc setting
  ledcSetup(0, 50, 10);  // 0ch 50 Hz 10bit resolution
  ledcAttachPin(servo_pin, 0); // 15pin, 0ch
  //シリアル通信開始
  Serial.begin(115200);
  //起動時にフラッシュライトを点滅させる
  for(int i=0; i<5; i=i+1) {
    digitalWrite(4, HIGH);
    delay(50);
    digitalWrite(4, LOW);
    delay(50);
  }
  
  // Wi-Fiに接続
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // ローカルIPを表示(このIPにスマホなどからアクセスします)
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}
void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients
  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" http-equiv=\"content-type\" charset=\"utf-8\"><title>ESP32 servo controller</title>");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>.block{display: inline-block; vertical-align: middle; height: 50px; font-size: 0;}");
            client.println(".btn-square-shadow {display: inline-block; width: 300px; height: 50px; text-decoration: none; background: #668ad8; color: #FFF; border-bottom: solid 4px #627295; border-radius: 3px; font-size: 30px;}");
            client.println(".btn-square-shadow:active {-webkit-transform: translateY(4px); transform: translateY(4px);  box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.2);  border-bottom: none;}");
            client.println("</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
            
            // Web Page
            client.println("</head><body style='text-align: center;'><h1>ESP32 サーボモーター コントロールパネル</h1>");
            //servo slide bar
            client.println("<div class='left block'> <a href='http://192.168.2.111/?angle=50&delay=500&'><button id='entrance_button' class='btn-square-shadow' type='button'>玄関の電気</button></a></div>");          
            client.println("<div class='right block'><a href='http://192.168.2.111/?angle=100&delay=500&'><button id='room_button' class='btn-square-shadow' type='button'>部屋の電気</button></a></div>");
            client.println("</body></html>");     
            //HTTPリクエストの処理部分
            delay_valueString = "500";
            pos1 = header.indexOf('=',18);
            pos2 = header.indexOf('&',18);
            if(pos1!=-1 && pos2!=-1) {
              delay_valueString = header.substring(pos1+1, pos2);
              Serial.print("pos:");
              Serial.println(pos1);
              Serial.println(pos2);
            } else {
              Serial.print("defaultサーボ待機時間:");
              Serial.println(delay_valueString.toInt());
            }
            Serial.print("サーボ待機時間:");
            Serial.println(delay_valueString.toInt());
            
            pos1 = header.indexOf('=');
            pos2 = header.indexOf('&');
            valueString = header.substring(pos1+1, pos2);
            Serial.print("URLパラメーター:");
            Serial.println(valueString);
            if(valueString.toInt()>=26 && valueString.toInt()<=123) {
             
              //サーボモーターを回転
              digitalWrite(4, HIGH);
              delay(50);
              digitalWrite(4, LOW);
              
              ledcWrite(0, servo_center);
              delay(delay_valueString.toInt());
              ledcWrite(0, valueString.toInt());
              delay(delay_valueString.toInt());
              ledcWrite(0, servo_center);
              // バグ防止
              delay(delay_valueString.toInt());
              ledcWrite(0, 0);
              
              digitalWrite(4, HIGH);
              delay(50);
              digitalWrite(4, LOW);
             
              
            } else {
              Serial.println("URLパラメーター エラー!");
            }
            
            // HTTPレスポンスの終了
            client.println();
            // Break out of the while loop
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }
    // Clear the header variable
    header = "";
    // 接続を切断
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}上記のコードのうち、以下の箇所をご自身の環境に合わせて変更してください。
| 行 | 変更箇所 | 補足 | 
| 6 ~ 7 | ssid = “abc-2g”; password = “pass”; | 家のWi-FiのSSIDとパスワードに変更 | 
| 102 ~ 103 | http://192.168.2.111 | ESP32に割り振られたIPアドレスに変更 | 
ESP32に割り振られたIPアドレスが分からない方は、一度6~7行目の変更箇所を変更した状態で書き込みをして起動し、Arduino IDEのシリアルモニターを見てみてください。(通信速度は115200bpsに設定してください。)

上記の画像のようにESP32に割り振られたIPアドレスが表示されるので、102~103行目のIPアドレスをこの値に書き変えてもう一度書き込んでください。
③スイッチボット本体の作成
次にESP32とサーボモーターを接続します。
以下のように接続してください。
| ESP32 ピン | 接続先 | 
| 5V(左上から1番目) | 5V電源 | 
| GND(左上から2番目) | 5V電源のグランド | 
| 3V3(右上から1番目) | サーボモーターの電源 | 
| IO16(右上から2番目) | サーボモーターの信号線 | 
| GND(右上から4番目) | サーボモーターのグランド | 
USBシリアルコンバータを5V電源として使用することもできますが、20㎝ほどの長さしかないため、必要な方は以下のような電源用のケーブルを使用してください。
使い方
ESP32を起動するとフラッシュが5回点滅し、1秒ほどで設定したwi-fiに接続されます。
その状態で、以下のようなURLにアクセスするとモーターが動きます。
http://ローカルIPアドレス/?angle=50&delay=500&angleにはモーターの回転角を指定します。モーターは一度中心位置に動いた後、angleで指定した角度動き、その後また中心位置にモーターが動きます。
※angleで指定できる値はサーボモーターが動ける範囲(23~123)までです。
delayにはモーターが動く間隔を指定します。例えば500とするとモーターが中心位置に動いた後0.5秒待機し、指定した角度にモーターが再び動き、また0.5秒待機した後中心位置に戻ります。
応用
IFTTTやBebotteと連携することでAlexaなどを使用して、声でスイッチを押させることもできるようになります。
連携方法についてはまた次回ご紹介します。

 
											 
											 
											 
											 
											 
											 
											 
											 
											









初心者すぎてほぼないも分かっていないのですが、最終的にON・OFFはどこで操作すればいいのでしょうか
スマホなどでESP32-CAMのIPアドレスにアクセスすればコントロールパネルが表示されるので、そこで操作できます。
また、
http://ローカルIPアドレス/?angle=50&delay=500&というように角度やモーターの待機時間を指定してURLにアクセスすることで、直接操作することもできます。
もう少し具体的に聞きたいことがあるのですがTwitterかインスタで質問できませんか?
はい、Twitterでも大丈夫です!
ネットワークに疎いもので…申し訳ありませんが質問させていただきます
もし外部から操作したい場合はどうすればいいのでしょうか
接続するURLを自分のグローバルIPにするだけではダメですよね…?
コメントありがとうございます。
外部のネットワークから接続するには、ルーターの管理画面にアクセスして、ポート開放(ポートフォワーディング)の設定をする必要があります。
設定の方法はルーターごとに違うので一概に説明することはできないので抽象的な説明となってしまいますが、
ルーターの説明書を見てポート開放またはポートフォワーディングと書かれた設定項目を探し、手順に沿ってESP32のローカルIPアドレスの80番ポートを指定すればできます。
設定したら
{グローバルIPアドレス}:80
という様にアクセスすれば外出先からアクセスできるようにできます。
ただ、集合住宅の無料の回線を使用している場合などは、回線が他の部屋の人の回線と共通であることが多いので、ポート開放の設定をしてもできない可能性が高いです。
自分で回線を契約しているのであれば問題ないです。
こちらの記事を元に組み立て・動作させるところまではできたのですが、スイッチに貼り付けるところがうまくいきません。
どのように固定しているか教えていただけますか?
よろしくお願いします。
コメントありがとうございます!
私は強力両面テープを使いました。
ホームセンターとかで500~1000円くらいで売ってますよ。
お返事ありがとうございます。
強力両面テープが家の物置に眠っていたので、それを使ってみようと思います!
すげー!スイッチボット高いから互換品(クライアントの方)無いかなと探してて通りすがったけど値段云々より内容に見入った。
コードとURLアクセスで制御する方がロマンあるわー。