スイッチボットが作りたかった

21:01・2022/02/24 公開

21:01・2022/02/24 更新

前置き

部屋の明かりをIot(Internet of Things)化してみたかったので色々調べ回っていたがスイッチボット(商品名)が売り切れていた(値段も思ってたより高かった)ので自作してみることにしました。

結論から言うと設置する際に家の壁を魔改造する必要があったのでやめました。
大人しくリモコン付きのシーリングライトを買いましょう

結果として「wifi経由でスマホから動かせるサーボモータ」が完成しました。

部品の調達

電子部品といえばこちら
https://akizukidenshi.com/catalog/ – 秋月電子通商

購入したのは、

  • ESP32-DevKitC-32E ESP32-WROOM-32E開発ボード 4MB
  • マイクロサーボ9g SG-90
  • ジャンパーワイヤ(オス-メス) 15cm 10本入り

以上3点。

電子工作に造詣が深いわけではないので素直に開発ボードを買いました。

他に必要なのは、「USB microB(データ通信用)」です。充電用と見分けつかなくて困る。幸い家に一本だけありました。

配線

ボードとモータをつなぐだけです。
茶色がGND、中心の橙が5V、余ったのが制御線なので13につなぎます。

Arduinoで書き込み

このESP32というのはarduinoで書き込みできます。
Software | Arduino
win10なのでwindows Appをダウンロードしてインストール。

ボードの読み込み

【参考】ESP32 MCU の Arduino IDE 設定

上記サイトを参考にボードをarduinoに読み込みます。
USBケーブルが充電用だとボードが読み込まれないので注意してください。

「マイコンボードの動作確認」ができたら開発の準備は完了です。

サーボモータを動かしてみる

【参考】ESP32でのサーボモーターの使い方
aruduinoと同じ記法でservoを使えるのでこちらの記事を参考にしました。

WiFiで制御してみる

本当はBluetoothでやりたかったけどIOSだと開発が面倒っぽいのでWifiにしました。

【参考】ESP32 の Wi-Fi を使ってみる

「ファイル > スケッチ例 > WiFi > SimpleWiFiServer」からサンプルスケッチを起動します。
サンプルスケッチではLEDを制御してみよう、と書いてありますが今回使うのはサーボモータなので改変します。

概要:
固定したIPにアクセスしてリンクをクリックするとサーボモータが動く。
起動してから6時間後にスリープして、ENボタンで復帰する。
コードが最後に書き込まれてからの、復帰回数を記録する。

コード中の「起動回数のカウント」についてはこちらから拝借しました。
ESP32のディープスリープを調べる
ENボタンでスリープから復帰します。

/*
サンプルスケッチのコメントは省略
*/

#include <WiFi.h>
// サーボをインクルード
#include <Servo.h>
Servo myservo;

const char* ssid     = "****";
const char* password = "****";
//IPを固定化
const IPAddress ip(192,168,0,6);
const IPAddress subnet(255,255,255,0);

WiFiServer server(80);
bool pos = 0;//sarvoの角度
int timer = 0;
RTC_DATA_ATTR int bootCount = 0; // RTCスローメモリに変数を確保


void setup()
{
    Serial.begin(115200);
    // 初回起動だけはシリアル初期化待ち
    if( bootCount == 0 ){
      delay(1000);
    }
    // 起動回数カウントアップ
    bootCount++;
    Serial.printf("起動回数: %d ", bootCount);
      // 起動方法取得
    esp_sleep_wakeup_cause_t wakeup_reason;
    wakeup_reason = esp_sleep_get_wakeup_cause();
    switch(wakeup_reason){
      case ESP_SLEEP_WAKEUP_EXT0      : Serial.printf("外部割り込み(RTC_IO)で起動\n"); break;
      case ESP_SLEEP_WAKEUP_EXT1      : Serial.printf("外部割り込み(RTC_CNTL)で起動 IO=%llX\n", esp_sleep_get_ext1_wakeup_status()); break;
      case ESP_SLEEP_WAKEUP_TIMER     : Serial.printf("タイマー割り込みで起動\n"); break;
      case ESP_SLEEP_WAKEUP_TOUCHPAD  : Serial.printf("タッチ割り込みで起動 PAD=%d\n", esp_sleep_get_touchpad_wakeup_status()); break;
      case ESP_SLEEP_WAKEUP_ULP       : Serial.printf("ULPプログラムで起動\n"); break;
      case ESP_SLEEP_WAKEUP_GPIO      : Serial.printf("ライトスリープからGPIO割り込みで起動\n"); break;
      case ESP_SLEEP_WAKEUP_UART      : Serial.printf("ライトスリープからUART割り込みで起動\n"); break;
      default                         : Serial.printf("スリープ以外からの起動\n"); break;
    }
  
     myservo.attach(13);//servoの制御端子を指定
     myservo.write(pos);//0

    delay(10);

    // We start by connecting to a WiFi network

    if (!WiFi.config(ip,ip,subnet)) {
     Serial.println("Failed to configure!");  
    }
    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    
    server.begin();
}

int value = 0;

void loop(){
 WiFiClient client = server.available();   // listen for incoming clients

  if (client) {                             // if you get a client,
    Serial.println("New Client.");           // print a message out 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
        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();

            // the content of the HTTP response follows the header:
            client.print("Click <a href=\"/H\">here</a>ON<br>");
            client.print("Click <a href=\"/L\">here</a>OFF<br>");

            // The HTTP response ends with another blank line:
            client.println();
            // break out of the while loop:
            break;
          } else {    // if you got a newline, then clear currentLine:
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }

        // Check to see if the client request was "GET /H" or "GET /L":
        if (currentLine.endsWith("GET /H")) {
          myservo.write(180);//servoを動かす
          delay(1000);
          myservo.write(pos);//0に戻す
          // GET /H turns the LED on
        }
        if (currentLine.endsWith("GET /L")) {
          myservo.write(180);//servoを動かす
          delay(1000);
          myservo.write(pos);//0に戻す
          // GET /L turns the LED off
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println("Client Disconnected.");
  }
  if(millis() >= (6 * 3600 * 1000)) {
    esp_deep_sleep_start();//6時間後にスリープ
  }
}

こうして「wifi経由でスマホから動かせるサーボモータ」が完成しました!
電源はモバイルバッテリーでも起動できるのでポータブルで動かせます。