🤖🍽️

Robot dispensador de comida para animales

Guía interactiva STEM. Toca las palabras rosadas para saber qué significan.

🏁 Empezamos aquí

Bienvenido al proyecto tecnológico

Vamos a construir una máquina que abre una compuerta y deja caer la comida de tu mascota o animal. Lo hace de dos maneras: cuando llega la hora del almuerzo, y cuando alguien acerca la mano.

¿Cómo funciona? En 4 pasos

Concepto ilustrativo del robot dispensador de comida para animales
Figura 1.1: Concepto general del dispensador automático con compuerta servo, sensor ultrasónico y temporizador.
  1. El reloj cuenta el tiempo. Un módulo llamado DS1302 sabe qué hora es, igual que el reloj de tu casa.
  2. El sensor vigila. El sensor ultrasónico mide si hay algo cerca, como los murciélagos.
  3. El Arduino decide. Es la pequeña computadora que pregunta: «¿ya es la hora?» o «¿hay una mano cerca?».
  4. El motor abre la tapa. El servomotor gira, la compuerta se abre 2 segundos y se cierra.

¿Cómo usar esta guía?

🧩

De a poco

Primero probamos una pieza sola. Después juntamos dos piezas. Al final, las tres juntas.

🖱️

Palabras rosadas

Si ves una palabra rosada con «?», tócala. Aparecerá un cartel que la explica en una frase.

📋

Botón Copiar

Cada programa tiene un botón verde. Lo copias y lo pegas en el Arduino IDE.

📖

Glosario

En el último botón del menú están todas las palabras nuevas, ordenadas de la A a la Z.

Reglas de seguridad

Antes de tocar los cables, desconecta el cable USB. Así el Arduino no tiene electricidad y no se puede dañar.
Nunca unas un cable de 5V directo con uno de GND. Eso se llama cortocircuito y quema la placa.
Trabaja con las manos secas y sobre una mesa limpia. La electricidad del Arduino es muy pequeña (5 voltios), no da toques, pero las piezas sí se pueden romper.
🧠 Lección

¿Qué es un Arduino?

El Arduino Uno es una computadora del tamaño de una tarjeta. No tiene pantalla ni teclado: lo que hace es leer sensores y mover cosas.

Piensa en el Arduino como el cerebro del proyecto. Los sensores son sus sentidos (oído, vista) y el motor son sus músculos.

Las partes de la placa

Diagrama de referencia de la placa Arduino Uno
Figura 2.1: Placa Arduino Uno mostrando el conector USB, chip microcontrolador, botón reset y pines (5V, GND, D0-D13).
Parte¿Para qué sirve?
Conector USBPor ahí entra la electricidad desde la computadora y también viaja el programa que escribimos.
Pines digitales (D0 a D13)Son los agujeritos donde se enchufan los cables de los sensores y del motor. Cada uno tiene un número.
Pin 5VDa 5 voltios de energía. Es como el «enchufe» para alimentar los módulos.
Pin GNDEs el camino de vuelta de la electricidad. Todo circuito necesita ida (5V) y vuelta (GND).
Chip negro grandeEs el microcontrolador: ahí adentro vive nuestro programa.
Botón RESETSi lo aprietas, el programa vuelve a empezar desde cero.

¿Cómo se le da una orden?

Escribimos un programa en la computadora, apretamos «Subir» y el programa se guarda dentro del chip. Desde ese momento el Arduino lo repite solo, aunque desconectes la computadora.

Todo programa de Arduino tiene 2 partes

void setup() {
  // Esto se hace UNA SOLA VEZ, al encender.
}

void loop() {
  // Esto se repite PARA SIEMPRE, miles de veces por segundo.
}

🔎 ¿Qué dice esto?

void setup()«Preparación». Es como ponerse los zapatos antes de salir: se hace una vez.
void loop()«Repetir». Es como pedalear una bicicleta: la misma acción, una y otra vez, sin parar.
{ }Las llaves guardan adentro las órdenes. Todo lo que está entre { y } pertenece a esa parte.
//Dos barras hacen un comentario. El Arduino lo ignora, es una nota para nosotros.
;El punto y coma es el «punto final» de cada orden. Si falta, el programa da error.

Palabras que verás en todos los códigos

📦

Variable

Una cajita con nombre donde el Arduino guarda un número que puede cambiar. Ej: int angulo = 0;

🔒

Constante

Una cajita que nunca cambia. Se escribe con const. Ej: const int trigPin = 6;

⏸️

delay()

«Espera aquí sin hacer nada». delay(1000) espera 1 segundo.

if()

«Si pasa esto, entonces haz aquello». Es la forma de decidir.

🔧 Lección

Las piezas del proyecto tecnológico

Usaremos 4 piezas principales. Aquí conocerás cada una antes de conectarla.

1. Arduino Uno — el cerebro

¿Qué hace?

Lee lo que dicen los sensores y manda órdenes al motor.

¿Cómo se ve?

Una plaquita azul con muchos agujeritos numerados a los costados.

Dato curioso

Nació en Italia en 2005, creado para que estudiantes de arte pudieran hacer inventos sin ser ingenieros.

2. Microservo SG90 — el músculo

Un servomotor es un motor especial: no gira sin parar como el de un ventilador. En lugar de eso, se coloca en el ángulo que le pidamos y se queda ahí.

Diagrama de referencia del Microservo SG90
Figura 3.1: Microservo SG90 indicando sus 3 cables (Rojo 5V, Marrón GND, Naranja Señal D9) y rango de 0° a 90°/180°.
📐

Sus ángulos

Entiende de 0° a 180°. Le decimos servo.write(90) y se pone justo a la mitad.

🎨

Sus 3 cables

Rojo = energía (5V). Marrón o negro = GND. Naranja o amarillo = señal (la orden).

🚪

En el proyecto

Su brazo empuja la compuerta de la comida: 0° = cerrada, 90° = abierta.

Un servo tiene un pequeño motor, unos engranajes y un sensor adentro. El sensor le avisa: «ya llegaste a 90°, detente».

3. Sensor HC-SR04 — el oído

Es el sensor ultrasónico. Tiene dos cilindros que parecen ojitos, pero en realidad son un altavoz y un micrófono.

Diagrama de referencia del sensor HC-SR04
Figura 3.2: Sensor HC-SR04 mostrando el cilindro TRIG (emisor de ultrasonido) y ECHO (receptor de eco).
  1. El cilindro TRIG lanza un sonido tan agudo que las personas no lo escuchamos.
  2. El sonido viaja, choca contra tu mano y rebota.
  3. El cilindro ECHO escucha el rebote y avisa cuánto tardó.
  4. El Arduino hace una cuenta y sabe a cuántos centímetros está tu mano.
Los murciélagos y los delfines hacen exactamente lo mismo. Se llama ecolocalización.

4. Módulo DS1302 — el reloj

Es un reloj de tiempo real (RTC). Guarda la hora, el minuto, el segundo y hasta la fecha.

Diagrama de referencia del módulo DS1302
Figura 3.3: Módulo de reloj DS1302 con su pila botón CR2032 que mantiene la hora guardada.
🔋

Tiene su propia pila

Por eso sigue contando la hora aunque el Arduino esté apagado, igual que un reloj de pulsera.

🔌

Habla por 3 cables

CLK marca el ritmo, DAT lleva los datos y RST avisa cuándo empieza la conversación.

🍽️

En el proyecto

Nos dice cuándo llegó la hora de comer y también cuenta los 2 segundos que la tapa queda abierta.

Piezas de apoyo

Diagrama de protoboard y cables jumper
Figura 3.4: Protoboard (tablita con agujeros) y cables jumper codificados por color (Rojo=5V, Negro=GND).
Pieza¿Para qué?
ProtoboardUna tablita con agujeros para conectar cables sin soldar nada.
Cables jumperCablecitos de colores con puntas rígidas para unir las piezas.
Cable USBLleva el programa y la energía desde la computadora al Arduino.
Cartón o maderaPara armar la caja, la compuerta y el tobogán por donde cae la comida.
🔌 Lección

Cómo conectar todo

Cada cable tiene un lugar exacto. Si uno se equivoca de agujero, la pieza no funciona (pero casi nunca se rompe).

La regla de los 3 tipos de cable

🔴

Cable rojo: 5V

Lleva la energía de ida. Va al pin marcado 5V.

Cable negro: GND

Es el camino de vuelta. Va a cualquier pin GND.

🟡

Cable de color: señal

Lleva la información. Va a un pin numerado como D9.

Imagina un tobogán de agua: el 5V es la manguera que sube el agua, el GND es el desagüe, y la señal es el silbato del salvavidas que da las órdenes.

Tabla oficial de conexiones

Esquema visual completo del circuito Arduino
Figura 4.1: Diagrama de circuito completo interconectando Arduino Uno, Servo SG90, Sensor HC-SR04 y Reloj DS1302.
PiezaCable de la piezaVa al Arduino
ServoRojo5V
Marrón o negroGND
Naranja o amarillo (señal)D9
HC-SR04VCC5V
GNDGND
TRIGD6
ECHOD7
DS1302VCC5V
GNDGND
CLKD4
DATD3
RST (o CE)D2
Truco para memorizar los pines de señal: 9 servo · 6 y 7 sensor · 2, 3 y 4 reloj.

Avisos importantes

Todos los GND deben estar unidos entre sí. Si el sensor y el Arduino no comparten el GND, el sensor da números locos.
Nunca unas 5V con GND directamente. Eso es un cortocircuito.
Conecta y desconecta siempre con el USB afuera.

Lista para revisar antes de encender

  1. ¿El cable rojo del servo está en 5V y no en un pin numerado?
  2. ¿TRIG está en D6 y ECHO en D7? (Si los cambias de lugar, el sensor no mide.)
  3. ¿El reloj tiene su pila puesta?
  4. ¿Ningún cable pelado toca a otro?
  5. Recién ahora: conecta el USB.
🔬 Prueba 1 pieza

Probamos el servo solo

Antes de juntar todo, probamos cada pieza por separado. Así, si algo falla, sabemos exactamente quién tiene la culpa.

Prueba A: ponerlo en 0 grados

Este programa coloca el servo en su posición inicial. Cuando ya esté en 0°, desconecta el USB y recién ahí atornilla el brazo con la compuerta cerrada.

#include <Servo.h>

Servo servo;

void setup() {
  servo.attach(9);   // La señal del servo está en el pin D9
  servo.write(0);    // Colocar el servo en 0 grados
}

void loop() {
  // No hacemos nada más.
}

🔎 ¿Qué dice este código?

#include <Servo.h>«Trae la caja de herramientas de los servos». Es una librería que ya viene con el Arduino IDE.
Servo servo;Creamos nuestro servo y lo llamamos servo. Es como ponerle nombre a una mascota.
servo.attach(9);«Mi servo está enchufado en el pin 9». Sin esta línea el Arduino no sabría por dónde mandar la orden.
servo.write(0);«Ve al ángulo 0 grados». write significa «escribir» una orden.
loop() vacíoEl servo ya está donde queríamos, así que no repetimos nada.
Resultado esperado: el servo gira una vez y se queda quieto en 0°.

Prueba B: recorrer todos los ángulos

Ahora el servo hará este recorrido, cambiando cada 2 segundos:

0° → 45° → 90° → 135° → 180° → 135° → 90° → 45° → 0°

#include <Servo.h>

Servo servo;

int angulo = 0;      // Empezamos en 0 grados
int paso = 45;       // Avanzamos de 45 en 45

void setup() {
  servo.attach(9);   // Señal del servo en D9
  servo.write(0);    // Posición inicial
  delay(2000);       // Esperar 2 segundos
}

void loop() {
  angulo = angulo + paso;
  servo.write(angulo);
  delay(2000);

  if (angulo == 180) {
    paso = -45;      // Empezar a regresar
  }

  if (angulo == 0) {
    paso = 45;       // Empezar a avanzar
  }
}

🔎 ¿Qué dice este código?

int angulo = 0;Una variable: una cajita llamada angulo que guarda el número 0. int significa «número entero».
int paso = 45;Otra cajita que guarda cuánto avanzamos cada vez.
angulo = angulo + paso;«El nuevo ángulo es el ángulo de antes más el paso». Si era 0 y el paso es 45, ahora vale 45.
if (angulo == 180)«¿El ángulo llegó a 180?» Los dos signos iguales == preguntan; un solo = guarda.
paso = -45;El paso se vuelve negativo, así que ahora restamos y el servo regresa.
Resultado esperado: el servo se mueve 45° cada 2 segundos, va y vuelve sin parar.
Fíjate que el servo nunca pasa de 180° ni baja de 0°, porque justo ahí cambiamos el signo del paso. ¡Es como una pelota rebotando entre dos paredes!
🔬 Prueba 1 pieza

Probamos el sensor de distancia

Este programa no mueve nada: solo escribe en la pantalla de la computadora a cuántos centímetros está el objeto más cercano.

Para ver los números abre el Monitor Serie (la lupa arriba a la derecha en Arduino IDE) y elige 9600 baudios abajo.
const int trigPin = 6;
const int echoPin = 7;

void setup() {
  Serial.begin(9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);

  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duracion = pulseIn(echoPin, HIGH, 30000);

  if (duracion == 0) {
    Serial.println("Sin eco");
  } else {
    float distancia = duracion * 0.0343 / 2;

    Serial.print("Distancia: ");
    Serial.print(distancia);
    Serial.println(" cm");
  }

  delay(500);
}

🔎 ¿Qué dice este código?

const int trigPin = 6;Una constante: le ponemos el nombre trigPin al número 6, para no olvidarnos qué pin era.
Serial.begin(9600);«Abre la conversación con la computadora a velocidad 9600». Sin esto no veríamos ningún mensaje.
pinMode(trigPin, OUTPUT);«El pin 6 va a hablar» (salida). Por ahí sale el sonido.
pinMode(echoPin, INPUT);«El pin 7 va a escuchar» (entrada). Por ahí llega el rebote.
digitalWrite(trigPin, HIGH);«Enciende el pin 6». Ese pulso es el que dispara el sonido.
delayMicroseconds(10);Espera 10 microsegundos: una diezmilésima parte de un parpadeo. El sensor necesita justo ese tiempito.
pulseIn(echoPin, HIGH, 30000)«Cronometra cuánto tiempo estuvo encendido el pin 7». Ese es el viaje del sonido, medido en microsegundos.
if (duracion == 0)Si el cronómetro devuelve 0, es que el sonido nunca volvió: no hay nada enfrente.
duracion * 0.0343 / 2La cuenta mágica. Ver la explicación de abajo. float guarda números con coma, como 12.5.
Serial.println(...)Escribe el mensaje en el Monitor Serie y salta a la línea siguiente (ln = línea).

¿De dónde sale la cuenta mágica?

  1. El sonido viaja a 343 metros por segundo, que es lo mismo que 0.0343 centímetros por microsegundo.
  2. Si multiplicamos el tiempo por esa velocidad, sabemos cuántos centímetros recorrió.
  3. Pero el sonido fue y volvió: hizo el camino dos veces. Por eso dividimos entre 2.
Resultado esperado: mueves la mano y los números cambian. Lejos = número grande. Cerca = número chico.
🔬 Prueba 1 pieza

Probamos el reloj DS1302

Primero instala la librería. En Arduino IDE ve a Programa → Incluir librería → Administrar bibliotecas, busca «Rtc by Makuna» e instálala. Una librería es un paquete de código que alguien más ya escribió y nos regala.

Paso 1: ponerle la hora (una sola vez)

El reloj sale de fábrica sin saber qué hora es. Este programa le copia la hora de tu computadora.

#include <ThreeWire.h>
#include <RtcDS1302.h>

// DAT = D3, CLK = D4, RST = D2
ThreeWire conexionRtc(3, 4, 2);
RtcDS1302<ThreeWire> rtc(conexionRtc);

void setup() {
  rtc.Begin();
  rtc.SetIsWriteProtected(false);
  rtc.SetIsRunning(true);

  // Copia al DS1302 la fecha y hora de la computadora.
  rtc.SetDateTime(RtcDateTime(__DATE__, __TIME__));
}

void loop() {
  // Este programa solo sirve para ajustar la hora.
}

🔎 ¿Qué dice este código?

ThreeWire conexionRtc(3, 4, 2);«Three wire» = tres cables. Le decimos en qué pines están DAT, CLK y RST, en ese orden.
rtc.Begin();«Despierta, reloj, vamos a hablar».
SetIsWriteProtected(false)El reloj tiene un seguro para que nadie le cambie la hora sin querer. Aquí lo quitamos.
SetIsRunning(true)«Empieza a contar».
__DATE__ y __TIME__Son palabras mágicas: cuando aprietas «Subir», se convierten en la fecha y hora exactas de tu computadora.
Esto se hace UNA SOLA VEZ. Si dejas este programa puesto, cada vez que se reinicie el Arduino la hora volverá al momento en que lo subiste. Por eso, en cuanto termine, sube el programa de abajo.

Paso 2: leer la hora

#include <ThreeWire.h>
#include <RtcDS1302.h>

// DAT = D3, CLK = D4, RST = D2
ThreeWire conexionRtc(3, 4, 2);
RtcDS1302<ThreeWire> rtc(conexionRtc);

void setup() {
  Serial.begin(9600);
  rtc.Begin();
  rtc.SetIsWriteProtected(false);
  rtc.SetIsRunning(true);
}

void loop() {
  RtcDateTime ahora = rtc.GetDateTime();

  Serial.print(ahora.Hour());
  Serial.print(":");
  Serial.print(ahora.Minute());
  Serial.print(":");
  Serial.println(ahora.Second());

  delay(1000);
}

🔎 ¿Qué dice este código?

rtc.GetDateTime()«Reloj, dime qué hora es». Nos devuelve un paquete con día, mes, año, hora, minuto y segundo.
RtcDateTime ahora = ...Guardamos ese paquete en una cajita llamada ahora.
ahora.Hour()Del paquete sacamos solo la hora. El puntito significa «la parte de».
Serial.print(":")Escribe los dos puntos para que se vea bonito: 8:30:15.
delay(1000);Espera 1 segundo antes de volver a preguntar.
Resultado esperado: en el Monitor Serie aparece la hora, avanzando de segundo en segundo.
Prueba genial: desconecta el USB, espera 5 minutos y vuelve a conectarlo. La hora seguirá correcta, ¡gracias a la pilita del módulo!
🧩 Prueba 2 piezas

Servo + reloj trabajando juntos

Ya sabemos que cada pieza funciona sola. Ahora las unimos: el reloj decide cuándo, y el servo se mueve.

El servo recorrerá 0°, 45°, 90°, 135°, 180° y regresará, pero esta vez el que da la orden no es delay(), sino los segundos del reloj: se mueve en los segundos 0, 2, 4, 6, 8…

#include <Servo.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>

// DAT = D3, CLK = D4, RST = D2
ThreeWire conexionRtc(3, 4, 2);
RtcDS1302<ThreeWire> rtc(conexionRtc);

Servo servo;

int angulo = 0;          // Comenzamos en 0 grados
int paso = 45;           // Cambiamos 45 grados cada vez
int ultimoSegundo = -1;  // Evita repetir una orden

void setup() {
  Serial.begin(9600);
  rtc.Begin();

  servo.attach(9);
  servo.write(0);
}

void loop() {
  RtcDateTime ahora = rtc.GetDateTime();
  int segundoActual = ahora.Second();

  // Entramos una sola vez cuando cambia el segundo.
  if (segundoActual != ultimoSegundo) {
    ultimoSegundo = segundoActual;

    Serial.print(ahora.Hour());
    Serial.print(":");
    Serial.print(ahora.Minute());
    Serial.print(":");
    Serial.println(segundoActual);

    // Se mueve en 0, 2, 4, 6, 8...
    if (segundoActual % 2 == 0) {
      angulo = angulo + paso;
      servo.write(angulo);

      if (angulo == 180) {
        paso = -45;
      }

      if (angulo == 0) {
        paso = 45;
      }
    }
  }
}

🔎 Las dos ideas nuevas

int ultimoSegundo = -1;Guarda el último segundo que ya atendimos. Empieza en -1 porque ese segundo no existe, así la primera vez siempre entra.
if (segundoActual != ultimoSegundo)!= significa «es distinto de». Traducción: «¿cambió el segundo? Solo entonces haz algo».
segundoActual % 2 == 0El símbolo % da el resto de una división. Si al dividir entre 2 el resto es 0, el número es par.

¿Por qué necesitamos ultimoSegundo?

El loop() se repite miles de veces por segundo. Si no lo controláramos, durante el segundo 4 el Arduino le daría al servo miles de órdenes seguidas y todo se volvería loco. Con ultimoSegundo le decimos: «esto ya lo hiciste, espera al siguiente segundo».

Es como cuando suena el timbre del recreo: quieres salir una vez cuando suena, no quedarte saliendo mil veces mientras suena.

Resultado esperado: el servo cambia de ángulo justo en los segundos pares, y en el Monitor Serie ves la hora avanzando.
🧩 Prueba 2 piezas

Servo + sensor trabajando juntos

Ahora la orden la da tu mano: si te acercas a 10 cm o menos, la tapa se abre 2 segundos y se cierra sola.

#include <Servo.h>

Servo servo;

const int trigPin = 6;
const int echoPin = 7;
const int DISTANCIA_MANO = 10;

void setup() {
  Serial.begin(9600);

  servo.attach(9);
  servo.write(0);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duracion = pulseIn(echoPin, HIGH, 30000);

  if (duracion != 0) {
    float distancia = duracion * 0.0343 / 2;

    Serial.print("Distancia: ");
    Serial.print(distancia);
    Serial.println(" cm");

    if (distancia <= DISTANCIA_MANO) {
      servo.write(90);  // Abrir
      delay(2000);      // Esperar 2 segundos
      servo.write(0);   // Cerrar
      delay(1000);
    }
  }

  delay(100);
}

🔎 ¿Qué dice este código?

const int DISTANCIA_MANO = 10;La distancia a la que consideramos «hay una mano». Está en MAYÚSCULAS porque es una constante que quizás quieras cambiar.
if (distancia <= DISTANCIA_MANO)<= significa «menor o igual que». Traducción: «¿está a 10 cm o más cerca?».
servo.write(90);Abrir la tapa.
delay(2000);Dejarla abierta 2 segundos para que caiga la comida.
servo.write(0);Cerrar la tapa.
delay(100);Una pausita entre medición y medición, para no marear al sensor.
Esta versión tiene un defecto a propósito: mientras el delay(2000) está corriendo, el Arduino queda congelado y no puede hacer nada más. Para dos piezas está bien, pero en el proyecto final el reloj contará esos 2 segundos sin congelar el programa.
Resultado esperado: acercas la mano, la tapa se abre; la alejas, se cierra a los 2 segundos.
🏆 Proyecto final

Las tres piezas juntas

Este es el dispensador terminado. Sabe hacer tres cosas:

Abrir a la hora

Cuando el reloj marca la hora del almuerzo, la tapa se abre sola.

Abrir con la mano

Si alguien acerca la mano a 10 cm, la tapa también se abre.

🔄

Cerrar sola

El reloj cuenta 2 segundos y la tapa se cierra, sin congelar el programa.

Antes de subirlo: cambia HORA_COMIDA y MINUTO_COMIDA por la hora que quieras probar. Para probar el proyecto en clase, pon una hora que falte 1 o 2 minutos.
#include <Servo.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>

// CONEXIONES
// Servo: D9
// HC-SR04: TRIG D6 y ECHO D7
// DS1302: DAT D3, CLK D4 y RST D2

Servo servoTapa;

const int ANGULO_CERRADO = 0;
const int ANGULO_ABIERTO = 90;

const int trigPin = 6;
const int echoPin = 7;
const int DISTANCIA_MANO = 10;
const int DISTANCIA_REACTIVAR = 15;

ThreeWire conexionRtc(3, 4, 2);
RtcDS1302<ThreeWire> rtc(conexionRtc);

// Cambia estos números para elegir la hora.
const int HORA_COMIDA = 8;
const int MINUTO_COMIDA = 0;

bool tapaAbierta = false;
bool manoYaDetectada = false;

long segundoEnQueAbrio = 0;
int ultimoSegundoLeido = -1;
int ultimoDiaAlimentado = -1;

// Convierte la hora en segundos del día.
long segundosDelDia(RtcDateTime tiempo) {
  return tiempo.Hour() * 3600L
       + tiempo.Minute() * 60L
       + tiempo.Second();
}

// Mide la distancia con el ultrasónico.
float medirDistancia() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);

  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duracion = pulseIn(echoPin, HIGH, 30000);

  if (duracion == 0) {
    return -1;
  }

  return duracion * 0.0343 / 2;
}

void abrirTapa(long segundoActual) {
  servoTapa.write(ANGULO_ABIERTO);
  tapaAbierta = true;
  segundoEnQueAbrio = segundoActual;
  Serial.println("Tapa abierta");
}

void cerrarTapa() {
  servoTapa.write(ANGULO_CERRADO);
  tapaAbierta = false;
  Serial.println("Tapa cerrada");
}

void setup() {
  Serial.begin(9600);

  rtc.Begin();

  servoTapa.attach(9);
  servoTapa.write(ANGULO_CERRADO);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  RtcDateTime ahora = rtc.GetDateTime();

  // La parte principal se ejecuta una vez por segundo.
  if (ahora.Second() != ultimoSegundoLeido) {
    ultimoSegundoLeido = ahora.Second();

    long segundoActual = segundosDelDia(ahora);

    Serial.print(ahora.Hour());
    Serial.print(":");
    Serial.print(ahora.Minute());
    Serial.print(":");
    Serial.println(ahora.Second());

    // Si está abierta, el DS1302 cuenta 2 segundos.
    if (tapaAbierta) {
      long tiempoAbierta = segundoActual - segundoEnQueAbrio;

      // Corrección por si pasa la medianoche.
      if (tiempoAbierta < 0) {
        tiempoAbierta = tiempoAbierta + 86400;
      }

      if (tiempoAbierta >= 2) {
        cerrarTapa();
      }
    }

    // Solo buscamos otra orden cuando está cerrada.
    if (!tapaAbierta) {
      float distancia = medirDistancia();

      Serial.print("Distancia: ");
      Serial.print(distancia);
      Serial.println(" cm");

      bool esHoraDeComer =
          ahora.Hour() == HORA_COMIDA &&
          ahora.Minute() == MINUTO_COMIDA &&
          ahora.Second() == 0 &&
          ahora.Day() != ultimoDiaAlimentado;

      // Apertura por hora programada.
      if (esHoraDeComer) {
        abrirTapa(segundoActual);
        ultimoDiaAlimentado = ahora.Day();
      }

      // Apertura al acercar la mano.
      else if (
        distancia > 0 &&
        distancia <= DISTANCIA_MANO &&
        !manoYaDetectada
      ) {
        abrirTapa(segundoActual);
        manoYaDetectada = true;
      }

      // Hay que alejar la mano para volver a activar.
      if (
        distancia == -1 ||
        distancia >= DISTANCIA_REACTIVAR
      ) {
        manoYaDetectada = false;
      }
    }
  }
}

Las ideas nuevas de este programa

🔎 Las cajitas de sí/no

bool tapaAbierta = false;Un bool es una cajita que solo guarda (true) o no (false). Aquí anota si la tapa está abierta.
bool manoYaDetectadaAnota si ya contamos esa mano, para no abrir 20 veces con la misma mano.
if (!tapaAbierta)El signo ! significa «NO». Traducción: «si la tapa NO está abierta».
&&Significa «Y». Todas las condiciones deben cumplirse a la vez.
||Significa «O». Basta con que se cumpla una.

🔎 Nuestras propias funciones

float medirDistancia()Una función es una receta con nombre. En vez de repetir 8 líneas cada vez, escribimos medirDistancia() y listo.
return -1;return es la respuesta que da la receta. Devolvemos -1 como manera de decir «no medí nada».
void abrirTapa(...)void significa que esta receta hace cosas pero no devuelve ninguna respuesta.
segundosDelDia(...)Convierte hora, minuto y segundo en un solo número grande. Así restar tiempos es tan fácil como restar números.

¿Por qué convertir la hora a segundos?

Restar horas es complicado: ¿cuánto va de 8:59:58 a 9:00:03? Pero si convertimos todo a segundos desde la medianoche, se vuelve una resta normal.

  1. Cada hora tiene 3600 segundos, así que multiplicamos las horas por 3600.
  2. Cada minuto tiene 60 segundos, así que multiplicamos los minutos por 60.
  3. Sumamos los segundos que ya teníamos.
  4. El día completo tiene 86400 segundos. Por eso, si la resta da negativa, sumamos 86400: es que pasó la medianoche.

¿Por qué existe DISTANCIA_REACTIVAR?

Si dejas la mano quieta frente al sensor, el Arduino la seguiría viendo y abriría la tapa sin parar. Con manoYaDetectada decimos «esta mano ya la conté», y solo la volvemos a contar cuando se aleja más de 15 cm. Es como el molinete del metro: tienes que salir para poder entrar de nuevo.

Cómo demostrarlo en clase

  1. Abre el Monitor Serie y muestra la hora corriendo: eso prueba que el reloj funciona.
  2. Acerca la mano: la tapa se abre. Mira cómo cambia el número de distancia.
  3. Aleja la mano y cuenta en voz alta: uno… dos… la tapa se cierra.
  4. Programa HORA_COMIDA para dentro de un minuto y espera. La tapa se abrirá sola.
Para la maqueta: usa una compuerta liviana de cartón. Si la compuerta pesa mucho, el servo zumbará sin moverla y hará falta una fuente de 5V aparte.
🛠️ Ayuda

Si algo no funciona

Que algo falle es parte del aprendizaje al construir el proyecto. Aquí están los problemas más comunes y cómo resolverlos. Toca cada pregunta para ver la respuesta.

🤖 El servo solo zumba y no se mueve

Está pidiendo más energía de la que recibe, o la compuerta pesa demasiado.

  • Comprueba que el cable rojo esté en 5V (no en un pin numerado).
  • Aligera la compuerta.
  • Verifica que la señal esté en D9.
📏 El sensor siempre dice «Sin eco»

El sonido no está volviendo.

  • Revisa que TRIG esté en D6 y ECHO en D7. ¡Es muy fácil cambiarlos!
  • Revisa VCC en 5V y GND en GND.
  • Pon la mano de frente a los cilindros, a unos 15 cm. Si la pones de costado, el sonido rebota hacia otro lado.
📐 El sensor da números que saltan mucho

Puede ser que el GND no esté bien unido, o que el sonido rebote en una superficie inclinada. Prueba apuntando a un cartón plano.

⏱️ El reloj repite el mismo segundo mil veces

No es un error del reloj: el loop() se repite miles de veces por segundo y pregunta la hora todo el tiempo. Por eso usamos la variable ultimoSegundo, que actúa una sola vez por segundo.

🔋 El reloj pierde la hora al desconectar

Revisa la pila del módulo DS1302: puede estar descargada, al revés o floja.

↩️ El servo gira para el lado contrario

El problema no es el programa, es el brazo. Sube el programa que lo pone en 0°, desconecta el USB y vuelve a atornillar el brazo con la compuerta cerrada.

💻 El Arduino IDE dice «error de compilación»

El programa tiene un error de escritura. Casi siempre es:

  • Falta un ; al final de una línea.
  • Falta una llave }.
  • No instalaste la librería Rtc by Makuna.
  • Escribiste una palabra con mayúscula o minúscula distinta. Para el Arduino, Serial y serial no son lo mismo.
🔌 No aparece el puerto para subir el programa

Revisa que el cable USB sea de datos (algunos cables solo cargan), prueba otro puerto USB, y en el menú Herramientas → Placa elige Arduino Uno.

👀 El Monitor Serie muestra símbolos raros

La velocidad no coincide. Abajo a la derecha del Monitor Serie elige 9600 baudios, igual que en Serial.begin(9600).

Recordatorio de pines

PinVa a
D9Señal del servo
D7ECHO del sensor
D6TRIG del sensor
D4CLK del reloj
D3DAT del reloj
D2RST del reloj
📖 Diccionario

Glosario: todas las palabras nuevas

Aquí están todas las palabras difíciles del proyecto, explicadas en una frase y ordenadas de la A a la Z.