Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Компоненты и материалы

Arduino Nano R3 1
Adafruit Real Time Clock (DS3231) 1
Сервопривод непрерывного вращения 1
Цереальный дозатор (сухой корм) 1
Датчик Холла униполярный 1
Стержневые магниты 3мм диаметр, 5мм длина 6
Adafruit RGB LCD 16x2 с подсветкой 1
I2C модуль для LCD (backpack) 1
Кнопка Apem momentary IP67 1
Маленькие тактовые кнопки 1
Светодиоды (общие) 6
Корпус Hammond Multi ABS с фланцем 1

Приложения и онлайн-сервисы

LCD character generator

О проекте

Обновление 2018-09-06

Мои две кормушки работают безупречно уже 7 месяцев. Мы заботимся о питомцах днём, но теперь они получают корм строго по расписанию. Очень доволен системой!

Зачем ещё одна кормушка?

Моя версия кормушки основана на микроконтроллере Arduino. Материалы относительно дешёвы, а сборка — проста!

Конечно, существует много вариантов, но я не нашёл готового проекта с нужными мне функциями, поэтому начал с нуля.

Функции:

Видео-демонстрации

Фотографии

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Необходимые детали

Лучше использовать более высокое напряжение для сервопривода, если возможно. Больше мощь! Мой HSR-2645CRH выдерживает 7.4V, для этого нужно:

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Онлайн есть калькуляторы LM317.

Контейнер для корма

Самые дешёвые и простые — céréal dispensers (дозаторы для сухих завтраков) в магазинах и на eBay. Купил двойной дозатор на eBay за 15 евро с доставкой (UK → Нидерланды).

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Сложность — сделать крепление. В интернете не нашёл помощи, полез в сарай и нашёл красивые куски кедрового дерева.

Деревянная основа 3 см толщины — никакой качки! Отверстие под основание дозатора (у меня 86 мм) должно быть точным — лучше просверлить меньше и доработать, чем слишком большое.

Смотрите фото, чтобы понять принцип сборки.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Сервопривод и крепление к палочке дозатора

Покупайте качественный сервопривод. Сначала взял MG996R и модифицировал для непрерывного вращения — деньги на ветер. Потом взял качественный HSR-2645CRH.

ОБЯЗАТЕЛЬНО модель непрерывного вращения!! Не пытайтесь модифицировать обычный сервопривод — плохо работает и не останавливается мгновенно.

Видео тестов сервопривода: кликните сюда

Крепление сервопривода к палочке дозатора

Казалось, самая сложная часть, но оказалась простой.

Взял кусок поликарбоната 5 мм, ножовкой вырезал диск ~6 см. В центре отверстие 8 мм (толщина оси дозатора. Пластиковая ось извлекается из палочки).

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

По краю 6 отверстий 3 мм под магниты. У палочки 6 резиновых лопастей — 6 магнитов, равномерно по 60°. Стержневые магниты вставляются плотно.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Датчик Холла фиксирует каждый из 6 магнитов — точно позиционируем и останавливаем палочку. Переменная задержки остановки сервопривода (servo stop delay) добавляет задержку после кормления, перед остановкой. Так можно точно настроить позицию палочки. Если «стоп» неверен, корм не выпадет полностью. Позиция датчика Холла может отличаться — подстройка задержки решит проблему.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Клеим ось палочки в диск поликарбоната.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Белый рог сервопривода болтится к диску 3 болтами M3. Используйте гайки как распорки и не затягивайте сильно!

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Теперь сервопривод часть дозатора — устанавливаем в деревянное крепление и измеряем высоту подставки под сервопривод.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Датчик Холла

Использован униполярный датчик Холла. Выдаёт LOW при приближении магнита и НЕ требует обратной поляризации для сброса в HIGH. Униполярный — проще.

!!! Датчик Холла требует 10кОм подтягивающий резистор.

Припаял SMD 0805 резистор прямо на датчик, но можно и по-своему. Не забывайте этот резистор!

Корпус от старой шариковой ручки использован как держатель датчика.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Корпус электроники

Корпус Hammond ABS с фланцем. 120 x 65 x 40 мм.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Для внешних соединений — точные гнездовые разъёмы, не дешёвые китайские. Использую во всех проектах. Ничего хуже плохих контактов, вызывающих трудноуловимые сбои.

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Для удобства прошивки Arduino — внешнее FTDI соединение:

Умный кормушка для домашних животных на Arduino Nano — Соберите сами
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Фронтальная панель

Пробовал много способов сделать дешёвую и красивую переднюю панель...

  1. Mod Podge для клеивания лазерной печати на корпус и покрытия сверху — ПРОВАЛ
  2. Прозрачный лак на основе полиуретана/акрилата. Очень прочный. Бумага отлично приклеилась, лакирование выглядело супер, НО не прилипало к тонеру лазерного принтера — ПРОВАЛ
  3. Лазерная печать передней панели, ламинация только верхней стороны PVC. Отличный вид, защита, красивые цвета. Потом клеил ламинированную бумагу на корпус лаком. Идея: лак пропиткает бумагу, делая её водостойкой. Но толстая бумага не пропускала лак, при тесте на воду бумага отслаивалась — ЧАСТИЧНЫЙ ПРОВАЛ, требует доработки
  4. Специальная алюминиевая фольга для лазерной печати. 2.25 евро/лист. Результат отличный, но хотел защитить тонер прозрачной фольей. Не получилось из-за дефектов (фото 1):
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Потом попытка защитить лаком — фольга стала сверхглянцевой! НО... лак снова не прилипал к тонеру. Забил на лак навсегда...

ИТОГО... Использовал фольгу без защиты... Показывает отпечатки пальцев при частом контакте (см. под кнопкой BACK на фото вверху, корпус в корме для кур) — не идеально, но достаточно для меня.

Фото больше не загрузить, видимо...

Всё, ребята... удачи!

Код

Pet Feed-O-Matic v 1.1 - 20-02-2018Arduino

v 1.1: нажатие Cancel1 или Cancel2 показывает объяснение

// c++ stuff... 
//#include <Arduino.h> 
//#line 1 
//#line 1 "/Users/Erik/Documents/PlatformIO/Projects/180202-151127-uno/src/Pet_Feeder_1_1_ENG.cpp" 
/* 
 _ _ /\ | | (_) / \ _ __ __| |_ _ _ _ __ ___ / /\ \ | '__/ _` | | | | | '_ \ / _ \ / ____ \| | | (_| | |_| | | | | | (_) | /_/ \_\_| \__,_|\__,_|_|_| |_|\___/ 
_____ _ ______ _ 
| __ \ | | | ____| | | | |__) |__| |_ | |__ ___ ___ __| | ___ _ __ 
| ___/ _ \ __| | __/ _ \/ _ \/ _` |/ _ \ '__| 
| | | __/ |_ | | | | __/ __/ (_| | __/ | 
|_| \___|\__| |_| \___|\___|\__,_|\___|_| 
Erik de Ruiter 
-----------------------------------------------------------------------------
To do:
- remote feeding?
- feeder stop delay as menu editable item
last change: dinsdag 20 februari 2018 - 17:20:28
FEATURES:
- *Accurate portions* each time! (by using a Hall sensor)
- Two feeding times, once a day
- *Extremely* accurate Real Time Clock (Only with genuine DS3231 chip)
- Cancel upcoming feeding individually for both timers with display and Led indicator. Automatically reset after time has passed.
- Manual feed function (one portion per button press)
- Adjustable portions for each of the two feeding times (1-9 portions)
- Overview of all set parameters in the main screen
- Easy to navigate menu system
- LED indication if feeding was successful
- LCD backlight timer (off after 30 sec, on with any button press)
- Backup in the event of a failing Hall sensor
- Hass sensor LED will blink until midnight if the Hall sensor has failed
- Time and other settings are safely stored in EEPROM
WEBSITES:
LCD HD44780 character generator website to make your own lcd symbols
https://omerk.github.io/lcdchargen/
Arduino sketch Large letter comment generator
https://patorjk.com/software/taag/#p=display&c=c%2B%2B&f=Big&t=Comment
Revisions:
zondag 28 januari 2018 - 20:17:10 
*/ 
//////////////////////////////////////////////////////////////////////////////// 
// USER CHANGEABLE VARIABLES 
//////////////////////////////////////////////////////////////////////////////// 
// used for the Hall sensor fail backup function. 
// the interval time should be somewhat larger than one 60 degree 
// turn of the feeder (one portion) 
#define HALL_SENSOR_BACKUP_VALUE 300 
// delay before stopping the feeder servo after feeding. This way you can 
// let the rotating peddle wheel stop at the right position 
#define FEEDER_STOP_DELAY 100 
// lcd backlight on time after pressing a button 
#define LCD_BACKLIGHT_ON_TIME 30000 
// exit menu without save after amount of time set here in ms 
#define MENU_TIMEOUT_VALUE 7000 
//////////////////////////////////////////////////////////////////////////////// 
// https:// github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library 
#include <LiquidCrystal_I2C.h> 
// https:// github.com/JChristensen/Button 
#include <Button.h> 
// https:// github.com/JChristensen/DS3232RTC 
#include <DS3232RTC.h> 
// https:// www.arduino.cc/playground/Code/Time 
#include <Time.h> 
// https:// arduino.cc/en/Reference/Wire (included with Arduino IDE) 
#include <Wire.h> 
#include <EEPROM.h> 
// CONNECTIONS: 
// 
// LCD (I2C module): 
// SCL - A5 
// SDA - A4 
// VCC 
// GND 
// interrupt hal sensor pin 
#define HALL_SENSOR_PIN 3 
#define BUTTON_BACK_PIN 4 
#define BUTTON_UP_PIN 5 
#define BUTTON_DOWN_PIN 6 
#define BUTTON_SELECT_PIN 7 
#define BUTTON_CANCEL1_PIN 8 
#define BUTTON_CANCEL2_PIN 9 
#define BUTTON_MANUAL_PIN 10 
#define LED_CANCEL1_PIN A0 
#define LED_CANCEL2_PIN A1 
#define LED_SUCCESS1_PIN A2 
#define LED_SUCCESS2_PIN A3 
// feeder Servo output Pin 
#define SERVO_OUTPUT_PIN 12 
// Hall sensor input Pin 
#define LED_HALL_SENSOR_PIN 13 
// address LCD(16x2) 0x27 or 0x3F 
LiquidCrystal_I2C lcd(0x3F, 16, 2); 
// define button library settings 
// A debounce time of 20 milliseconds usually works well 
// for tactile button switches. 
#define DEBOUNCE_MS 20 
// ms required before repeating on long press 
#define REPEAT_FIRST 1000 
// repeat interval for long press 
#define REPEAT_INCR 200 
// To keep things simple, we use the Arduino's internal pullup resistor. 
#define PULLUP true 
#define INVERT true 
// Declare the buttons 
Button buttonSelect (BUTTON_SELECT_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonUp (BUTTON_UP_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonDown (BUTTON_DOWN_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonBack (BUTTON_BACK_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonCancel1 (BUTTON_CANCEL1_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonCancel2 (BUTTON_CANCEL2_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
Button buttonManual (BUTTON_MANUAL_PIN, PULLUP, INVERT, DEBOUNCE_MS); 
// The number that is adjusted 
int count; 
// Previous value of count (initialized to ensure it's different when 
// the sketch starts) 
int lastCount = -1; 
// A variable time that is used to drive the repeats for long presses 
unsigned long rpt = REPEAT_FIRST; 
// used for the menu time-out 
unsigned long timeoutValue = 0; 
// manual cancel feeding times variables 
boolean manualCancelFeed1 = false; 
boolean manualCancelFeed2 = false; 
// Manual feed option 
boolean manualFeed = false; 
// Feed amount (in portions) 
int feedAmount1 = 1; 
int feedAmount2 = 1; 
bool feederSuccess = false; 
// feeder portions 
int portions = 0; 
int turns = 0; 
// input actions 
enum {btnSELECT, btnUP, btnDOWN, btnBACK, btnCANCEL1, btnCANCEL2, btnMANUAL, trigTIMEOUT}; 
// States of the Finite State Machine (FSM) 
enum STATES { 
  MAIN, MENU_EDIT_FEEDTIME1, MENU_EDIT_FEEDTIME2, MENU_EDIT_FEEDAMOUNT, 
  MENU_EDIT_TIME, MENU_EDIT_DATE, MENU_EDIT_SETTINGS, 
  EDIT_FEED_TIME1_HOUR, EDIT_FEED_TIME1_MINUTE, EDIT_FEED_TIME1_ON_OFF, 
  EDIT_FEED_TIME2_HOUR, EDIT_FEED_TIME2_MINUTE, EDIT_FEED_TIME2_ON_OFF, 
  EDIT_FEED_AMOUNT1, EDIT_FEED_AMOUNT2, 
  EDIT_HOUR, EDIT_MINUTE, EDIT_DAY, EDIT_MONTH, EDIT_YEAR, 
  EDIT_SERVO_STOP_DELAY, EDIT_SERVO_BACKUP_DELAY, 
}; 
// Holds the current state of the system 
STATES state; 
int8_t userInput; 
int8_t trigger; 
int Second; 
int Minute; 
int Hour; 
int Day; 
int Month; 
int Year; 
int8_t DoW; 
String day_of_week; 
unsigned char address, data; 
int testt = 0; 
int feed_time1_hour; 
int feed_time1_minute; 
bool feed_time1_active = false; 
bool alarm1Activated = false; 
int feed_time2_hour; 
int feed_time2_minute; 
bool feed_time2_active = false; 
bool alarm2Activated = false; 
// used for the blink function when editing values 
uint32_t blink_interval = 500; 
uint32_t blink_previousMillis = 0; 
uint32_t blink_currentMillis = 0; 
boolean blink_state = false; 
// used for the blink function when editing values 
uint32_t spinningWheel_interval = 170; 
uint32_t spinningWheel_previousMillis = 0; 
uint32_t spinningWheel_currentMillis = 0; 
int spinningWheelSymbol = 0; 
// used for the Hall sensor fail backup function 
// the interval time should be somewhat larger than one 60 degree 
// turn of the feeder (one portion). 
uint32_t hallSensorBackup_interval = HALL_SENSOR_BACKUP_VALUE; 
uint32_t hallSensorBackup_currentMillis = 0; 
boolean hallSensorFail = false; 
// used for the lcd backlight timer 
uint32_t lcdBacklight_interval = LCD_BACKLIGHT_ON_TIME; 
uint32_t lcdBacklight_currentMillis = 0; 
boolean RTC_error = true; 
boolean long_press_button = false; 
// Define custom symbols for the LCD display 
byte bell_symbol_Char[8] = { B00100, B01110, B01110, B01110, B11111, B00100, B00000, B00000 }; 
byte inverted_one_Char[8] = { 0b11111, 0b11011, 0b10011, 0b11011, 0b11011, 0b11011, 0b10001, 0b11111 }; 
byte inverted_two_Char[8] = { 0b11111, 0b11011, 0b10101, 0b11101, 0b11011, 0b10111, 0b10001, 0b11111 }; 
byte arrow_up_Char[8] = { 0b00100, 0b01110, 0b11111, 0b01110, 0b01110, 0b01110, 0b01110, 0b00000 }; 
byte arrow_down_Char[8] = { 0b00000, 0b01110, 0b01110, 0b01110, 0b01110, 0b11111, 0b01110, 0b00100 }; 
byte inverted_p_Char[8] = { 0b11111, 0b10001, 0b10101, 0b10001, 0b10111, 0b10111, 0b11111, 0b00000 }; 
byte backslash_Char[8] = { 0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000, 0b00000 }; 
byte thickDash_Char[8] = { 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b00000, 0b00000, 0b00000 }; 
volatile boolean hallSensorActivated = false; 
// Interrupt 1 
void HallSensorIsr() { 
  hallSensorActivated = true; 
  // turn on Hall sensor LED 
  digitalWrite(LED_HALL_SENSOR_PIN, HIGH); 
} 
// These lines are needed to make this sketch a C++ file. 
// I use it because I edit the code with the Atom 
// text editor and the PlatformIO add-on 
void HallSensorIsr(); 
void setup(); 
void loop(); 
void change_states(); 
void check_inputs(); 
void transition(int trigger); 
void check_alarm(); 
void check_manual_feed(); 
void display_menu_option_set_feedtime1(); 
void display_menu_option_set_feedtime2(); 
void display_menu_option_set_feed_amount(); 
void display_menu_option_set_time(); 
void display_menu_option_set_date(); 
void midnight_reset(); 
void display_time(); 
void displayFeedingAmouts(); 
void displayFeedingTimes(); 
void set_feedAmount(); 
void set_time(); 
void set_date(); 
void set_feeding1_time(); 
void set_feeding2_time(); 
void get_time(); 
void get_date(); 
void write_time(); 
void write_date(); 
void write_feeding_time1(); 
void write_feeding_time2(); 
void write_feedamount(); 
void get_feedamount(); 
void get_feed_time1(); 
void get_feed_time2(); 
void check_RTC(); 
byte decToBcd(byte val); 
byte bcdToDec(byte val); 
void leading_zero(int digits); 
void blinkFunction(); 
void displaySpinningWheel(); 
void startFeederServo(); 
void stopFeederServo(); 
void activateFeeder(int portions); 
void check_LcdBacklight(); 
void lcd_backlight_ON(); 
void ledsAndLcdDisplayStartup(); 
void hallSensorCheck(); 
#line 355 
// ****************************************************************************** 
// SETUP 
void setup() { 
  // activate the lcd screen 
  lcd.begin(); 
  // turn lcd backlight on and start backlight off-timer 
  lcd_backlight_ON(); 
  // start I2C 
  Wire.begin(); 
  // hall sensor input to detect rotation of the feeder/feed amount output check pin 
  pinMode(HALL_SENSOR_PIN, INPUT_PULLUP); 
  pinMode(LED_HALL_SENSOR_PIN, OUTPUT); 
  pinMode(SERVO_OUTPUT_PIN, OUTPUT); 
  pinMode(LED_CANCEL1_PIN, OUTPUT); 
  pinMode(LED_CANCEL2_PIN, OUTPUT); 
  pinMode(LED_SUCCESS1_PIN, OUTPUT); 
  pinMode(LED_SUCCESS2_PIN, OUTPUT); 
  // set default state of LED's to OFF 
  digitalWrite(LED_CANCEL1_PIN, LOW); 
  digitalWrite(LED_CANCEL2_PIN, LOW); 
  digitalWrite(LED_SUCCESS1_PIN, LOW); 
  digitalWrite(LED_SUCCESS2_PIN, LOW); 
  digitalWrite(LED_HALL_SENSOR_PIN, LOW); 
  lcd.createChar(0, thickDash_Char); 
  lcd.createChar(1, bell_symbol_Char); 
  lcd.createChar(2, backslash_Char); 
  lcd.createChar(3, inverted_p_Char); 
  lcd.createChar(4, inverted_one_Char); 
  lcd.createChar(5, inverted_two_Char); 
  lcd.createChar(6, arrow_up_Char); 
  lcd.createChar(7, arrow_down_Char); 
  // set feeder Servo to default state OFF 
  stopFeederServo(); 
  Wire.begin(); 
  // set RTC as the Syncprovider 
  setSyncProvider(RTC.get); 
  // time in sec of resync with RTC 
  setSyncInterval(60); 
  // Disable the default square wave of the SQW pin. 
  RTC.squareWave(SQWAVE_NONE); 
  // Attach an interrupt on the Hall sensor (when input turns LOW) 
  // every 60 degree turn of the feeder shaft, the hall sensor 
  // should generate an interrupt 
  attachInterrupt(INT1, HallSensorIsr, FALLING); 
  // display test 
  ledsAndLcdDisplayStartup(); 
  // Initial state of the FSM 
  state = MAIN; 
  // read the stored alarm value from the Arduino memory 
  get_feed_time1(); 
  get_feed_time2(); 
}// End SETUP 
// ****************************************************************************** 
// LOOP 
void loop() { 
  // change states of FSM 
  change_states(); 
  // check inputs (buttons) 
  check_inputs(); 
  // check if alarm was called 
  check_alarm(); 
  // check if manual feed was requested 
  check_manual_feed(); 
  // at midnight, reset some variables 
  midnight_reset(); 
  // check connection RTC 
  check_RTC(); 
  // Check the Hall sensor function 
  hallSensorCheck(); 
  // check if lcd backlight must be turned off 
  check_LcdBacklight(); 
}// End of LOOP 
// ****************************************************************************** 
// ****************************************************************************** 
// ****************************************************************************** 
// ****************************************************************************** 
//****************************************************************************** 
// Finite State Machine 
void change_states() { 
  // states 
  switch (state) { 
  //--------------------------------------- 
  case MAIN: 
    display_time(); 
    displayFeedingAmouts(); 
    displayFeedingTimes(); 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDTIME1: 
    display_menu_option_set_feedtime1(); 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDTIME2: 
    display_menu_option_set_feedtime2(); 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDAMOUNT: 
    display_menu_option_set_feed_amount(); 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_TIME: 
    display_menu_option_set_time(); 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_DATE: 
    display_menu_option_set_date(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME1_HOUR: 
    set_feeding1_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME1_MINUTE: 
    set_feeding1_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME1_ON_OFF: 
    set_feeding1_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME2_HOUR: 
    set_feeding2_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME2_MINUTE: 
    set_feeding2_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME2_ON_OFF: 
    set_feeding2_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_AMOUNT1: 
    set_feedAmount(); 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_AMOUNT2: 
    set_feedAmount(); 
    break; 
  //--------------------------------------- 
  case EDIT_HOUR: 
    set_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_MINUTE: 
    set_time(); 
    break; 
  //--------------------------------------- 
  case EDIT_DAY: 
    set_date(); 
    break; 
  //--------------------------------------- 
  case EDIT_MONTH: 
    set_date(); 
    break; 
  //--------------------------------------- 
  case EDIT_YEAR: 
    set_date(); 
    break; 
  //--------------------------------------- 
  } 
} 
//****************************************************************************** 
// Check INPUTS 
void check_inputs() { 
  // first check if timeout has occurred 
  if ( millis() - timeoutValue > MENU_TIMEOUT_VALUE ) { 
    userInput = trigTIMEOUT; 
    transition(userInput); 
  } 
  // check state of buttons 
  buttonSelect.read(); 
  buttonUp.read(); 
  buttonDown.read(); 
  buttonBack.read(); 
  buttonManual.read(); 
  buttonCancel1.read(); 
  buttonCancel2.read(); 
  // check manual cancel Feed1 button 
  switch (buttonCancel1.wasPressed()) { 
  case 1: 
    // invert variable value (true to false and vise versa) 
    manualCancelFeed1 = !manualCancelFeed1; 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    // message when Cancel1 button is pressed 
    if (manualCancelFeed1 == 1) { 
      lcd.clear(); 
      lcd.setCursor(0, 0); 
      // 0123456789012345 - LCD screen character counter 
      lcd.print("Upcoming Feed #1"); 
      lcd.setCursor(0, 1); 
      lcd.print("cancelled once "); 
      delay(2000); 
      lcd.clear(); 
    } else if (manualCancelFeed1 == 0) { 
      lcd.clear(); 
      lcd.setCursor(0, 0); 
      // 0123456789012345 - LCD screen character counter 
      lcd.print("Upcoming Feed #1"); 
      lcd.setCursor(0, 1); 
      lcd.print("canceling undone"); 
      delay(2000); 
      lcd.clear(); 
    } 
    break; 
  } 
  // check manual cancel Feed2 button 
  switch (buttonCancel2.wasPressed()) { 
  case 1: 
    // invert variable value (true to false and vise versa) 
    manualCancelFeed2 = !manualCancelFeed2; 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    // message when Cancel1 button is pressed 
    if (manualCancelFeed2 == 1) { 
      lcd.clear(); 
      lcd.setCursor(0, 0); 
      // 0123456789012345 - LCD screen character counter 
      lcd.print("Upcoming Feed #2"); 
      lcd.setCursor(0, 1); 
      lcd.print("cancelled once "); 
      delay(2000); 
      lcd.clear(); 
    } else if (manualCancelFeed2 == 0) { 
      lcd.clear(); 
      lcd.setCursor(0, 0); 
      // 0123456789012345 - LCD screen character counter 
      lcd.print("Upcoming Feed #2"); 
      lcd.setCursor(0, 1); 
      lcd.print("canceling undone"); 
      delay(2000); 
      lcd.clear(); 
    } 
    break; 
  } 
  // check manual Feed button 
  switch (buttonManual.wasPressed()) { 
  case 1: 
    manualFeed = true; 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    break; 
  } 
  // check MENU/SELECT button 
  switch (buttonSelect.wasPressed()) { 
  case 1: 
    userInput = btnSELECT; 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    transition(userInput); 
    break; 
  } 
  // check UP button 
  switch (buttonUp.wasPressed()) { 
  case 1: 
    userInput = btnUP; 
    transition(userInput); 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    break; 
  } 
  // check long press UP button 
  switch (buttonUp.wasReleased()) { 
  case 1: 
    long_press_button = false; 
    rpt = REPEAT_FIRST; 
    break; 
  } 
  switch (buttonUp.pressedFor(rpt)) { 
  case 1: 
    // increment the long press interval 
    rpt += REPEAT_INCR; 
    long_press_button = true; 
    userInput = btnUP; 
    transition(userInput); 
    break; 
  } 
  // check DOWN button 
  switch (buttonDown.wasPressed()) { 
  case 1: 
    userInput = btnDOWN; 
    transition(userInput); 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    break; 
  } 
  // check long press DOWN button 
  switch (buttonDown.wasReleased()) { 
  case 1: 
    long_press_button = false; 
    rpt = REPEAT_FIRST; 
    break; 
  } 
  switch (buttonDown.pressedFor(rpt)) { 
  case 1: 
    // increment the long press interval 
    rpt += REPEAT_INCR; 
    long_press_button = true; 
    userInput = btnDOWN; 
    transition(userInput); 
    break; 
  } 
  // check btnBACK button 
  switch (buttonBack.wasPressed()) { 
  case 1: 
    userInput = btnBACK; 
    transition(userInput); 
    //turn on lcd backlight manually 
    lcd_backlight_ON(); 
    break; 
  } 
} 
//****************************************************************************** 
// Check for state transition trigger 
void transition(int trigger) { 
  switch (state) { 
  //--------------------------------------- 
  case MAIN: 
    // set time-out timer 
    timeoutValue = millis(); 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDTIME1; 
    } else if (trigger == btnBACK) { 
      //lcd.clear(); 
      //state = ALARM1_AND_2_TIME; 
    } 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDTIME1: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers 
    if (trigger == btnUP) { 
      //no action, this is the first menu 
    } else if (trigger == btnDOWN) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDTIME2; 
    } 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = EDIT_FEED_TIME1_HOUR; 
    } 
    if (trigger == btnBACK) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDTIME2: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers 
    if (trigger == btnUP) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDTIME1; 
    } else if (trigger == btnDOWN) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDAMOUNT; 
    } 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = EDIT_FEED_TIME2_HOUR; 
    } 
    if (trigger == btnBACK) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_FEEDAMOUNT: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers 
    if (trigger == btnUP) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDTIME2; 
    } else if (trigger == btnDOWN) { 
      lcd.clear(); 
      state = MENU_EDIT_TIME; 
    } 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = EDIT_FEED_AMOUNT1; 
    } 
    if (trigger == btnBACK) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_TIME: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers 
    if (trigger == btnUP) { 
      lcd.clear(); 
      state = MENU_EDIT_FEEDTIME2; 
    } 
    if (trigger == btnDOWN) { 
      lcd.clear(); 
      state = MENU_EDIT_DATE; 
    } 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = EDIT_HOUR; 
    } 
    if (trigger == btnBACK) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    break; 
  //--------------------------------------- 
  case MENU_EDIT_DATE: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers 
    if (trigger == btnUP) { 
      lcd.clear(); 
      state = MENU_EDIT_TIME; 
    } else if (trigger == btnDOWN) { 
      //no action, end of menu items! 
    } 
    if (trigger == btnSELECT) { 
      lcd.clear(); 
      state = EDIT_DAY; 
    } 
    if (trigger == btnBACK) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    break; 
  //--------------------------------------- 
  case EDIT_FEED_TIME1_HOUR: 
    // set time-out timer 
    timeoutValue = millis(); 
    // check for time-out 'button' trigger 
    if (trigger == trigTIMEOUT) { 
      lcd.clear(); 
      state = MAIN; 
    } 
    // Now check for button triggers ... This file has been truncated, please download it to see its full contents.

Схемы

Arduino based Pet Feeder
Умный кормушка для домашних животных на Arduino Nano — Соберите сами

Использован Arduino IDE 1.8.2. ОСТОРОЖНО: НЕ ДОЛЖНО БЫТЬ ДВУХ ВЕРСИЙ ОДНОЙ БИБЛИОТЕКИ В ПАПКЕ LIBRARIES!!

arduino_pet_feeder_final_eng_v1_0-ino_hex_libraries_dsoo0HscCr.zip

Производственный процесс

  1. Производство кормов для домашних животных: от ингредиентов к регулированию
  2. DIY-консоль управления Photoshop на Arduino Nano RP2040 Connect
  3. Сборка Bluetooth-робота-шпиона на Arduino с управлением с телефона
  4. Набор Arduino Nano: CrazyCircuits, датчики DFRobot и инструменты — создавайте проекты с уверенностью
  5. Tetris на Arduino Nano: самостоятельная сборка 16×8 LED-матрицы на 74HC595
  6. Собираем свой Tamagotchi на Arduino: цифровой питомец своими руками
  7. Управление двумя шаговыми двигателями через джойстик на Arduino Nano — простой урок (Visuino)
  8. Удаленное кормление питомца: Arduino-кормушка с ИК-пультом
  9. Ручной дозиметр (Гейгер-счетчик) на Arduino Nano: сборка в 3D-корпусе
  10. TFT Shield 2.8" 320×240 SPI для Arduino Nano — высокопроизводительный дисплей