目的
自宅でリクガメ、ヒョウモントカゲモドキ等の爬虫類を飼っている。
彼らの飼育をするのに、保温球等を利用して温度管理を行っている。
エアコンと違い、ちょうど良い温度に調整はしてくれないので、温度の調節を行う機能を作りたい。
目標
以下の3つの目標を段階的に達成していくことを目指す
①目標温度を維持できるようにする。
②温度データをパソコンに記録する。
③屋外からスマホで操作できるようにする。
①目標温度を維持できるようにする。
目標温度を維持する為の、保温球の制御は単純で、
・一定温度以下なら点灯
・一定温度以上なら消灯
を実現するのみ
■実現案

上記図のような単純な仕組みで実現を目指す。
それぞれのブロックで、以下の機器を用いる。
■マイコン
ESP-32を使用。このマイコンにはWifi通信モジュールが搭載されている。USB給電。
当初、Aruduinoを使用していたが、PCとWifi通信をしたかった為、こちらに変更した。
Arduino IDE用のライブラリが公開されており、Aruduinoと同様にプログラムを組める。
実装するプログラムは、センサの温度が一定以上ならアウトプットOFF、一定以下ならONの単純なプログラムである。単純な実装の為、ソースは割愛する。
https://www.amazon.co.jp/gp/product/B07MH58JS2/ref=ppx_yo_dt_b_asin_title_o08_s00?ie=UTF8&psc=1
■温度センサ
DHT11を使用。以下のキットに付属していた。
https://www.amazon.co.jp/gp/product/B06XF2HZGT/ref=ppx_yo_dt_b_asin_title_o05_s00?ie=UTF8&psc=1
■保温球
なんでもいい。
■?部分
難儀したのはこの部分。
どのようにマイコンから、高電圧の交流を操作すればよいか難儀し、紆余曲折した。
紆余曲折1(リレー)
低電圧で高電圧を操作したいときはやっぱりリレー!
ということで以下を購入。
https://www.amazon.co.jp/gp/product/B07W5MZ2C7/ref=ppx_yo_dt_b_asin_title_o05_s00?ie=UTF8&psc=1
延長ケーブルを裁断し、リレーを間に挟めば目的は達成できる。
しかし、当方電子工作は素人もいいところである。

恐ろしい。家庭用電源で感電しそうで恐ろしい。
正常に動作したものの、これが原因で火事にでもなったら洒落にならない。
素人の私はリレーの使用を諦めました。
紆余曲折2(物理)
延長コンセントケーブルの切り替えスイッチをモータで押す作戦。
この方法で制御している記事を見たことがある為、自分もやってみた。

まぁ、いちようこれで制御はできた。
とはいえ、そのうち補強が外れたりしないかが心配なうえ、サーボモータを使用した為に、待機消費電力が大きい欠点がある。やるならDCモーターでやるべきか。
紆余曲折3(Wifi通信(既製品を利用))
最終的にこの方法に落ち着いた。
既存のスマートコンセントを利用する。
本来、各スマートコンセントは、専用のアプリを介さねば操作できない。
しかし、リバースエンジニアリングにより、通信プロトコルを解析した猛者のおかげで、ESP-32から操作することが可能となった。専用アプリが行っている操作通信プロトコルを、このESP-32から発信し、操作してやろうというわけだ。詳細は以下。
https://lab.sasapea.mydns.jp/2018/08/24/tplinksmartplug/
目標1達成
以下のように温度に合わせて点消灯を制御することに成功した。
これでひとまず、爬虫類棚の中を暑すぎず寒すぎずで維持することができる。
まだ季節的に実稼働はしませんが。
②温度データをパソコンに記録する。
以下のような仕組みで、計測データをPCに送る。
また、保温球の点消灯制御も、前項にてソケット通信を介して行うこととなった為、記載を更新している。

上記を実現するには以下を追加で作成する必要がある。
・ESP32用のWifi通信プログラム
・デスクトップPC(win)用のWifi通信プログラム
また通信は以下のシーケンスで行われる。①~⑤をどのように実装しているかを事項で説明する。
また通信は以下のシーケンスで行われる。①~⑤をどのように実装しているかを事項で説明する。

ESP32用のWifi通信プログラム
①ESP32サーバの起動 (Aruduino言語)
AruduinoのWiFi通信ライブラリを活用していく。
まずはESP32が、クライアントからの接続を受け取れるようにしておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include "WiFi.h" #include "WiFiClient.h" #include "WiFiUdp.h" /*WiFi通信を行う為に必要な各クラスの定義*/ WiFiClient client; WiFiUDP udp; WiFiServer server(80); void setup() { /*Wifiに接続*/ WiFi.mode(WIFI_STA); WiFi.begin("Buffalo-*-****", "***********"); // WiFi-APに接続するためのSSID/PASSWORDを指定 while (WiFi.status() != WL_CONNECTED) //WiFiに接続されるまで繰り返す { Serial.print("."); delay(100); } //接続成功 /*本マイコンのWifiサーバーとしての動作開始*/ server.begin(); } |
②デスクトップからの送信(C#言語)
デスクトップからESP32にメッセージを送る。
やりとりはソケット通信(TCP/IP)で行う。ソケット通信とは?というのは以下のサイトが分かりやすい。
https://www.itmanage.co.jp/column/tcp-ip-protocol/
また、ソケット通信では宛先の特定にIPアドレスを用いる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using System.Net; using System.Net.Sockets; public string SendAndReceive() { int port = 80; //TcpClientを作成し、サーバーと接続する System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient("192.***.**.**", port); //NetworkStreamを取得する System.Net.Sockets.NetworkStream ns = tcp.GetStream(); //読み取り、書き込みのタイムアウトを10秒にする //デフォルトはInfiniteで、タイムアウトしない //(.NET Framework 2.0以上が必要) ns.ReadTimeout = 10000; ns.WriteTimeout = 10000; //サーバーにデータを送信する //文字列をByte型配列に変換 System.Text.Encoding enc = System.Text.Encoding.UTF8; byte[] sendBytes = enc.GetBytes(”GET_DATA”); //データを送信する ns.Write(sendBytes, 0, sendBytes.Length); ns.Close(); return "まだ受信できてない"; } |
※この段階では、まだ送信できていない(おそらく、ns.Write()の処理が完了前に、ns.Close()してしまっている為)
ここ⇒https://teratail.com/questions/194341
とかを参考にしました。
ESP32サーバへ送信するメッセージの内容は”GET_DATA”という文字列とします。
ESP32サーバがクライアントからメッセージを受信した際、”GET_DATA”という文字列をトリガーに、温湿度の返答を行うようにする為です。
③ESP32側の受信(Aruduino言語)
まずは以下の以下の処理で、クライアントからのメッセージに対してアクションを起こせるようにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void loop() { WiFiClient client = server.available(); // 着信したクライアントを格納 if (client) { // クライアントを受信していたら String currentLine = ""; // クライアントからの受信データを保持する文字列を作成します while (client.connected()) { // クライアントが接続している間ループ //クライアントからのメッセージを受信するとここの処理が動く } // close the connection: client.stop(); Serial.println("Client Disconnected."); } } |
次に、メッセージの中身が参照できるようにしよう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void loop() { WiFiClient client = server.available(); // 着信したクライアントを格納 if (client) { // クライアントを受信していたら String currentLine = ""; // クライアントからの受信データを保持する文字列を作成します while (client.connected()) { // クライアントが接続している間ループ if (client.available()) { // クライアントから読み取るバイトがある場合、 char c = client.read(); // バイトを読み取る currentLine += c; // currentLineの最後に、新しい読み取りバイトを追加 } if (currentLine.endsWith("GET_DATA")) { //クライアントからの受信データに"GET_DATA"が含まれていた場合 //ここに返信処理をいれる。 } } } // close the connection: client.stop(); } } |
これでクライアントからの受信データをString型に置き換え、”GET_DATA”の文字列が送られてきているのか確認ができる。
④ESP32からデスクトップへの返信(Aruduino言語)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void loop() { WiFiClient client = server.available(); // 着信したクライアントを格納 if (client) { // クライアントを受信していたら String currentLine = ""; // クライアントからの受信データを保持する文字列を作成します while (client.connected()) { // クライアントが接続している間ループ if (client.available()) { // クライアントから読み取るバイトがある場合、 char c = client.read(); // バイトを読み取る currentLine += c; // currentLineの最後に、新しい読み取りバイトを追加 } if (currentLine.endsWith("GET_DATA")) { //クライアントからの受信データに"GET_DATA"が含まれていた場合 client.println(String(温度) + "," + String(湿度)); } } } // close the connection: client.stop(); } } |
③の状態から追加することは単純で、client.println()を実行するだけだ。
⑤デスクトップで返答を受信(C#言語)
②のメソッドに、データを受信する処理を追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
using System.Net; using System.Net.Sockets; public string SendAndReceive() { int port = 80; //TcpClientを作成し、サーバーと接続する System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient("192.***.**.**", port); //NetworkStreamを取得する System.Net.Sockets.NetworkStream ns = tcp.GetStream(); //読み取り、書き込みのタイムアウトを10秒にする //デフォルトはInfiniteで、タイムアウトしない //(.NET Framework 2.0以上が必要) ns.ReadTimeout = 10000; ns.WriteTimeout = 10000; //サーバーにデータを送信する //文字列をByte型配列に変換 System.Text.Encoding enc = System.Text.Encoding.UTF8; byte[] sendBytes = enc.GetBytes(”GET_DATA”); //データを送信する ns.Write(sendBytes, 0, sendBytes.Length); System.IO.MemoryStream ms = new System.IO.MemoryStream(); byte[] resBytes = new byte[256]; int resSize = 0; do { //データの一部を受信する resSize = ns.Read(resBytes, 0, resBytes.Length); //Readが0を返した時はサーバーが切断したと判断 if (resSize == 0) { break; } //受信したデータを蓄積する ms.Write(resBytes, 0, resSize); //まだ読み取れるデータがあるか、データの最後が\nでない時は、 // 受信を続ける } while (ns.DataAvailable || resBytes[resSize - 1] != '\n'); //受信したデータを文字列に変換 string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length); ms.Close(); //末尾の\nを削除 resMsg = resMsg.TrimEnd('\n'); ns.Close(); return resMsg; } |
System.IO.MemoryStreamを用いて、受信したデータの取り出しを行う。
受信したデータを文字列に変換し、返り値としている。
目標②達成
上記方法を用いて、ESP32から、温湿度データを取得することができた。
先程のソケット通信のデータをcsvで日時と共に記録するコード組み、以下のような温湿度記録を得ることができた。

試しにグラフ化
③屋外からスマホで操作できるようにする。
自宅の家電を、屋外から操作できたらなんて便利なんでしょう!ということで実現していく。
今回の保温球は、自動でONOFFを操作してくれるので、「WiFiに繋いでいないスマホ」から自宅のLEDをチカチカできたら目標達成とします。
さて、ではどうやってスマホから自宅のデスクトップサーバに繋ぎましょうか。
今回はUnityアセットのPUN2を使います。え?なんでUnity?
Unityを使う理由は以下。
・同じプロジェクトでPC用とスマホ用、両方のビルドができる。
・私がPUN2使ったことがあるから(これしか使ったことない・・)
・いずれ3Dキャラクターのアシスタントとか実装したいと夢見ているから。
というわけでこんな感じのやり取りを行うことになります。

やっていることは以下の流れになります。
①スマホ と デスクトップPCで,Photon Cloudサーバーを介して通信を行う。
②デスクトップPCからマイコンへソケット通信を行う。
③マイコンが家電 (今回はLED) を操作する。
UnityアセットのPUN2で通信を行う。
PUN2の扱い方は以下のサイトが凄すぎる為、当サイトでは端折ります。
https://zenn.dev/o8que/books/bdcb9af27bdd7d
目標③達成
具体的なPUN2通信の詳細は省きましたが、上記図のように、自宅サーバ(デスクトップPC)とスマホの通信さえできれば、屋外から家電の操作は簡単に実現できる。
今後の課題
・サーバのクラウド化
現状、サーバを自宅のデスクトップPCとしている為、常にPCを起動していなければいけない。なので、PCを消せないし、PCに何かあればシステムが停止してしまう。
これを解消すべく、サーバプログラムをクラウド上で動作できれば、尚よいなと思いました。
・UIの洗礼化
温度記録の参照や、スマホから操作する際に見た目をもっとそれっぽく!そう、それっぽくしたい。それだけ。
以上です。
コメント