Como ya explicamos en el inicio de este tutorial el robot tendrá dos modos de operación: modo juego y modo automático. Cada modo tendrá las siguientes características:
Modo juego
-
- Para entrar en modo juego se deberá mantener pulsado el botón de la parte superior entre 1 y 3 segundos (no más de 3 segundos).
- Se iniciará una secuencia de inicio de juego con sonido donde el robot girará a izquierda y a derecha y se moverá hacia adelante durante unos segundos. Una vez que se pare se iniciará una cuenta atrás para señalar que el juego está a punto de comenzar.
- El robot tendrá un LED que aleatoriamente se pondrá de color rojo, verde o azul durante un tiempo determinado. Tanto el color seleccionado como el tiempo serán aleatorios. Evitaremos que un mismo color (solo para el color verde y rojo) se repita demasiado tiempo de manera seguida.
- Si se pone el LED de color verde te podrás acercar hacia el robot e intentar apretar el botón rojo antes de que cambie de color. Cuando está en verde nunca podrás ser detectado. Si logras apretar el botón rojo mientras se encuentra el color verde activo “habrás ganado” y el robot realizará una secuencia de fin de juego con sonido.
- Si se pone el LED de color rojo tendrás que quedarte quieto porque ahora el robot sí que te podrá detectar. Si has sido detectado el robot emitirá una señal y empezará a moverse hacia adelante a toda velocidad para no ser atrapado durante un periodo de tiempo. Si has sido detectado más de 3 veces mientras el color del LED está en rojo “habrás perdido” y el robot emitirá una secuencia de sonido de “fin de juego”.
- Cuando el LED se ponga de color azul significará “precaución” ya que en breve el LED se pondrá en verde o en rojo, por tanto o bien decides quedarte quieto o por el contrario puedes decidir seguir acercándote al robot con el riesgo de que se pueda poner el LED en rojo y ser detectado.
Modo automático
-
- Para entrar en modo automático se deberá mantener pulsado el botón de la parte superior más de 3 segundos.
- El robot correrá indefinidamente hasta que se apriete nuevamente el botón rojo de la parte superior.
En ambos modos el sensor de ultrasonidos hará que cuando el robot se encuentre con un obstáculo no choque y lo esquive girando siempre a la izquierda.
El código (sketch) con la funcionalidad anteriormente descrita la tenéis para descargar en el siguiente enlace con todas las librerías necesarias:
Descargar sketch del robot mario
Seguidamente os detallo las funciones principales presentes en el código:
void Avance(int velocidad). Hace avanzar el robot a una velocidad determinada.
void Retroceso(int velocidad). Hace retroceder el robot a una velocidad determinada.
void Izquierda(int velocidad). Hace girar a la izquierda el robot a una velocidad determinada.
void Derecha(int velocidad). Hace girar a la derecha el robot a una velocidad determinada.
void Parar(). Hace parar al robot.
El valor de velocidad puede estar comprendido entre 0 y 255.
Las funciones anteriores se basan en la explicación ya realizada en el apartado de conexionado y es la siguiente:
-
- Los pines del controlador de motor ENA, IN1 e IN2 controlarán el motor del lado derecho (A) y los pines ENB, IN3 e IN4 controlarán el motor del lado izquierdo (B).
- Los pines IN1, IN2, IN3 e IN4 controlan la dirección de giro de forma que si IN1 está a HIGH e IN2 a LOW, el motor (A) girará en un sentido. Por el contrario si IN1 está a LOW e IN2 a HIGH girará en el otro (lo mismo con los pines IN3 e IN4 para el motor (B) ).
- Los pines ENA y ENB nos servirán para controlar la velocidad de giro de los motores los cuales están conectados a una señal PWM de nuestro Arduino que nos permitirá poder especificar la velocidad.
void Correr(). Esta función hace avanzar el robot a máxima velocidad pero si el sensor de movimiento detecta un obstáculo dentro de la distancia mínima especificada hará girar el robot a la izquierda para evitarlo hasta que ya no detecte ningún obstáculo. Para medir la distancia utilizaremos la librería NewPing.
URL librería New Ping: https://github.com/PaulStoffregen/NewPing
void desactivarLed(). Hace que el LED esté apagado y no emita ningún color.
void activarLed(String color). Permite activar el LED para que muestre uno de los siguientes colores : rojo, verde y azul.
void melodia(parámetros … ). Permite hacer sonar una melodía especificando las notas, su duración y el número total de notas que la componen utilizando la librería NewTone y el archivo que contiene el listado de notas con la frecuencia asociada (pitches.h).
URL Librería New Tone: : https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home
void senalAcusticaConIndicador (parámetros…). Permite emitir un sonido con indicación luminosa, especificando el tono y duración del sonido, color de la indicación luminosa (verde, rojo y azul) y el número de veces que queremos repetir la secuencia.
boolean lecturaPin(int pin). Permite hacer la lectura a un pin ( digitalRead() ) para saber si está LOW o HIGH asegurando que no existen falsas lecturas ya que el estado se ha mantenido estable (no ha cambiado) por un periodo de tiempo.
void secuenciaInicio(). Realiza la siguiente secuencia de inicio: el robot gira a izquierda y a derecha y se mueve hacia adelante durante unos segundos. Posteriormente se para y se inicia una cuenta atrás para señalar que el juego está a punto de comenzar.
void esperarSiDeteccionPrevia(). Esta función nos asegura que antes de realizar una nueva detección no exista una detección previa activa. Si existe, se iluminará el LED en color azul hasta que el sensor esté listo para una nueva detección.
En la siguiente imagen se muestra el comportamiento de la función:
Como se puede apreciar estando el color verde activo se ha detectado un movimiento. Imaginemos que el siguiente color para activarse fuera el rojo y que encima la persona no se ha movido ¿qué sucedería? pues que el robot consideraría que la persona se ha movido ya que la activación del color rojo ha sucedido justo en el periodo de tiempo Tx de una detección anterior que era cuando el color verde estaba activo y la persona si podía moverse.
Para solucionar estos casos la función esperarSiDeteccionPrevia() pondrá el color azul como activo y lo mantendrá hasta que se termine el ciclo Tx + Ti de la detección anterior (tiempo marcado como Tz en la imagen) asegurando que cuando se produzca una detección ésta sea válida ya que se ha producido en el periodo de tiempo en que el color rojo se encontraba activo (que era el siguiente color que por azar tocaba) y la persona no podìa moverse.
NOTA: Podríamos haber dejado que esta función sólo actuara cuando el siguiente color es el rojo ya que es en estos casos cuando en realidad se tiene en cuenta las detecciones de movimiento pero si lo hiciéramos así sabríamos que después de cada color azul siempre vendría un color rojo. Como queremos que todo sea aleatorio y que no exista un patrón, que es la gracia del juego, también la función actuará aunque el color siguiente sea el verde. Por tanto esta función actuará siempre que exista una detección previa activa en su periodo de tiempo Tx independientemente del siguiente color que se active.
Esta función está basada en la explicación ya realizada en el apartado donde se detallaba el funcionamiento del sensor de movimiento. Recordemos que nuestro sensor se encuentra configurado en modo “non-retriggering” (posición “L”).
En el siguiente diagrama se puede apreciar el funcionamiento del sensor en modo “non-retriggering”:
El pulso generado al detectarse un movimiento consta de dos tiempos:
-
- Tx. Tiempo de duración que se mantiene la salida en “high” (3.3 V) cuando se detecta un movimiento.
- Ti. Tiempo durante el cual el sensor no detectará movimiento.
Como se puede apreciar en la imagen anterior cuando se activa la salida al detectarse movimiento se genera un pulso de duración “Tx” más un tiempo Ti donde el sensor no puede detectar movimiento. Aunque durante el tiempo “Tx” se detecte otro movimiento el sensor no vuelve a generar nuevamente un pulso y solo al terminar el periodo Tx + Ti del pulso activo el sensor podrá generar un nuevo pulso al detectar un nuevo movimiento.
Este tiempo teóricamente varía entre los 3s hasta los 5 minutos. Para nuestro robot debemos poner el tiempo “Tx” al mínimo valor posible.
Con mi sensor actual tengo pulsos aproximados de unos 3,3 s de DelayTime (Tx) más unos 2,3s de BlockTime (Ti) en los cuales no detecta nada. Por tanto: Tx (Delay Time) + Ti (Block time) = 5,6s.
NOTA: Tendréis que hacer mediciones para determinar estos tiempos en el sensor PIR que estéis utilizando.
void Trigger(). Esta función es utilizada por la interrupción attachInterrupt(digitalPinToInterrupt(pinPIR), Trigger, RISING) presente en el código y se dispara cada vez que el PIR detecta una señal (pasa de LOW a HIGH) y nos servirá para guardar el momento en que se ha detectado un movimiento.
Una vez vistas las funciones anteriores comentaremos la siguiente instrucción:
randomSeed(analogRead(A3)). Cuando estamos en modo “juego” existen dos puntos en el código en los que utilizamos la función random: uno para generar aleatoriamente el color verde o rojo y por otro para generar aleatoriamente el tiempo en que este color esté activo.
La función randomSeed (incluída dentro del setup de nuestro sketch) inicializa la función random con el valor pasado por parámetro.
Para conseguir que cada vez que iniciemos el robot tengamos secuencias diferentes tenemos que pasarle cada vez un valor diferente en el parámetro ¿y cómo hacemos esto? pues pasando a esta función como parámetro el valor de una entrada analógica que no utilicemos en nuestro sketch mediante la función analogRead() ya que cuando un pin analógico no es utilizado devolverá un valor diferente al hacer su lectura debido al ruido eléctrico.
Para finalizar comentar que:
-
- Debemos esperar entre 30 segundos y 1 minuto para que el sensor de movimiento se inicialice correctamente antes de empezar a utilizarlo. Para ello cada vez que se active el robot se encenderá el LED de color rojo durante este periodo de tiempo y no se podrá empezar a jugar hasta que se ponga el LED de color verde.
- Es posible que mientras está el color rojo activo (en modo juego) estemos dentro de un periodo de tiempo Ti (Block Time), en este caso el PIR no podrá detectar ningún movimiento hasta que termine este intervalo de tiempo Ti. Recordemos que la función esperarSiDeteccionPrevia() sólo actúa si hay una detección previa activa y estamos dentro de su periodo de tiempo Tx (no durante el intervalo de tiempo Ti) asegurándonos que se termine el ciclo Tx + Ti de esa detección previa para no causar detecciones erróneas durante el juego. Hay que recordar que el sensor de movimiento está continuamente funcionando y por tanto detectando movimiento. En la siguiente imagen se muestra lo comentado anteriormente:
Como se puede apreciar estando el color verde activo el PIR ha detectado movimiento y cuando se ha puesto el color rojo como activo el sensor se encontraba en el intervalo de tiempo Ti y por tanto no podrá detectar ningún movimiento. En estos casos la función esperarSiDeteccionPrevia() no actúa. Por tanto en el periodo de tiempo marcado como Tz aunque la persona se mueve no será detectada aunque este el color rojo activo. Esta particularidad también formará parte del juego.