[Из песочницы] Кормушка для животных с применением ATTiny85

AutomaticCatFeeder-69991.jpgСобираясь в прошлом году в отпуск, решил приобрести автоматическую кормушку для домашних питомцев. Выбор был сделан в пользу изделия брэнда Animal Planet. Уже точно не помню, почему именно была выбрана эта модель, вероятно, на тот момент времени она оптимально сочетала в себе невысокую цену и достаточно хорошие потребительские свойства, представление о которых были составлены по отзывам на веб сайте amazon.com.

Устройство было в скором времени получено по почте, собрано, включено и в него был засыпан корм. Кошки сразу одобрили дизайн, радостно прибегали на шум моторчика и принимались поедать корм еще до того, как он заканчивал высыпаться сверху из бачка. Все бы хорошо, если бы не одно «но». Мне никак не удавалось добиться того, чтобы корм высыпался по установленному расписанию, ради чего собственно и затевалось дело. Но пытливые руки не сдавались и экспериментально было выяснено, что расписание иногда работает, правда, только при условии, что изначально не устанавливалось реальное время на часах. Поняв, что имею дело с явным «багом», обратился к продавцу и тот радостно заверил, что это известная проблема и мне бесплатно заменят устройство более новой версией, лишенной указанной проблемы.Прошло несколько дней, прислали замену. В «новой версии» прибора «баг» воспроизвелся в полном объеме. К сожалению, времени на разбирательство до отъезда не оставалось и кормление кошек было поручено друзьям. После возвращения, в силу упавшей актуальности, проблема была заброшена и забыта.

Выбирая для себя учебную задачу для ознакомления с микроконтроллерами и платформой Arduino, вспомнил про забытую кормушку. Было решено использовать уже готовый механизм, реализовать систему подачу корма по расписанию, а также проверить теорию условного рефлекса Павлова.

Механизм диспенсера корма устроен таким образом, что на каждый полный поворот крыльчатки приходится 4 замыкания контакта, что соответствует 4 порциям корма. Подсчитывая число замыканий контактов можно определить, сколько корма высыпалось в тарелку.

Прототипирование устройства и отладка производилась на платформе Arduino Uno. Для подачи корма по расписанию на Ebay был приобретен модуль реального времени DS3231 cо встроенной батарейкой, в котором можно запрограммировать два будильника.

21b2a6d67f084227aa9b5a8fe662fc09.jpg

Предполагаемый алгоритм будет простым. Контроллер будет периодически просыпаться, чтобы проверить, не сработал какой-либо из запрограммированных будильников, и засыпать дальше для экономии энергии. В случае срабатывания будильника будет проиграна мелодия и высыпано требуемое количество корма. Реализация прототипа на Arduino трудностей не вызвала и ее рассмотрение мы пропустим.

В конструкции донорской кормушки есть отсек для 3-х батареек «D», от которых будет работать конечное устройство. Для уменьшения потребляемой энергии и продления жизни батареек нужно избавиться от лишнего обвеса Arduino. Для разработки конечного устройства был выбран контроллер ATTiny85. Допустимое напряжение питания для него находится в диапазоне 2.7–5.5V, поэтому применения регуляторов напряжения не потребуется и устройство может питаться напрямую от батарейки.

При проектировании конечного устройства были решены следующие задачи, на которых хотелось бы остановиться подробнее:

Уменьшение потребления энергии. Достигается переводом контроллера в спящий режим и обесточивания ненужных в данный момент цепей, в частности, питание на модуль RTC подается только на время, необходимое для операций с этим модулем. В моей реализации потребляемый устройством ток в режиме ожидания составляет примерно 300 mkA. Решение проблемы с недостаточным числом выводов контроллера. Обычно это решается добавлением сдвигового регистра или переназначением функции вывода Reset. В моем случае было возможным использовать один из выводов поочередно для вывода звукового сигнала на динамик и чтения состояния контакта. Кроме того, были использованы перемычки для возможности отключения внутренних цепей устройства при переключении контроллера в режим программирования. Принципиальная электрическая схема устройства.2f458dbff5a349e09fc6786e88b72596.pngКонтакты J1-J4, J8–10 служат для подключения Arduino Uno в качестве программатора. Перемычки J5-J7 — для отключения внутренних цепей при программировании Attiny85. В качестве силового элемента в цепи питания электродвигателя используется N-канальный полевой транзистор с изолированным затвором рассчитанным на максимальный ток 500 mA (пусковой ток мотора не превышает 150–200 mA). Очень важно подключить сглаживающий конденсатор (С2 на схеме) большой емкости к VCC. Без него, особенно при питании от батарейки, в момент запуска мотора программа сбивается.

Скетч #include //http://github.com/JChristensen/DS3232RTC #include //http://playground.arduino.cc/Code/Time #include //https://github.com/adafruit/TinyWireM #include //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 = 0×90 | (8-octave); // for 1MHz clock TCCR1 = 0×90 | (11 — octave); // for 8MHz clock OCR1C = divisor — 1; // set the OCR delay (duration); TCCR1 = 0×90; // 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 запускается один раз, затем я его убираю и перезаливаю скетч заново. Часы достаточно точные и коррекции времени в дальнейшем не потребуется.

Контроллер выходит из спящего режима на очень короткое время каждые 20 секунд (вполне можно увеличить то 1 минуты). В это время подается питание на RTC, производится проверка напряжения питания при помощи функции readVcc () и состояние будильников ALARM_1 и ALARM_2. При понижении напряжения ниже установленного порога 3.5 V на динамик выводится короткий звуковой сигнал. Если сработал любой из будильников которые установлены у меня на 7 часов вечера и 7 часов утра, то проигрывается музыкальная мелодия функцией playTune () и включается мотор. При подсчете порций высыпанного корма используется программное подавление дребезга контакта замыкающегося при повороте крыльчатки. Для уменьшения потребления энергии важно остановить вращение мотора в момент размыкания контактов.

Устройство было собрано методом навесного монтажа на небольшой плате размером 5×7 см.

69fc003271cc433887fe6bb7e45b60c0.jpg

Ну и в заключение кино про котиков:

[embedded content]

© Habrahabr.ru