ATTiny85を使用した動物用フィーダー





去年休暇に行って、私は自動ペットフィーダーを購入することにしました。 選択は、Animal Planetブランドの製品を支持して行われました。 このモデルが選ばれた理由は正確には覚えていませんが、おそらく当時は低価格とかなり良い消費者の特性を最適に組み合わせていました。



デバイスはすぐに郵便で受け取られ、組み立てられ、同梱され、そこに飼料が注がれました。 猫はすぐに設計を承認し、喜んでモーターの騒音に頼り、タンクの上部から十分な睡眠をとる前に食べ物を食べ始めました。 1つの「ではない」としても、すべてがうまくいくでしょう。 実際にビジネスが開始されたため、確立されたスケジュールに従って十分な睡眠をとるためにフィードを取得することができませんでした。 しかし、好奇心hands盛な手はあきらめず、実際には時計にリアルタイムが設定されていないという条件でのみスケジュールが機能することが実験的に判明しました。 私が明確な「バグ」に対処していることに気付いて、売り手に目を向けると、彼はこれが既知の問題であり、指定された問題のない新しいバージョンでデバイスを交換することを喜んで保証しました。



数日が経過し、交換品を送りました。 デバイスの「新しいバージョン」では、「バグ」が完全に再現されました。 残念ながら、出発までに時間がなく、猫の飼育は友人に委ねられていました。 戻った後、関連性が低下したため、問題は放棄され忘れられました。



マイクロコントローラとArduinoプラットフォームに慣れるために自分でトレーニングタスクを選択すると、忘れられていた餌入れを思い出しました。 既製のメカニズムを使用して、スケジュールに沿って飼料供給システムを実装し、条件付き反射のパブロフの理論をテストすることも決定されました。



飼料ディスペンサーのメカニズムは、インペラーが完全に回転するたびに、4食分量に対応する4つの接触クロージャーがあるように設計されています。 接点閉鎖の数を数えることで、プレートに注がれた食べ物の量を判断できます。



デバイスのプロトタイピングとデバッグは、Arduino Unoプラットフォームで実行されました。 Ebayのスケジュールに従って給電するために、2つのアラームをプログラムできるバッテリーを内蔵したDS3231リアルタイムモジュールを購入しました。







提案されたアルゴリズムは簡単です。 コントローラは定期的に起動して、プログラムされたアラームのいずれかが機能していないかどうかを確認し、エネルギーを節約するためにさらにスリープ状態になります。 アラームの場合、メロディが再生され、必要な量のフィードが注がれます。 Arduinoでのプロトタイプの実装は問題を引き起こさなかったため、検討をスキップします。



ドナーフィーダーの設計には、3つの「D」バッテリー用のコンパートメントがあり、そこから最終的なデバイスが動作します。 エネルギー消費を減らしてバッテリー寿命を延ばすには、余分なArduinoボディキットを取り除く必要があります。 エンドデバイスの開発では、ATTiny85コントローラーが選択されました。 許容される供給電圧は2.7〜5.5Vの範囲であるため、電圧レギュレータを使用する必要はなく、デバイスはバッテリから直接給電できます。



最終的なデバイスを設計するときに、次のタスクが解決されました。これについて詳しく説明します。







デバイスの概略図。







連絡先J1-J4、J8-10は、Arduino Unoをプログラマーとして接続するために使用されます。 ジャンパーJ5-J7-Attiny85のプログラミング時に内部回路を無効にします。 最大電流500 mA(モーターの始動電流は150-200 mAを超えない)に設計された絶縁ゲートを備えたNチャネル電界効果トランジスターは、電気モーターの電源回路のパワーエレメントとして使用されます。 大容量の平滑コンデンサ(図のC2)をVCCに接続することは非常に重要です。 特にバッテリー駆動の場合、それがないと、モーターが起動するとプログラムがクラッシュします。



スケッチ
#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC #include <Time.h> //http://playground.arduino.cc/Code/Time #include <TinyWireM.h> //https://github.com/adafruit/TinyWireM #include <Narcoleptic.h> //https://code.google.com/p/narcoleptic/ //The library was modified for ATTiny85 as described here: //http://www.willowdesign.info/blog/digistump-cricket-generator/ const int Note_D = 213; const int Note_G = 159; const int Note_A = 142; const int Note_B = 127; const int Speaker = 1; const int feedPin = 1; const int RTCPowerPin = 4; const int motorPin = 3; const int MAX_FEED_TURN = 2; //Dispenced food volume int feedCounter = 0; // counter for the number of feed revolution int feedState = HIGH; // current state of the feed contacts int lastFeedState = HIGH; // previous state of the feed contacts int reading = HIGH; long lastDebounceTime = 0; // the last time the output pin was toggled long debounceDelay = 20; // the debounce time; increase if the output flickers void setup(void) { pinMode(RTCPowerPin, OUTPUT); digitalWrite(RTCPowerPin, HIGH); RTC.squareWave(SQWAVE_NONE); setSyncProvider(RTC.get); //set the system time to 17h 35m on 22 March 2015 //setTime(17, 35, 0, 22, 3, 2015); //RTC.set(now()); pinMode(motorPin, OUTPUT); digitalWrite(motorPin, LOW); // initialize the feed contact pin as a input: pinMode(feedPin, INPUT); } void loop(void) { digitalWrite(motorPin, LOW); digitalWrite(RTCPowerPin, HIGH); //Turn RTC power ON delay(50); //ensure RTC is stable after powering it ON RTC.setAlarm(ALM1_MATCH_HOURS, 0, 0, 7, 0); //morning feed alarm RTC.setAlarm(ALM2_MATCH_HOURS, 0, 0, 19, 0); //evening feed alarm digitalWrite(RTCPowerPin, LOW); //Turn RTC power OFF digitalWrite(Speaker, LOW); Narcoleptic.delay(20000); // During this time power consumption is minimized digitalWrite(RTCPowerPin, HIGH); if (readVcc() < 3500) { playBatteryLow(); } delay(50); //ensure RTC is stable after powering it ON if (RTC.alarm(ALARM_1) || RTC.alarm(ALARM_2)) { //has any of 2 Alarm1s triggered? //yes, act on the alarm // Wake up CPU. feedCounter = 0; // Wake up the CAT - play some music playTune(); // Turn ON food dispenser motor digitalWrite(motorPin, HIGH); while (feedCounter < MAX_FEED_TURN) { // read the feed input pin: reading = digitalRead(feedPin); // compare the feedState to its previous state if (reading != lastFeedState) { // reset the debouncing timer lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > debounceDelay) { if (reading != feedState) { feedState = reading; // if the state has changed, increment the counter if (feedState == HIGH) { // if the current state is HIGH then the feed contacts // went from on to off feedCounter++; } else { // if the current state is HIGH then the feed contact // went from on to off: } } } // save the current state as the last state, //for next time through the loop lastFeedState = reading; } } } void TinyTone(unsigned char divisor, unsigned char octave, unsigned long duration) { //http://www.technoblogy.com/show?KVO //TCCR1 = 0x90 | (8-octave); // for 1MHz clock TCCR1 = 0x90 | (11 - octave); // for 8MHz clock OCR1C = divisor - 1; // set the OCR delay(duration); TCCR1 = 0x90; // stop the counter delay(duration * 1.30); // pause between notes } void playTune(void) { pinMode(Speaker, OUTPUT); TinyTone(Note_D, 4, 125); TinyTone(Note_D, 4, 125); TinyTone(Note_G, 4, 250); TinyTone(Note_G, 4, 200); TinyTone(Note_G, 4, 62); TinyTone(Note_A, 4, 250); TinyTone(Note_A, 4, 200); TinyTone(Note_A, 4, 62); TinyTone(Note_D, 5, 350); TinyTone(Note_B, 4, 150); TinyTone(Note_G, 4, 300); delay(350); TinyTone(Note_D, 5, 500); delay(200); TinyTone(Note_D, 5, 500); delay(200); TinyTone(Note_D, 5, 500); delay(200); TinyTone(Note_D, 6, 1000); TCCR1 = 0; // stop the timer pinMode(Speaker, INPUT); } void playBatteryLow(void) { pinMode(Speaker, OUTPUT); TinyTone(Note_D, 4, 125); TinyTone(Note_D, 6, 250); TCCR1 = 0; // stop the timer pinMode(Speaker, INPUT); } long readVcc() { //http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/ // Read 1.1V reference against AVcc // set the reference to Vcc and the measurement to the internal 1.1V reference // #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) // ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) // ADMUX = _BV(MUX5) | _BV(MUX0); // #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = _BV(MUX3) | _BV(MUX2); // #else // ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // #endif delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Start conversion while (bit_is_set(ADCSRA, ADSC)); // measuring uint8_t low = ADCL; // must read ADCL first - it then locks ADCH uint8_t high = ADCH; // unlocks both long result = (high << 8) | low; result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 return result; // Vcc in millivolts }
      
      







スケッチへのコメント



このコードは、インターネットで見つかった関数とコードフラグメントを広範囲に使用します。 ソースへのリンクは、コードへのコメントで提供されます。



RTCでリアルタイムに記録するためのコードが1回実行された後、それを削除してスケッチを再読み込みします。 時計は非常に正確であり、将来の時刻修正は必要ありません。



コントローラは20秒ごとに非常に短い時間スリープモードから起動します(その1分を増やすことはかなり可能です)。 この時点で、RTCに電力が供給され、readVcc()関数とアラームALARM_1およびALARM_2のステータスを使用して電圧がチェックされます。 電圧が3.5 Vの設定しきい値を下回ると、短い音声信号がスピーカーに出力されます。 午後7時と午後7時に作動していたアラームのいずれかが作動した場合、音楽のメロディーがplayTune()関数で再生され、モーターがオンになります。 供給されたフィードの部分をカウントするとき、インペラが回転するときに閉じるインペラの接触バウンスのソフトウェア抑制が使用されます。 エネルギー消費を削減するには、接点が開いたときにモーターの回転を停止することが重要です。



デバイスは、5x7 cmの小さなボードに表面実装する方法で組み立てられました。







さて、結論として、猫に関する映画:






All Articles