Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Alimentador para animais de estimação com peças impressas em 3D

Componentes e suprimentos

Arduino Nano R3
× 1
Módulo de relógio em tempo real DS 1307
Você pode usar qualquer marca de RTC, tenho certeza de que existem opções mais baratas
× 1
Módulo regulador de tensão LM-317
× 1
Servo de rotação contínua
× 1
Distribuidor de Cereais
× 1
ímãs de 8 mm
× 6
Sensor Hall (unipolar)
Certifique-se de que o sensor que você obteve é ​​UNIPOLAR para que você não precise da polaridade reversa do ímã para redefinir a saída do sensor
× 1
Adafruit RGB Backlight LCD - 16x2
× 1
Mochila I2C LCD
Se preferir, você pode comprar a tela LCD com a mochila já anexada
× 1
LED de 5 mm:Vermelho
× 1
LED de 5 mm:Verde
× 1
Botão de pressão, momentâneo
× 5
Adaptador de entrada de energia DC fêmea
× 1
Verruga de parede de 9-12 volts
× 1
Resistor 10k ohm
Para o sensor hall. Conecte entre Vcc e pinos de sinal
× 1
Resistor 330 ohm
Para LEDs. Você pode usar uma resistência maior se não quiser que sejam tão brilhantes
× 2

Ferramentas e máquinas necessárias

Impressora 3D (genérica)
Drill / Driver, Cordless
Serra circular

Sobre este projeto





A história por trás do comedouro para animais


Eu tenho brincado com microcontroladores por um tempo e queria tentar ramificar a partir de tutoriais. Antes da Covid-19, eu realmente não tinha um cronograma definido e trabalhava muitas horas. O horário de alimentação do meu cachorro estava realmente começando a sofrer e estava causando um desconforto visível. Minha área também está sujeita a inundações durante a temporada de furacões. Infelizmente, houve alguns casos em que não consegui voltar para casa para alimentar minha filha. Eu precisava encontrar uma solução para que, se eu não conseguisse voltar para casa, meu cachorro não passasse fome. Em vez de comprar um por $ 30- $ 40, por que não construir um por $ 100 +? Estou brincando.





O alimentador!


Meu comedouro para cães usa um microcontrolador Arduino. Havia alguns recursos-chave de que eu precisava que a maioria das outras versões de comedouros para cães não tinha. Ou seja, uma solução para a alimentação de recuperação de queda de energia e abordar o problema de alimentos que ficam presos no mecanismo de distribuição. Minha área também sofre de quedas de energia aleatórias. As interrupções geralmente nunca duram muito, então não vi necessidade de redundância de energia. Eu também queria algo que fosse fácil de desmontar e limpar. Tenha cuidado ao lavar as peças impressas em 3D, você pode usar água morna, NÃO água quente, a menos que planeje imprimir com ABS ou PET-G.

A lista completa de recursos é a seguinte:
  • Duas mamadas por dia
  • Cronometragem precisa com relógio em tempo real
  • Alteração manual da hora do relógio em tempo real
  • Opção de alimentação manual
  • Indicação de LED do sensor hall e falha do relógio em tempo real
  • Visão geral dos tempos de alimentação, hora atual e conclusões de alimentação na tela principal
  • Menu fácil de navegar
  • Reinício de alimentação por falta de energia (alimentará quando a energia voltar)
  • Tempos de alimentação e conclusões armazenados com segurança na EEPROM
  • Servo "jiggle" no caso de comida ficar presa durante a distribuição





Demo


Vídeo de demonstração em breve!





O que mais você precisa?

  • 4 parafusos M3-0,5 x 16 mm (roda codificadora)
  • 4 parafusos M3-0,5 x 10 mm (suporte do servo)
  • 12x porcas sextavadas M3
  • 4 porcas de segurança M3
  • 16 x 1-1 / 2 pol. (38 mm) Parafusos multiuso para madeira
  • cola para madeira
  • 6 x 0,315 x 0,118 polegadas (8 x 3 mm) ímãs
  • Madeira de 1-1 / 2 x 1-1 / 2 polegadas (3,8 x 3,8 cm) para pernas e montagem servo
  • 1/2 x 6 pol. (1,27 x 15,24 cm) de madeira para o fundo e a base





As peças impressas em 3D


Recentemente, adquiri uma impressora 3D e pensei, qual a melhor maneira de aprender e colocá-la em uso do que imprimir peças personalizadas para o meu comedouro para animais de estimação. Tudo impresso é PLA + e foi impresso usando um Ender 3 Pro usando Cura como fatiador. Os STLs para todas as partes podem ser encontrados na página do projeto Github. Link abaixo. Leia o arquivo README para obter instruções de impressão e configurações do divisor.





A habitação


A caixa foi projetada com conveniência e simplicidade em mente. Não são necessários parafusos ou porcas. Simplesmente coloque os componentes dentro e fora. Cada inserção possui 4 abas que prendem os componentes no lugar.

Embora eu tenha bagunçado inicialmente o slot da tela LCD, voltei e consertei o modelo no Fusion 360. Eu estava com preguiça de voltar e imprimi-lo novamente. Usei alguns parafusos M3 0,5 x 6 mm extras que tinha para prendê-lo. Existem quatro espaçadores em cada canto do slot de LCD com orifícios para que você possa proteger a tela, se necessário. Infelizmente, nunca tirei fotos da tampa da caixa antes de colocar tudo dentro.





O acessório da roda codificadora


A roda codificadora da roda serve a dois propósitos:
  • Fornece ao Arduino feedback de posição do servo
  • Anexa a pá de borracha ao servo

Uma porção de alimentação é igual a 1/6 (60 graus) de volta da roda. Use uma escala de alimentos para medir a quantidade de comida que seu animal de estimação recebe por alimentação e, em seguida, ajuste o número da porção até obter uma faixa que satisfaça essa quantidade. Acredito que uma alimentação para mim foi de cerca de 173 gramas de comida. Uma porção de 17 me deu uma faixa de 170-177 gramas por alimentação. Tudo depende do tamanho da sua ração. Certifique-se de que a buzina esteja posicionada entre as 8 porcas sextavadas M3.





O suporte do servo e a montagem do sensor Hall


Este suporte servo personalizado também segura o sensor Hall e monta o servo em uma peça de madeira de 1-1 / 2 x 1-1 / 2 pol. (3,8 x 3,8 cm). O comprimento da madeira vai depender de onde o servo fica (mais sobre isso mais tarde). Há muito espaço para erros com a montagem, então não se preocupe muito em obter o comprimento perfeito.





O funil, rampa de alimentação e trilho de rampa


Isso constitui o sistema de distribuição de alimentos. A comida desce do distribuidor através do funil, para a rampa e para a tigela de comida. Infelizmente, não tirei fotos do trilho do chute antes de montá-lo.





O quadro


* Exoneração de responsabilidade * Não tirei fotos de todas as etapas do processo de montagem. Algumas dessas fotos terão ignorado etapas, mas ainda assim o acompanharei passo a passo para a montagem da estrutura.

Faça furos de perfuração perfeitos para um parafuso de escala que você use.

Este é o distribuidor de cereais que comprei na Amazon. Existem alguns que têm vários dispensadores, se você tiver vários animais de estimação e precisar de vários comedouros, eu só preciso de um. A marca é Honey Can Do, mas tenho certeza de que qualquer marca funcionará.

A primeira coisa que comecei foi remover o botão da haste que se conectava à roda de pás do dispensador. Usei uma serra manual para remover a maçaneta. Você pode usar uma ferramenta elétrica se tiver uma. Não corte a haste ainda mais onde termina a parte prateada. Corte bem na base do botão.

Depois de serrar a maçaneta, remova o material restante cortando os 3 suportes vistos na última foto acima. Isso demorou um pouco. Imagino que será mais rápido se você tiver a ferramenta elétrica certa. Depois de remover esses três suportes, o pedaço que sobrou na própria haste deve sair com um pouco de graxa de cotovelo. Você terá que lixar a parte da haste mais próxima do botão para que ela se encaixe perfeitamente na roda do codificador.

Em seguida, começamos a construir a base da estrutura. Peguei a madeira de 1/2 x 6 polegadas (1,27 x 15,24 cm) e cortei-as em dois pedaços de 8 polegadas (20,32 cm). Isso formará a base e a parte traseira da estrutura do alimentador. Aplique um pouco de cola para madeira e use 2 dos pregos multifuncionais para uni-los em forma de L. Você precisará adicionar alguns suportes em ângulo reto para reforçar o painel traseiro e a conexão do painel inferior. Use 4 parafusos multiuso e um pouco de cola de madeira para prendê-los nos painéis traseiro e inferior. Não tenho uma imagem desta etapa, mas você pode vê-los nas imagens mais abaixo.

A partir daqui, você deve cortar o suporte do dispensador a uma altura de cerca de 11,43 cm (4,5 pol.). Não precisa ser perfeito. Tente chegar o mais perto que puder. O funil permite algum espaço de manobra. Depois de cortar o suporte no tamanho certo, coloque-o contra a parte de trás e certifique-se de que está bem assentado na base. Depois de colocá-lo na posição, use um lápis ou caneta para marcar o centro de onde ficará a extremidade dispensadora do recipiente. Em seguida, você precisará fazer um orifício de 2 polegadas (5,08 cm) em todo o painel da base. É fundamental que você meça duas vezes e corte uma vez com este. Esse será o buraco visto na foto acima.

Depois de perfurar o orifício no painel da base, estamos prontos para anexar o suporte do dispensador ao painel traseiro. O que você deseja fazer é posicionar o suporte contra o painel traseiro (mostrado na primeira imagem abaixo). Existem duas áreas de cubículos sob o anel do suporte do dispensador. É aqui que você deseja que os furos estejam (veja a imagem abaixo). Use um lápis ou caneta para marcar a altura onde os dois orifícios devem estar no painel traseiro. Você quer que eles fiquem o mais próximo possível do centro do suporte dispensador. Existem dois parafusos que conectam o anel superior do suporte à parte inferior que você cortou. Tenha cuidado para não bater neles ao perfurar. Novamente, lembre-se de medir duas vezes e perfurar uma vez. Ou duas vezes, neste caso.

Agora que já perfuramos os orifícios, podemos prender o suporte ao painel traseiro usando os parafusos sextavados de 5/16 polegadas, porcas sextavadas de 5/16 polegadas e arruelas de 5/16 polegadas. Você quer ir em frente e empurrar os parafusos certificando-se de colocar arruelas na extremidade hexagonal dos parafusos antes de empurrá-los. Depois de saírem do outro lado, coloque o outro conjunto de arruelas no lado rosqueado e comece a apertar manualmente as porcas sextavadas. Isso vai ser um pouco complicado. Depois de apertar as porcas manualmente, você terá que usar um soquete para segurar a porca e apertar ainda mais. Apenas cerca de 1/4 de volta ou mais. Tenha cuidado para não apertar demais.

Agora que o suporte está preso com segurança, podemos adicionar o pedaço de madeira de 1/2 x 1/2 polegada em que o servo irá se assentar. A duração disso vai depender de onde seu servo estará sentado. Vá em frente e monte o mecanismo de alimentação conectando o servo ao chifre na roda do codificador e a roda do codificador à pá de borracha dentro do recipiente de plástico. Coloque o recipiente no anel superior e meça onde o servo fica no painel de base. Não se preocupe em ser perfeito. O suporte do servo permite um pouco de espaço de manobra. Use cola de madeira e um único parafuso multiuso para prender o suporte de madeira.

O próximo passo é prender as pernas ao painel da base do alimentador. O comprimento das pernas vai depender da altura da tigela de comida do seu animal de estimação. Minha cadela tem sua tigela de comida em um suporte elevado; portanto, eu precisava que meu alimentador ficasse bem alto. Use 4 dos parafusos multiuso e um pouco de cola de madeira para prendê-los no lugar. Eu recomendo colocar uma viga cruzada entre as duas pernas dianteiras e traseiras e outra viga cruzada entre as vigas cruzadas como mostrado abaixo para maior estabilidade. Use um total de 6 parafusos multiuso e um pouco de cola de madeira para prender as peças nas pernas.

As próximas etapas são:
  • insira o funil no orifício que perfuramos no painel de base
  • anexe o servo ao suporte do servo
  • prenda o suporte ao suporte de madeira

Fixe o servo ao suporte usando os 4 parafusos M3 x 10 mm e 4 das porcas hexagonais M3. Assim que o servo estiver preso, podemos fixar o suporte no suporte de madeira do servo. Use dois dos parafusos multiuso para prender levemente o suporte no suporte de madeira. Não aperte demais ou você danificará o suporte. Certifique-se de seguir as etapas nessa ordem. O funil vai elevar um pouco o servo e ele se encaixa perfeitamente na extremidade do recipiente de plástico, então é impossível colocá-lo se o recipiente de plástico já estiver no suporte de anel superior.

As etapas finais serão prender o suporte deslizante do chute e o próprio slide. Você deseja posicionar o suporte ligeiramente atrás do orifício no painel da base. Você deseja que ele avance o máximo que puder, para que o slide saia da moldura do alimentador. Use dois dos parafusos multiuso para prender o suporte na parte inferior do painel da base do quadro. É melhor fazer isso com o slide no suporte, pois o suporte tem alguma flexibilidade e você vai querer um encaixe bem seguro entre o slide e o suporte.





A eletrônica


Infelizmente, nunca tirei fotos do processo de soldagem. Não há muito a fazer. Apenas solde cada componente em seus pinos correspondentes e você estará pronto para continuar. Se você quiser usar cabeçalhos de alfinete, também pode fazer dessa forma. Há espaço suficiente abaixo e acima do slot do Arduino para permitir os pinos e conectores. Eu definitivamente recomendo soldar tudo junto antes de colocar todos os componentes em seus respectivos slots.

Liguei meu Arduino por meio do pino de alimentação externa não regulado (pino 30). Isso requer uma tensão de entrada entre 7-20 volts porque essa tensão é alimentada pelo regulador integrado do Arduino. Se desejar alimentá-lo via USB, você só precisa se certificar de que está fornecendo 5 Volts, NÃO 7-20 Volts.

Certifique-se de soldar o resistor de 10k Ohm entre os pinos Vcc e Signal no sensor Hall. Caso contrário, você não obterá uma leitura. Além disso, não se esqueça de conectar todos os componentes ao aterramento comum. Cometi o erro de perder um dos fundamentos e meu sistema funcionaria por um tempo, mas o sensor do corredor eventualmente começou a falhar. Foi um dia muito bom para o meu cachorro.

Acabei fazendo conectores personalizados para o sensor Hall e conexões servo. Soldei os fios em cabeçotes de pinos machos. Eles ficam pendurados na parte inferior do gabinete. Para o sensor Hall, fiz um adaptador fêmea personalizado, cortando, removendo e soldando alguns fios do conector Dupont que eu tinha por aí.

Para o Vcc e o trilho de aterramento, cortei a seção do trilho de alimentação de algumas protuberâncias perma-proto extras que eu tinha por aí. Qualquer coisa semelhante que você tenha por perto funcionará. Verifique novamente suas conexões antes de colocar o trilho em seu slot. Esta é a coisa mais difícil de sair depois de entrar. Também aprendi isso da maneira mais difícil.

É isso! Espero que vocês se divirtam construindo isso tanto quanto eu. Parece desafiador no início, mas quanto mais você se dedica, mais fácil fica. Deixe-me saber se você tiver alguma dúvida sobre qualquer coisa. Feliz mexer!

Código

  • Código do alimentador
Código do alimentador C / C ++
 // Versão final para o comedouro / * Características:- Menu fácil de navegar - Visão geral dos horários de alimentação, horário atual, recomendações de alimentação e porção de alimentação na tela principal - Porções controláveis ​​usando um sensor Hall para feedback - Preciso manutenção de tempo com chip DS1307 - Pode alterar manualmente o tempo definido no chip DS1307 - Duas alimentações por dia - Opção de alimentação manual - Reinício de alimentação em caso de queda de energia - LED de indicação do sensor Hall e relógio de tempo real - Tempos de alimentação e conclusões armazenados com segurança em EEPROM - Servo "jiggle" no caso de comida ficar presa * / # include  #include  #include  #include  #include  # include  #include  // Cria um objeto servo para controlar seu servoServo myservo; // Atribuindo todos os meus pinos de entrada e I / O # 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 // Definindo todos os botões, trabalha com o JC_Button libraryButton enterBtn (ENTER_BUTTON_PIN); Botão upBtn (UP_BUTTON_PIN); Botão downBtn (DOWN_BUTTON_PIN); Botão backBtn (BACK_BUTTON_PIN); Botão manualFeedBtn (MANUAL_BUTTON_PIN); LCD I2C e RTCLiquidCrystal_I2C lcd (0x27, 16, 2); RTC_DS1307 rtc; // Variáveis ​​usadas em todo o codeunsigned long hallSensorTime; unigned long rotationTime =400; Unigned long led_previousMillis =0; const long long interval_delay =1000; int ledState =LOW; boolean manualFeed =false; boolean hall_sensor_fail =false; longo sem sinal blink_previousMillis =0; sem sinal longo blink_currentMillis =0; sem sinal longo blink_interval =500; sem sinal longo delay_currentMillis =0; boink long delay_previousMillis =0; boink longo delay_previousMillis =0; contagem interna; alimentação booleana1_complete =falso; alimentação booleana2_complete =falso; alimentação booleana1_trigger =falso; alimentação booleana2_trigger =falso; servo booleanoOn =tru e; // Interrupção volátil do sensor Hall boolean hallSensorActivated =false; volatile int isr_count =1; void hallActiveISR () {hallSensorActivated =true; digitalWrite (HALL_LED_PIN, HIGH); isr_count =isr_count + 1;} / * Eu uso enums aqui para manter um melhor controle de qual botão está sendo pressionado ao invés de apenas ter cada botão configurado com um valor interno. * / enum {btnENTER, btnUP, btnDOWN, btnBACK,}; / * Estados da Máquina de Estado. A mesma coisa aqui com o tipo enum. Torna mais fácil acompanhar em qual menu você está ou deseja ir, em vez de atribuir a cada menu um valor intreger * / enum STATES {MAIN, MENU_EDIT_FEED1, MENU_EDIT_FEED2, MENU_EDIT_TIME, MENU_EDIT_PORTION, EDIT_FEED1_HOUR, EDIT_FEED1_MIN, EDIT_FEED1_MIN, EDIT_FEED2, EDIT_FEED1_MIN, EDIT_FEED2, EDIT_FEED2_MIN, EDIT_FEED2 , EDIT_MIN, EDIT_PORTION}; // Mantém o estado do estado machineSTATES state; // User input variablesint Hour; int Minute; int piece; int feed_time1_hour; int feed_time1_min; int feed_time2_hour; int feed_time2_min; int userInput; // Marca de verificação de caractere especial check_Char [8] ={B00000, B00000, B00001, B00011, B10110, B11100, B11000, B00000}; // ======================A configuração ===============================void setup () {Wire.begin (); Serial.begin (9600); lcd.init (); lcd.backlight (); lcd.createChar (0, check_Char); if (! rtc.begin ()) {Serial.println ("Não foi possível encontrar RTC!"); } // rtc.adjust (DateTime (F (__ DATE__), F (__ TIME__))); // Os botões enterBtn.begin (); upBtn.begin (); downBtn.begin (); backBtn.begin (); manualFeedBtn.begin (); // Configurando o estado inicial do State Machine state =MAIN; // Configurando entradas e saídas pinMode (POWER_LED_PIN, OUTPUT); pinMode (HALL_LED_PIN, OUTPUT); pinMode (hallPin, INPUT); / * Interrupção de fixação ao pino que se conecta ao sensor hall. O sensor Hall que usei está definido como ALTO e fica BAIXO quando encontra um ímã. É por isso que está definido como FALLING * / attachInterrupt (digitalPinToInterrupt (hallPin), hallActiveISR, FALLING); // estado padrão dos LEDs digitalWrite (POWER_LED_PIN, HIGH); digitalWrite (HALL_LED_PIN, LOW); / * Essas funções obtêm o tempo de alimentação armazenado, alimentação concluída e tamanho da porção da EEPROM na inicialização. Fiz isso porque tenho interrupções de energia aleatórias aqui onde moro. * / get_feed_time1 (); get_feed_time2 (); get_completed_feedings (); get_portion ();} // ======================The Loop =============================void loop () {changing_states (); check_buttons (); check_feedtime (); check_rtc (); manual_feed_check ();} // ===============As funções =========================/ * Usos a Biblioteca JC_Button para verificar continuamente se um botão foi pressionado. Dependendo de qual botão é pressionado, ele define a variável userInput a ser usada na função menu_transitions * / void check_buttons () {enterBtn.read (); upBtn.read (); downBtn.read (); backBtn.read (); manualFeedBtn.read (); if (enterBtn.wasPressed ()) {Serial.println ("Você pressionou Enter!"); userInput =btnENTER; menu_transitions (userInput); } if (upBtn.wasPressed ()) {Serial.println ("Você pressionou!"); userInput =btnUP; menu_transitions (userInput); } if (downBtn.wasPressed ()) {Serial.println ("Você pressionou!"); userInput =btnDOWN; menu_transitions (userInput); } if (backBtn.wasPressed ()) {Serial.println ("Você pressionou para trás!"); userInput =btnBACK; menu_transitions (userInput); } if (manualFeedBtn.wasPressed ()) {Serial.println ("Você está alimentando manualmente!"); manualFeed =true; }} // =========================================================/ * Esta função determina o que é mostrado, dependendo de qual menu ou "estado" você está. Cada menu tem uma função que mostra o respectivo menu * / void changing_states () {switch (state) { case PRINCIPAL:display_current_time (); display_feeding_times (); display_portion (); pausa; case MENU_EDIT_FEED1:display_set_feed_time1_menu (); pausa; case MENU_EDIT_FEED2:display_set_feed_time2_menu (); pausa; case MENU_EDIT_TIME:display_set_time_menu (); pausa; case MENU_EDIT_PORTION:display_set_portion_menu (); pausa; case EDIT_FEED1_HOUR:set_feed_time1 (); pausa; case EDIT_FEED1_MIN:set_feed_time1 (); pausa; case EDIT_FEED2_HOUR:set_feed_time2 (); pausa; case EDIT_FEED2_MIN:set_feed_time2 (); pausa; case EDIT_HOUR:set_the_time (); pausa; case EDIT_MIN:set_the_time (); pausa; case EDIT_PORTION:set_the_portion (); pausa; }} // =========================================================/ * Esta é a parte de transição da máquina de estado. Isso é o que permite que você vá de um menu para outro e altere os valores * / void menu_transitions (int input) {switch (state) {case PRINCIPAL:if (input ==btnENTER) {lcd.clear (); estado =MENU_EDIT_FEED1; } if (input ==btnBACK) {hall_sensor_fail =false; } pausa; // ------------------------------------------------ ---- case MENU_EDIT_FEED1:if (input ==btnBACK) {lcd.clear (); estado =PRINCIPAL; } else if (input ==btnENTER) {lcd.clear (); estado =EDIT_FEED1_HOUR; } else if (input ==btnDOWN) {lcd.clear (); estado =MENU_EDIT_FEED2; } pausa; // ------------------------------------------------ ---- case EDIT_FEED1_HOUR:// Precisa disso para evitar que o servo desligue enquanto ajusta o tempo servoOn =false; if (input ==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; } pausa; //---------------------------------------------------- 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*"); atraso (1000); lcd.clear (); servoOn =true; write_feeding_time1 (); state =MAIN; } pausa; //---------------------------------------------------- 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; } pausa; //---------------------------------------------------- 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; } pausa; //---------------------------------------------------- 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*"); atraso (1000); lcd.clear (); servoOn =true; write_feeding_time2 (); state =MAIN; } pausa; //---------------------------------------------------- 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; } pausa; //---------------------------------------------------- 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; } pausa; //---------------------------------------------------- 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*"); atraso (1000); lcd.clear (); rtc.adjust(DateTime(0, 0, 0, Hour, Minute, 0)); state =MAIN; } pausa; //---------------------------------------------------- case MENU_EDIT_PORTION:if (input ==btnUP) { lcd.clear(); state =MENU_EDIT_TIME; } else if (input ==btnENTER) { lcd.clear(); state =EDIT_PORTION; } pausa; //---------------------------------------------------- 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*"); atraso (1000); lcd.clear (); write_portion(); state =MAIN; } pausa; }}//=====================================================// 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); pausa; //---------------------------------------------------- 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 (""); } pausa; } 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); pausa; //---------------------------------------------------- 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 (""); } pausa; } 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); pausa; //---------------------------------------------------- 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 (""); } pausa; } 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); delay(30); myservo.write(93); delay(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

Peças personalizadas e gabinetes

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

Esquemas

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.

Processo de manufatura

  1. Concluindo as peças impressas em 3D com… giz de cera?
  2. Sensor de rastreamento de linha com RPi
  3. API do sensor ambiental com um RPi
  4. Sensor Portenta e Termopar (com MAX6675)
  5. Melhor controle de qualidade com peças impressas em 3D
  6. Amarre-se com peças impressas em 3D funcionais!
  7. Re-imaginando o Go Kart com peças impressas em 3D
  8. Webinar:Imprimindo peças impressas em 3D fortes com Eiger
  9. Drones extremos impressos em 3D
  10. A corrida para o final com peças de veículos SAE de fórmula impressa em 3D