K.Shj が 2021年02月28日18時03分09秒 に編集
初版
タイトルの変更
実家みまもりオブジェ
タグの変更
ESP32
IoT
秋葉原2021
メイン画像の変更
本文の変更
コロナ禍は徐々に収束しそうな気配を見せつつあるものの、未だに心配な日々が続きます。 そんな状況の中、実家に親が一人で住んでいて、自分は飛行機を使うほどの遠方にいるのでこまめに顔を出すこともできず、毎日ちゃんと暮らしているのか心配…… かといって、毎日電話やLINEで連絡を取り合うのもおっくうだし、毎日安否を確認するほど親も弱っているわけではないので、監視カメラみたいなガチの見守りシステムほどではない"ゆるい"感じで、見守りというか、お互いの無事を軽く伝えあうようなものがあったらいいのに。 と思って、試しに作ってみたのがこちらの実家見守りオブジェです。 ![キャプションを入力できます](https://camo.elchika.com/db3db5f8b690530d9c4a4e369294cd5619cf0dce/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39323166343764622d643036382d343638382d623163332d6133396266323739623837622f36653464663862652d616466332d343965612d616461322d376264626132643563336361/) ハードウェア投稿祭に間に合わせるために急いで作ったので、まだブレッドボード上の実装なものを「完成品です」みたいな顔で出すのはお恥ずかしいですが、機能はできているので許してください…… これは、**実家の部屋の照明がついているときはこの家型オブジェの中に仕込んだLEDが点灯し、反対に実家の照明が消えて暗い状態のときはオブジェのLEDも消灯する**というものです。 点灯時はこんな感じ。家の照明っぽくなるよう、電球色LEDを使ってます。 ![キャプションを入力できます](https://camo.elchika.com/082e7e54f7ac6f32342cfa0e7e6c6b22b1ceeb4d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39323166343764622d643036382d343638382d623163332d6133396266323739623837622f30356664636166662d383133392d343137372d393836632d363332383830303237633537/) 同じものを実家の方にも置いておけば、逆に私の家の照明がついているのか消えているのか、実家においたオブジェのLED状態からわかります。 これで、夜なのにいつまで経っても電気をつけていないとか、逆に深夜なのにいつまでも電気がつきっぱなしになっていたりすると「何かがおかしいな?」と気づきます。 日々の常識的な時間帯にLEDが点いたり消えたり……と状態が切り替わるのを見ることで、相手が毎日普通に生活していることがわかるというものです。 (親の立場からすると、私がいつまでも夜ふかしせずちゃんと寝ているかどうかもチェックできるというわけです。) **1,概要** 今回作った機能はこのような流れで実行されます。 ① 相手の端末が送信した明るさデータをAmbientから読み込み ② 読み込んだ数値に応じて、マイコンに繋げたLEDの点灯状態を切り替える。 ③ 部屋の明るさを照度センサからADCで読み取る。 ④ ADCで読み取った数値をIoT用サーバー Ambientに送信 マイコンにはインターネット対応のESP32を使います。 また、本プロダクトは私の家に置く用と相手の家(実家)に置くものの2つがありますので、区別して呼ぶために以降はそれぞれ「1号機」「2号機」と表現します。 **2,回路** 回路については、1号機、2号機とも全く共通です。 今回は電源周りなどの配線はんだ付けが面倒だったので、マイコン単体ではなくESP32の開発ボードを使いました↓。 [ESP32-DevKitC ESP-WROOM-32開発ボード](https://akizukidenshi.com/catalog/g/gM-11819/) 各ピンの役割(何を繋ぐか)は次の通り。 | ピン番号 | 役割 | |:---:|:---| | 15| 相手の家の明るさを知らせる 電球色LED | | 5 | 相手側端末の通信エラーを示す赤色LED | | 33 | 明るさ測定のADCピン | | 19 | 明るさ測定用フォトトランジスタの電圧供給 | 回路図はこのようになります。 ![キャプションを入力できます](https://camo.elchika.com/db5a41432510102ab9340c17ceafc7bf5905af07/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39323166343764622d643036382d343638382d623163332d6133396266323739623837622f33643037333632312d646262362d343739342d626537662d633062373633343733636536/) フォトトランジスタのコレクタは19番ピンに繋いでいます。19番ピンではなく電源(3.3V)に繋いでも全く同じ動作はできるのですが、電源と繋いでしまうと周囲が明るいときは常にトランジスタがオンになっていて電流が流れ続けてしまうため、電力的にもったいないです。 照度測定のときだけオンになればいいので、電源ではなく19番ピンに繋ぎ、測定時は一旦19番ピンをHIGHにした後、33番ピンの電圧をADCで読み、終わったらまた19番ピンはLOWにします。 19番ピンがHIGHになった後33番ピンの電圧が明るさに応じた値に落ち着くまでちょっと時間がかかるので、33番ピンの電圧をADCで測定開始するまで少し(0.5秒)待つことにしています。 **3,プログラム** ESP32に書き込むプログラムは次のようになりました。 ```arduino:実家簡易みまもりオブジェ #include <Arduino.h> #include <HTTPClient.h> #include "Ambient.h" #include <ArduinoJson.h> WiFiClient client; Ambient ambient; const char* ssid = "○○○○○○"; const char* password = "○○○○○○"; float light = 0; float light_other = 0; unsigned int channel_write = ○○○○○○; // Ambientの書き込み用チャネルID unsigned int channel_read = ○○○○○○; // Ambientの読み込み用用チャネルID const char* writeKey = "○○○○○○"; // 書き込み用チャネルのライトキー const char* readKey = "○○○○○○"; // 読み込み用チャネルのリードキー int sec = 0;//時間カウント用 char cnt = 0;//自分の送信回数カウント用 char cnt_other = 0;//相手の送信回数カウント用 void dataRead() { String url = "http://ambidata.io/api/v2/channels/"; url += channel_read; url += "/data?&readKey="; url += readKey; url += "&n=1"; HTTPClient httpClient; httpClient.begin(url); int httpCode = httpClient.GET(); String httpResponse = httpClient.getString(); httpClient.end(); //文字列の編集(大括弧を消す) int mojisu = httpResponse.length(); httpResponse.setCharAt(0, ' ');//0番目(=1文字目)を半角スペースに置換 httpResponse.setCharAt(mojisu-1, ' ');//最後の文字を半角スペースに置換 //JSONドキュメントを作成(サイズは実際のデータに合わせる。https://arduinojson.org/v6/assistant/) StaticJsonDocument<500> doc; // JSONドキュメントをパース DeserializationError error = deserializeJson(doc, httpResponse); // パース処理エラー時 if (error) { Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } //データの抜き取り light_other = doc["d1"];//Ambientのd1に保存された値(=相手環境の明るさ)を抜き取り int sendTimes = doc["d2"];//Ambientのd2に保存された値(=相手端末の送信回数)を抜き取り if(sendTimes != cnt_other)//相手の送信回数が変わっている(=最新情報が更新されている)とき { cnt_other = sendTimes; if(light_other >= 100)//明るさデータがしきい値よりも大きい(=明るい)とき { digitalWrite(15, HIGH);//電球色LEDを点灯 digitalWrite(5, LOW);//相手の通信エラーを示す赤LEDを消灯 } else //明るさデータがしきい値よりも小さい(=暗い)とき {digitalWrite(15, LOW);//電球色LEDを消灯 digitalWrite(5, LOW);//相手の通信エラーを示す赤LEDを消灯 } } else//相手の送信回数が変わってない(=最新情報が更新されていない)とき { digitalWrite(15, LOW);//電球色LEDは消灯 digitalWrite(5, HIGH);//相手の通信エラーを示す赤LEDを点灯 } } void setup() { Serial.begin(115200); delay(10); Serial.println("Start"); WiFi.begin(ssid, password); // Wi-Fi APに接続 while (WiFi.status() != WL_CONNECTED) { // Wi-Fi AP接続待ち delay(100); } Serial.print("WiFi connected\r\nIP address: "); Serial.println(WiFi.localIP()); ambient.begin(channel_write, writeKey, &client); // チャネルIDとライトキーを指定してAmbientの初期化 pinMode(15, OUTPUT);//電球色LED pinMode(5, OUTPUT);//赤LED(エラー用) pinMode(19, OUTPUT);//フォトトランジスタの電源用 for(int i = 0;i<4;i++)//初期セットアップの無事終了をエラー用LEDの点滅で表現 {digitalWrite(5, HIGH); delay(100); digitalWrite(5, LOW); delay(100);} } void loop() { sec += 1; if(sec == 600)//10分経った時 { sec = 0; dataRead(); cnt += 1; digitalWrite(19,HIGH);//フォトトランジスタに電圧を印加 delay(500);//電圧が落ち着くまで少し待つ light = analogRead(33);//明るさを33番ピンのADCで測定 digitalWrite(19,LOW);//フォトトランジスタへの電圧供給を終わる ambient.set(1, light); // 明るさをデータ1にセット ambient.set(2, cnt); // 送信回数をデータ2にセット ambient.send(); // データをAmbientに送信 Serial.println("送信終了"); } delay(1000); } ``` まずは[Ambient](https://ambidata.io/)に会員登録し、チャネルIDやリードキー、ライトキーを手に入れておいてください。 ここで、Ambientではチャネルを2つ作っておきます。なぜかというと、動作テストの中で1号機と2号機が同一のチャネルに書き込みを行うという動作がうまく行かないことがあったため。うまいこと同一チャネルを使う方法もあるかもしれませんが、Ambientのドキュメント的にも各々の機器はそれぞれ独立したチャネルに書き込みを行うような運用だったので、別々にしておいたほうがいいのかな……と思い、それぞれに専用のチャネルを作りました。 というわけで、上に載せたコード内の○○○○○○で記した部分は、1号機と2号機で内容が違ってきますので、下の表を参考に埋めてください。 | | 1号機 | 2号機 | |:---:|:---:|:---:| |channel_write| **1号機用**チャネルのID | **2号機用**チャネルのID | |channel_read | **2号機用**チャネルのID | **1号機用**チャネルのID | |writeKey | **1号機用**チャネルのライトキー | **2号機用**チャネルのライトキー | |readKey | **2号機用**チャネルのリードキー | **1号機用**チャネルのリードキー | | ssid |**こちら側**のWi-FiのSSID | **実家**のWi-FiのSSID | |password| **こちら側**のWi-Fiのパスワード | **実家**のWi-Fiのパスワード | 各端末で、データを書き込みに行くチャネルと読み取りに行くチャネルが違うためにこのような感じになります。それ以外の部分は1号機も2号機も共通です。 各端末は10分に一度、まずは相手側端末の明るさデータを読み込みに行きます。相手側機器が使うチャネルのリードキーを使って情報を読みます。 そして、相手側端末の明るさ情報と合計送信回数の読み込みを行い、明るさ情報に応じてLEDをつけたり消したりします。その際のしきい値は、環境に合わせて適宜変えてください。 私の場合は、間接照明がついてて多少暗いような状態でも"明るい"という判定になるよう、かなり小さいしきい値にしていますので、部屋が真っ暗に近くならない限りは"暗い"という判定はされません。 また、なぜ合計送信回数を読み込んだりするのが必要なのかと言うと、読み込んだ相手側端末の明るさデータがいつまでも変わっていない時、**本当に明るさが変わっていないのか、通信エラーで情報が更新されていないだけなのか**を見極める必要があるからです。 データの読み込みを行った時、相手側端末の送信回数が変わっていなければ、相手側の情報が更新されておらず、何らかの理由で通信エラーが起きていることがわかります。 エラーを検知したら、それが治って情報が更新されるまで、すなわち相手側の送信回数が変化するまでの間は、エラーを知らせる5番ピンの赤LEDを点灯させます。 相手側データの受信とそれに応じたLED点灯状態の反映が終わったら、次は自分の明るさ情報をADCで測定して値を送信します。その時、明るさ情報だけではなく「今まで自分が何回データ送信をしたか」という値も一緒に送ります。送信回数の値はもちろん、送信ごとに増えていきます。 それぞれの機器は自分のチャネルに対して、明るさデータと合計送信回数の2つを送信します。 ambient.set(n, a); という風に書くと、データnに数値(変数)aをセットするという意味になりますので、データ1を明るさ情報、データ2を合計送信回数として送ります。 データの送受信は、電力節約とサーバーへの負荷を減らすため、10分に一度に制限しています。そのため、お互いの家の照明の状態と端末LEDの点灯状態には最大10分間のタイムラグがあり、完全リアルタイムではありません。(まあ、リアルタイムである必要もないので問題ありませんが……) **4,3Dプリンターで外観作り** とりあえず家っぽい形にしようかな、程度のアイデアしかなく、デザイン能力もないのでオーソドックスな家型の形状を3D CADソフトで作りました。 ![キャプションを入力できます](https://camo.elchika.com/9bf6699a416793adb4b50902334d6ba70a97226b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39323166343764622d643036382d343638382d623163332d6133396266323739623837622f62656262643032332d643431342d343632642d383835322d353764643565356564623366/) 屋根と本体(?)は別体にして出力して、後でくっつけます。このほうが、造形時のオーバーハング部を気にしなくていいので造形成功率が上がります(良い3Dプリンターならこういうことを気にする必要すらないのかも) 十字のパーツは窓枠として穴にはめ込むつもりで作りましたが、見た目的に光が遮られて邪魔なので結局つけませんでした。 この中にLEDを仕込みます。家庭の照明風になるように、電球色のLEDを選びました。エラーを示す赤色LEDは、家の中ではなく外に設置しました。最終的には冒頭の写真のようになります。 あとはこれに電源をつないで部屋においておくだけで、まるでこのオブジェが実家そのもので、電気がついているのを窓の明かりで確かめるように知ることができます。 **5, 最後に** 今回は私の家と実家の2つだけでしたが、Ambientは無料のアカウントでも8個までチャネルを作ることができるので、同じ方法で複数の全ての機器に読み込みを行っていくことで、全端末数を最大8個まで増やすことができます。 オブジェを一軒家ではなくマンションみたいな形にして、オブジェの各部屋を各端末に割り当てて多チャンネル化すれば、兄弟姉妹等まで含めて家族みんな別々のところに住んでいるようなケースでも対応できます。 我が家がまさにそれなので、いつか多チャンネルのマンションタイプも作ってみようかな。 おわり。