投稿者: sassy

  • WIFI 5V電源切替器を作る

    WIFI 5V電源切替器を作る

    雨センサーなど一家に1台で十分なのに探求心をくすぶられ何種類も作ってしまう自分であるが
    5V ACアダプターの設置数も多くなり、使ってないのにコンセントにさしてあり、無駄な電力を消費
    させている状況があります。

    切り替え器があれば解決するのですが、設置場所まで行っていちいち切り替えるのは面倒な作業です。
    そこでNODE-RED上からポンと切り替えられる遠隔電源切り替え機「WIFI 5V電源切替器」を作るこ
    とにしました。

    このような用途にはリレーを用いますが、一般のリレーは、コイルを励磁した状態で使用しないと
    いけません、最近は「ラッチリレー」という動作する瞬間だけ励磁すれば良い便利なパーツが入手
    出来ますのでこれを使用することにしました。
    ラッチリレーをセットするのにもある程度のパワーが必要ですので定番の2SC1815を使用してセット
    を行います。
    リセットの方は、3個のラッチリレー同時に行いますので2SC1815では荷が重すぎるのでリレーもし
    くはパワーMOS FETを使用します。
    操作する側で今どこに切り替わっているのかも知りたいのでESP-WROOM-02の内部EEPROMにステイ
    タスを書き込んでおき必要な時に読みだすようにしてあります。

    ラッチリレー G6BK-1114P-US-P6B DC5
    購入先 秋月電子 ¥590

    WIFI 5V電源切替器 回路図(リレー式)

    WIFI 5V電源切替器 回路図(MOS FET式)

    2SC1815を使用してSETを叩きます。
    RESETは3個のコイルを同時に励磁しますので安全の為、小型リレーまたはMOS FETで行います。
    気を付けることは、このラッチリレーの仕様書が少々分かり難く+ とか(-)とか同時に記載しているので
    どっちだよという感じで間違えやすいのです。
    よく読めば恐らくちゃんと書いてあるとは思いますが、大体の人はマニュアルをちゃんと読みません。
    私もそのての人間なので、最初逆極性に繫いでいました。
    そうするとSETとRESETが逆に働くようになりRESETを励磁するとSETにSETを励磁するとRESET動作
    するようになります。
    逆でもとりあえず動作するので余計に問題があります。
    待てよ、有名な日本のメーカーだし説明書が間違っている筈はないと思い、極性を逆にしたら正常
    動作するようになりました。
    逆極性で使用すると内蔵のダイオードが壊れることがあると説明書にありましたので危なかった。
    オムロンの仕様書は、素人には非常に分かりづらい物なので使い方が正しいのか良く分からない。

    スクリプト ESP-WROOM-02用

    NODE-RED上から切り替え操作を行うスクリプトを示します。
    ただスイッチを切り替えろだけの命令だと、「お前やっとけよ」で終わる指示になるので念のため
    「分りました」という返事を戻すようにしています。
    15秒以内に「分かりました」の返事が来ないと「Google HOME」から故障しているのメッセージ
    が音声で出ます。

    ちゃんとやるなら、本当に電源が切り替わっているのか確認の部分を付けるべきですが、そうな
    るとESP-WROOM-02のポートがあと3つ必要になります。
    今回の用途はそれほど重要なものでは無いので省きます。
    その代わりに、切り替えた時点のステイタスをESP-WROOM-02のEEPROMに書き込んでおき必要
    な時に操作側NODE-REDから読み出せるようにしてあります。

    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>
    #include <EEPROM.h>
    
    #define LED   16
    #define J1    4
    #define J2    5
    #define J3    14
    #define RESET 12
    
    #define EEPROM_SIZE 32
    #define SW_STATUS 0
    
    const char* ssid = "your_ssid";
    const char* password = "your_passwd";
    const char* mqtt_server = "192.168.1.200";
    
    WiFiClient espClient;
    PubSubClient client(espClient);
    
    // ===== リレー状態 =====
    
    enum RelayState {
      IDLE,
      RESETTING,
      ACTIVATING
    };
    
    RelayState relayState = IDLE;
    
    int targetRelay = 0;
    
    unsigned long stateStartTime = 0;
    
    const unsigned long pulseDuration = 200;
    const unsigned long resetDuration = 200;
    
    char status = '4';
    
    // ===== WiFi =====
    
    void setup_wifi() {
    
      WiFi.config(
        IPAddress(192,168,1,100),
        IPAddress(192,168,1,1),
        IPAddress(255,255,255,0)
      );
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
      }
    }
    
    // ===== MQTT reconnect =====
    
    void reconnect() {
      while (!client.connected()) {
        String clientId = "ESP8266-";
        clientId += String(random(0xffff), HEX);
        if (client.connect(clientId.c_str())) {
          client.subscribe("po_sw");
          char buf[2];
          buf[0] = status;
          buf[1] = 0;
          client.publish("po_sw/status", buf);
        } else {
          delay(1000);
        }
      }
    }
    
    // ===== MQTT callback =====
    
    void callback(char *topic, byte *payload, unsigned int length) {
      if (length == 0) return;
      char cmd = (char)payload[0];
      if (cmd >= '1' && cmd <= '3') {
        targetRelay = cmd - '0';
        relayState = RESETTING;
        stateStartTime = millis();
        status = cmd;
        EEPROM.put(SW_STATUS, status);
        EEPROM.commit();
      }
    
      if (cmd == '4') {
        relayState = RESETTING;
        targetRelay = 0;
        stateStartTime = millis();
        status = '4';
        EEPROM.put(SW_STATUS, status);
        EEPROM.commit();
      }
    
      if (cmd == '5') {
        char buf[2];
        buf[0] = status;
        buf[1] = 0;
        client.publish("po_sw/status", buf);
      }
    }
    
    // ===== リレー制御 =====
    
    void handleRelayState() {
      unsigned long now = millis();
      switch (relayState) {
        case RESETTING:
          digitalWrite(RESET, HIGH);
          if (now - stateStartTime >= resetDuration) {
            digitalWrite(RESET, LOW);
            if (targetRelay > 0) {
              relayState = ACTIVATING;
              stateStartTime = now;
            } else {
              relayState = IDLE;
              char buf[2];
              buf[0] = status;
              buf[1] = 0;
              client.publish("po_sw/status", buf);
            }
          }
          break;
        case ACTIVATING:
          if (targetRelay == 1) digitalWrite(J1, HIGH);
          if (targetRelay == 2) digitalWrite(J2, HIGH);
          if (targetRelay == 3) digitalWrite(J3, HIGH);
          if (now - stateStartTime >= pulseDuration) {
            digitalWrite(J1, LOW);
            digitalWrite(J2, LOW);
            digitalWrite(J3, LOW);
            relayState = IDLE;
            char buf[2];
            buf[0] = status;
            buf[1] = 0;
            client.publish("po_sw/status", buf);
          }
          break;
        case IDLE:
          break;
      }
    }
    
    // ===== SETUP =====
    
    void setup() {
    
      pinMode(LED, OUTPUT);
    
      pinMode(J1, OUTPUT);
      pinMode(J2, OUTPUT);
      pinMode(J3, OUTPUT);
      pinMode(RESET, OUTPUT);
    
      digitalWrite(J1, LOW);
      digitalWrite(J2, LOW);
      digitalWrite(J3, LOW);
      digitalWrite(RESET, LOW);
    
      EEPROM.begin(EEPROM_SIZE);
    
      EEPROM.get(SW_STATUS, status);
    
      if (status < '1' || status > '4') status = '4';
    
      Serial.begin(115200);
      setup_wifi();
      client.setServer(mqtt_server, 1883);
      client.setCallback(callback);
    }
    
    // ===== LOOP =====
    
    void loop() {
      if (!client.connected()) reconnect();
      client.loop();
      handleRelayState();
      digitalWrite(LED, !digitalRead(LED));
      delay(10);
    }

    NORE-RED

    [{"id":"51644164ecd25af4","type":"tab","label":"WIFI電源セレクター","disabled":false,"info":"","env":[]},{"id":"31f648988b36e84e","type":"inject","z":"51644164ecd25af4","name":"ON J1","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"str","x":150,"y":140,"wires":[["8ca6a03c362b31bc","0d18aedbecc26ce3"]]},{"id":"8ca6a03c362b31bc","type":"mqtt out","z":"51644164ecd25af4","name":"","topic":"po_sw","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"61e45e3515b67f80","x":430,"y":140,"wires":[]},{"id":"1bae666389ea6d41","type":"inject","z":"51644164ecd25af4","name":"ON J2","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"str","x":150,"y":180,"wires":[["8ca6a03c362b31bc","0d18aedbecc26ce3"]]},{"id":"7dc9b0be34b7e790","type":"inject","z":"51644164ecd25af4","name":"ON J3","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"str","x":150,"y":220,"wires":[["8ca6a03c362b31bc","0d18aedbecc26ce3"]]},{"id":"a36d53c5aaf309d3","type":"function","z":"51644164ecd25af4","name":"Acknowledge","func":"var status = msg.payload;\nif (status == \"1\" || status == \"2\" || status == \"3\" || status == \"4\" || status == \"5\"){\n    global.set(\"sw_ak\",\"1\");\n}else{\n    global.set(\"sw_ak\", \"0\")\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":770,"y":480,"wires":[[]]},{"id":"9e0bb162f702d9ea","type":"delay","z":"51644164ecd25af4","name":"","pauseType":"delay","timeout":"15","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":560,"y":220,"wires":[["3cb1cd6dafb0f1b5"]]},{"id":"3cb1cd6dafb0f1b5","type":"function","z":"51644164ecd25af4","name":"Check if it arrived","func":"var ak = global.get(\"sw_ak\");\nif (ak == \"0\"){\n    var data = \"遠隔スイッチが正常動作していません、チェックして下さい。\";\n    msg.payload = data;\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":220,"wires":[["020e5b7b2f401466"]]},{"id":"0d18aedbecc26ce3","type":"function","z":"51644164ecd25af4","name":"Initialization","func":"global.set(\"sw_ak\",\"0\");\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":220,"wires":[["9e0bb162f702d9ea"]]},{"id":"020e5b7b2f401466","type":"cast-to-client","z":"51644164ecd25af4","name":"","url":"","contentType":"","message":"","language":"ja","ip":"192.168.1.10","port":"","volume":"","x":770,"y":320,"wires":[[]]},{"id":"6eca4a185c27e861","type":"mqtt in","z":"51644164ecd25af4","name":"","topic":"po_sw/status","qos":"2","datatype":"auto-detect","broker":"61e45e3515b67f80","nl":false,"rap":true,"rh":0,"inputs":0,"x":590,"y":480,"wires":[["ceaf6e62bd19aa18","a36d53c5aaf309d3"]]},{"id":"ceaf6e62bd19aa18","type":"debug","z":"51644164ecd25af4","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":800,"y":560,"wires":[]},{"id":"dd91264aa131f3a0","type":"inject","z":"51644164ecd25af4","name":"スイッチ状態報告","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"po_sw","payload":"5","payloadType":"str","x":230,"y":580,"wires":[["81cf1fc6e78ddb1e"]]},{"id":"81cf1fc6e78ddb1e","type":"mqtt out","z":"51644164ecd25af4","name":"po_sw","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"61e45e3515b67f80","x":410,"y":580,"wires":[]},{"id":"61e45e3515b67f80","type":"mqtt-broker","name":"","broker":"192.168.1.200","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":4,"keepalive":60,"cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"200c1afd26fe557a","type":"global-config","env":[],"modules":{"node-red-contrib-cast":"0.2.17"}}]

    組み込み完成品