ESP8266(WROOM02)でスマート・カーテン

以前からカーテンの自動化には興味があったが探してみると横に引くタイプの物は数多く出回っているが日本で圧倒的なシェアを誇るというか我が家にたまたま付いてたTOSOクリエティドラム・ツインワンチェーンに対応したものはない。なぜないのかはわからないがないなら作るまでということで頑張って作ってみることにしてみた。

ワンチェーン・タイプはチェーンを引く操作だけで外側と内側の2つのカーテンを上げ下げすることができるすぐれものであるが制御するのはそう難しくもない。だが設定時刻のみでカーテンを上げ下げするだけではなんとなくつまらないし、我が家ではルンバが活躍してくれているのでルンバの掃除を邪魔しないようカーテンを少し持ち上げる機能もほしかったりする。また、私だけかもしれないが長年生きていると外が明るくなったら起きて暗くなったら寝るという生活リズムが体に染みついてくるようなので2個の光センサーによる室内外の明るさや経緯度から自動計算した日の出/日の入り時刻により開閉できるようにしてみた。経緯度はグーグルマップで調べることが出来るしタイムゾーンオフセットと組み合わせることで地球上の全地域の日の出と日の入り時刻が計算可能となる。但し、近似式による計算であることに加え標高なども考慮してないのでそれなりの誤差(数分程度)は出てしまうが精度はそれほど重要でないので問題ないはずだ。

日の出と日の入り時刻の計算についてはラジオペンチさんの記事を参考にさせて頂いた。ラジオペンチさんはHW/SWともに秀でている優秀な方のようだ。感謝です。<(_ _)>
Arduinoで日の出・日の入り時刻を計算
※うるう年とUTC対応を行った。

今回のCPUは在庫処分したかったESP8266(WROOM02)と最近投稿したATtiny10のLED光センサーを組み合わせてみた。なお、光センサーによる制御は夜間の自動車のライトなどの外光に反応し開閉することがないよう昼間の時間帯(設定可能)のみの制御としている。ちなみにファームウェアのESP32対応はADC処理のみの簡単な変更でOKなのでなんでいまどきESP8266使うのって思う人はぜひ挑戦してみて頂きたい。

カーテン開閉用のモーターは、アマゾンで見つけた Bringsmart 小型 dcモーター 電動機 12v 30rpmの軸径と軸長がちょうど良く消費電流も少なくていい感じだ。トルクと開閉速度との兼ね合いが見当もつかなかったので66rpmと90rpmのものも試してみたがトルクが足りず30rpm以下のものででないと駄目だという結論に落ち着いた。カーテン開閉時間は2分半程度と遅いが回転が早すぎたりトルクがありすぎたりするとカーテンを壊してしまう恐れもあるし普段見ていないところで勝手に開閉するのであれば多少遅くとも気にはならないだろう。ちなみに、このモーターの気になる点は少し煩いところと耐久性。耐久性は実際に使ってみないとわからないけどね...

モーター制御にはブラシ付きDCブラシモータドライバ TB67H450FNGを使ってみた。使いやすくて便利なチップだ。

カーテン制御はカーテン上昇端の判定をカーテンの保護も兼ねてモーター電流の増加で判断しているがカーテン開閉に必要なトルクを得るための電流値はカーテンの重量等にも影響されるため最大電流値は設定により変更可能としている。また、起動直後のカーテン開閉状況がわからないことから起動時にはカーテンを一旦閉めてしまうことで開閉状況を合わせるようにしているが開閉時のモーター駆動時間や最大電流値の設定が不適切だと開閉状態が合わなくなってしまうことがある。そういう場合は設定値を調整する必要があるが設定値の範囲はそんなにシビアなものではないので調整は簡単に出来るはずだ。

カーテン制御は以下のように行われる。カーテンの状態にかかわらず確実に開閉させるためには余計なステップが必要となる。

【カーテンを開ける場合のモーター制御】
1.モーターを確実に回すために少しだけ逆回転させる。(時間※) 
2.モーター電流が設定値を超えるまで正回転させる。
3.カーテン負荷を抜くために少しだけ逆回転させる。(時間1)

【カーテンを閉める場合のモーター制御】
1.モーターを確実に回すために少しだけ逆回転させる。(時間※) 
2.カーテンが確実に持ち上がるまで正回転させる。(時間2)
3.カーテン負荷を抜くために少しだけ逆回転させる。(時間1)
4.カーテンが落ちる(かもしれない)まで待つ。(時間3)
5.カーテンを少し持ち上げるために正回転させる。(時間1+α)
6.カーテンを落とすために逆回転させる。(時間1+α)
7.カーテンが落ちるまで待つ。(時間3)
8.ルンバ用にカーテンを少し持ち上げる。(時間4)

※過負荷によりモーターが回らないときの電流値は最大電流未満かつ一定となることに注意しよう。

モーターは回れ!と命令しても物理的に回らないときがあるのが厄介かもしれない。そういえば、昔昔、プログラムは正しく命令しているのだから回らないモーターのほうが悪いと訴える若い開発者(もしかしてそういう私も若かった?)を見かけたことがあるが大きな間違いだ。スイッチを入れれば絶対回るという考え方に問題がある。そういう気づきができるかどうかが将来の分かれ道になると思ったほうがいいだろう。経験しないとわからないことかもしれないが...

【モーター取付ブラケット】
現物合わせで作ったブラケットとプーリー。3Dプリンターは便利だ!


【取付手順】
+ネジを外す。外したネジは再利用するので無くさないようにしよう。

プーリーを外す。レール穴径は6mm。6mm以上の軸径だとレール穴には入らないのでアダプターの厚み調整が必要となる。

プーリーとチェーンはどっかにしまい込むと間違いなく忘れて行方不明になるのでネジ止めにかけておくのがお勧めだ。

本体ケースをセットした取付ブラケットを取り付けて完了。取り付けには1分もかからないし元に戻すのも簡単だ。

両面テープで固定した手動開閉SW。上ボタンは内側カーテン、下ボタンは外側カーテンに対応。短く押すとカーテンが開き、3秒以上の長押しでカーテンが閉まる。同時押しで停止。同時押し+長押しで両側カーテンを閉める。などの操作が出来る。開閉はMQTT経由でも可能。

両面テープで固定した外光センサー。

ルンバ用にカーテンを持ち上げておくことができる。

只今、絶賛量産中!(笑)

【回路図】
抵抗は少ないけどコンデンサーがこれでもかっていうくらいある。省略可能なコンデンサーもいくつかあるんだけどハンダホールを見てしまうと無性にハンダ付けしたくなるのはなぜだろう?(笑)

【基板】
上に飛び出してるのは室内光センサー。というとなんだかカッコいいけど中華製の安物のLEDでっせ。でも感度いいアルよ!(笑)

ケースの外側に露出させるため飛び出させているが上にエアコンがあったりすると室内照明の影になってしまうため外付けセンサーとしたほうが良かったかも...いまさらですけど。

【プロパティ・ページ】
こんな感じの設定にすると、日の出以降の外がある程度明るくなってきたときにカーテンを開け、日の入り時刻になったとき或いは外が暗くなり室内照明を付けたときにカーテンを閉めるという動きをさせることが出来る。
室内外の光センサーのスレッショルド値は、センサー値が設定値より低ければ明るいと判断し高ければ暗いと判断する。設定のおおよその目安であるが、室内光センサーは夜に照明を付けたときのセンサー値よりも少し大きく(+2000くらい)し、外光センサーはたまには早起きしてみてこのぐらいの明るさならカーテン明けてもいいかなというときのセンサー値を設定すれば良いだろう。現在のセンサー値はスレッショルド値の横の括弧内に表示しているので参考にしよう。ちなみにセンサー値10000は照明を必要とする暗さだ。

【アレクサ等との連携】
Alexaと連携できるライブラリで有名なのはesp8266-alexa-wemo-emulator/fauxmoESP/espalexaあたりのようであるがどれも使い方に制約があるし、そもそもこれらは既製品のフリをするエミュレーションなので突然使えなくなってしまう可能性大だ。で、他の物をと探してみたところESP8266にも対応しているSinricProを見つけた。
これはAlexa/GoogleHome/IFTTT(予定)に対応し3デバイスまで無料で使えるがそれ以上は1デバイスあたり年間3$の追加費用がかかる。費用的には問題ない料金ではあるがデバイス登録が増えるとセットアップやメンテナンスが大変になるだけだし家に3個以上のカーテンは普通にあるのではないだろうかということで無料で使えるデバイス1個だけ登録して複数のカーテンを制御する方法を思いついた。ブラインド・デバイスは電源オンオフと開閉状態(0-100)が制御できるから状態としては電源2個+開閉101個で計103個の状態が制御できる。それを分散割り当てすれば複数デバイスが制御できることになる。そういう使い方が良いのかどうかは微妙な気もしないではないが制御情報をどう使うかまでは規定されていないようなので問題はないと思う。たぶん。早速作って見たのはSinricProからの制御情報をMQTT制御メッセージに変換して送信するだけのアプリだ。最大9個までのカーテン制御が可能となる。カーテンの開閉状態(0-100)を10で割った値をカーテンIDとし、10で割った余りの値でカーテンの開閉状態を指定するものとする。ちなみにカーテンID0は全カーテンを制御する。
アレクサ連携は便利だ。声で指示するだけでカーテンの開け閉めができるなんて楽過ぎてどんどん堕落の底に落ちてしまいそうだ。(笑)

当初はカーテン・ファームウェアに組み込むつもりであったがメモリ不足で動作しなかったためESP8266/ESP32で動作する単体アブリとして作ってみた。余っているESP8266に書き込んでどっかに転がして置けばいいだろう。但し、ESP8266だとかなり厳しいメモリ状況で動作するため誤動作の心配がある。ESP32或いはRaspberryPiを使ったほうが精神的には良さそうだ。MQTTサーバー(mosquitto推奨)がない場合はRaspberryPiのほうが良いかも。

【SinricProコネクト(ESP8266/ESP32)】

【SinricProコネクト(RaspberryPi with python3)】
Pythonって多く人たちがライブラリを作ってくれたおかげで凄く便利に使えるというのは理解できているのだがなんとなく好きにはなれないし覚える気にもなれないのはなぜだろう...生きてきた時代のプログラミングに対する思いの違いなのかなぁ...
それはともかく、このプログラム動かすとCPU100%になるのはちょっと頂けない。なんとかしてほしいなぁ...と思って調べてみたら、最後の client.handle_all(udp_client)をclient.handle_all(udp_client, sleep=1)のようにスリープ引数に1を指定すれば良いみたいだ。但し、ループ中に単純にスリープさせるだけみたいなので最大1秒の遅延が発生するものと思われる。カーテン制御なので1秒程度の遅延は問題ないのだが完全なイベント駆動型にしたいところだ...

※websockets8.1では動作するが9.1ではエラーとなり動作しないので注意すべし!

※RaspberryPiなどの場合、avahiの設定ファイルとして この内容を登録しておくとmDNSでmqttサーバーアドレスが引けるので便利。

【修正履歴】
2022-03-09
先日の[TODO]コードの修正が間違っていたので再修正。

2022-03-07
夕方、室内が暗くなってきたとき室内照明を付けると自動でカーテンが閉まるはずが閉まらない?以前に西日対策として追加した[TODO]コードに問題があるとわかったので修正。但し、まだ完全な対策ではないので[TODO]のまま。いつになったら[TODO]が取れるんだろう?(-_-;)

2022-01-25
モーターのラッシュカレントを計測し誤動作する場合があったので電流計測タイミングを変更。

2022-01-23
より強いモーターに交換したため最大電流値変更のための修正、逆起電力回避のためのモーター制御ロジックの修正、ソフトスタート機能(PWM対応)の追加、ライト制御値にヒステリシスを追加、その他バグ修正などのためにcurtain_toso.hを修正。ハードウェアについては下記投稿を参照すべし。

ESP8266(WROOM02)でスマート・カーテン (その2)

2021-12-06
前回の改良の影響でカーテンが自動で開かなくなることがあったため改良。あと、モーターの音がやけにうるさくなってきたようだ。モーターの耐久性が心配だったが想定外に速く駄目になりそうな予感が...そろそろ次のモーター探しをしたほうが良さそうだ。(-_-;)

2021-11-23
実は妻には反対されるだろうなと思いつつも妻のいない時を見計らってカーテンを自動化してしまったのだが、予想通り、妻は開口一番【元に戻して!!】と不満たらたらであったが、ようやく慣れてくれたのか便利さに気が付いてくれたみたいだ。ただ、ルンバ用に持ち上げる機能を設定していると夜でも少し空いてるのが気になっていたらしく夜は完全に閉まっていた方がいいねと言ってくれたので速攻で修正。確かにその通りだ。ユーザーの意見には耳を傾けるべきだね。(/・ω・)/

2021-11-17
長らく利用していたOpenHab1をOpenHab3に移行したついでにalexa連携もSinricProからOpenHabに変更。OepnHab3はarmbian(OrangePi-Zero)BusterへのインストールはZULU11-JDKがインストールできなくて断念してしまったが、Focalはapt install openhabを実行するだけでZULU11-JDKも同時にインストールされるしapt updateも早くてお勧めだ。

ちなみにOpenHab3のログを見ると約1分おきに下記メッセージが出力されるがなんだろう...
[INFO ] [ocontrol.internal.WebSocketConnection] – Web Socket close 1005. Reason: null

2021-11-03
毎日ではないが14:30-15:30頃になると何故かカーテンが閉まってしまうことがある。太陽高度が低い西日が室内を照らすことにより外よりも内のほうが明るいと判断してしまいカーテンを閉めてしまうことが原因だった。外よりも内が明るいというのは夜に照明をつけた場合だけと思っていたのが敗因ではあったものの、考え方によってはそれが正解だったりもしそうな話ではある...とりあえず外がある程度明るいときは外/内の明るさの比較をしないように修正してみた。

2021-10-16
設置当初からときどき夜中に勝手にカーテンが動き出す...こ、これはもしや超常現象というやつか?と少しドキドキしたりもしたが漸く原因が判明。アレクサ連携で使っているSinricProの仕業だった。Pythonで作ったSinricProのサービスが何故か夜中になるとときどき落ちてしまい自動で再起動するのだが、そのときSinricProクラウドは前回の最終操作を再送するという仕様のためだった。
再送は止められないようなのでサービス起動後の15秒間の間に受信した操作要求は無視するようにsinricpro_connect.pyを修正。
これで今夜からは安心して眠れそうだ。(-_-;

2021-09-19
curtain_toso.hを修正。カーテンを閉めるとき最初に逆回転させるのを忘れていたので追加。なくても問題ないんだけど安心安全のためには必要だ。

2021-09-18
curtain_toso.hを修正。カーテンを閉めるときカーテンの状態により途中で引っかかってしまう場合があった。これでたぶん完璧だ。

2021-09-14
esp8266_toso.ino/curtain_toso.hを修正。カーテンを閉める処理をより汎用的に処理できるよう定数値を除去。カーテン開閉状況を取得するためのメソッド名を変更。

2021-09-12
esp8266_toso.ino/curtain_toso.hを修正。カーテンを閉めた後に再度閉める操作をしても無視していたが無視せずに毎回処理するように変更。
カーテン上昇端の設定をしやすくするため現在のカーテン位置情報(秒)をリミット時間の横に表示するように変更。

2021-09-06
curtain_toso.h を修正。カーテンを開けるとき、カーテン上昇端をモーター電流の増加だけで判断していたのを制限時間(motor time limit)経過した場合にも上昇端と判定するように仕様変更。この変更によりカーテンの上昇端を任意の位置にすることが可能となった。

2021-08-19
curtain_toso.h がバグッてたので修正。

【ファームウェア】