📜 ⬆️ ⬇️

Animal Feeder with ATTiny85



Going on vacation last year, I decided to purchase an automatic pet feeder. The choice was made in favor of the product brand Animal Planet. I don’t remember exactly why this model was chosen, probably, at that time, it optimally combined the low price and fairly good consumer properties, which were compiled from reviews on the website amazon.com.

The device was soon received by mail, collected, turned on and feed was filled in it. The cats immediately approved the design, happily resorted to the noise of a motor and began to eat food before it even finished getting enough sleep from the top of the tank. All is good, if not for one “but”. I could not manage to get the feed to sleep in accordance with the established schedule, for which the matter was actually started. But inquisitive hands did not give up and it was experimentally found that the schedule sometimes works, but only on the condition that the real time was not set on the clock. Realizing that I was dealing with an obvious "bug", I turned to the seller and he happily assured that this is a known problem and I will replace the device for free with a newer version, deprived of this problem.

A few days passed, they sent a replacement. In the “new version” of the device, the “bug” was reproduced in full. Unfortunately, there was no time left for a hearing before departure, and feeding the cats was entrusted to friends. After returning, due to fallen relevance, the problem was abandoned and forgotten.
')
Choosing for myself an educational task for acquaintance with microcontrollers and the Arduino platform, I remembered about the forgotten feeding trough. It was decided to use the ready-made mechanism, to implement a feed supply system on a schedule, and also to test the theory of Pavlov’s conditioned reflex.

The feed dispenser mechanism is designed in such a way that for each complete rotation of the impeller there are 4 contact closures, which corresponds to 4 feed portions. By counting the number of contact closures, you can determine how much feed is poured into the plate.

Device prototyping and debugging was done on the Arduino Uno platform. To feed on a schedule on Ebay, the DS3231 real-time module was purchased with a built-in battery in which you can program two alarms.



The proposed algorithm will be simple. The controller will wake up periodically to check if any of the programmed alarms did not work, and then go to sleep to save energy. If the alarm goes off, the melody will be played and the required amount of feed will be poured. The implementation of the prototype on the Arduino did not cause difficulties and we will skip its consideration.

In the design of the donor feeder there is a compartment for 3 “D” batteries, from which the end device will work. To reduce energy consumption and prolong battery life, you need to get rid of the extra weight of the Arduino. The ATTiny85 controller was chosen for the development of the end device. The permissible supply voltage for it is in the range of 2.7-5.5V, so the use of voltage regulators is not required and the device can be powered directly from the battery.

When designing the final device, the following tasks were solved, which I would like to dwell upon:



The electrical circuit of the device.




The pins J1-J4, J8-10 are used to connect the Arduino Uno as a programmer. Jumpers J5-J7 - for disabling internal circuits when programming Attiny85. An N-channel field effect transistor with an insulated gate designed for a maximum current of 500 mA is used as a power element in the motor power supply circuit (the starting current of the motor does not exceed 150-200 mA). It is very important to connect a high-capacity smoothing capacitor (C2 in the diagram) to the VCC. Without it, especially when powered by a battery, when the engine starts, the program gets lost.

Sketch
#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 } 


Comments to the sketch


The code is widely used functions and code fragments found on the Internet. References to the sources are given in the comments to the code.

The code for recording real time in RTC runs once, then I remove it and reload the sketch again. The clock is quite accurate and time correction is no longer required.

The controller goes out of sleep mode for a very short time every 20 seconds (it is quite possible to increase that 1 minute). At this time, the power is supplied to the RTC, the supply voltage is checked using the readVcc () function and the status of the ALARM_1 and ALARM_2 alarms. When the voltage drops below the set threshold of 3.5 V, a short beep is output to the speaker. If any of the alarm clocks that are set for me at 7 o'clock in the evening and 7 o'clock in the morning, the musical melody is played with the playTune () function and the motor is turned on. When calculating portions of the sprinkled feed, a software suppression of contact bounce is made that closes when the impeller turns. To reduce energy consumption, it is important to stop the rotation of the motor at the time of contact opening.

The device was assembled by mounting on a small 5x7 cm board.



So in conclusion a movie about cats:

Source: https://habr.com/ru/post/254829/


All Articles