ESP-WROOM-02とMQTT + openHABでホームオートメーションに挑戦 (2)

とある日 工作をしている私に家人から質問がありました

家人:「一体その品物に、いくら掛かっているのですか?」私:「2,000~3,000円ぐらいかな」家人:「また、また、尾戯れを」私:「本当ですってば」

しかし嘘ではありません。一番高価な部品はesp-wroom-02モージュールで、これが約1000円。AC100VーDC5V電源とケースが各400円程。SSRキットが250円。+その他部品ですから大体3,000円以内で収まっていると思いますよ。

(しかしながら、原因不明で電源が壊れてしまい廃棄処分になった1号機の部品代や将来使うかもしれないと思い購入してしまったドップラーセンサーモジュール、スケッチ書き込みのための超小型USBシリアル変換モジュール、予備の部品、etcを合計すると、それなりの金額になっているのは間違いない事実ではあります。)

回路図

回路図

回路図は基本的に前回と同じですが次の変更を加えました。AC100V-DC5V電源にe-bayから仕入れた Ultra-compact power module HLK-PM01(出力5v 0.6A)を使い、電灯線制御にソリッドリレー。 さらに将来の自動化を目的にフォトトランジスタによる照度センサー。そしてローカルで電灯を制御するためにSW3を新設しました。

【デプロイした2号機】USBケーブルの先端にUSBシリアル変換モジュール。

【デプロイした2号機】USBケーブルの先端にUSBシリアル変換モジュール。スケッチを書き込むために仮にAC100Vへ配線して通電しています。 スケッチの書き込み時は100Vに通電せずUSB側から給電するようにします。(※追記 100vに通電して書き込みを行うとAC屋内配線の関係で最悪の場合PCを破損する可能性があります。※さらに追記 esp-wroom-02の電源および信号線は最大電圧3.3Vなのでご注意ください。写真のUSBシリアル変換アダプタは秋月電子通商の「FT234X 超小型USBシリアル変換モジュール」ですが、これは電源出力端子が+5Vでした。誤解を与えかねない写真を載せてしまい申し訳ありませんでした。)

技術もないのに省スペースを追求しすぎて1号機は失敗してしまいました。教訓を活かして2号機は秋月電子の 【ユニバーサル基板 Bタイプ(95x72mm)】とケースにホームセンターで仕入れた【PVKボックス(プールボックス) 未来工業】に集積度を上げず余裕を持って作ることにしました。

既存の電灯配線に制御回路を繋げなくてはいけませんが、これが結構大変です。制御回路を配置する場所が問題となります。通常、電灯は図の様に配線されているので設置候補として図中A,B,Cの場所があります。Bは一番良い場所なのですが、大抵見えない屋根裏とかに隠蔽されているので設置や保守が大変です。

Cからは何とか回路を引き出すことが出来ますが2芯ケーブルの線間電圧が100VではないのでESP-WROOM-02のための電源が別途必要になります。

残るはAの電灯直下ですが、こちらは雨風にさられることになりますので、そちらの対策が必要になります。

配線01

たまたま私の家の門柱灯はEE-Switch【暗くなると自動的にONになる】が露出配線で繋がっていて、この場所にEE-Switchと入れ替える形で制御回路を設置することができました。

(ちなみにこの配線工事は自宅であっても法律上、電気工事士の有資格者が行う必要があるので注意が必要です。)

家庭などのAC配線では線間電圧(100V)という概念のほかに対地電圧という概念が重要になります。アースされているのが接地側で感電の危険性が高いのが非接地側となります。

電気器具もなるべく感電しないようになっていて、白熱電球の人が触れやすいネジの部分の電極は接地側になるよう配線されています。同じ理由で差し込むときガイドになるコンセントの左側が右側より大きく、接地側に接続されています。

また、配線の色は原則として接地側が白又は緑で、非接地側には他の色を使う決まりになっています。(場合により例外もあります)
EE_switch
テスターや未来工業の「検電くん」を使い調査した結果、自宅のEE-スイッチに来ている3芯ケーブルも白が接地側、黒が非接地側、赤が電灯に繋がるように配線されていました。

ESP-WROOM-02のArduinoスケッチは前回のものを少し手直しいたしました。変更点は以下の通りです。

●アナログインターフェースPIN 【TOUT】に繋がっている照度センサのフォトトランジスタの値を analogread(A0) 命令で0Vから1Vを0~1024の値に変換して読み込みます。

手持ちの部品、回路では全く光が入らない状態でも0にならず、大体20ぐらいの値が読み込まれました。この値はMQTT Broker /home/gatepost/sensor/stateへパブリッシュしてOpenhabに伝えます。

【失敗してしまった1号機】

失敗してしまった1号機

失敗した1号機はこの値が不安定だったのでいろいろ弄っている内に、なぜか電源が壊れてしまったのでした。

●DHCPからスタティックIPアドレスに変更しました。DHCPで受け取ったアドレスにはリース期間が設定されているので、その期間が過ぎると一旦接続が切れてしまいます。

●手元で電灯を操作するために 【IO4】にSWを新設しました。当初、割り込みを使ってスケッチを記述しようとしたのですが、読み込んでいるライブラリと相性が悪いのかうまく動作しませんでした。

今回はOPENHARDWARE.CO.ZAさんのコードを使わせていただきました。ただ、このスケッチではMQTTサーバに繋がっていないと動作しません。改善の余地があります。

●ESP-WROOM-02がバックグラウンドでTCP等の処理をする時間を確保するためにloop()の最後に delay(10) を挿入しました。

●SSRをスイッチするためにトランジスタを1段入れましたのでON/OFFの極性が逆になりました。またloop()中 mills()がオーバーフローしたとき(起動してから5ヶ月後)挙動がわからないのでabs()で絶対値の差で比較しました。

/*
 Basic ESP8266 MQTT example

 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.

  2015/10/12 改変
  - subscribes to the topic "/home/gatepost/lamp/com", printing out any messages
   - If the characters of the topic "/home/gatepost/lamp/com" are "ON", switch ON the ESP Led, "OFF" switch off

  2015/11/08 改変
  - phototransistor (照度センサー)  analogRead(A0)
  - Static IP
  - a physical button on IO4 (turns the device on/off)
    --> https://openhardwarecoza.wordpress.com/2015/04/05/openhab-mqtt-arduino-and-esp8266-part-5-2-combined-switch-and-sensor-module-based-on-arduino/
*/ 

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.

const char* ssid = "your_SSID";
const char* password = "your_Password";
const char* mqtt_server = "192.168.xx.xx";  // mqttサーバのアドレスを設定します。
IPAddress ip(192,168,xx,xx);
IPAddress gateway(192,168,xx,xx);
IPAddress subnet(255,255,255,0);

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
int buttonPin = 4;
boolean buttonState = LOW; //stroage for current button state
boolean lastState = LOW;//storage for last button state
boolean OutputPinState = LOW;
int analog_val;
char illumination[5];

void setup() {
  pinMode(5, OUTPUT);     // Initialize the 5 pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  //set static ip part
  WiFi.config(ip,gateway,subnet);

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

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

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }

  // check whether ON or OFF
  if( payload[0] == 'O' && payload[1] == 'N' ) {
    digitalWrite( 5, HIGH);
    OutputPinState = HIGH;
    client.publish("/home/gatepost/lamp/state", "ON");
  }
  else if( payload[0] == 'O' && payload[1] == 'F' && payload[2] == 'F' ) {
    digitalWrite( 5, LOW );
    OutputPinState = LOW;
    client.publish("/home/gatepost/lamp/state", "OFF");
  }
  else {
    Serial.print( "Unknown command : " );
    // Serial.println( (char*)payload );
  }
  Serial.println();
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect

    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("/home/gatepost/lamp/com");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

 unsigned long now = millis();
  if (abs(now - lastMsg) > 5000) {
    lastMsg = now;
    analog_val = analogRead(A0);                 // read photoTr
    itoa(analog_val,illumination,10);
    client.publish("/home/gatepost/sensor/state",illumination);
    ++value;
    snprintf (msg, sizeof(msg), "hello world from Gatepost. ILLUMINATION=%u #%ld  ",analog_val, value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("outTopic", msg);

  }
  buttonState = digitalRead(buttonPin);
  if (buttonState == LOW && lastState == HIGH){   //if button has just been pressed
    Serial.println("pressed");
    delay(300);                                   //prevent from chattering

    //toggle the state of the LED
    if (OutputPinState == HIGH){
      digitalWrite(5, LOW);
      OutputPinState = LOW;
      client.publish("/home/gatepost/lamp/state", "OFF");

    } else {
      digitalWrite(5, HIGH);
      OutputPinState = HIGH;
       client.publish("/home/gatepost/lamp/state", "ON");
    }
  }
  lastState = buttonState;
  delay(10);
}

デプロイ後、スマートフォンから電灯のON/OFFが出来るようになりました。めでたしめでたしです。でもこれで終了ではありません。まだまだ続きます。

リモート操作する門柱灯です

リモート操作する門柱灯です