Pavlov’s Feeder. Un dispensador automático de comida para mascotas con Arduino




Contenidos

Introducción



El proyecto consiste en un dispensador automático de comida para mascotas que incluye un reloj para poder programar la dispensa de comida en un horario determinado. Cuando se ha rellenado el bol, el dispensador emite un sonido que intenta emular el de una campana para avisar a la mascota.

Este dispositivo ha sido pensado con el fin de alimentar a la mascota cuando sus dueños no están en casa durante periodos de tiempo cortos, como cuando se han ido a trabajar.

En el siguiente vídeo podéis ver la explicación de nuestro código y un ejemplo de funcionamiento del aparato:




Autoría



Proyecto realizado por el grupo 4 de la asignatura Sistemas Empotrados y Tiempo Real del curso 2018/2019 en el Campus de Móstoles de la URJC, formado por:

  • Marina Cabeza González
  • Patricia Sánchez Fernández
  • Celia Velasco Martínez



Componentes utilizados



Los componentes de los que no se indica el precio son aquellos que ya teníamos o hemos reciclado.


Componentes electrónicos


  • Placa de Arduino Uno (proporcionada por el profesor)
  • Servomotor (9,99€)
  • Pantalla LCD 16×2 con botones de Keyestudio (8,99€)
  • RTC DS3231 (7,09€)
  • Altavoz 
  • Cables de puente hembra a hembra (3,90€)
  • Cables macho a macho
  • Protoboard


Componentes no electrónicos


  • Tubo de PVC (3,08€)
  • Codo de PVC (0,52€)
  • Botella de agua de 2,5L (0,49€)
  • Cuenco de plástico (0,75€)
  • Caja de madera
  • Bridas y cinta aislante
  • Placa de metacrilato
  • Clip



Montaje


Diseño inicial



En el diseño inicial teníamos pensado poner una báscula que pesara
la comida necesaria para la mascota. En el diseño final no incluimos báscula y
la cantidad de comida se mide en el tiempo que se deja abierto el paso de esta
por el tubo (que se bloquea y desbloquea mediante una pestaña accionada por el
servomotor). 
También se disponía de ledes para indicar el estado del
dispensador, que finalmente no se incluyeron al realizar la pantalla LCD la
misma función.



A continuación, se muestra un boceto inicial del proyecto.







Componentes electrónicos



Hemos montado el circuito de nuestro proyecto como se indica en el siguiente diagrama de conexiones realizado en la herramienta Fritzing (puedes hacer click en la imagen para verla más de cerca).


Hemos utilizado una pantalla LCD para mostrar la hora actual y la hora programada para la dispensa de comida. También se muestra un mensaje cuando se sirve la comida y otro cuando termina de servirse. La pantalla cuenta con unos botones que se utilizarán para modificar la hora de la comida.

El servomotor mueve una trampilla que permite o bloquea el paso del pienso. Cuando este se haya servido, el altavoz emitirá un sonido que intenta emular el de una campana.

Para mantener la hora actual se dispone de un chip RTC DS3231 con una pila integrada.

A continuación, se muestra una imagen de los componentes electrónicos conectados antes de montarlos en la estructura.


Estructura del dispensador



En cuanto a la estructura del dispensador, hemos reciclado una caja de madera que hemos pintado de blanco y a la que hemos realizado los agujeros necesarios para incluir una garrafa de 3L que almacena el pienso y un bol, ambos comunicados mediante un tubo y un codo de PVC. La caja incluye una tapa extraíble que permite acceder al resto de componentes con facilidad.

El tubo de PVC cuenta con una muesca en la que se introduce la trampilla (una placa de metacrilato) conectada al servomotor que se ha mencionado anteriormente.


En el siguiente GIF se muestra el funcionamiento del servomotor.


Aquí podemos ver algunas imágenes del proceso de montaje:


Añadimos un trozo de una botella de producto lavavajillas para hacer una especie de rampa para el pienso y que este no cayese demasiado rápido desde la botella por el tubo.

Aspecto final del proyecto:


Problemas encontrados



En un principio pensamos incluir una báscula para comprobar
la cantidad de pienso que se vertía al bol y también la que quedaba en la
garrafa, pero nos encontramos con que la construcción de una báscula para
Arduino casi requería de un proyecto paralelo por su complejidad. Por lo tanto,
decidimos no incluirla y controlar el pienso dispensado mediante el tiempo de
apertura de la trampilla controlada por el servomotor.

El principal problema con el que nos hemos encontrado a la
hora de llevar a cabo nuestro proyecto definitivo ha sido el de almacenar la
hora actual en el RTC
. Nos llevó un tiempo dar con la librería adecuada para el chip que adquirimos y conseguir que el tiempo no se inicializase cada vez a la hora en la que
se compiló nuestro programa.

Además, tuvimos que añadir raíles para la pestaña manejada por el
servomotor porque se movía descontroladamente. Al principio pusimos un alambre
para conectar el servo a la placa, pero este se acababa doblando y terminamos
poniendo un clip más resistente.



Posibles mejoras



Nuestro dispensador es compacto y de un tamaño reducido
porque está pensado para mascotas de talla pequeña o mediana. En el caso de
necesitarse para una mascota más grande, se podría sacar el cuenco fuera de la
caja, alargar el tubo y sujetar el cuenco a la caja mediante un soporte para
que no se mueva.

Otra posible mejora sería incluir una báscula bajo el cuenco
para comprobar si la mascota ha comido o si por el contrario queda pienso en el
cuenco. Incluso se podría utilizar IoT y enviar un mensaje al móvil del dueño
indicándole que su mascota no se ha terminado el pienso.
El proyecto también se podría mejorar añadiendo una opción para poder
cambiar la hora actual sin tener que cargar en la placa el sketch para inicializar la hora en el RTC, lo que convertiría el producto en algo más usable para personas sin conocimientos de informática.

Código



La explicación de nuestro código podéis encontrarla en los comentarios dentro del mismo y en el vídeo que se incluye más arriba. El código lo hemos implementado y subido a la placa mediante el IDE oficial de Arduino para Windows.


Librerías utilizadas



Hemos utilizado las librerías Arduino.h, LiquidCrystal.h, Wire.h y Servo.h, incluidas por defecto en el IDE de Arduino, y también la librería RTClib.h, que puede encontrarse en el siguiente repositorio de GitHub: https://github.com/adafruit/RTClib.

Para utilizar esta librería, debes descargarla y guardarla en el directorio Arduino/libraries, que se crea al instalar el IDE de Arduino y seguramente se encuentre en tu carpeta Documentos.


Sketch principal

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#include <Arduino.h>
#include <LiquidCrystal.h> /* Library for the LCD */

#include <Wire.h>
#include "RTClib.h"

#include <Servo.h>

/* Initialize the library with interface pins numbers: */
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int val;

/* Speaker (buzzer): */
int speakerPin = 13;

/* Real time clock: */
RTC_DS3231 rtc;

/* Servo: */
Servo myservo;
int pos = 0; /* Variable to store the servo position */
int servoPin = 10;

DateTime now;
byte hour, minute;
String h, m;

int feedHour = 14;
int feedMin = 0;
boolean foodTime = true;

void setup() {

  /* Set speaker pin as an output pin: */
  pinMode(speakerPin, OUTPUT);
  /* Set up LCD's number of columns and rows: */
  lcd.begin(16, 2);
  /* Start RTC: */
  rtc.begin();
  
}

void loop() {

  now = rtc.now();
  printTime(now);

  /* LCD buttons are all connected to A0 pin with this values:
   * 0-50: UP
   * 50-150: RIGHT
   * 150-300: DOWN
   * 300-500: LEFT
   * 500-750: SELECT
   */
  val = analogRead(A0);
  if ((val >= 500) && (val <= 750)) { // SELECT
    setFeedHour();
    delay(1000);
  }
  
  now = rtc.now();
  printTime(now);
  
  delay(2000);

  if ((feedHour == now.hour()) && (feedMin == now.minute()) 
      && foodTime) {
    feed();
  }

  /* We use 'foodTime' so food doesn't get served twice 
   * if the feed routine ends in less than a minute. */
  if (feedMin != now.minute()) {
    foodTime = true;
  }
  
}

void feed() {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Sirviendo comida");
  
  /* Food gets served: */  
  /* Attach servo on pin 10 (We attach servo only when
   * we need it and detach it afterwards to stop it 
   * from shaking) */
  myservo.attach(servoPin); 
  /* Servo moves to open lid for food to fall through.
   * 'pos' goes from 0 to 180 degrees in steps of 1 degree
   * and then gets written to servo*/
  for (pos = 0; pos <= 120; pos += 1) {
    myservo.write(pos);
    /* Wait 15ms for the servo to reach the position */
    delay(15); 
  }
  delay(1000);
  /* Go back from 180 to 0 degrees to close the lid: */
  for (pos = 120; pos >= 0; pos -= 1) {
    myservo.write(pos);
    delay(15);
  }
  delay(3000);
  /* Detach servo: */
  myservo.detach();

  bell();
  
  foodTime = false;

}

void bell() {

  for (int i=0; i<6; i++) {

    /* Bell sound: */
    tone(speakerPin, 1915);
    delay(700);
    tone(speakerPin, 1700);
    delay(700);
    tone(speakerPin, 1519);
    delay(700);
    noTone(speakerPin);
  
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Hora de la");
    lcd.setCursor(0, 1);
    lcd.print("comida!!!!");
  
    delay(5000);

    now = rtc.now();
    printTime(now);

    delay(5000);

  }
  
}

/* Prints actual time and feed time.
 * If hour or minute is just one digit, a zero
 * will be added for formatting purposes */
void printTime(DateTime t) {

  /* Actual time */
  lcd.setCursor(0, 0);
  lcd.print("H. actual: ");
  hour = t.hour();
  if (hour < 10) { 
    h = "0" + String(hour); 
  } else { 
    h = String(hour); 
  }
  lcd.print(h);
  lcd.print(':');
  minute = t.minute();
  if (minute < 10) { 
    m = "0" + String(minute); 
  } else { 
    m = String(minute);
  }
  lcd.print(m);

  /* Feed hour */
  lcd.setCursor(0, 1);
  lcd.print("H. comida: ");
  if (feedHour < 10) {
    h = "0" + String(feedHour);
  } else {
    h = String(feedHour); 
  }
  lcd.print(h);
  lcd.print(':');
  if (feedMin < 10) { 
    m = "0" + String(feedMin); 
  } else { 
    m = String(feedMin); 
  }
  lcd.print(m);

  return;

}

void setFeedHour() {
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Cambiar hora de");
  lcd.setCursor(0, 1);
  lcd.print("comida: ");
  
  int hourAux = feedHour;

  while(true) {
  
    lcd.setCursor(8, 1);
    if (hourAux < 10) { 
      lcd.print("0");
      lcd.print(hourAux);
    } else { 
      lcd.print(hourAux); 
    }
    lcd.setCursor(10, 1);
    lcd.print(':');
    lcd.setCursor(11, 1);
    if (feedMin < 10) {
      m = "0" + String(feedMin); 
    } else { 
      m = String(feedMin); 
    }
    lcd.print(m);

    delay(200);

    /* We check which button is being pressed */
    val=analogRead(A0);      
    if ((val>=50) && (val<=150)) { /* UP + 1 hour */
      hourAux = (hourAux + 1) % 24; 
    } else if ((val>=150) && (val<=300)) { /* DOWN - 1 hour */
      hourAux = hourAux - 1; 
      if (hourAux < 0) {
        hourAux = 23;
      }
    }
    else if ((val>=300)&&(val<=500)) { /* LEFT cancel */
      break;
    }
    if ((val>=0)&&(val<=50)) { /* RIGHT save */
      feedHour = hourAux;
      setFeedMin();
      break;
    }
    
  }
  
  return;
  
}

void setFeedMin() {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Cambiar min. de");
  lcd.setCursor(0, 1);
  lcd.print("comida: ");
  
  int minAux = feedMin;
  
  while(true) {

    lcd.setCursor(8, 1);
    if (feedHour < 10) { 
      h = "0" + String(feedHour); 
    } else { 
      h = String(feedHour);
    }
    lcd.print(h);
    lcd.setCursor(10, 1);
    lcd.print(':');
    lcd.setCursor(11, 1);
    if (minAux < 10) {
      m = "0" + String(minAux); 
    } else {
      m = String(minAux); 
    }
    lcd.print(m);
    
    delay(200);

    /* We check which button is being pressed */
    val = analogRead(A0);
    if((val>=50) && (val<=150)) { /* UP + 1 min */
      minAux = (minAux + 1) % 60; 
    } else if((val>=150) && (val<=300)) { /* DOWN - 1 min */
      minAux = minAux - 1; 
      if (minAux < 0) {
        minAux = 59;
      }
    } else if((val>=300) && (val<=500)) { /* LEFT cancel */
      break;
    } else if((val>=500) &&( val<=750)) { /* SELECT save */
      feedMin = minAux;
      break;
    }
        
  }
  
  return;
  
}

Sketch auxiliar para poner el reloj en hora



Este código solo necesita ejecutarse una vez para inicializar el chip RTC con la hora actual. El chip RTC mantendrá la hora gracias a la pila que lleva incorporada.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

void setup() {

  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  /* The rest of the code is not necessary to set the time,
   * we're just making sure it was set correctly: */
  Serial.begin(9600);
  
  DateTime now = rtc.now();
  printDate(now);
  
}

void loop() {

}

void printDate(DateTime date) {
  
   Serial.print(date.year(), DEC);
   Serial.print('/');
   Serial.print(date.month(), DEC);
   Serial.print('/');
   Serial.print(date.day(), DEC);
   Serial.print(" ");
   Serial.print(date.hour(), DEC);
   Serial.print(':');
   Serial.print(date.minute(), DEC);
   Serial.print(':');
   Serial.print(date.second(), DEC);
   Serial.println();
   
}

Conclusiones



Este proyecto nos ha servido para familiarizarnos con Arduino y su entorno de desarrollo, así como todas las posibilidades que ofrece.

En cuanto al precio del dispensador, el hecho de haberlo construido nosotras mismas no lo ha reducido respecto a adquirir uno similar en una tienda o en Internet. Esto se debe a la baja estandarización del producto. 
Este proyecto también nos ha servido como experiencia para saber cómo es un proyecto real en el ámbito laboral.



Anexo: Manual de funcionamiento



Este dispositivo es un dispensador automático de comida de
mascotas que incorpora un reloj programable. Para utilizar este producto siga
los siguientes pasos:
  1. Enchufe el producto a la corriente.
  2. Se iluminará la pantalla, en la que aparecerá la hora actual y la hora de la comida predeterminada (las 14:00).
  3. Para modificar dicha hora pulse el botón SELECT durante 1-2 segundos.
  4. Una vez pulsado aparecerá un mensaje pidiendo el cambio de hora. Mediante los botones UP y DOWN puede incrementar o disminuir la hora.
  5. Cuando haya modificado la hora pulse RIGHT para cambiar los minutos igual que en el paso 4.
  6. Puede cancelar cualquiera de estas acciones pulsando LEFT.
  7. Para guardar la nueva hora de la comida pulse SELECT.
  8. Espere a la hora programada y el dispensador echará la comida para su mascota.
  9. Recuerde rellenar el dispositivo con el pienso favorito de su mascota.

También te podría gustar...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *