ながらくブログ更新を怠っていたが怠けたくて怠けていたわけではない。
今年になってパソコンの前に座ると猫の津田ちゃんが異常に甘えてきて何もできなくなるのだ。その理由についてはここでは深く触れないが・・・
そんな中でこの記事を書けたのは職場で時間があるからだ。タダ乗りさせてくれる職場とBlynkに感謝。
ということで「かまち灯など家庭内電灯壁スイッチのスマート化」について詳細を述べていきたい。
お断り
家庭の壁内部の電源をいじる場合には電気工事士の免許が要ります。免許がない場合には免許保持者に代わりに作業してもらうか確認してもらうかしましょう。
この記事を見てそのまま真似されて万一事故が起きても当ブログの管理者は一切の責任を負いません。ご了解いただける方のみ読み進めてください。
今回のゴール
1.声で各部屋のスイッチをON/OFFできるようにする
壁のスイッチに直接手を触れずにオンやオフできるようにしよう。
リビングなどのシーリングライトだったら今どきはリモコンで操作できるタイプが多数売られているし、リモコン操作できるシーリングライトならSwitchBotなどのスマート機器で操作できるが、元の壁スイッチがオフになっていたらそれはできなくなる。
それを回避するために今回は壁のスイッチそのものをオンオフしてしまってさらにアレクサやGoogleAssistantに声がけして操作できるようにしてしまおうというわけだ。
2.スケジューリングして例えば日の入り時刻に合わせて点灯とか
スマート化できてアレクサやGoogleAssistantを経由して操作できるようになると今度はアレクサやGoogleAssistantの基本機能でデバイスを時間のトリガーで操作できるようになる。
つまり玄関外の電灯を日の入り時刻10分前に点灯させて、日の出時刻の10分後に消灯させるとか。日の出や日の入りの時刻って毎日違うがアレクサやGoogleに任せてしまえば毎日違う時間に点灯消灯させることも簡単だ。
すごくスマートな仕掛けでワクワクしないですかい?
前提条件
今回の工作の前提となる要件がいくつかあるのでここで挫折する人がもしかしたら99%ぐらいいるかもしれないのが残念だが述べておかねばならない。
- Node-REDサーバーが家の中でラズベリーパイまたはWindows等で常時稼働している
- Blynkアカウントを取っていてプロジェクトを作れる(できれば自前サーバー立ててあれば完璧)
各スイッチ仕様
では我が家を例にした各場所のスイッチ仕様について説明しておく。一致すればほぼ真似るだけで同じことが実現できるかもしれない。
玄関の仕様
構成は以下のようになっている。
- 玄関外灯
- かまち灯
- 浴室ファン
- 浴室ファン昇降サーボ
もともとは玄関には外灯、かまち灯、廊下灯(3路スイッチ)の3つが一体になったホタルスイッチがついている。そこにかつてラズベリーパイとサーボでスイッチ操作をしたことがあったが、スマートとは言いながらちょっとイマイチ見栄えのしない仕掛けだったので今回はリレーを介して操作する。
リビングの仕様
リビングのスイッチは以下の構成だ。
- リビング灯
- キッチン灯
我が家は玄関から廊下を経由してメインルームに入ると右がリビングで左がキッチンとなっているので入口から入ってすぐのスイッチはこの2個で構成されている。
準備するもの
ハードウェア
玄関
玄関については以下のものを準備する。
- ESP32(ポート数多めのやつ)
Espressif純正 ESP-WROOM-32D開発ボード ESP32-DevKitC-32D すぐに試せるLED付き
¥2,420(2022/06/02 16:29時点の価格)
平均評価点:
>>楽天市場で探す
>>Yahoo!ショッピングで探す - 電源テーブルタップ(AC電源がすぐ近くにあれば不要)
- USB充電器&MicroUSBケーブル
- ブレッドボード
サンハヤト SAD-101 ニューブレッドボード
¥555(2022/06/02 16:30時点の価格)
平均評価点:
>>楽天市場で探す
>>Yahoo!ショッピングで探す - リレー
ANMBEST 2点 5Vリレーモジュールとオプトカプラーの高レベルまたは低レベルのトリガー拡張ボードRaspberry PiArduino用 (5V 2チャンネル)
¥849(2022/06/02 16:31時点の価格)
平均評価点:
>>楽天市場で探す
>>Yahoo!ショッピングで探すまたはソリッド・ステート・リレー(SSR)キット 25A(20A)タイプ: 組立キット(モジュール) 秋月電子通商-電子部品・ネット通販
注意点としてコイル式リレーはdigital.Write(ポート番号,0)で作動、SSRはdigital.Write(ポート番号,1)で作動という違いがある。 - ケース
壁の中に入れられなければ100均でもどこでも調達して体裁をある程度よくしておこう。 - ついでにDHT11
OSOYOO DHT11 デジタル 温度 湿度 センサー モジュール デジタル温湿度測定 アルドゥイーノやRaspberry Pi 2 3電子工作用 5個セット
¥1,379(2022/06/02 16:37時点の価格)
平均評価点:
>>楽天市場で探す
>>Yahoo!ショッピングで探す温度湿度検知機能をもたせたかったので自分はこれもつけた。基板についたものを買えばそのまま使えるが単体のものを買うと抵抗を噛ませる必要がある。
ESP8266でもできなくはないが、我が家の場合はここで浴室ファンのオンオフリレーおよび手動スイッチ、それとファン昇降のための360度回転サーボ、上死点検知と下死点検知のマイクロスイッチなどを接続したいのでGPIOのポート数の多いESP32のほうが使い勝手がよい。
リビング
リビングについては2個の電灯をオンオフするだけなので2ポートで、さらに物理スイッチも使えるようにしたいのでもう2ポートということで最低4ポート使えるボードがあればよい。
- ESP01Sとリレーのセット
先程のESP32と2チャンネルリレーがキュッと1つにまとまったようなもの。ポート数が限られているのでESP32ほどいろいろできないがホタルスイッチの後ろに入れることができる程度に小さいのでスイッチの制御だけならこれがベスト。
- ボード書き込みモジュール
上記のリレーとESP01Sが一体になったものにプログラムを書き込むときに必要になる治具。リレーからESP01Sを外してこちらに嵌めてUSBにて刺すと書き込むことができる。ただし後述するがちょっと加工が必要。
- AC→DC5Vアダプター
AC100V電源からDC5Vに変換してくれるアダプター。USB充電器の中身だけむき出しみたいなもの。ちなみにESP01Sというマイコンボードは3.3V駆動のようなのだがリレーが5V必要で3.3Vに変換したアダプターだとまともに動かない。5Vの電源を供給するとリレーモジュールがESP01Sには3.3Vを供給してくれるっぽい。
- ヒューズ&ヒューズボックス
AC電源から取るときは安全のために必ずつけよう。ちなみにAC線の黒いのと白いのとどっちに噛ませるかというと黒いほうが電気が来る方だから黒い線の側にヒューズを噛ませること。
電源は玄関に設置するESP32と違ってMicroUSBの電源ポートがないものなので近くのACコンセントの裏から電源を取り5V変換アダプターを通して電源供給する。
もし玄関の電灯も同じ構成、つまり2つの電灯制御だけでよければリビングと同じやり方で低コストで実現できる。
共通部品
- 単芯銅線
壁スイッチと接続するには直径1.6mmぐらいの単芯銅線が必要だ。撚り線では使えない - 0.5スケ程度の銅線
単芯の銅線を用意してもボードまたはジャンプワイヤーとつなげるには細めのリード線でつけないと使い勝手が悪い。したがって単芯銅線と細い銅線をハンダづけして使う - ジャンプワイヤー適量
この手の工作をするには適量を常に持つようにしよう
ソフトウェア
Arduino IDE
マイコンボードにプログラミングするにはフリーで使えるArduino IDEという開発環境を使う。
ウィンドウズでもマックでもLinuxでもそれぞれの版が用意されているので自分のマシンで開発できる。
ここではこれ以上触れないので必要であればインストールからプログラミング開始までの手順をググって探してほしい。
余談になるが自分はプログラムコードをGoogle Driveのフォルダに入れていて職場でも自宅でも同じプログラムコードを編集できるようにしている。
さらにChromebookにLinux環境をいれているので編集とコンパイルだけならそっちでもできる。とにかくクラウドに保存しておけば場所が変わってパソコンが変わっても作業の続きができるのは便利だ。
Blynkライブラリ
これだけあれば良いというわけではないが、Blynkライブラリにはお世話になってます。
ほかにもいれておかなければならないライブラリはあるがこれのおかげで好きなだけIoTの夢を膨らませることができるので感謝感激雨あられだ。
手順
プログラム書く
プログラムコードを以下に載せる。OTAもできるようにしてあるのでこれをボードに書き込む前に一回スケッチ例→ArduinoOTA→BasicOTAのコードを自分のWi-Fi環境に書き換えてアップロードしておくことをおすすめする。
const char* BLYNK_FIRMWARE_VERSION = "V0.0.2";
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include "ESP8266ESP32.h"
#include <time.h>
#define LOCALSERVER // ローカルサーバー使用可ならコメント解除
#define atMYHOME // 家か別の場所かでWi-Fi設定変わる
#define HOSTNAME "ESP32_Entrance" // OTA表示用ホスト名
#define DeviceName "ESP32玄関" // メッセージ表示用デバイス名
//#define PUSH_BUTTON // 押しボタンならコメント外す
#define DEVICES 4 // デバイス数
#define DEBOUNCE_TIME 500
#define RELAY_DELAY_MSEC 100 //リレー作動後の待機時間 ミリ秒
#define AUTH "kkpvdUyQDB38JhMjePZEBqnL9EjccxCg"
// Blynk AUTH TOKENと接続サーバー定義のマクロ
#if defined(LOCALSERVER) && defined(atMYHOME) // 家の中
#define AUTHDEFINE AUTH, IPAddress(192,168,0,182), 8080
#elif defined(LOCALSERVER) // 他のWi-Fi
#define AUTHDEFINE AUTH, IPAddress(39,111,197,94), 8080
#else
#define AUTHDEFINE AUTH
#endif
// define the GPIO connected with Relays and switches
#define RELAYPIN_1 D15 // 外灯リレー
#define RELAYPIN_2 D4 // かまち灯リレー
#define RELAYPIN_3 RX2 // 浴室ファンリレー
#define RELAYPIN_4 RX2 // dummy
#define SWITCHPIN1 D26 // 外灯スイッチ
#define SWITCHPIN2 D21 // かまちスイッチ
#define SWITCHPIN3 D18 // 浴室ファンスイッチ
#define SWITCHPIN4 D18 // dummy
#define UPPER_LIMIT_PIN D25 // ファン上死点スイッチ
#define LOWER_LIMIT_PIN D32 // ファン下死点スイッチ
#define DHTPIN D22 // 温度センサー定義
#define SERVOPIN D19 // サーボピン
// Blynk vertual PIN
#define VPIN_NOTICE V0 // スマホへ通知するか否か
#define VPIN_1 V30 // 玄関外灯
#define VPIN_2 V31 // かまち灯
#define VPIN_3 V32 // 浴室ファン
#define VPIN_6 V35 // ファン昇降
#define TEMP_PIN V36 // 温度
#define HUMI_PIN V37 // 湿度
#define VPIN_ALIVE V91 // 生存時刻報告と再起動
#define JST 3600*9 // 日本標準時間の定義
#define DHTTYPE DHT11 // DHT型の指定
DHT dht(DHTPIN, DHTTYPE); // DHTセンサーライブラリーを使うための設定
//Wi-Fi設定
char ssid[] = "TP-Link_B7F0";
char pass[] = "57754536";
BlynkTimer timer1;
int servoTurnLimit = 0; // サーボ作動割込時のカウンター
int timerID; // サーボ作動割込用タイマーID
// 各ポートやVPINなど配列で扱うとプログラムが減らせるかと思った
const int VPIN[] = {VPIN_1,VPIN_2,VPIN_3,VPIN_6};
bool toggleState[DEVICES]; // リレー状態
bool lastSwitchState[DEVICES]; // 直前のスイッチ状態
unsigned long lastSwitchChange[DEVICES]; // 直前のスイッチ変更時刻
const int relayPINs[] = {RELAYPIN_1,RELAYPIN_2,RELAYPIN_3,SERVOPIN};
const int switchPINs[] = {SWITCHPIN1,SWITCHPIN2,SWITCHPIN3,SWITCHPIN4};
const String mes[6][2] = {
{"外灯が消えました","外灯が点きました"},
{"かまち灯が消えました","かまち灯が点きました"},
{"浴室ファンが回りました","浴室ファンが止まりました"},
{"ファン下降しました","ファン上昇しました"},
{"通知をオフにしました","通知をオンにしました"},
{"再起動します","起動しました"}
};
void DisplayTime(char *); // 現在時刻を取得して"HH:MM:SS"で返すルーチン
int notice_flug = 0; // スマホへ通知するしないフラグ
// for servo control definition
#define FAN_UP_LOOP 100 // タイマー割込でサーボに上昇指示をする回数限度
// パワー不足で100回もやってる
#define FAN_DOWN_LOOP 50 // タイマー割込でサーボに下降指示をする回数限度
// 下降時には力をあまり必要としない
// ↓ここの数値は理解しきれてない
#define LEFT_TURN 500 // 91~180で 左回り 数字が大きいほど高速
#define RIGHT_TURN 2200 // 0~89で右回り 数字が小さいほど高速
// 2400右 500左
// 時刻をテキストで返す
void DisplayTime(char *strings)
{
time_t timet;
struct tm *tm;
timet=time(NULL);
tm = localtime(&timet);
// 毎日15時に再起動
if((tm->tm_hour == 15) && (tm->tm_min == 0)){
if((tm->tm_sec == 0) || (tm->tm_sec == 1) || (tm->tm_sec == 2)){
Blynk.notify(DeviceName + mes[5][0]);
ESP.restart();
}
}
sprintf(strings,"%02d:%02d:%02d",tm->tm_hour, tm->tm_min, tm->tm_sec);
}
// LEDを点滅させるルーチン
void led_blink(int OnMsec,int OffMsec,int n,int PORT)
{
for(int i=0;i<n;i++){ digitalWrite(PORT, LOW); delay(OnMsec); digitalWrite(PORT, HIGH); delay(OffMsec); } } // 気温湿度チェックして報告する割込 void checkTemprature() { float temperature = dht.readTemperature(); // 温度読み取り float humidity = dht.readHumidity(); // 湿度読み取り char str[13];char str2[20]; DisplayTime(str); //↓生存報告もここで行う ファームウェアVer.と現在時刻をサーバーに返す sprintf(str2, "%s %s", BLYNK_FIRMWARE_VERSION, str); Blynk.setProperty(VPIN_ALIVE,"offLabel", str2); // 読み取りに失敗しているかチェック // 失敗している場合はもう一度、温湿度の読み取りをする if (isnan(temperature) || isnan(humidity)) { Serial.println("Failed to read from DHT sensor!"); return; } // 気温35度超えてたら猫も危険かと思って警告入れてる if(temperature > 35){
String body = String("玄関の温度が高すぎています");
Blynk.notify(body);
Blynk.email("vyd00762@gmail.com", "Subject: 玄関温度上昇", body);
}
Blynk.virtualWrite(TEMP_PIN, temperature);
Blynk.virtualWrite(HUMI_PIN, humidity);
}
// 指定された番号のリレーを反転させる処理 relay=0オリジン
void relayOnOff(int relay)
{
bool toggleStateX = !toggleState[relay];
digitalWrite(relayPINs[relay], toggleStateX);
if(notice_flug){
Blynk.notify(DeviceName + mes[relay][toggleStateX]);
}
delay(50);
toggleState[relay] = toggleStateX;
delay(RELAY_DELAY_MSEC); // ここらへんのdelayは適当
}
// リレーポート(GPIO)初期化
void setupRelays()
{
Serial.println("setupRelays()...");
for(int i = 0;i<DEVICES;i++){
pinMode(relayPINs[i], OUTPUT);
}
}
// BLYNKつながったときに呼ばれる処理
// Blynk.configで繋がってすぐ呼ばれるわけではなさそう
BLYNK_CONNECTED()
{
for(int i=0;i<DEVICES;i++){
Blynk.syncVirtual(VPIN[i]);// Blynkアプリとデバイスを同期
delay(RELAY_DELAY_MSEC);
}
Blynk.setProperty(VPIN_6,"onLabel","ファン下げる");
Blynk.setProperty(VPIN_6,"offLabel","ファン上げる");
Blynk.virtualWrite(VPIN_6,0);
Blynk.notify(DeviceName + mes[5][1]); // 起動しました通知
}
// 物理スイッチのポート(GPIO)初期化
void setupFlipSwitches()
{
for(int i = 0;i<DEVICES;i++){
lastSwitchChange[i] = millis();
pinMode(switchPINs[i], INPUT_PULLUP);
delay(10);
}
pinMode(LOWER_LIMIT_PIN, INPUT_PULLUP);
pinMode(UPPER_LIMIT_PIN, INPUT_PULLUP);
}
// loop内から呼ばれるスイッチ検出処理
// チャタリング防止のために最低時間が経っていたらボタン処理入るようになってる
void with_internet()
{
for(int i=0;i<DEVICES;i++){ unsigned long actualMillis = millis(); // get actual millis unsigned long lastSwitchChangeX = lastSwitchChange[i]; // get the timestamp when flipSwitch was pressed last time (used to debounce / limit events) if (actualMillis - lastSwitchChangeX > DEBOUNCE_TIME){ // if time is > debounce time...
bool lastSwitchStateX = lastSwitchState[i]; // get the lastSwitchState
bool SwitchStateNow = digitalRead(switchPINs[i]); // read the current flipSwitch state
if (SwitchStateNow != lastSwitchStateX) { // if the Switch has changed...
#ifdef PUSH_BUTTON
if (!SwitchStateNow) { // if the tactile button is pressed
lastSwitchState[i] = !SwitchStateNow;//2022-03-28変更
#else
lastSwitchState[i] = SwitchStateNow;//2022-03-28変更
#endif
lastSwitchChange[i] = actualMillis; // update lastSwitchChange time
if(abs(lastSwitchChange[1]-lastSwitchChange[0] < RELAY_DELAY_MSEC+100)){
Blynk.notify(DeviceName + mes[5][0]);
ESP.restart();
}
delay(RELAY_DELAY_MSEC);
relayOnOff(i);
Blynk.virtualWrite(VPIN[i], toggleState[i]); // Update Button Widget
#ifdef PUSH_BUTTON
}
#endif
}
}
}
}
// 通知オンオフ切替のボタン処理
BLYNK_WRITE(VPIN_NOTICE)
{
notice_flug = param.asInt();
Blynk.notify(DeviceName + mes[4][notice_flug]);
}
// 外灯の処理
BLYNK_WRITE(VPIN_1)
{
toggleState[0] = param.asInt();
digitalWrite(relayPINs[0], toggleState[0]);
delay(RELAY_DELAY_MSEC);
if(notice_flug){
Blynk.notify(DeviceName + mes[0][toggleState[0]]);
}
led_blink(100,100,1,wifiLed);
}
// かまち灯の処理
BLYNK_WRITE(VPIN_2)
{
toggleState[1] = param.asInt();
digitalWrite(relayPINs[1], toggleState[1]);
if(notice_flug){
Blynk.notify(DeviceName + mes[1][toggleState[1]]);
}
delay(RELAY_DELAY_MSEC);
led_blink(100,100,2,wifiLed);
}
// 浴室ファンの処理
BLYNK_WRITE(VPIN_3)
{
toggleState[2] = param.asInt();
digitalWrite(relayPINs[2], toggleState[2]);
delay(RELAY_DELAY_MSEC);
if(notice_flug){
Blynk.notify(DeviceName + mes[2][toggleState[2]]);
}
led_blink(100,100,3,wifiLed);
}
// サーボ回転ルーチン
void my_pwm(int v) {
for (int i=0;i<40;i++) { digitalWrite(SERVOPIN, HIGH); delayMicroseconds(v); digitalWrite(SERVOPIN, LOW); delayMicroseconds(10000); delayMicroseconds(10000 - v); } delay(100); } // ファン上昇イベント発生時にタイマー割込で3秒毎に呼ばれるファン上昇の処理 void upTimerTask() { my_pwm(RIGHT_TURN); servoTurnLimit -=1; delay(100); } // ファン下降イベント発生時にタイマー割込で3秒毎に呼ばれるファン下降の処理 void downTimerTask() { my_pwm(LEFT_TURN); servoTurnLimit -=1; delay(100); } //ファン上昇または下降 BLYNK_WRITE(VPIN_6) { toggleState[3] = param.asInt(); if(timer1.isEnabled(timerID)) { timer1.deleteTimer(timerID); delay(10); } if(toggleState[3] == 1){//1:上昇 0:下降 servoTurnLimit = FAN_UP_LOOP; // 上昇タイマー割込イベントを発生させる timerID = timer1.setInterval(3000L, upTimerTask); Blynk.setProperty(VPIN_6,"onLabel","上昇中"); if(notice_flug){ Blynk.notify("ファン上昇させます"); } }else{ servoTurnLimit = FAN_DOWN_LOOP; // 下降タイマー割込イベントを発生させる timerID = timer1.setInterval(3000L, downTimerTask); Blynk.setProperty(VPIN_6,"offLabel","下降中"); if(notice_flug){ Blynk.notify("ファン下降させます"); } } } // blynkがつながっているかチェックするルーチン void checkBlynkStatus() // called every 3 seconds by SimpleTimer { bool isconnected = Blynk.connected(); if (isconnected == false) { #ifndef DEBUG digitalWrite(wifiLed, HIGH); //Turn off WiFi LED #endif } if (isconnected == true) { // ちゃんとつながっていたら気温湿度チェック処理を呼ぶ checkTemprature(); #ifndef DEBUG digitalWrite(wifiLed, LOW); //Turn on WiFi LED #endif } } // 生存確認通知ボタンタップで再起動(生存してないと結局このボタンは効かない) BLYNK_WRITE(VPIN_ALIVE) { int para = param.asInt(); if(para == 1){ ESP.restart(); } } void setup() { Serial.begin(115200); pinMode(wifiLed, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); pinMode(SERVOPIN, OUTPUT); dht.begin(); setupRelays(); setupFlipSwitches(); ////// Wi-Fi接続処理 ////// WiFi.mode(WIFI_STA); //WiFi.disconnect(); Serial.println(); Serial.println(); Serial.println("Startup"); WiFi.begin(ssid, pass); Serial.print("Connect to SSID => ");Serial.println(ssid);
Serial.print("password => ");Serial.println(pass);
int i = 0;
while (WiFi.status() != WL_CONNECTED){
Serial.print(".");Serial.print(i);i++;if(i>20)ESP.restart();
led_blink(400,100,1,wifiLed);
}
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname(HOSTNAME);
// No authentication by default
ArduinoOTA.setPassword(BLYNK_FIRMWARE_VERSION);
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
delay(1000);
configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
timer1.setInterval(3000L, checkBlynkStatus); // check if Blynk server is connected every 3 seconds
Blynk.config(AUTHDEFINE);
Serial.println("Succesfully Connected!!!");
led_blink(50,50,20,wifiLed);
//setupIndicatorLamp();
}
void loop()
{
ArduinoOTA.handle();
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("WiFi Not Connected");
}
else
{
Blynk.run();
}
timer1.run(); // Initiates SimpleTimer
//timer3.run();
with_internet();
if(digitalRead(UPPER_LIMIT_PIN) == 0){// 上限スイッチがオンなら
Serial.println("UPPER LIMITが0なのでサーボ緩めるはず");
if(timer1.isEnabled(timerID)) { // タイマーが有効なら
timer1.deleteTimer(timerID); // タイマーを解除する
delay(10);
}
my_pwm(LEFT_TURN); // ワイヤーを緩める
Blynk.setProperty(VPIN_6,"onLabel","ファン下げる");
}
if(digitalRead(LOWER_LIMIT_PIN) == 1){
Serial.println("LOWER LIMITが1なのでサーボ緩めるはず");
if(timer1.isEnabled(timerID)) {
timer1.deleteTimer(timerID);
delay(10);
}
my_pwm(RIGHT_TURN);
Blynk.setProperty(VPIN_6,"offLabel","ファン上げる");
}
if(servoTurnLimit <= 0){
if(timer1.isEnabled(timerID)) {
timer1.deleteTimer(timerID);
delay(10);
}
}
}
ESPボードとリレーと壁スイッチ線をつなぐ
ここらへんからは文字で説明するより図解または写真で見せて解説を少し追加する程度にする。
玄関灯スイッチ
▼玄関灯のスイッチの裏側は元はこんなふうになっている。
▼この写真はごちゃごちゃすぎていて意味ないな。
▼この写真も単なる自己満足。
ESP32と繋がっているのは以下のポート
GPIO番号 | 印刷表記 | 接続先 |
15 | D15 | 玄関外灯リレーの+線 |
4 | D4 | かまち灯リレーの+線 |
16 | RX2 | 浴室ファンリレーの+線 |
26 | D26 | 壁の玄関外灯スイッチのどちらか |
21 | D21 | 壁のかまち灯スイッチのどちらか |
18 | D18 | 浴室ファンスイッチのどちらか |
25 | D25 | ファン昇降上死点マイクロスイッチのどちらか |
32 | D32 | ファン昇降下死点マイクロスイッチのどちらか |
22 | D22 | DHT11のデータ線 |
19 | D19 | 浴室ファン昇降サーボのデータ線 |
GND | 上記の片側またはー(マイナス)線 |
リビングスイッチ
- スイッチ1(リビング)の片側につける
- スイッチ2(キッチン)の片側につける
- 2番リレーから出ていてキッチン灯の線と接続する
- 2番リレーから出ていてキッチン灯の線と接続する
- 1番リレーから出ていてリビング灯の線と接続する
- 1番リレーから出ていてリビング灯の線と接続する
- AC電源から電気を取りヒューズと5V変換アダプターが接続されている
- 2本GNDにはんだ付けしている(5V変換アダプターのOUTPUTー側と同じ)
▼裏側はこんなふうになっている。必要に応じてはんだ付け&グルーガンで補強をしている。
リビングのESP01Sと繋がっているのは以下のポート。実際にはESP01Sをリレー一体型モジュールに嵌めてしまうのでポートを意識する必要はほとんどない。
GPIO番号 | 印刷表記 | 接続先 |
0 | IO0 | リビング灯リレー制御 |
1 | TX | リビング灯物理スイッチ |
2 | IO2 | キッチン灯リレー制御 |
3 | RX | キッチン灯物理スイッチ |
参考サイト:How to Use ESP-01 ESP-01S Pins and Leds : 9 Steps – Instructables
プログラム内では上記のように扱ってコントロールするがリレーポートの結線はない。ボードに一体になっているから。
TXとRXは本来アナログIOなのだがデジタルピンとしても使えるらしいし、実際に使えてる。
ただそのTXとRXを利用したスイッチの結線は必要になってくる。写真のように裏側にハンダ付けしてリード線を伸ばすという方法をとった。
そしてESP01Sに書き込むにはこのボード書き込みモジュールを使うと簡単だ。写真の部分にコードをハンダ付けするなどしてUSBに刺すときショートさせて刺したら離してよい。ショートさせずにUSBに刺すと書き込みはできないがプログラムを入れてあれば起動がかかる。
一応ブレーカー落としておこう
作業を始める前に所定の管轄のブレーカーを落としておくことを忘れないようにしよう。
じゃないとビリっとくるぐらいじゃ済まないかもしれないし、家が炎上する危険もなくはない。
スマートスピーカーに躾する
プログラミングや設置の前に済ませてしまってもいいが、これをやっておくと声で操作できるようになる。
Node-RED Alexa Bridge
Node-RED Alexa Home Skill Bridge
Node-RED Alexa Home Skill Bridgeというサービスにてデバイスを登録しておくとNode-REDでそのデバイスに指示を出せるようになる。
よってAlexa→Node-RED→デバイスに指示という流れで声がけで操作可能になる。
これについてはアレクサから自宅のパソコンを起動するという記事で図解入りで説明しているので参照してほしい。
Google Assistant Bridge
google assistantに声がけで操作可能になるというのは上記のAlexaがGoogleに置き換わっただけでほとんどやることは同じ。
Node-redに登録
Node−REDは予めラズベリーパイにインストールしておく必要がある。これについては先程貼ったリンクと同じ記事にて少し触れている。インストールから稼働まではかなり簡単なのでRaspberry Piが動いていればすぐできると思う。
詳細な動き
Blynkアプリ
それぞれのスイッチON/OFFでついたり消えたり
声でONやOFF
Alexaアプリにて
その他→定型アクション→右上の+マークと進んで
定型アクション名、実行条件と設定してアクションを追加で玄関灯などNode-RED Alexa Home Bridgeで設定したデバイスのオンやらオフやらを決める。
GoogleAssistantアプリにて
同じようにGoogleAssistantアプリの初期の画面でルーティン→右下+→で追加しよう。
アレクサアプリかGoogle Assistantアプリでスケジュール
あとがき
存在を忘れてからが本番
長くなってしまったので途中で飽きたから少し時間をおいて追記しようと思う。
一旦これで公開する。
追記数カ月後
玄関のスイッチはまったく不具合なく動作している。気持ちいいいほど。
夕方になると日の入り時刻に連動して点灯し、朝になると日の出時刻に連動して消灯する。
もっとも点いたり消えたりする瞬間に立ち会えるタイミングはほとんどないのだが、いつのまにか点いている。消えている。という具合。
そしてリビングのスイッチはどうかというと不具合でまくりで数ヶ月経っても装着に至っていない。
どうもリレーモジュールのついた基板に問題があるのか、ESP01Sのポートに問題があるのか切り分けしきれない。
自分の作業場で動作テストをするとほぼ問題なく動くのにいざ壁の中に仕込むとずーっとWi-Fiを掴まないということがいちばんの問題かも。
とにかくリレーがいきなり切れたりついたりというのが単体テストでも起こるし結合テストでも起こるので使い物にならないという結論。今のところ。