Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Mangeoire pour animaux de compagnie avec pièces imprimées en 3D

Composants et fournitures

Arduino Nano R3
× 1
Module d'horloge temps réel DS 1307
Vous pouvez utiliser n'importe quelle marque de RTC, je suis sûr qu'il existe des options moins chères
× 1
Module régulateur de tension LM-317
× 1
Servo à rotation continue
× 1
Distributeur de céréales
× 1
Aimants de 8 mm
× 6
Capteur à effet Hall (unipolaire)
Assurez-vous que le capteur que vous obtenez est UNIPOLAIRE afin que vous n'ayez pas besoin de l'inversion de polarité de l'aimant pour réinitialiser la sortie du capteur
× 1
Adafruit RGB rétro-éclairé LCD - 16x2
× 1
Sac à dos LCD I2C
Si vous préférez, vous pouvez acheter l'écran LCD avec le sac à dos déjà attaché
× 1
LED 5 mm :Rouge
× 1
LED 5 mm :Vert
× 1
Bouton-poussoir, momentané
× 5
Adaptateur de prise d'alimentation CC femelle
× 1
Verrue murale 9-12 volts
× 1
Résistance 10k ohm
Pour le capteur à effet Hall. Connectez-vous entre les broches Vcc et Signal
× 1
Résistance 330 ohm
Pour les LED. Vous pouvez utiliser une résistance plus élevée si vous ne les voulez pas si brillants
× 2

Outils et machines nécessaires

Imprimante 3D (générique)
Perceuse/visseuse, sans fil
Scie circulaire

À propos de ce projet

L'histoire de la mangeoire pour animaux

Je joue avec les microcontrôleurs depuis un certain temps et je voulais essayer de sortir des tutoriels. Avant Covid-19, je n'avais pas vraiment d'horaire fixe et je travaillais assez longtemps. L'horaire d'alimentation de mon chien commençait vraiment à souffrir et entraînait un certain inconfort visible. Ma région est également sujette aux inondations pendant la saison des ouragans. Malheureusement, il y a eu quelques cas où je n'ai pas pu rentrer à la maison pour nourrir ma vieille fille. Je devais trouver une solution, donc si je ne pouvais pas rentrer à la maison, mon chien n'aurait pas faim. Au lieu d'en acheter un pour 30 $ à 40 $, pourquoi ne pas en construire un pour 100 $ et plus ? Je rigole.

La mangeoire !

Ma mangeoire pour chien utilise un microcontrôleur Arduino. Il y avait quelques fonctionnalités clés dont j'avais besoin que la plupart des autres versions de mangeoires pour chiens n'avaient pas. À savoir, une solution pour l'alimentation de récupération de panne de courant et la résolution du problème des aliments coincés dans le mécanisme de distribution. Ma région souffre également de pannes de courant aléatoires. Les pannes ne durent généralement jamais très longtemps, je n'ai donc pas vu le besoin d'une redondance de l'alimentation. Je voulais aussi quelque chose qui soit facile à démonter et à nettoyer. Soyez prudent lorsque vous lavez les pièces imprimées en 3D, vous pouvez utiliser de l'eau tiède, PAS de l'eau chaude, sauf si vous prévoyez d'imprimer avec de l'ABS ou du PET-G.

La liste complète des fonctionnalités est la suivante :

  • Deux repas par jour
  • Horloge précise avec horloge en temps réel
  • Changement manuel de l'heure de l'horloge temps réel
  • Option d'alimentation manuelle
  • Indication LED de la panne du capteur à effet Hall et de l'horloge en temps réel
  • Vue d'ensemble des heures de repas, de l'heure actuelle et de la fin des repas sur l'écran principal
  • Menu de navigation facile
  • Reprise de l'alimentation en cas de panne de courant (se nourrira lorsque le courant sera rétabli)
  • Les heures de repas et les achèvements stockés en toute sécurité dans l'EEPROM
  • Servo « secoue » en cas de coincement des aliments lors de la distribution

Démo

Vidéo de démonstration à venir !

De quoi d'autre avez-vous besoin ?

  • 4 vis M3-0,5 x 16 mm (roue codeuse)
  • 4 vis M3-0,5 x 10 mm (support servo)
  • 12x écrous hexagonaux M3
  • 4x écrous de blocage M3
  • 16x 1-1/2 pouce (38 mm) vis à bois polyvalentes
  • Colle à bois
  • 6 aimants de 0,315 x 0,118 pouces (8 x 3 mm)
  • Bois de 1-1/2 x 1-1/2 pouces (3,8 x 3,8 cm) pour les pieds et le support de servo
  • Bois 1/2 x 6 po (1,27 x 15,24 cm) pour le dos et la base

Les pièces imprimées en 3D

J'ai récemment obtenu une imprimante 3D et j'ai pensé, quelle meilleure façon d'apprendre et de l'utiliser que d'imprimer des pièces personnalisées pour ma mangeoire pour animaux de compagnie. Tout ce qui est imprimé est en PLA+ et a été imprimé à l'aide d'un Ender 3 Pro utilisant Cura comme trancheuse. Les STL de toutes les pièces sont disponibles sur la page du projet Github. Lien ci-dessous. Lisez le fichier README pour les instructions d'impression et les paramètres de la trancheuse.

Le logement

Le boîtier est conçu dans un souci de commodité et de simplicité. Aucune vis ou écrou nécessaire. Insérez et retirez simplement les composants. Chaque insert a 4 onglets qui maintiennent les composants en place.

Même si j'ai d'abord gâché la fente de l'écran LCD, je suis revenu et j'ai corrigé le modèle dans Fusion 360. J'étais tout simplement trop paresseux pour revenir en arrière et le réimprimer. J'ai utilisé des vis M3 0,5 x 6 mm supplémentaires qui traînaient pour le fixer. Il y a quatre entretoises à chaque coin de la fente LCD avec des trous pour que vous puissiez fixer l'écran si nécessaire. Malheureusement, je n'ai jamais pris de photos du couvercle du boîtier avant d'avoir tout mis en place.

L'accessoire roue codeuse

La roue codeuse a deux fonctions :

  • Fournit à l'Arduino un retour de position du servo
  • Fixe la palette en caoutchouc au servo

Une portion d'alimentation est égale à 1/6e (60 degrés) de tour de la roue. Utilisez une balance pour mesurer la quantité de nourriture que votre animal reçoit par repas, puis ajustez le nombre de portions jusqu'à ce que vous obteniez une plage qui satisfasse cette quantité. Je crois qu'une alimentation pour moi était d'environ 173 grammes de nourriture. Une portion de 17 m'a donné une fourchette de 170 à 177 grammes par repas. Tout dépend de la taille de vos croquettes. Assurez-vous que le klaxon est placé entre les 8 écrous hexagonaux M3.

Le support de servo et le support de capteur à effet Hall

Ce support de servo personnalisé contient également le capteur à effet Hall et monte le servo sur un morceau de bois de 1-1/2 x 1-1/2 pouces (3,8 x 3,8 cm). La longueur du bois dépendra de l'emplacement du servo (nous y reviendrons plus tard). Il y a beaucoup de place pour l'erreur avec la monture, alors ne vous inquiétez pas trop pour obtenir la longueur parfaite.

L'entonnoir, la goulotte à nourriture et le rail de la goulotte

Cela constitue le système de livraison de nourriture. Les aliments descendent du distributeur à travers l'entonnoir et sur la goulotte et dans le bol alimentaire. Malheureusement, je n'ai pas pris de photos du rail de la goulotte avant de le monter.

Le cadre

*Avertissement* Je n'ai pas pris de photos de chaque étape du processus d'assemblage. Certaines de ces images auront sauté des étapes, mais je vais quand même vous guider étape par étape pour l'assemblage du cadre.

Assurez-vous de prépercer les trous pour toutes les vis que vous utilisez. Vous ne voulez pas fendre le bois !

C'est le distributeur de céréales que j'ai acheté sur Amazon. Il y en a qui ont plusieurs distributeurs si vous avez plusieurs animaux de compagnie et avez besoin de plusieurs mangeoires, je n'en avais besoin que d'un. La marque est Honey Can Do, mais je suis sûr que n'importe quelle marque fonctionnera.

La première chose sur laquelle j'ai commencé a été de retirer le bouton de la tige qui se connectait à la roue à aubes du distributeur. J'ai utilisé une scie à main pour retirer le bouton. Vous pouvez utiliser un outil électrique si vous en avez un. Ne coupez pas la tige plus bas là où se termine la partie argentée. Coupez juste à la base du bouton.

Après avoir scié le bouton, retirez le matériau restant en coupant les 3 supports vus sur la dernière photo ci-dessus. Cela a pris un certain temps. J'imagine que ce sera plus rapide si vous avez le bon outil électrique. Une fois que vous avez retiré ces trois supports, le morceau restant sur la tige elle-même devrait se détacher avec un peu d'huile de coude. Vous devrez poncer la partie de la tige la plus proche du bouton pour qu'elle s'intègre bien dans la roue codeuse.

Ensuite, nous commençons à construire la base du cadre. J'ai pris le bois de 1/2 x 6 pouces (1,27 x 15,24 cm) et je les ai coupés en deux morceaux de 8 pouces (20,32 cm). Cela constituera la base et l'arrière du cadre de la mangeoire. Appliquez de la colle à bois et utilisez 2 des clous multi-usages pour les joindre en forme de L. Vous devrez ajouter quelques supports à angle droit pour renforcer la connexion du panneau arrière et du panneau inférieur. Utilisez 4 vis multi-usages et de la colle à bois pour les fixer aux panneaux arrière et inférieur. Je n'ai pas de photo de cette étape, mais vous pouvez les voir sur des photos plus bas.

De là, vous devez couper le support du distributeur à une hauteur d'environ 4,5 pouces (11,43 cm). Il n'a pas besoin d'être parfait. Essayez de vous en rapprocher le plus possible. L'entonnoir permet une certaine marge de manœuvre. Une fois que vous avez coupé le support à la bonne taille, placez-le contre le dos et assurez-vous qu'il repose à plat sur la base. Une fois que vous l'avez en position, utilisez un crayon ou un stylo pour marquer le centre de l'endroit où se trouvera l'extrémité de distribution du récipient. Vous devrez ensuite percer un trou de 5,08 cm (2 pouces) tout au long du panneau de base. Il est essentiel de mesurer deux fois et de couper une fois avec celui-ci. Ce sera le trou vu dans l'image ci-dessus.

Une fois que vous avez percé le trou dans le panneau de base, nous sommes prêts à fixer le support du distributeur au panneau arrière. Ce que vous voulez faire est de placer le support contre le panneau arrière (montré dans la première image ci-dessous). Il y a deux compartiments sous l'anneau du support de distribution. C'est là que vous voulez que les trous soient (voir l'image ci-dessous). Utilisez un crayon ou un stylo pour marquer la hauteur des deux trous sur le panneau arrière. Vous les voulez aussi près que possible du centre du support du distributeur. Il y a deux vis qui relient l'anneau supérieur du support à la partie inférieure que vous coupez. Attention à ne pas les heurter lors du perçage. Encore une fois, n'oubliez pas de mesurer deux fois et de percer une fois. Ou deux fois, dans ce cas.

Maintenant que nous avons percé les trous, nous pouvons fixer le support au panneau arrière à l'aide des boulons hexagonaux de 5/16 pouces, des écrous hexagonaux de 5/16 pouces et des rondelles de 5/16 pouces. Vous voulez aller de l'avant et pousser les boulons à travers en vous assurant de placer des rondelles à l'extrémité hexagonale des boulons avant de les pousser à travers. Une fois qu'ils sont sortis de l'autre côté, placez l'autre jeu de rondelles sur le côté fileté, puis commencez à serrer les écrous hexagonaux à la main. Cela va être un peu délicat. Après avoir serré les écrous à la main, vous devrez utiliser une douille pour maintenir l'écrou et les serrer davantage. Seulement environ 1/4 de tour environ. Attention à ne pas trop serrer.

Maintenant que le support est sécurisé, nous pouvons maintenant ajouter le morceau de bois de 1/2 x 1/2 pouce sur lequel le servo va s'asseoir. La longueur de cela va dépendre de l'endroit où votre servo sera assis. Allez-y et assemblez le mécanisme d'alimentation en attachant le servo au klaxon de la roue codeuse et la roue codeuse à la palette en caoutchouc à l'intérieur du récipient en plastique. Placez le conteneur dans l'anneau supérieur et mesurez l'emplacement du servo à partir du panneau de base. Ne vous inquiétez pas qu'il soit parfait. Le support de servo permet un peu de marge de manœuvre. Utilisez de la colle à bois et une seule vis polyvalente pour fixer le support en bois.

L'étape suivante consiste à attacher les pieds au panneau de base du chargeur. La longueur des pattes dépendra de la hauteur du bol de nourriture de votre animal. Mon chien a son bol de nourriture sur un support surélevé ; par conséquent, j'avais besoin que ma mangeoire soit assise assez haut. Utilisez 4 des vis multi-usages et de la colle à bois pour les fixer en place. Je recommande de mettre une traverse entre les deux pattes avant et les deux arrière et une autre traverse entre les traverses comme indiqué ci-dessous pour plus de stabilité. Utilisez un total de 6 vis multi-usages et de la colle à bois pour fixer les pièces aux pieds.

Les prochaines étapes sont :

  • insérez l'entonnoir dans le trou que nous avons percé dans le panneau de base
  • fixez le servo au support du servo
  • fixez le support au support en bois

Fixez le servo au support à l'aide des 4 vis M3 x 10 mm et 4 des écrous hexagonaux M3. Une fois le servo fixé, nous pouvons fixer le support sur le support de servo en bois. Utilisez deux des vis polyvalentes pour fixer légèrement le support sur le support en bois. Ne serrez pas trop ou vous endommagerez le support. Assurez-vous de suivre les étapes dans cet ordre. L'entonnoir va soulever un peu le servo et il s'ajuste bien contre l'extrémité du récipient en plastique, il est donc impossible de l'enfiler si le récipient en plastique est déjà assis dans le support de l'anneau supérieur.

Les dernières étapes consisteront à fixer le support coulissant de la goulotte et la glissière elle-même. Vous voulez positionner le support légèrement derrière le trou dans le panneau de base. Vous le voulez aussi loin que possible pour que la glissière dégage le cadre du chargeur. Utilisez deux des vis universelles pour fixer le support au bas du panneau de base du cadre. Il est préférable de le faire avec la glissière dans le support car le support a une certaine flexibilité et vous voudrez un ajustement assez sûr entre la glissière et le support.

L'électronique

Malheureusement, je n'ai jamais pris de photos du processus de soudure. Il n'y a pas grand chose cependant. Soudez simplement chaque composant à ses broches correspondantes et vous devriez être prêt à partir. Si vous souhaitez utiliser des en-têtes de broche à la place, vous pouvez également le faire de cette façon. Il y a suffisamment d'espace sous et au-dessus du slot Arduino pour permettre les broches et les connecteurs. Je recommande vraiment de tout souder ensemble avant de placer tous les composants dans leurs emplacements respectifs.

J'ai alimenté mon Arduino via la broche d'alimentation externe non régulée (broche 30). Cela nécessite une tension d'entrée comprise entre 7 et 20 volts car cette tension est alimentée par le régulateur embarqué de l'Arduino. Si vous souhaitez l'alimenter via USB, il vous suffit de vous assurer que vous l'alimentez en 5 Volts, PAS en 7-20 Volts.

Assurez-vous de souder la résistance de 10k Ohm entre les broches Vcc et Signal sur le capteur à effet Hall. Sinon, vous n'obtiendrez pas de lecture. N'oubliez pas non plus de connecter tous les composants à une terre commune. J'ai fait l'erreur de manquer l'un des motifs et mon système fonctionnerait pendant un certain temps, mais le capteur à effet hall finirait par tomber en panne. Ce fut une très bonne journée pour mon chien.

J'ai fini par fabriquer des connecteurs personnalisés pour le capteur à effet Hall et les connexions des servos. J'ai soudé les fils aux embases mâles. Ceux-ci pendent du fond de l'enceinte. Pour le capteur à effet Hall, j'ai fabriqué un adaptateur femelle personnalisé en coupant, dénudant et soudant quelques fils de connecteur Dupont que j'avais qui traînaient.

Pour le Vcc et le rail au sol, j'ai coupé la section du rail d'alimentation de la planche à pain perma-proto supplémentaire que j'avais qui traînait. Tout ce que vous avez autour fonctionnera. Vérifiez simplement vos connexions avant de placer le rail dans son emplacement. C'est la chose la plus difficile à sortir une fois qu'elle est dedans. J'ai aussi appris cela à la dure.

C'est ça! J'espère que vous aurez autant de plaisir à construire cela que moi. Cela semble difficile au début, mais plus vous y entrez, plus cela devient facile. Faites-moi savoir si vous avez des questions sur quoi que ce soit. Bon bricolage !

Code

  • Code d'alimentation
Code d'alimentationC/C++
// Version finale pour le distributeur d'aliments pour animaux de compagnie/* Caractéristiques :- Menu facile à naviguer - Aperçu des heures d'alimentation, de l'heure actuelle, de la fin des repas et de la portion d'alimentation sur l'écran principal - Portions contrôlables à l'aide d'un capteur à effet Hall pour le retour - Précis maintien de l'heure avec la puce DS1307 - Peut modifier manuellement l'heure définie dans la puce DS1307 - Deux alimentations par jour - Option d'alimentation manuelle - Redémarrage de l'alimentation en cas de panne de courant - Indication par LED du capteur à effet Hall et de l'horloge en temps réel - Les heures d'alimentation et les achèvements sont stockés en toute sécurité dans l'EEPROM - Servo "secousse" en cas de coincement des aliments*/#include #include #include #include #include # include #include // Crée un objet servo pour contrôler votre servoServo myservo;// Affectation de toutes mes broches d'entrée et d'E/S#define ENTER_BUTTON_PIN 11#define UP_BUTTON_PIN 10#define DOWN_BUTTON_PIN 9#define BACK_BUTTON_PIN 8#define POWER_LED_PIN 5#define MANUAL_BUTTON_PIN A1#define hallPin 2#define HALL_LE D_PIN 7#define SERVO_PIN 6// Définition de tous les boutons, fonctionne avec la bibliothèque JC_Button. LCD I2C et RTCLiquidCrystal_I2C lcd (0x27, 16, 2);RTC_DS1307 rtc;//Variables utilisées tout au long du codeunsigned long hallSensorTime;unsigned long rotationTime =400;unsigned long led_previousMillis =0;const long interval_delay =1000;unvaled long =2000 ; int ledState =LOW;boolean manualFeed =false;boolean hall_sensor_fail =false;unsigned long blink_previousMillis =0;unsigned long blink_currentMillis =0;unsigned long blink_interval =500;unsigned long delay_currentMillis =0;unsigned long delay_previous =0;booleMillis =0 nombre int;alimentation booléenne1_complete =false;alimentation booléenne2_complete =false;alimentation booléenne1_trigger =false;alimentation booléenne2_trigger =false;servobooléen =tru e;//Hall sensor interruptvolatile booléen hallSensorActivated =false;volatile int isr_count =1;void hallActiveISR(){ hallSensorActivated =true; digitalWrite(HALL_LED_PIN, HAUT); isr_count =isr_count + 1;}/* J'utilise ici des énumérations pour mieux savoir quel bouton est enfoncé, au lieu de simplement définir chaque bouton sur une valeur entière.*/enum { btnENTER, btnUP, btnDOWN, btnBACK,} ; /* États de la machine à états. Même chose ici avec le type enum. Cela facilite le suivi du menu dans lequel vous vous trouvez ou souhaitez accéder au lieu de donner à chaque menu une valeur entière*/enum STATES { MAIN, MENU_EDIT_FEED1, MENU_EDIT_FEED2, MENU_EDIT_TIME, MENU_EDIT_PORTION, EDIT_FEED1_HOUR, EDIT_FEED1_MIN, EDIT_FEED2_FEED_H, EDIT_FEED2_FEED_H, EDIT_FEED1_HOUR , EDIT_MIN, EDIT_PORTION};// Contient l'état de l'état de la machine d'étatSTATES ; // Variables d'entrée utilisateurint Hour;int Minute;int portion;int feed_time1_hour;int feed_time1_min;int feed_time2_hour;int feed_time2_min;int userInput;// Markbyte de contrôle de caractère spécial check_Char[8] ={ B00000, B00000, B00001, B00011, B10110, B11100, B11000, B00000};//======================La configuration===========================configuration vide() { Wire.begin(); Serial.begin(9600); lcd.init(); LCD rétro-éclairage(); lcd.createChar(0, check_Char); if (!rtc.begin()) { Serial.println("Impossible de trouver RTC!"); } // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); //Les boutons enterBtn.begin(); upBtn.begin(); downBtn.begin(); backBtn.begin(); manualFeedBtn.begin(); //Configuration de l'état initial de la machine à états state =MAIN; //Configuration des entrées et sorties pinMode(POWER_LED_PIN, OUTPUT); pinMode(HALL_LED_PIN, SORTIE); pinMode(hallPin, INPUT); /* Attacher une interruption à la broche qui se connecte au capteur à effet Hall. Le capteur à effet Hall que j'ai utilisé est réglé sur HAUT et devient BAS lorsqu'il rencontre un aimant. C'est pourquoi il est défini sur FALLING */ attachInterrupt (digitalPinToInterrupt(hallPin), hallActiveISR, FALLING); // état par défaut des LED digitalWrite(POWER_LED_PIN, HIGH); digitalWrite (HALL_LED_PIN, BAS); /* Ces fonctions obtiennent le temps d'alimentation stocké, l'alimentation terminée et la taille de la portion de l'EEPROM au démarrage. Je l'ai fait parce que je reçois des pannes de courant aléatoires ici où je vis. */ get_feed_time1(); get_feed_time2(); get_completed_feedings(); get_portion();}//======================La boucle============================boucle vide() { changer_états(); check_buttons(); check_feedtime (); check_rtc(); manual_feed_check ();}//===============Les fonctions =======================/* Utilisations la bibliothèque JC_Button pour vérifier en permanence si un bouton a été enfoncé. Selon le bouton enfoncé, il définit la variable userInput à utiliser dans la fonction menu_transitions*/void check_buttons () { enterBtn.read(); upBtn.read(); downBtn.read(); backBtn.read(); manualFeedBtn.read(); if (enterBtn.wasPressed()) { Serial.println ("Vous avez appuyé sur Entrée !"); userInput =btnENTER; menu_transitions(userInput); } if (upBtn.wasPressed()) { Serial.println ("Vous avez appuyé vers le haut !"); userInput =btnUP; menu_transitions(userInput); } if (downBtn.wasPressed()) { Serial.println ("Vous avez appuyé vers le bas !"); userInput =btnDOWN; menu_transitions(userInput); } if (backBtn.wasPressed()) { Serial.println ("Vous avez appuyé en arrière !"); userInput =btnBACK; menu_transitions(userInput); } if (manualFeedBtn.wasPressed()) { Serial.println ("Vous alimentez manuellement !"); alimentationmanuelle =vrai ; }}//=====================================================/* Cette fonction détermine ce qui est affiché, selon le menu ou "l'état" dans lequel vous vous trouvez. Chaque menu a une fonction qui affiche le menu respectif*/void Changing_states() { switch (state) { cas PRINCIPAL :display_current_time(); display_feeding_times(); display_portion(); Pause; cas MENU_EDIT_FEED1 :display_set_feed_time1_menu(); Pause; cas MENU_EDIT_FEED2 :display_set_feed_time2_menu(); Pause; cas MENU_EDIT_TIME :display_set_time_menu(); Pause; cas MENU_EDIT_PORTION :display_set_portion_menu (); Pause; cas EDIT_FEED1_HOUR :set_feed_time1(); Pause; cas EDIT_FEED1_MIN :set_feed_time1(); Pause; cas EDIT_FEED2_HOUR :set_feed_time2(); Pause; cas EDIT_FEED2_MIN :set_feed_time2(); Pause; cas EDIT_HOUR :set_the_time(); Pause; cas EDIT_MIN:set_the_time(); Pause; cas EDIT_PORTION :set_the_portion(); Pause; }}//=====================================================/* C'est la partie transitionnelle de la machine d'état. C'est ce qui permet de passer d'un menu à l'autre et de changer les valeurs*/void menu_transitions(int input) { switch (state) { case MAIN:if (input ==btnENTER) { lcd.clear(); état =MENU_EDIT_FEED1 ; } if (entrée ==btnBACK) { hall_sensor_fail =false; } Pause; //------------------------------------------------ ---- case MENU_EDIT_FEED1:if (input ==btnBACK) { lcd.clear(); état =PRINCIPAL; } else if (entrée ==btnENTER) { lcd.clear(); état =EDIT_FEED1_HOUR ; } else if (entrée ==btnDOWN) { lcd.clear(); état =MENU_EDIT_FEED2 ; } Pause; //------------------------------------------------ ---- case EDIT_FEED1_HOUR:// Besoin de ceci pour empêcher le servo de s'éteindre lors du réglage du temps servoOn =false; if (entrée ==btnUP) { feed_time1_hour++; if (feed_time1_hour> 23) { feed_time1_hour =0; } } else if (input ==btnDOWN) { feed_time1_hour--; if (feed_time1_hour <0) { feed_time1_hour =23; } } else if (input ==btnBACK) { lcd.clear(); servoOn =true; state =MENU_EDIT_FEED1; } else if (input ==btnENTER) { state =EDIT_FEED1_MIN; } break; //---------------------------------------------------- case EDIT_FEED1_MIN:if (input ==btnUP) { feed_time1_min++; if (feed_time1_min> 59) { feed_time1_min =0; } } else if (input ==btnDOWN) { feed_time1_min--; if (feed_time1_min <0) { feed_time1_min =59; } } else if (input ==btnBACK) { state =EDIT_FEED1_HOUR; } else if (input ==btnENTER) { lcd.clear(); lcd.setCursor(0, 0); lcd.print( "*Settings Saved*"); retard(1000); lcd.clear(); servoOn =true; write_feeding_time1 (); state =MAIN; } break; //---------------------------------------------------- case MENU_EDIT_FEED2:if (input ==btnUP) { lcd.clear(); state =MENU_EDIT_FEED1; } else if (input ==btnENTER) { lcd.clear(); state =EDIT_FEED2_HOUR; } else if (input ==btnDOWN) { lcd.clear(); state =MENU_EDIT_TIME; } break; //---------------------------------------------------- case EDIT_FEED2_HOUR:servoOn =false; if (input ==btnUP) { feed_time2_hour++; if (feed_time2_hour> 23) { feed_time2_hour =0; } } else if (input ==btnDOWN) { feed_time2_hour--; if (feed_time2_hour <0) { feed_time2_hour =23; } } else if (input ==btnBACK) { lcd.clear(); servoOn =true; state =MENU_EDIT_FEED2; } else if (input ==btnENTER) { state =EDIT_FEED2_MIN; } break; //---------------------------------------------------- case EDIT_FEED2_MIN:if (input ==btnUP) { feed_time2_min++; if (feed_time2_min> 59) { feed_time2_min =0; } } else if (input ==btnDOWN) { feed_time2_min--; if (feed_time2_min <0) { feed_time2_min =59; } } else if (input ==btnBACK) { state =EDIT_FEED2_HOUR; } else if (input ==btnENTER) { lcd.clear(); lcd.setCursor(0, 0); lcd.print( "*Settings Saved*"); retard(1000); lcd.clear(); servoOn =true; write_feeding_time2 (); state =MAIN; } break; //---------------------------------------------------- case MENU_EDIT_TIME:if (input ==btnUP) { lcd.clear(); state =MENU_EDIT_FEED2; } else if (input ==btnENTER) { lcd.clear(); state =EDIT_HOUR; } else if (input ==btnDOWN) { lcd.clear(); state =MENU_EDIT_PORTION; } break; //---------------------------------------------------- case EDIT_HOUR:if (input ==btnUP) { Hour++; if (Hour> 23) { Hour =0; } } else if (input ==btnDOWN) { Hour--; if (Hour <0) { Hour =23; } } else if (input ==btnBACK) { lcd.clear(); state =MENU_EDIT_TIME; } else if (input ==btnENTER) { state =EDIT_MIN; } break; //---------------------------------------------------- case EDIT_MIN:if (input ==btnUP) { Minute++; if (Minute> 59) { Minute =0; } } else if (input ==btnDOWN) { Minute--; if (Minute <0) { Minute =59; } } else if (input ==btnBACK) { state =EDIT_HOUR; } else if (input ==btnENTER) { lcd.clear(); lcd.setCursor(0, 0); lcd.print( "*Settings Saved*"); retard(1000); lcd.clear(); rtc.adjust(DateTime(0, 0, 0, Hour, Minute, 0)); state =MAIN; } break; //---------------------------------------------------- case MENU_EDIT_PORTION:if (input ==btnUP) { lcd.clear(); state =MENU_EDIT_TIME; } else if (input ==btnENTER) { lcd.clear(); state =EDIT_PORTION; } break; //---------------------------------------------------- case EDIT_PORTION:if (input ==btnUP) { portion++; if (portion> 20) { portion =1; } } else if (input ==btnDOWN) { portion--; if (portion <1) { portion =20; } } else if (input ==btnBACK) { lcd.clear(); state =MENU_EDIT_PORTION; } else if (input ==btnENTER) { lcd.clear(); lcd.setCursor(0, 0); lcd.print( "*Settings Saved*"); retard(1000); lcd.clear(); write_portion(); state =MAIN; } break; }}//=====================================================// This function checks the feed time against the current timevoid check_feedtime (){ DateTime now =rtc.now(); if (now.second() ==0) { if ((now.hour() ==feed_time1_hour) &&(now.minute() ==feed_time1_min)) { feeding1_trigger =true; if (servoOn) { if (feeding1_complete ==false) { lcd.clear(); lcd.setCursor(3, 0); lcd.print ("Dispensing"); lcd.setCursor(1, 1); lcd.print("First Feeding"); startFeeding(); } } } else if ((now.hour() ==feed_time2_hour) &&(now.minute () ==feed_time2_min)) { feeding2_trigger =true; if (servoOn) { if ( feeding2_complete ==false) { lcd.clear(); lcd.setCursor(3, 0); lcd.print ("Dispensing"); lcd.setCursor(0, 1); lcd.print("Second Feeding"); startFeeding(); } } } } // Midnight Reset if ( (now.hour() ==0) &&(now.minute() ==0)) { feeding1_complete =false; feeding2_complete =false; EEPROM.write(4, feeding1_complete); EEPROM.write(5, feeding2_complete); } /*If power outtage happens during a feed time, this checks to see if the feed time has passed and if the feeding occurred. If not, it feeds. */ if ( (now.hour()>=feed_time1_hour) &&(now.minute()> feed_time1_min)) { if ((feeding1_complete ==0) &&(feeding1_trigger ==0)) { lcd.clear(); lcd.setCursor(5, 0); lcd.print ("Uh-Oh!"); lcd.setCursor(2, 1); lcd.print("Power Outage"); startFeeding(); } } if ( (now.hour()>=feed_time2_hour) &&(now.minute()> feed_time2_min)) { if ((feeding2_complete ==0) &&(feeding2_trigger ==0)) { lcd.clear(); lcd.setCursor(5, 0); lcd.print ("Uh-Oh!"); lcd.setCursor(2, 1); lcd.print("Power Outage"); startFeeding(); } }}//=====================================================// Displays the set portion menu optionvoid display_set_portion_menu () { lcd.setCursor(2, 0); lcd.print("Menu Options"); lcd.setCursor(0, 1); lcd.print("Set the Portion");}//=====================================================// Displays the menu where you change the current timevoid set_the_time (){ lcd.setCursor(2, 0); lcd.print("Set the Time"); switch (state) { //---------------------------------------------------- case EDIT_HOUR:if (blink_state ==0) { lcd.setCursor(5, 1); add_leading_zero(Hour); } else { lcd.setCursor(5, 1); lcd.print(" "); } lcd.print(":"); add_leading_zero(Minute); Pause; //---------------------------------------------------- case EDIT_MIN:lcd.setCursor(5, 1); add_leading_zero(Hour); lcd.print(":"); if (blink_state ==0) { lcd.setCursor(8, 1); add_leading_zero(Minute); } else { lcd.setCursor(8, 1); lcd.print(" "); } break; } blinkFunction();}//=====================================================// Displays the menu where you change the feeding portionvoid set_the_portion (){ lcd.setCursor (0, 0); lcd.print("Set the Portion"); switch (state) { case EDIT_PORTION:if (blink_state ==0) { lcd.setCursor(7, 1); add_leading_zero(portion); } else { lcd.setCursor(7, 1); lcd.print(" "); } } blinkFunction();}//=====================================================//Displays the menu option for setting the timevoid display_set_time_menu () { lcd.setCursor(2, 0); lcd.print("Menu Options"); lcd.setCursor(2, 1); lcd.print("Set the Time");}//=====================================================// Displays the menu where you change the second feeding timevoid set_feed_time2 (){ lcd.setCursor(0, 0); lcd.print("Set Feed Time 2"); switch (state) { //---------------------------------------------------- case EDIT_FEED2_HOUR:if (blink_state ==0) { lcd.setCursor(5, 1); add_leading_zero(feed_time2_hour); } else { lcd.setCursor(5, 1); lcd.print(" "); } lcd.print(":"); add_leading_zero(feed_time2_min); Pause; //---------------------------------------------------- case EDIT_FEED2_MIN:lcd.setCursor(5, 1); add_leading_zero(feed_time2_hour); lcd.print(":"); if (blink_state ==0) { lcd.setCursor(8, 1); add_leading_zero(feed_time2_min); } else { lcd.setCursor(8, 1); lcd.print(" "); } break; } blinkFunction();}//=====================================================// Displays the menu where you change the first feeding timevoid set_feed_time1 (){ lcd.setCursor(0, 0); lcd.print("Set Feed Time 1"); switch (state) { //---------------------------------------------------- case EDIT_FEED1_HOUR:if (blink_state ==0) { lcd.setCursor(5, 1); add_leading_zero(feed_time1_hour); } else { lcd.setCursor(5, 1); lcd.print(" "); } lcd.print(":"); add_leading_zero(feed_time1_min); Pause; //---------------------------------------------------- case EDIT_FEED1_MIN:lcd.setCursor(5, 1); add_leading_zero(feed_time1_hour); lcd.print(":"); if (blink_state ==0) { lcd.setCursor(8, 1); add_leading_zero(feed_time1_min); } else { lcd.setCursor(8, 1); lcd.print(" "); } break; } blinkFunction();}//=====================================================// Adds a leading zero to single digit numbersvoid add_leading_zero (int num) { if (num <10) { lcd.print("0"); } lcd.print(num);}//=====================================================/* Displays the feeding time on the main menu as well as the check mark for visual comfirmation of a completed feeding*/void display_feeding_times () { //Displaying first feed time lcd.setCursor(0, 0); lcd.print ("F1:"); add_leading_zero(feed_time1_hour); lcd.print(":"); add_leading_zero(feed_time1_min); lcd.print(" "); if (feeding1_complete ==true) { lcd.write(0); } else { lcd.print(" "); } //Displaying second feed time lcd.setCursor(0, 1); lcd.print("F2:"); add_leading_zero(feed_time2_hour); lcd.print(":"); add_leading_zero(feed_time2_min); lcd.print(" "); if (feeding2_complete ==true) { lcd.write(0); } else { lcd.print(" "); }}//=====================================================// Displays the current time in the main menuvoid display_current_time () { DateTime now =rtc.now(); lcd.setCursor(11, 0); add_leading_zero(now.hour()); lcd.print(":"); add_leading_zero(now.minute());}//=====================================================// Displays the menu option for setting the first feed timevoid display_set_feed_time1_menu () { lcd.setCursor(2, 0); lcd.print("Menu Options"); lcd.setCursor(0, 1); lcd.print("Set Feed Time 1");}//=====================================================// Displays the meny option for setting the second feed timevoid display_set_feed_time2_menu () { lcd.setCursor(2, 0); lcd.print("Menu Options"); lcd.setCursor(0, 1); lcd.print("Set Feed Time 2");}//=====================================================// Displays the feeding portion in the main menuvoid display_portion (){ lcd.setCursor (12, 1); lcd.print("P:"); add_leading_zero(portion);}//=====================================================// Starts the feeding process.void startFeeding(){ // attach the servo to the pin myservo.attach(SERVO_PIN); count =1; hallSensorTime =millis(); // loop so that the servo runs until desired portion is reached while (count <=portion) { servoStart(); if (hallSensorActivated ==true) { // digitalWrite(LED_PIN,HIGH); count =count + 1; //resetting for next interrupt hallSensorTime =millis(); hallSensorActivated =false; digitalWrite(HALL_LED_PIN, LOW); } /* Moved the servo clockwise a bit to dislodge food stuck in the dispensing mechanism */ if (millis() - hallSensorTime> rotationTime) { hall_sensor_fail =true; Serial.println("I'm in Jiggle"); jiggle(); } } // Keeps track of which feeding just happened and writes it to EEPROM if ((feeding1_complete ==false) &&(feeding2_complete ==false)) { feeding1_complete =true; EEPROM.write(4, feeding1_complete); } else if ((feeding1_complete ==true) &&(feeding2_complete ==false)) { feeding2_complete =true; EEPROM.write(5, feeding2_complete); } servoStop(); digitalWrite(HALL_LED_PIN, LOW); /* Detaches the servo from the pin so that it is no longer recieving a signal. You may have to add a delay before this so the sensor stops when a magnet is over the hall sensor. There was significant momentum left in my system that I did not need it */ myservo.detach(); lcd.clear(); delay_currentMillis =millis(); while (millis() - delay_currentMillis <=delay_interval) { lcd.setCursor(2, 0); lcd.print ("Feeding Done"); } lcd.clear();}//=====================================================void servoStart(){ myservo.write(180);}//=====================================================void servoStop(){ // this value will vary, you have to find it through trial and error myservo.write(94);}//=====================================================// "jiggles" the servo in case food gets stuckvoid jiggle(){ myservo.write(80); retard(30); myservo.write(93); retard(30); myservo.write(180);}//=====================================================// Writes the hour and minute valies set for 1st feeding to the EEPROMvoid write_feeding_time1 (){ EEPROM.write(0, feed_time1_hour); EEPROM.write(1, feed_time1_min);}//=====================================================// Writes the hour and minute values set for 2nd feeding to the EEPROMvoid write_feeding_time2 () { EEPROM.write(2, feed_time2_hour); EEPROM.write(3, feed_time2_min);}//=====================================================// Writes portion value set to the EEPROMvoid write_portion (){ EEPROM.write(6, portion);}//=====================================================// Reads the hour and minute values from 1st feed time from EEPROMvoid get_feed_time1 (){ feed_time1_hour =EEPROM.read(0); if (feed_time1_hour> 23) feed_time1_hour =0; feed_time1_min =EEPROM.read(1); if (feed_time1_min> 59) feed_time1_min =0;}//=====================================================// Reads the hour and minute values from 2nd feed time from EEPROMvoid get_feed_time2 (){ feed_time2_hour =EEPROM.read(2); if (feed_time2_hour> 23) feed_time2_hour =0; feed_time2_min =EEPROM.read(3); if (feed_time2_min> 59) feed_time2_min =0;}//=====================================================// Reads portion set value from EEPROMvoid get_portion (){ portion =EEPROM.read(6);}//=====================================================// Reads boolean value of whether or not feedings have occured from EEPROMvoid get_completed_feedings(){ feeding1_complete =EEPROM.read(4); feeding2_complete =EEPROM.read(5);}//=====================================================/* Checks to see if the hall sensor has failed to read a magnet and blinks the red LED*/void check_hall_sensor () { if (hall_sensor_fail ==true) { if (blink_state ==0) { digitalWrite(HALL_LED_PIN, HIGH); } else { digitalWrite(HALL_LED_PIN, LOW); } blinkFunction(); } else { digitalWrite(HALL_LED_PIN, LOW); hall_sensor_fail =false; }}//=====================================================// Checks if you push the manual feed buttonvoid manual_feed_check () { if (manualFeed ==true) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Manual Feeding"); startFeeding(); manualFeed =false; }}//=====================================================// checks to see if RTC is runningvoid check_rtc () { if (!rtc.isrunning()) { led_blink(); }}//=====================================================/* Blinks the red led when RTC has failed. Note:the led will be blinking at a different rate than when the hall sensor fails*/void led_blink(){ unsigned long led_currentMillis =millis(); if (led_currentMillis - led_previousMillis>=interval_delay) { led_previousMillis =led_currentMillis; if (ledState ==LOW) { ledState =HIGH; } else { ledState =LOW; } digitalWrite(HALL_LED_PIN, ledState); }}//=====================================================// Creates the blinking effect when changing valuesvoid blinkFunction(){ blink_currentMillis =millis(); if (blink_currentMillis - blink_previousMillis> blink_interval) { blink_previousMillis =blink_currentMillis; blink_state =!blink_state; }}//=====================================================
Link to Code on my Github
https://github.com/russo08/Pet-feeder/blob/main/feeder_final.ino

Pièces et boîtiers personnalisés

Fusion 360 and STL files on my Github
Here are all the fusion 360 files in case you want to customize them for different component sizes. I have also provided the STL files. The only model not on there is the tube for the hall sensor. That should be pretty easy to model and print.https://github.com/russo08/Pet-feeder.git

Schémas

This is the circuit schematic. You can change it up if you need to. If you do, just remember to make the same adjustments in the code.

Processus de fabrication

  1. Finir des pièces imprimées en 3D avec… des crayons ?
  2. Capteur de suivi de ligne avec RPi
  3. API de capteur environnemental avec un RPi
  4. Capteur Portenta et thermocouple (avec MAX6675)
  5. Meilleur contrôle qualité avec les pièces imprimées en 3D
  6. Assemblez-vous avec des pièces fonctionnelles imprimées en 3D !
  7. Réimaginer le Go Kart avec des pièces imprimées en 3D
  8. Webinaire :Impression de pièces imprimées en 3D solides avec Eiger
  9. Drones extrêmes imprimés en 3D