¿Quieres ahorrarte menos de 5€, pasando incontables horas de entretenimiento, diseñando, programando y soldando, para finalmente empotrar el resultado dentro de una caja?

Si la respuestas es “sí”, este es tu hilo. En caso negativo, puedes ir a tu tienda de mascotas favorita y ahorrarte mucho tiempo y dinero.

Referencias

Como siempre que nos enfrentamos a cualquier nuevo proyecto, lo mejor es echar un vistazo para ver qué se le ha ocurrido a alguien antes que a ti y sobre todo, ver hasta dónde ha llegado.

En cuanto a la alimentación de peces con Arduino, hay gran cantidad de bibliografía en Internet. En nuestro caso, partiremos de esta idea https://www.instructables.com/id/Arduino-Fish-Feeder/

De lo duro (hardware)

Necesitaremos lo siguiente

  • Un Arduino mini pro
  • Un servo motor
  • Un pulsador para instalarlo en chasis
  • Un bote que de algún modo tenga cierta forma cilíndrica
  • Una caja para meter todo

Al bote porta alimentos, se le debe prácticar un agujero perpendicular a su eje, de forma que por ese agujero caerá la comida en cada ciclo. En mi caso, use una broca de 5mm, teniendo en cuenta que las bolitas de comida serían de la mitad de tamaño.

El Arduino elegido es una variante pequeña, porque ¿para qué lo quieres más grande? Pero bueno, si quieres usar uno de mayor tamaño, allá tú, la receta es la misma.

Desde mi humilde punto de vista, lo más pesado es darle a todo cierta presentación, elegir una caja adecuada y un bote de dimensiones igualmente correctas.

El montaje final sería más o menos como el de la foto de la portada.

De lo blando (software)

El programa del que partiremos es el del enlace anterior. Principalmente, lo que hace el programa es girar 180 el servo motor, de forma que el agujero que hemos hecho en el bote cilíndrico, pasará de estar arriba a abajo. Añadiremos a este ciclo un agitado por si alguna bola se atasca. Cada 12 horas se repetirá este movimiento. Se pueden cambiar los parámetros para que esto se produzca solo una vez al día o con mayor frecuencia. Dependerá bastante del tamaño del agujero y de la cantidad de comida que empíricamnete puedas comprobar que cae en cada ciclo. Además deberás tener en cuenta otros parámetros como el número de peces que quieras alimientar.

Al pulsar el botón, se alimentará a los peces en ese momento. Desde ese momento, cada 12 horas, se repetirá el movimiento de manera automática. Así que si pulsas el botón a las 8am, el ciclo se repetirá a las 8pm, después a las 8am del día siguiente, etc. Teóricamente el aparato podría estar encendido 52 días. Pasado este tiempo, se desborda el contador de milisegundos y puede ser el caos alimenticio. En principio no debe ocurrir por la aritmética al restar números sin signo.

El programa es el siguiente. Basicamente, en ciclos de un día alimenta a los peces entre una a cuatro veces (según la variable FEEDINGS_PER_CYCLE). Inicialmente tiene un valor de 3, por lo que cada 8 horas dispensará la comida.

Además, el alimentador tiene un botón. Este botón hace lo siguiente según el tiempo que se pulse:

  • Menos de 200ms, no hace nada.
  • Entre 200 ms y 1 segundo, alimenta a los peces. Esta alimentación es “extra” y la siguiente dispensación se producirá a la hora que ya estaba programada. Por lo tanto, si quieres premiar a los peces puntualmente con más comida, elige esta opción.
  • Entre 1 segundo y 2 segundos: comida una vez al día.
  • Entre 2 segundo y 3 segundos: comida dos veces al día.
  • Entre 3 segundo y 4 segundos: comida tres veces al día.
  • Entre 4 segundo y 5 segundos: comida cuatro veces al día.
  • Más de 5 segundos: cancela la selección, es decir, no se hace nada.

 

#include <Servo.h>

#define PROD
#if defined(DEVEL)
unsigned long CYCLE_INTERVAL = 60000; // 1 minute
unsigned long FEEDINGS_PER_CYCLE = 4;
#else
unsigned long CYCLE_INTERVAL = 86400000; // 1 day
unsigned long FEEDINGS_PER_CYCLE = 3;
#endif

Servo myservo; // create servo object to control a servo


long SHAKES = 1; // Shakes to move the feed


int PIN_5V = 2;
int PIN_IN = 3;
int PIN_LED = 13;
int PIN_STEPPER_MOTOR = 9;
int PIN_FEED_NOW = 3;

unsigned long lastFeedingTime;
unsigned long timeBetweenFeedings;


int STATUS = HIGH;


/*
* Define timers. Set machine state to feed after timeBetweenFeedings
*/
void setupTimers() {
lastFeedingTime = millis();
timeBetweenFeedings = CYCLE_INTERVAL / FEEDINGS_PER_CYCLE;
}

/**
* Is it feeding time?
*/
bool isFeedingTime() {
// Espera a la siguiente alimentación
if(millis() - lastFeedingTime >= timeBetweenFeedings) {
lastFeedingTime = lastFeedingTime + timeBetweenFeedings;
blink(); // Heartbeat
return true;
} else {
return false;
} 
}

void setup()
{
pinMode(PIN_5V, OUTPUT);
digitalWrite(PIN_5V, HIGH);

pinMode(PIN_IN, INPUT);

pinMode(PIN_FEED_NOW, INPUT_PULLUP);

setupTimers();

}

void blink() {
digitalWrite(PIN_LED, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(PIN_LED, LOW); // turn the LED off by making the voltage LOW
delay(1000);
}

/* 
* Soft movement of servo n-times 
*/
void blinkServo(int n) {
// Attaches the servo on pin to the servo object
myservo.write(0);
myservo.attach(PIN_STEPPER_MOTOR);

for(int i = 0 ; i < n ; i++) {
myservo.write(20);
delay(100);
myservo.write(0);
if(n > 0) {
delay(1000);
}
}

// Disconnect servo
myservo.detach();
}
/*
* Default servo blinkg = 1
*/
void blinkServo() {
blinkServo(1);
}

/*
* Move servo
*/
void feed() {

int pos = 0; // variable to store the servo position

// Attaches the servo on pin to the servo object
myservo.write(0);
myservo.attach(PIN_STEPPER_MOTOR);

// Move servo to feeding position
for (pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(10); // waits ms for the servo to reach the position
}

// Shaking
int shakes = SHAKES;
while(shakes > 0) {
delay(1000);
myservo.write(90);
delay(1000);
myservo.write(180);
delay(1000);
shakes--;
}

// Move servo back to its initial position
for (pos = 180; pos >= 0; pos -= 1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(10); // waits ms for the servo to reach the position
}

// Disconnect servo
myservo.detach();

}


/**
* If release time is:
* [0, 200[ ms --> Do nothing
* [500, 1000[ ms --> Feed now (and not update timers)
* [1000, 2000[ ms --> Set FEEDINGS_PER_CYCLE = 1 && shake servo
* [2000, 3000[ ms --> Set FEEDINGS_PER_CYCLE = 2 && shake servo twice
* [3000, 4000[ ms --> Set FEEDINGS_PER_CYCLE = 3 && shake servo 3 times
* [4000, 5000[ ms --> Set FEEDINGS_PER_CYCLE = 3 && shake servo 4 times
* [5000, ...[ ms --> Do nothing
*/
void feedControl() {
unsigned long pressTime = millis();
// Wait until release
unsigned long nextServoBlinkTime = millis();
int servoBlinks = 0;
while(digitalRead(PIN_FEED_NOW) == LOW) {
// Wait
if(millis() - nextServoBlinkTime >= 1000) {
servoBlinks++;
blinkServo();
nextServoBlinkTime += 1000;
}
}

if(millis() - pressTime < 200) {
servoBlinks = -1;
}

switch(servoBlinks) {
case -1:
// Do nothing
break;
case 0:
feed();
break;
case 1:
FEEDINGS_PER_CYCLE = 1;
setupTimers();
break;
case 2:
FEEDINGS_PER_CYCLE = 2;
setupTimers();
break;
case 3:
FEEDINGS_PER_CYCLE = 3;
setupTimers();
break;
case 4:
FEEDINGS_PER_CYCLE = 4;
setupTimers();
break;
default:
// Cancels
servoBlinks = 0;
}

blinkServo(servoBlinks);

}

/**
* Arduino loop
*/
void loop()
{
if(digitalRead(PIN_FEED_NOW) == LOW) {
feedControl();
} else {
if(isFeedingTime()) {
feed();
}
}
}


Seguiremos actualizando según disponibilidad.