Кот будил меня в 6:03 каждое утро — не из злобы, а из расчёта: миска пустая, человек рядом. Отпуск на четыре дня сломал схему окончательно: соседка забывала, пакет корма на полу был опустошён к полудню первого дня, а дальше кот голодал с достоинством и местью. Автокормушка — тот редкий проект, где Arduino окупается за первую же неделю: серво за двести рублей, банка и полсотни строк кода закрывают вопрос графика питания навсегда. Расчёт у конструкции честный: бункер на пол-литра вмещает около трёхсот граммов сухого корма, коту на четыре-пять килограммов этого хватает на два-три дня, а серво делает всю работу, пока вы живёте своей жизнью.
Моя первая версия работала, но недолго. Я назначил закрытому положению угол 0°, где у серво механический упор, — и она каждые 20 мс билась в него под нагрузкой, греясь и жуя собственные шестерни. Через две недели шестерни облизались, заслонка повисла, кот снова победил. Вторая ошибка была на руку коту: мгновенно распахнутая заслонка выстреливала корм фонтаном мимо миски, и половина порции оказывалась на полу. В этом уроке соберём дозатор без обеих граблей: заслонка-сектор, углы с запасом от упора, выдача порции по расписанию на millis(). Электрическую базу берём готовой из урока про подключение SG90 и из урока про внешнее питание с общим минусом — там же написано, почему бункер с загрузкой нельзя питать от USB.
Заслонка против шнека
Пересмотрев десятки самоделок, я насчитал три механики. Самая народная — заслонка-сектор: у банки из-под чипсов или пластиковой бутылки в дне вырезают сектор, снизу ставят серво с качалкой; серво поворачивает диск, окно совпадает с горлом — порция сыплется. Это ровно наша схема: одна SG90, ноль токарных работ, сборка за вечер. Так сделана и половина гайдов в сети, и это неспроста: механика прощает кривые руки, а порция настраивается углом, без напильника.
Вторая механика — шнек: винт в трубе под бункером, каждый оборот подаёт ровно один шнек корма. Дозирует точнее и не даёт корму сыпаться лавиной, но требует редукторный мотор, контроль оборотов и борьбу с заклиниванием — для первого проекта я бы не брался. Третья — карусель с ячейками: диск с отверстиями, каждое отверстие вмещает порцию; честный компромисс, но круглые детали приходится печатать на 3D-принтере. И общий закон для всех трёх: только сухой корм. Влажный липнет к стенкам за сутки, комкуется в пробку и закисает — это не перестраховка, а физика.
Банку для первого бункера берите на 300–500 мл с широким горлом: корм в бункере живёт собственной жизнью, ему нужен объём, чтобы пересыпаться, а не столб, стоящий на заслонке. Дно — это сам сектор: диск из жести или пластика с вырезом в четверть круга, сидящий на качалке серво. Я свой первый вырез делал ножницами по жести и косял углы, пока не догадался: сектор должен быть чуть меньше внутреннего диаметра горла, чтобы кромка всегда перекрывала край — иначе корм сыплется по контуру, минуя окно. Крепёж — пара качалок из комплекта SG90 и термоклей; устройство самой коробочки, от редуктора до потенциометра обратной связи, мы разбирали отдельно в уроке про анатомию серво, пригодится, когда захотите понять, почему вал не крутится по кругу.
| Механика | Порция | Застревает | Что нужно |
|---|---|---|---|
| Заслонка-сектор | ширина окна дна, на крупном зерне — идеально | на мелкой щели и пыли | серво SG90, банка, нож |
| Шнек | один оборот = один шнек | жирные и мягкие гранулы | мотор с редуктором, труба |
| Карусель | ячейка диска | почти не застревает | печатный диск, серво |
Закрыто — это не ноль: углы и тайминги
Числа, которые я выстрадал на своих шестернях. Закрытое положение ставьте в 15–20° от нуля: на 0° у серво упор, и держать заслонку там — значит каждые 20 мс упираться мотором в пластик. Открытое — 60–75°: угол поворота и есть окно порции, чем шире открыто, тем больше проходит корма. Хотите порцию крупнее — меняете константу с 60 на 75 в скетче, а не механику. В этом главный козырь серво-дозатора перед самодельным тайником с верёвочкой: калибровка — это одна строка кода.
Время открытого состояния — 600–800 мс: при корме с гранулами 8–10 мм зерно успевает высыпаться целиком, проверено на котовом пакете; мелким гранулам 4–5 мм хватает и 500 мс. Держать открытым дольше не страшно, а вот открывать резко не надо: корм идёт фонтаном, пылит и рассыпается мимо миски. Если хочется плавности — шагайте угол по 5° каждые 20–30 мс, целый рецепт такого движения лежит в уроке про плавное движение серво. Порцию калибруйте на пустом прогоне с горстью корма: пересыпало — сузили окно на десять градусов, недосыпало — расширили; два прогона, и константа подобрана под вашу банку и ваш корм. И ещё одна деталь, о которую я спотыкался: качалку надевайте на вал при закрытом положении — так, чтобы сектор перекрывал горло без натяга. Если надеть наугад и потом выставлять углы скетчем, половина хода уйдёт на компенсацию кривой посадки.
Скетч дозатора
Расписание держим на millis(), не на delay(): у спящей на delay() версии не выйдет ни кнопки «покормить сейчас», ни Serial, ни правок тайминга на лету. Каркас простой: запоминаем millis() последней выдачи, и как только разница перевалила за период — выдаём порцию. Принцип такого планировщика без блокировки разобран в уроке про millis() вместо delay(), здесь применяем его как готовую деталь. Сравнение делаю вычитанием — millis() - poslednee >= PERIOD — а не сложением меток: переменная unsigned long переполняется на 49,7 суток, и вычитание остаётся честным даже через переполнение. В реальной кормушке я ставлю период в 12 часов — две выдачи в день, иначе кот стремительно превращается в бочку.
Второй канал — ручное кормление: любая строка из Serial вызывает выдачу немедленно. Это не украшение: когда кот смотрит на вас так, что расписание теряет власть, команда нужна без перепрошивки. Как принимать из порта не просто факт команды, а число — порция по запросу, с точностью до градуса — пригодится в уроке про числа из Serial, а в следующем уроке этого раздела мы доведём такой ввод до ума на этой же серво. И ещё счётчик порций в Serial: через день гляжу, сколько реально выдалось. Счёт растёт, а миска пустая — значит, порция не выпала, и это самый ранний симптом пробки: механика ломается раньше кода.
#include <Servo.h> Servo srv;const int ZAKRYTO = 15; // закрыто — в отрыве от упораconst int OTKRYTO = 75; // ширина окна = размер порцииconst unsigned long PERIOD = 4000; // демо: раз в 4 секунды // для кота: 43200000 — это 12 часовunsigned long poslednee = 0;int porcii = 0; void setup() { Serial.begin(9600); srv.attach(11); srv.write(ZAKRYTO); // при старте заслонка закрыта Serial.println("Kormushka gotova"); poslednee = millis();} void loop() { if (Serial.available() > 0) { // покормить сейчас while (Serial.available() > 0) Serial.read(); nakormit(); } if (millis() - poslednee >= PERIOD) { poslednee = millis(); nakormit(); }} void nakormit() { porcii = porcii + 1; Serial.print("Kormlenie N="); Serial.println(porcii); srv.write(OTKRYTO); delay(700); srv.write(ZAKRYTO); delay(400);}Что смотреть в симуляторе. Циферблат серво ходит между 15° и 75° и после каждой выдачи останавливается на 15° — если стрелка уходит в ноль, вы где-то поставили закрытие в 0° и воспроизвели мою ошибку с упором. В реальной сборке следующий шаг — научиться скармливать не просто факт команды, а число: открыть ровно на 60°, и порция стала меньше. Приём таких чисел — тема соседнего раздела, а в этом продолжении мы подробно разберём Serial.parseInt на той же серво. Дальше по вкусу: крутить размер порции потенциометром — рецепт ручного задания угла уже есть в уроке про потенциометр и серво — или повесить выдачу на ИК-пульт, до которого мы доберёмся третьим уроком. И практический совет из сборки: сигнал серво любит короткие провода — если бункер стоит далеко от платы, удлиняйте питание с запасом сечения, а сигнальный протягивайте цельным проводом, иначе словите дёрганья, о которых у нас есть отдельный урок. Механику для реального кота я бы всё же сначала погонял пустой банкой: счётчик в мониторе — это удобно, но ни одна строка кода не заменит взгляда на то, как зерно реально сыплется в миску.
- 1. Почему закрытое положение ставят в 15–20°, а не в 0°?
- 2. Кот ест дважды в день. Какая строка задаёт период выдачи в 12 часов?
- 3. Серво при выдаче гудит и не доходит до открытого положения. Первое подозрение?
- 4. Зачем выдача держится открытой 600–800 мс?
- 5. Влажный корм в бункере автокормушки — это…
- Порция — это угол: закрыто 15–20°, открыто 60–75°, константа в скетче.
- Период — на millis() с вычитанием; сутки = 86400000 мс.
- Выдача: открыли, 600–800 мс, закрыли; резко — фонтан.
- Серво гудит и не доезжает — пробка в горле, а не баг в коде.
- Счётчик в Serial: растёт, а миска пуста — механика.
Частые вопросы по теме урока
- Почему закрытое положение ставят в 15–20°, а не в 0°?
- На 0° и 180° стоит упор: серво каждые 20 мс бьётся в него с перегрузом, шестерни облизываются за недели. Маленький отступ — дешёвая страховка. На 0° серво упирается в механический упор, греется и изнашивает шестерни
- Кот ест дважды в день. Какая строка задаёт период выдачи в 12 часов?
- 12 часов — это 12 x 3600 x 1000 = 43 200 000 миллисекунд. Вариант с delay() выглядит похоже, но замораживает скетч на полсуток. const unsigned long PERIOD = 43200000;
- Серво при выдаче гудит и не доходит до открытого положения. Первое подозрение?
- Гудение под нагрузкой — почерк механики: пробка в горле, узкая щель или подпор. Ток при этом 650–700 мА, до просадки USB недалеко. Горло забилось пробкой корма — серво упирается в засор
- Зачем выдача держится открытой 600–800 мс?
- При гранулах 8–10 мм корм пересыпается за 600–800 мс. Резкое короткое окно оставляет полпорции в бункере, долгое просто бессмысленно. Чтобы зерно успело высыпаться из сектора
- Влажный корм в бункере автокормушки — это…
- Влажный корм за сутки слёживается в пробку и закисает. Все три механики дозатора рассчитаны на сухое зерно с сыпучестью. Комки и пробка: липнет и закисает — годится только сухой