ホームへ

IoT試作の例 その2・照明スイッチ遠隔操作

設置経緯

  1. とあるパソコン教室(設置場所はページの最後)で、教室内には照明のスイッチが無く、教室の外、数十メートル先に行かなくては、照明のオンオフができない。
  2. 建物は賃貸で、壁や天井内部の配線工事が面倒。
  3. 教室でプロジェクターを使うので、頻繁に照明のオンオフを行う必要がある。
  4. 本装置を設置する前は、誰かが廊下を猛ダッシュして、スイッチの操作をしにいっていた
  5. 装置を設置してからは、スマホやパソコンの画面からブラウザを通してスイッチの操作を可能にした

システム概要

装置側

  1. 装置本体は、WiFi子機に設定し、建物内のWifi親機と通信する
  2. 電源投入直後は初期設定の値をレンタルサーバー内のJSON形式のテキストファイルを所得し解析する
  3. 装置はレンタルサーバー内のオンオフ用のテキストファイルを定期的に取得する
  4. 取得した値をもとに、サーボモーターを作動させ、照明のスイッチ操作する
  5. 装置全体

ブラウザ側の操作画面

  1. 画面をクリックまたはタップをすると、照明が消灯する画面

    画面を切り替え、サーバーのテキストファイルにOFFの情報を書き込む

  2. 画面をクリックまたはタップをすると、照明が点灯する画面

    画面を切り替え、サーバーのテキストファイルにONの情報を書き込む

主要部品

  1. Wi-Fiモジュール ESP-WROOM-02 購入元:秋月電子 購入時価格@650円
  2. マイクロサーボ SG92R 購入元:秋月電子 購入時価格@500円
  3. 低損失三端子レギュレーター 3.3V1A TA48033S 購入元:秋月電子 購入時価格@100円
  4. LED 各色 購入元:秋月電子 購入時価格@18円

システム仕様

  1. WiFiモジュール ESP8266のプログラム作成はarduino IDEを使用
    ARDUINOホームページ

    arduinoソースコード クリックで展開
                
    #include 
    
    #include 
    #include 
    
    // サーボモーター用ライブラリの読み込み
    #include 
    
    // モータードライバーのIN1を接続するピン
    const int servoPin1 = 16;
    // モータードライバーのIN1を接続するピン
    const int servoPin2 = 14;
    
    // 黄LEDを接続しているピン
    const int yellowPin = 12;
    //赤LEDを接続しているピン
    const int redPin = 13;
    
    // サーボの角度
    
    int ANGLE_FLAT1;
    int ANGLE_FLAT2;
    int ANGLE_KADOU;
    
    // サーボモーターのインスタンス
    Servo servo1;
    Servo servo2;
    
    // 転送スピード
    #define SERIAL_SPEED  115200
    
    #define WLAN_SSID    wifi親機のSSID
    #define WLAN_PASS    wifi親機のパスワード
    
    // サーバーのホスト名
    #define HOST_NAME     "yose.co.jp"
    
    // サーボ設定初期値を取得するPHPファイルが格納されている場所のファイルパス
    #define PHP_PATH_INI      ファイルパス
    
    // E教室用 PHPファイルが格納されている場所のファイルパス
    #define PHP_PATH      ファイルパス
    // D教室用 PHPファイルが格納されている場所のファイルパス
    #define PHP_PATH_D      ファイルパス
    
    // ポート番号
    #define PORT_NUMBER   80
    
    int swSts_e;//スイッチの状態を保持する変数
    int swSts_d;//スイッチの状態を保持する変数
    
    void setup(void) {
      // デジタル3番ピンをサーボモーター用として設定
      servo1.attach(servoPin1);
      servo2.attach(servoPin2);
    
      //パソコンとのシリアル通信のポートを開ける
      Serial.begin(SERIAL_SPEED);
    
      // Wi-Fi設定
      setupWiFi();
    
      // これを入れないと、ちゃんと光らない)
      pinMode(yellowPin, OUTPUT);
      pinMode(redPin, OUTPUT);
      // 黄LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(yellowPin, LOW);
      // 赤LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(redPin, LOW);
    
      // サーボの初期値を取得と設定
      servoInii();
    
      //ホーンの位置を中立にする
      servo1.write(ANGLE_FLAT1);
      servo2.write(ANGLE_FLAT2);
    
    }
    
    // サーボの初期値を取得と設定関数
    void servoInii(void) {
      //ソケットの生成
      WiFiClient client;
    
      //ソケットの接続要求
      if (!client.connect(HOST_NAME, PORT_NUMBER)) {
        Serial.println("servoInii connection failed");
        Serial.println("servoInii wait 5 sec...");
        delay(5000);
        return;
      }
    
      Serial.println("servoInii TCP success");
      Serial.println("servoInii Requesting URL: ");
      Serial.println(PHP_PATH_INI);
    
      // サーバーへ送信するデータ
      char sendData[256] = "";
      sprintf(sendData, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", PHP_PATH_INI, HOST_NAME);
      Serial.print("servoInii sendData = ");
      Serial.println(sendData);
    
    
      // サーバーへデータを送信
      client.print(sendData);
      unsigned long timeout = millis();
      while (client.available() == 0) {
        if (millis() - timeout > 5000) {
          Serial.println(">>> servoInii Client Timeout !");
          client.stop();
          return;
        }
      }
      // サーバーから取得した文字列
      uint8_t buffer[1024] = {0};
    
      uint32_t len = client.read(buffer, sizeof(buffer));
    
      for (uint32_t i = 0; i < 1024; i++) {
        Serial.print((char)buffer[i]);
      }
    
      String json = parseJson(len, buffer);
      Serial.print("json = ");
      Serial.println(json);
    
      // ANGLE_FLAT1;
      //int ANGLE_FLAT2;
      //int ANGLE_KADOU;
      //    String resultString = parseBuf(len, buffer);
    
      // JSON用の固定バッファを確保。
      //この例では200文字分。
      StaticJsonBuffer<200> jsonBuffer;
    
      // char[]の形式でJSONの文字列を持つ。
      // char* 形式では文字列のコピーを保持してしまうためヨロシクナイ
      //  char json[] =
      //      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
    
      // JsonObjectはJSONBufferに格納、パースされた結果を参照保持する。
      // データを参照する場合はこのJsonObjectを見に行く。
      // メモリはスコープから出た際に開放される。
      JsonObject& root = jsonBuffer.parseObject(json);
    
      // パースが成功したか確認。できなきゃ終了
      if (!root.success()) {
        Serial.println("parseObject() failed");
        return;
      }
    
      // データの取り出しは暗黙の型変換
      ANGLE_FLAT1 = root["flat"];
      ANGLE_FLAT2 = root["flat"];
      ANGLE_KADOU = root["move"];
    
      // データの表示。.
      Serial.print("ANGLE_FLAT1 = ");
      Serial.println(ANGLE_FLAT1);
      Serial.print("ANGLE_FLAT2  = ");
      Serial.println(ANGLE_FLAT2);
      Serial.print("ANGLE_KADOU  = ");
      Serial.println(ANGLE_KADOU);
      Serial.println(" ");
    
      Serial.println("servoInii end");
      /*
        servoInii end servoInii end servoInii end servoInii end
      */
    }
    
    String parseJson(int len, uint8_t buffer[1024]) {
      // サーバーから取得した文字列
      String resultJson = "";
    
      // サーバーから取得した文字列を1つずつ格納
      for (uint32_t i = 0; i < len; i++) {
        resultJson += (char)buffer[i];
        Serial.print(i);
        Serial.print("文字目から ");
        Serial.println((char)buffer[i]);
      }
      Serial.print("1 resultJson = ");
      Serial.println(resultJson);
    
    
      // 文字列 { を検索
      int kaisi = resultJson.indexOf("{") ;
      Serial.print("kaisi = ");
      Serial.println(kaisi);
    
      // 文字列 } を検索
      int syuryo = resultJson.indexOf("}") + 1;
      Serial.print("syuryo = ");
      Serial.println(syuryo);
    
      // 検索にHitしたら
      if (kaisi > 0) {
        // 全体の文字列の長さを取得する
        int resultJsonLength = syuryo - kaisi ;
        Serial.print("resultJsonLength = ");
        Serial.println(resultJsonLength);
    
        // 検索がHitした位置+検索した文字列数から最後までを取得
        String resultJson2 = resultJson.substring(kaisi, syuryo);
        Serial.print("parse 1 resultJson2 = ");
        Serial.println(resultJson2);
    
        return resultJson2;
      }
      else {
        Serial.println("parseJson 失敗");
      }
    }
    /*
      Wi-Fiを設定します
    */
    void setupWiFi() {
      Serial.print("Connecting to ");
      Serial.println(WLAN_SSID);
    
      WiFi.begin(WLAN_SSID, WLAN_PASS);
    
     while (WiFi.status() != WL_CONNECTED) {
        delay(3000);
        Serial.print(".");
      }
      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
    
      delay(3000);
      
    }
    
    void loop(void) {
      d_room();
      e_room();
    
    }
    void d_room(void) {
      //ソケットの生成
      WiFiClient client;
    
      //ソケットの接続要求
      if (!client.connect(HOST_NAME, PORT_NUMBER)) {
        Serial.println("connection failed");
        Serial.println("wait 5 sec...");
        delay(5000);
        return;
      }
    
      Serial.println("TCP success");
      Serial.println("Requesting URL: ");
      Serial.println(PHP_PATH_D);
    
      // サーバーへ送信するデータ
      char sendData[256] = "";
      sprintf(sendData, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", PHP_PATH_D, HOST_NAME);
      Serial.print("sendData = ");
      Serial.println(sendData);
    
    
      // サーバーへデータを送信
      client.print(sendData);
      unsigned long timeout = millis();
      while (client.available() == 0) {
        if (millis() - timeout > 5000) {
          Serial.println(">>> Client Timeout !");
          client.stop();
          return;
        }
      }
      //  WiFi.send((const uint8_t*)sendData, strlen(sendData));
    
      // サーバーから取得した文字列
      uint8_t buffer[1024] = {0};
    
      uint32_t len = client.read(buffer, sizeof(buffer));
    
      //  if (client.available() > 0) {
      //   char c = client.read();
      //    char c = client.read();
      // 取得した文字列の長さ
      //    len = client.available();
    
      for (uint32_t i = 0; i < 1024; i++) {
        Serial.print((char)buffer[i]);
      }
      //  }
      //  char c = client.read();
      //Serial.print("c = ");
      //Serial.println(c);
    
      //Serial.print("client.available() = ");
      //Serial.println(client.available());
    
      // 1文字以上取得できていれば
      if (len > 0) {
        // サーバーから取得した文字列を解析
        String resultString = parseBuf(len, buffer);
    
        Serial.print("loop no naka resultString = ");
        Serial.println(resultString);
    
        // 取得した文字列が"ON"ならば
        //Serial.println("if resultString ");
    
        if (resultString == "ON") {
          Serial.println("SW ON");
          // サーボのホーンをONの方に回転させ戻る(点灯)
          if (swSts_d == 0) {
            swOn_d();
            swSts_d = 1;
          }
    
        }
        // 取得した文字列が"ON"以外ならば
        else {
          Serial.println("SW OFF");
          // サーボのホーンをOFFの方に回転させ戻る(消灯)
          if (swSts_d == 1) {
            swOff_d();
            swSts_d = 0;
          }
        }
      }
    
      // 処理を止める
      delay(2000);
      Serial.println("d_room end");
    }
    
    void e_room(void) {
      //ソケットの生成
      WiFiClient client;
    
      //ソケットの接続要求
      if (!client.connect(HOST_NAME, PORT_NUMBER)) {
        Serial.println("connection failed");
        Serial.println("wait 5 sec...");
        delay(5000);
        return;
      }
    
      Serial.println("TCP success");
      Serial.println("Requesting URL: ");
      Serial.println(PHP_PATH);
    
      // サーバーへ送信するデータ
      char sendData[256] = "";
      sprintf(sendData, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", PHP_PATH, HOST_NAME);
      Serial.print("sendData = ");
      Serial.println(sendData);
    
    
      // サーバーへデータを送信
      client.print(sendData);
      unsigned long timeout = millis();
      while (client.available() == 0) {
        if (millis() - timeout > 5000) {
          Serial.println(">>> Client Timeout !");
          client.stop();
          return;
        }
      }
      //  WiFi.send((const uint8_t*)sendData, strlen(sendData));
    
      // サーバーから取得した文字列
      uint8_t buffer[1024] = {0};
    
      uint32_t len = client.read(buffer, sizeof(buffer));
    
      //  if (client.available() > 0) {
      //   char c = client.read();
      //    char c = client.read();
      // 取得した文字列の長さ
      //    len = client.available();
    
      for (uint32_t i = 0; i < 1024; i++) {
        Serial.print((char)buffer[i]);
      }
      //  }
      //  char c = client.read();
      //Serial.print("c = ");
      //Serial.println(c);
    
      //Serial.print("client.available() = ");
      //Serial.println(client.available());
    
      // 1文字以上取得できていれば
      if (len > 0) {
        // サーバーから取得した文字列を解析
        String resultString = parseBuf(len, buffer);
    
        Serial.print("loop no naka resultString = ");
        Serial.println(resultString);
    
        // 取得した文字列が"ON"ならば
        //Serial.println("if resultString ");
    
        if (resultString == "ON") {
          Serial.println("SW ON");
          // サーボのホーンをONの方に回転させ戻る(点灯)
          if (swSts_e == 0) {
            swOn_e();
            swSts_e = 1;
          }
    
        }
        // 取得した文字列が"ON"以外ならば
        else {
          Serial.println("SW OFF");
          // サーボのホーンをOFFの方に回転させ戻る(消灯)
          if (swSts_e == 1) {
            swOff_e();
            swSts_e = 0;
          }
        }
      }
    
      // 処理を止める
      delay(2000);
      Serial.println("d_room end");
    }
    
    /*
      サーバーから取得した文字列を解析します
    */
    String parseBuf(int len, uint8_t buffer[1024]) {
      // サーバーから取得した文字列
      String resultCode = "";
    
      // サーバーから取得した文字列を1つずつ格納
      for (uint32_t i = 0; i < len; i++) {
        resultCode += (char)buffer[i];
      }
    
      // 文字列(led-status:)を検索
      int lastLF = resultCode.indexOf("sw-status:");
      Serial.print("lastLF = ");
      Serial.println(lastLF);
      // 検索にHitしたら
      if (lastLF > 0) {
        // 全体の文字列の長さを取得する
        int resultCodeLength = resultCode.length();
        Serial.print("resultCodeLength = ");
        Serial.println(resultCodeLength);
    
        // 検索がHitした位置+検索した文字列数から最後までを取得
        String resultString = resultCode.substring(lastLF + 10, resultCodeLength);
        //    String resultString = resultCode.substring(lastLF + 10, 12);
        Serial.print("parse 1 resultString = ");
        Serial.println(resultString);
    
        // 前後のスペースを取り除く
        resultString.trim();
        resultString.substring(0, 2);
        String resultString2 = resultString.substring(0, 2);
        Serial.print("parse 2 resultString2 = ");
        Serial.println(resultString2);
    
        return resultString2;
      }
      else {
        return "OFF";
      }
    }
    
    void swOff_d() {
      // 回転角度
      int angle;
    
      for (angle = ANGLE_FLAT2; angle <=  ANGLE_FLAT2 + ANGLE_KADOU; angle++) {
        // サーボモーターを回転
        servo2.write(angle);
        // 処理を10ミリ秒停止
        delay(10);
      }
      // 黄LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(yellowPin, HIGH);
      // 赤LEDを接続しているピンをLOW状態に変更(消す)
      digitalWrite(redPin, LOW);
    
      // 中立に戻す
      servo2.write(ANGLE_FLAT2);
      Serial.println("swOn flat");
      delay(2000);
    
    }
    
    void swOn_d() {
      // 回転角度
      int angle;
    
      // 中立からOFFまで1度ずつ回転
    
      for (angle = ANGLE_FLAT2;  ANGLE_FLAT2 - ANGLE_KADOU <= angle; angle--) {
        // サーボモーターを回転
        servo2.write(angle);
        // 処理を10ミリ秒停止
        //    Serial.print("angle = ");
        //    Serial.println(angle);
        delay(10);
      }
    
      // 赤LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(redPin, HIGH);
      // 黄LEDを接続しているピンをLOW状態に変更(消す)
      digitalWrite(yellowPin, LOW);
    
      // 中立に戻す
      servo2.write(ANGLE_FLAT2);
      delay(2000);
    
    }
    
    void swOn_e() {
      // 回転角度
      int angle;
      // 中立からONまで1度ずつ回転
      for (angle = ANGLE_FLAT1; angle <=  ANGLE_FLAT1 + ANGLE_KADOU; angle++) {
        // サーボモーターを回転
        servo1.write(angle);
        // 処理を10ミリ秒停止
        delay(10);
      }
    
      // 黄LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(yellowPin, HIGH);
      // 赤LEDを接続しているピンをLOW状態に変更(消す)
      digitalWrite(redPin, LOW);
    
      // 中立に戻す
      servo1.write(ANGLE_FLAT1);
      Serial.println("swOn flat");
      delay(2000);
    
    }
    
    void swOff_e() {
      // 回転角度
      int angle;
    
      // 中立からOFFまで1度ずつ回転
      for (angle = ANGLE_FLAT1;  ANGLE_FLAT1 - ANGLE_KADOU <= angle; angle--) {
        // サーボモーターを回転
        servo1.write(angle);
        // 処理を10ミリ秒停止
        //    Serial.print("angle = ");
        //    Serial.println(angle);
        delay(10);
      }
    
      // 赤LEDを接続しているピンをHIGH状態に変更(点灯)
      digitalWrite(redPin, HIGH);
      // 黄LEDを接続しているピンをLOW状態に変更(消す)
      digitalWrite(yellowPin, LOW);
    
      // 中立に戻す
      servo1.write(ANGLE_FLAT1);
      delay(2000);
    
    }
                
                

  2. サーボは強力な両面テープで貼っている
  3. サーボホーン(腕の部品)の可動範囲の角度は設定可能になっている

  • 2年ぐらい稼働していますが、サーボが2回壊れました、信頼性の高い部品を使用すると、より安心ですね。
  • 実際に試験運用をお願いしている教室は合同会社 七彩 教育訓練事業部 様です。