Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Onderdelen en Benodigdheden

Arduino Nano R3 × 1
Adafruit Real Time Clock × 1
Continuous Rotation Servo × 1
Graanverspreider × 1
Hall-sensor UNIPOLAIR × 1
Staafmagneten 3mm diameter, 5mm lengte × 6
Adafruit RGB Backlight LCD - 16x2 × 1
LCD I2C Backpack × 1
Apem Drukknop Momentary IP67 × 1
Kleine Drukknoppen Momentary × 1
LED (generiek) × 6
Hammond Multi ABS Behuizing met Flens × 1

Apps en Online Diensten

LCD Karakter Generator

Over Dit Project

Update 2018-09-06

Mijn twee dierenvoeders werken al 7 maanden foutloos. We zorgen natuurlijk overdag voor onze dieren, maar nu krijgen ze op vaste tijden hun voer. Erg tevreden met het systeem!

Waarom Nog Een Dierenvoeder?

Mijn versie van een dierenvoeder is gebaseerd op een Arduino-microcontroller. De gebruikte materialen zijn relatief goedkoop en ik denk dat het makkelijk te bouwen is!

Natuurlijk zijn er veel versies beschikbaar, maar ik kon geen compleet project vinden dat klaar was om te bouwen met de functies die ik nodig had, dus ik begon vanaf nul.

Dit zijn de functies:

  • Nauwkeurige porties elke keer! (door gebruik van een Hall-sensor)
  • Twee voedtijden per dag
  • Extreem nauwkeurige Real Time Clock (alleen met echte DS3231-chip)
  • Je kunt een aankomende voeding afzonderlijk voor beide timers annuleren met display- en LED-indicator. Annuleren wordt automatisch hersteld nadat de ingestelde tijd is verstreken.
  • Handmatige voederfunctie (één portie per knopdruk)
  • Instelbare porties voor elk van de twee voedtijden (1-9 porties)
  • Overzicht van alle ingestelde parameters in het hoofscherm
  • Gemakkelijk te navigeren menusysteem
  • LED-indicatie als voeding succesvol was
  • LCD-verlichtingstimer (uit na 30 sec, aan bij elke knopdruk)
  • Tijd en overige instellingen veilig opgeslagen in geheugen (EEPROM)

Demonstratie Video's:

Voor video klik HIER

Meer gedetailleerde uitlegvideo: klik HIER

De dierenvoeder in slow-mo actie! klik HIER

Foto's:

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Benodigde Onderdelen:

  • 1x Graanverspreider (goedkoop op eBay)
  • Wat (schroot) hout om de graanverspreider-bevestiging te bouwen
  • 1x Arduino Uno
  • 1x I2C DS3231 (extreem nauwkeurig) of DS1307 Real Time Clock
  • 1x LCD-display 16 karakters / 2 regels
  • 1x I2C backpack voor het LCD-display
  • 1x Continuous Rotation Servo (koop een goede!)
  • 1x Honeywell SS443R Unipolaire Hall-sensor
  • 1x 12Volt/1 Amp externe voeding (wandadapter)
  • 7x Drukknoppen - Momentary
  • 6x LED (2x ROOD, 3x GROEN, 1x GEEL)
  • 2x weerstand 1kΩ (voor de rode LEDs)
  • 4x weerstand 560Ω (voor de groene en gele LEDs)
  • 1x behuizing voor de elektronica
  • Stekker vrouwelijk en connectoren om de externe servo en Hall-sensor aan te sluiten
  • HEEL VEEL HEET LIJM.

Het is het beste om een hogere spanning voor de servo te gebruiken als dat mogelijk is. Meer kracht! Mijn HSR-2645CRH Servo kan 7,4 Volt aan, dus als je dat ook wilt heb je nodig:

  • Perfboard
  • LM317T Variabele spanningsregelaar
  • 3 weerstanden om de uitgangsspanning in te stellen (1kΩ, 150kΩ en 4k7)
  • 2 gepolariseerde condensators (220µF, 10µF)
  • Koelrib niet nodig, de servo draait slechts 1-2 seconden per dag ;)
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Online vind je veel LM317 calculators.

De Dierenvoeder Container

De goedkoopste en makkelijkste zijn de graanverspreiders die je in veel winkels en op eBay vindt. Ik kocht een dubbele graanverspreider op eBay voor 15 euro inclusief verzending (UK naar Nederland).

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

De uitdaging was een bevestiging te maken voor de voeder en rondkijken op het internet hielp niet veel, dus ik keek in mijn schuur en vond mooie stukken Cederhout.

Het hout dat de plastic graanverspreider vasthoudt is 3 cm dik, dus geen wobbelen! Je moet zeker weten dat het gat (mijn voederbasis is 86mm) strak zit, dus meet voordat je boort! Liever een kleiner gat boren en het iets vormgeven dan een gat dat te groot is.

Bekijk de foto's om het idee te krijgen waarom en hoe het samen past.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

De Servo en Bevestiging aan het Verspreiderwieltje

Zorg dat je een goede servo koopt. Eerst kocht ik een MG996R servo die ik voor continu rotatie aanpaste, maar dat was geldverspilling... Daarna kreeg ik kwalitatieve, in mijn geval de HSR-2645CRH Servo.

Het MOET een continuous rotation model zijn!! Probeer niet een normale servo aan te passen zoals ik deed. Het presteerde niet goed genoeg en stoppte niet direct.

Video van de servo-test: klik HIER

De servo-aansluiting op het verspreiderwieltje

Nu was de vraag: hoe Verbind ik de servo met het wieltje van de voeder? Het leek eerst het moeilijkste deel van het project, maar was in feite heel simpel.

Ik nam een stukje 5mm polycarbonaat en zaagde met een freetzaag een ronde schijf van ongeveer 6 cm. In het midden boorde ik een gat van 8 mm (de dikte van de voederas. Deze plastic as is verwijderbaar van het wieltje).

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Aan de buitenkant boorde ik 6 gaten van 3 mm, de dikte van de magneten. Deze worden gebruikt om de Arduino feedback te geven over de positie van het wieltje.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Omdat het wieltje 6 rubberen schoven had, gebruikte ik 6 magneten en verdeelde ze rond de rand van de polycarbonaatschijf. 360 graden gedeeld door 6 is 60 graden. De staafmagneten passen strak.

De Hall-sensor zal elk van de 6 magneten detecteren, zodat we die informatie kunnen gebruiken om het wieltje precies te positioneren en te stoppen. De servo-stopvertragingstijd-variabele wordt gebruikt om een vertraging na voeren, voor het stoppen van de servo toe te voegen. Met deze vertraging kun je de exacte positie van het wieltje afstellen. Dit is belangrijk omdat als de 'stop'-positie verkeerd is, het voer niet volledig het wieltje verlaat. Ook de positie van de Hall-sensor kan in jouw geval anders zijn. Het aanpassen van de servo-stopvertragingstijd lost dit op.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Nu kunnen we de wieltjesas in de polycarbonaatschijf lijmen.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

De witte ronde servo-hoorn wordt met 3 M3 bouten op de polycarbonaatschijf bevestigd. Gebruik extra moeren als spacers en draai niet te vast!

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Nu is de servo onderdeel van de voeder-montage, dus we kunnen hem in de houten bevestiging zetten en meten hoe hoog het houtstuk onder de servo moet zijn.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

De Hall-sensor

De Hall-sensor die ik gebruikte is van het UNIPOLAIRE type. Het geeft een LAAG-uitgang als een magneet in de buurt is en HEEFT NIET de omgekeerde polariteit van de magneet nodig om de uitgang weer HOOG te maken. Dus gebruik een unipolaire Hall-sensor, veel makkelijker.

!!! De Hall-sensor heeft een 10kΩ pull-up weerstand nodig.

Ik monteerde een 0805 SMD-weerstand direct op de Hall-sensor, maar je kunt het op jouw manier doen. Vergeet deze weerstand niet!

Vervolgens nam ik een oude balpen en gebruikte het plastic als houder voor de sensor.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

De Elektronica Behuizing

Ik gebruikte een Hammond ABS-behuizing met flens. 120 x 65 x 40 mm

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Voor de externe verbindingen gebruikte ik female precision socket connectors, niet de goedkope Chinese. Ik gebruik deze veel in al mijn projecten. Niets is erger dan slechte verbindingen die (moeilijk te vinden) fouten veroorzaken.

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Om het programmeren van de Arduino makkelijker te maken, heb ik een externe FTDI-verbinding:

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano
Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Het Frontpaneel

Ik probeerde veel manieren om een goedkoop en mooi frontpaneel te maken...

Eerst gebruikte ik Mod Podge om een papier laserprint op de voorkant van de behuizing te lijmen en er meer Mod Podge overheen te doen. → MISLUKT

Toen probeerde ik een helder lak, gebaseerd op polyurethaan acrylaat dispersie. Zeer duurzaam en sterk als gedroogd. Ik lijmde het papier heel goed op de behuizing en het lakken van de bovenzijde van de frontpaneel-print zag er heel goed uit MAAR het wilde niet hechten aan de laserprinter-toner delen van de print. → MISLUKT

Daarna nam ik de laserprint van het frontpaneel en lamileerde alleen de bovenkant met lamineer-PVC. Zeer mooie afwerking, beschermt de voorkant en laat de kleuren prachtig opkomen. Toen lijmde ik het gelamineerde papier op de behuizing met de lak. Mijn idee was dat de lak het papier zou doorvoegen, waardoor het waterbestendig zou worden. Maar omdat ik dik papier gebruikte, werkte dat niet en na het natmaken om waterbestendigheid te testen, krool het papier van de behuizing af. → DEELTELIJK MISLUKT, verder te onderzoeken.

Nog een poging: ik kocht speciaal aluminiumfolie, gemaakt voor laserprinten. 2,25 euro per vel. Het resultaat was geweldig maar ik wilde de toner beschermen met een speciaal helder folie. Dat was geen succes omdat ik de imperfecties niet kon verwijderen zoals op foto 1 te zien is:

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Dan nog een poging om te beschermen met de heldere lak, het maakte de alu-foil extreem hoogglans! MAAR... opnieuw, de lak wilde niet hechten aan de toner, dus ik gaf de lak permanent op...

UITERAARD... Ik gebruikte de folieprint zonder bescherming... Het zal vingerafdrukken tonen als het vaak behandeld wordt (kijk onder de BACK-knop van de foto bovenaan deze pagina, behuizing in de kippenvoer) dus niet ideaal maar nog steeds goed genoeg voor mij.

Meer foto's zijn blijkbaar niet toegestaan dus...

Dat is het mensen... veel plezier!

Code

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

v 1.1: indrukken van Cancel1 of 2 knoppen toont een uitleg

// 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 
- Hall 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 
Revisies: 
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 an 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 hall 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 ... 
    // Dit bestand is afgekapt, download het voor de volledige inhoud. 
  }
}

Schema's

Arduino-gebaseerde Dierenvoeder

Bouw Je Eigen Slimme Dierenvoeder met Arduino Nano

Ik gebruikte Arduino IDE 1.8.2. LET OP: HEB NIET TWEE VERSIES VAN DE ZELFDE BIBLIOTHEKEN IN JE LIBRARIES MAP!!

arduino_pet_feeder_final_eng_v1_0-ino_hex_libraries_dsoo0HscCr.zip

Productieproces

  1. Huisdiervoederproductie: Van Ingrediënten tot Regulering – Een Complete Gids
  2. Bouw een DIY Photoshop-bedieningsconsole met Arduino Nano RP2040
  3. Bluetooth-gecontroleerde Arduino Spybot bouwen
  4. Arduino Nano Bundle: CrazyCircuits, DFRobot Sensoren & Tools – Bouw Projecten met Vertrouwen
  5. Bouw een Tetris-spel met Arduino Nano op een Zelfgemaakte 16×8 LED-matrix
  6. Bouw Je Eigen Arduino Tamagotchi: Een Digitale Huisdier DIY Project
  7. Twee Stappenmotors Sturen met Arduino Nano & Joystick – Simpele Tutorial
  8. Arduino-gebaseerde Op Afstand Bedienbare Dierenvoeder: DIY Kit & Bouwgids
  9. Bouw een handgehoude Geiger-teller met Arduino Nano
  10. Hoogwaardige 2,8″ TFT Shield voor Arduino Nano – 320×240 SPI Display
  11. Slimme 3D-geprint Dierenvoeder Aangedreven door Arduino Nano