※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
【目次】
1.はじめに
金魚を飼っているので、旅行などで不在のときに金魚に自動でエサを与える装置があると便利かと思い、そのような装置を作ってみました。
ホームセンターのペットコーナーでは写真1のような製品が並んでいました。これらを参考に取り組んでみました。
※なんとか動作するようにはなりましたが、誤作動しないという保証が得られず、あくまで趣味の範囲での一つの試みです。
この記事を参考にされる場合は、十分に安全に配慮し、自己責任にて取り組まれるようにお願いします。
2.全体の構成
全体の構成としては、
- メカ的な装置を作り、それをモーターで動かすことで金魚にエサを与える
- 1日に2回(朝と夕)、自動でエサを与える
- 時間の測定にはNTPサーバーを利用する。そのためにWifiにてインターネットに接続する
- エサやりの後は次のエサを与える時刻までスリープモードに入る
●2つの思案
作るにあたり2つほど方式を思いつきました。
- アルキメディアン・スクリューを使う(スクリュー方式)
- 小さな穴をいくつか開け、振動させることで下に落とす(フリカケ方式)
の2つです。
2.1スクリュー方式による装置
アルキメディアン・スクリューとは図2に示すようなものです。古代ギリシャで活躍したアルキメデスが考案したとされるスクリューです。現在でも鉱山などで使われているそうです。
Silberwolf (size changed by: Jahobr) - File:Archimedes-screw_one-screw-threads_with-ball_3D-view_animated.gif created by Silberwolf, CC 表示-継承 2.5, https://commons.wikimedia.org/w/index.php?curid=2489813による
実際に製作したものを写真2に示します。
黄色い部分が3Dプリンターで作った部分です。モデル等は付録に示します。
動力部分はタミヤの工作シリーズを使いました。使っていないものを引っ張り出してきました。
回路の部分は3章で詳しく見ます。小型のマイコンであるXiao Esp32 C3 というものを使いました。(たまたま昔買っておいたものです)
3.回路について
・XiaoESP32C3の基本データです
qiita.com
入力電圧は5Vとありますが、4.4Vのリチウムイオンバッテリーである18650を使用しました。
モーターの駆動にはD10(11番ピン)を使用し、トランジスター2SC4811を用いてスイッチの代わりとして使っています。
ベース抵抗は10KΩとし、これによりコレクタ電流は最大で2.7Aまで駆動可能です。(計算上)
(モニター用にLEDもつなぎました)
D8は上の記事を参考にしてプルアップしてあります。
D9にはタクトswを付けましたが、現在ソースコード中では特に使用していないです。
(ディープスリープ中にGPIO割り込みがかけられるようにしたかったのですが、うまく働かなかったのでやめました)
それとWifiを使用したのでアンテナも取り付けました。
4.ソースコード
このシステムを動かすにはxiaoにソースコードを書き込む必要があります。xiao の開発にはArduino IDEを用いました。
リスト1にその内容を示します。
リスト1
#include <WiFi.h> #include <NTPClient.h> #include <WiFiUdp.h> #include <esp_sleep.h> #include <esp_bt_main.h> #include <esp_bt.h> #include <esp_wifi.h> //RTC coprocessor領域に変数を宣言することでスリープ復帰後も値が保持できる RTC_DATA_ATTR int COUNT = 1; // 定刻の配列のインデックス RTC_DATA_ATTR bool firstTime = true; // このコードの初回起動時ならtrue const char* ssid = ""; const char* password = ""; const int ledPin = D10; // LEDが接続されているピン番号 WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000); unsigned long epochTime = 0; // エポックタイムを保存する変数 unsigned long referenceTime = 0; // 時刻を秒で表現する変数 float hours = 0.0; // 時刻を計算 unsigned long waitTime = 0; // ディープスリープの時間を保存する変数 #define TIME_MAX 2 // 起床時間(wakeupTime)の要素の数 float wakeupTime[ TIME_MAX ] = {8.0, 19.0}; // 起床時間 void setup() { Serial.begin(115200); pinMode(ledPin, OUTPUT); Serial.printf("Cause:%d\r\n", esp_sleep_get_wakeup_cause()); wakeup_cause_print(); // WiFi接続 WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { Serial.println("Connecting to WiFi..."); delay(1000); } Serial.println("Connected to WiFi"); // NTPクライアントの初期化と時刻の更新 timeClient.begin(); timeClient.update(); // エポックタイムを取得して保存 epochTime = timeClient.getEpochTime(); // 1日ごとの秒数 3600*24=86400 // UTG との時差+9時間 referenceTime = (epochTime + 3600 * 9) % (3600 * 24); // 時刻を計算 hours = (float)referenceTime / 3600.0; Serial.println("referenceTime, hours:"); Serial.println(referenceTime); Serial.println(hours); // 給餌は8時と19時 // 定刻に起床したかどうかの判定 // (時間を分に換算している) float diff = (wakeupTime[COUNT] - hours) * 60.0; // 定刻10分以内に起床したときにLEDをon(diffがマイナスの場合も考慮している) if((diff * diff) < 100.0){ // 8時と18時にLEDをオン digitalWrite(ledPin, HIGH); delay(5000); // 10秒間LEDをオン digitalWrite(ledPin, LOW); referenceTime += 5; // COUNTの更新 COUNT++; if(COUNT >= TIME_MAX){ COUNT = 0; } } // 再度計算 単位は分 diff = (wakeupTime[COUNT] - hours) * 60.0; //waitTimeを計算する if(COUNT == 0 && diff < 0){ // 日付が変わる手前で、再度スリープするときの処理 waitTime = ((unsigned long)wakeupTime[0] + 24) * 3600UL - referenceTime; }else{ //それ以外ときの処理。約2分手前で起床するように120を引いている waitTime = (unsigned long)wakeupTime[COUNT] * 3600UL - referenceTime - 120; } //waitTimeを表示する Serial.println("waitTime:"); Serial.println(waitTime); delay(50); // waitTime--; Serial.println("COUNT:"); Serial.println(COUNT); // スリープ前にwifiとBTを明示的に止めている // 止めないとエラーになる esp_bluedroid_disable(); esp_bt_controller_disable(); esp_wifi_stop(); // ディープスリープモードに入る esp_sleep_enable_timer_wakeup(waitTime * 1000000ULL); // waitTime秒後に起床 esp_deep_sleep_start(); } void loop() { // ループは使用しません } // 起床時の要因をメッセージとして表示する void wakeup_cause_print() { esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); switch (wakeup_reason) { case 0: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_UNDEFINED"); break; case 1: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_ALL"); break; case 2: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_EXT0"); break; case 3: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_EXT1"); break; case 4: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_TIMER"); break; case 5: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_TOUCHPAD"); break; case 6: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_ULP"); break; case 7: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_GPIO"); break; case 8: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_UART"); break; case 9: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_WIFI"); break; case 10: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_COCPU"); break; case 11: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG"); break; case 12: Serial.println("Wakeup caused by ESP_SLEEP_WAKEUP_BT"); break; default: Serial.println("Wakeup was not caused by deep sleep"); break; } }
全体の流れを図4に示します。リスト中にコメント文もありますし、順を追って読めば解読可能かと思います。
〇稼働時間について
1回のエサやりの時間は5秒です。一定時間にエサが出ていく量は、容器内のエサの量、ボードの傾斜角などでわりと大きく変わります。
そしてエサやりの時間でも吐出するエサの量は変わります。
また金魚のエサの量はおおよそ金魚の体重の0.4%が好ましいとされています。(以下参照)
金魚に与える餌の適量を確認する方法kingyobu.wordpress.com
筆者の飼っている金魚の場合、4匹合計で約200gなので0.004をかけると0.8gに相当します。
これは5秒間駆動することでエサが落ちてくる値となっています。
(私事でした)
【バグ解決にいたったエラーメッセージ】
リスト中、loop()関数の下にある関数は以下のリンク先の記事を参照させていただきました。
③での関数を利用することにより、バグを一つ解決することができました。
バグの解決のために、起床したときのシリアルモニタでのメッセージを見ると、正常に起床したことをうかがわせる内容でした。
それまでどうしても起床の時間が最大で5分ばかりずれてしまい理由が分からなかったのですが、
正常に起床していると分かり調べ直しところ、Arduinoなど世の中のマイコンのクロックはだいたい1%ほどの精度であるとの記述がありました。
(5 × 60)÷ (24 × 3600) = 0.35[%]
となるので、24時間ほどでで5分ほどの誤差は精度範囲内というところに落ち着きました。
※ただし次の記事では誤差は200ppm程度とのことでした。上で求めた0.35%は3500ppmに当たるので疑問符が残りますが、運用上問題ないので今後の課題とします。
lab.seeed.co.jp
【小ネタ】
Xiaoに出来上がったスケッチを書きこむときには、構成図での電池からの給電を切る必要がありました。そうしないとPCがXiaoを認識しないで、UARTのCOMポートが開かないという症状が発生しました。
5.動画
実際にエサを与えているシーンを貼っておきます。
youtu.be
動画を見て分かるかと思いますが、フリカケ方式は現在使用していないです。
理由としてはエサの量のコントロールがいまいちむずかしいのと、スクリュー方式のみでも運用できているからです。
フリカケ方式には悪いですが、今はエサを入れていない状況で、定刻に1~2分ズレて同じように動いています。
(誤差の影響みたいです)
6.まとめ
自動で金魚にエサを与える装置を作ってみました。
現在も朝、夕に稼働していて、3日に1度程度重さを量って減ったエサの量を計算で求めています。
今後トラブルが起きないか、試験を続けていきます。