electrónica

Juego de batalla naval con Arduino, LCD y comunicación serial

Introducción

No hace mucho me fue encargado un pequeño proyecto en donde tenía que simular el juego de una batalla naval usando un Arduino, dos módulos USB a TTL, algunos leds controlados con un 74hc595 y una LCD con el módulo I2C.

Trataré de ser lo más específico posible y no omitir ningún detalle.

Recuerda que todo esto es modificable, y con un poco de tiempo puedes adaptarlo a muchos otros escenarios. Por ejemplo, puede que no necesites los leds, o que en lugar de USB a TTL utilices Bluetooth, etcétera. Todo queda en ti.

Funcionamiento

Los barcos los ubicaría el jugador 1 (con coordenadas) e irían apareciendo sobre la LCD. Cada que se ubicaba uno de estos, se encendía un led indicando que el barco “estaba vivo”. Después, el jugador 2 trataba de darles sin ver la pantalla, usando al igual que el jugador 1, la comunicación serial.

Si le daba a un barco, el led que indicaba su estado se apagaba. Pero por ejemplo, para los que ocupan muchos espacios (es decir, los barcos grandes como submarinos, portaaviones, etcétera) se les tenía que destruir por completo para que su led se apagase.

El usuario 2 tenía 18 intentos para derribar todos los barcos. Ganaba si los desaparecía antes de que sus intentos se acabasen.

Adicional a esto, un buzzer sonaba indicando éxito o error en cada disparo. Y dichos disparos se marcaban en la LCD.

Materiales

  • Arduino UNO, o cualquier equivalente. (Lo he probado sólo con MEGA y UNO)
  • Pantalla LCD de 20 x 4 (o de 16 x 2) – para dibujar el escenario
  • Módulo I2C para LCD – para ahorrar cables
  • 7 leds – para el estado de los barcos
  • 7 resistencias –  para dichos leds
  • 1 Circuito integrado 74hc595 – para controlar los leds
  • Un buzzer – para indicar error o éxito
  • 2 módulos USB a TTL – uno a la pc del jugador 1. El otro a la del jugador 2

Requisitos

Tienes que saber la dirección del módulo I2C para la LCD, y también debes tener instalada la librería. Para ello, y para no hacer largo este post, separé esto en otros tutoriales:

Una vez que hayas leído eso, podemos continuar.

Paso 1: El escenario

El juego en sí no es complicado. Básicamente es un arreglo en donde estaremos cambiando los valores. Me decidí por un arreglo de tipo char, porque se me hace ligero y a su vez perfecto para los usos que le daremos.

Para comenzar vamos a definir la altura y anchura (o como se diga) del escenario; es decir, del arreglo, que debe medir lo mismo que nuestra LCD.

Si nuestra LCD es de 16×2, el arreglo será de 16×2. Si es de 20×4, el arreglo será de 20 x 4.

De todas maneras, haremos modificables las medidas. Así, primero podemos probar con una LCD pequeña, la de 16×2, que es la más popular. Y después, cambiar las constantes y conectar una de 20 x 4 u otras medidas.

Pero dejemos de hablar y veamos el código del escenario y las constantes que usaremos para pintar el arreglo, ya que los valores que habrán dentro de éste serán unos, y los que mostraremos al usuario final en la LCD serán otros:

Carguemos ese código a nuestra tarjeta, conectando a esta la LCD a través del módulo I2C. Veremos algo así:

¿Y eso es todo? claro que sí, no se ve a simple vista, pero el escenario ya está pintado. No se nota nada, porque declaramos la constante AGUA en un espacio en blanco.

Pero hagamos un experimento, cambiemos la constante AGUA y en lugar de ese espacio pongamos un punto (.), compilemos y veamos lo que sucede:

 Si el lector no se ha dado cuenta, esto sirve de muchas cosas. En primer lugar ya vimos que gracias a las constantes podemos cambiar la forma en la que el usuario observa el juego.

Por otro lado, se ve la utilidad de la constante DEBERIA_IMPRIMIR_LETRAS, pues si está en true nos mostrará detalladamente el escenario, no sólo cosas transparentes.

Muy bien, con esto ya tenemos una pequeña parte. Ahora veremos cómo modificar ese arreglo, parsear coordenadas, encender leds, apagarlos, y finalmente cómo llamar a estos métodos escuchando el puerto Serial.

Paso 2: la ubicación de los barcos | coordenadas

Como lo dije arriba, haremos que esto sea flexible. Así que crearemos funciones que sin importar de dónde vengan los datos, funcionarán.

Para ello, declararemos una función para poner determinado barco en determinada coordenada. Como estamos trabajando realmente con un arreglo, las coordenadas son índices o posiciones del mismo. Y como estamos pintando exactamente ese arreglo en la LCD, sólo tenemos que actualizar el dato y dibujar. ¿Inteligente, no?

Pedir coordenadas de barcos

Comenzaremos pidiendo la ubicación de cada barco. No me gusta mucho esta parte del código, me habría gustado hacerla un poco más independiente del número de barcos.

Recordemos que el usuario introducirá las coordenadas a través de la comunicación serial, ya sea utilizando el Monitor serial que el IDE de Arduino provee, u otro.

Como dijimos que se utilizarán 2 comunicaciones seriales, tenemos que escuchar a una u otra. Escuchamos entonces a la del jugador uno. Mandamos un mensaje de bienvenida y comenzamos a pedir las ubicaciones:

Es importante notar la línea que dice:

while (!serialJugadorUno.available());

Sin ella, no habría podido esperar a que el usuario introdujera las coordenadas. Lo que hace ese fragmento es pausar el programa indefinidamente, hasta que haya algo que leer en el Serial. También están los métodos encenderLedDeesCoordenadaValida e intentarDibujarBarco, que veremos más abajo.

Comprobar coordenadas válidas

De este método me siento muy orgulloso, me gustó la manera en la que lo hice. Primero verifica si existe una coma en la cadena. En caso de que no, de una vez regresamos falso.

En caso de que sí, la cortamos. X sería lo que hay desde la posición 0 hasta en donde hayamos encontrado la coma. Y sería desde donde encontramos la coma, hasta el final de la cadena.

Acabo de darme cuenta de que cuando lo hice (que por cuestiones de tiempo no pude arreglar, jaja) había un error al parsear las coordenadas si ponía length – 1. Pero al poner length – 2, todo iba bien. Creo (y sólo creo) que es porque hay un salto de línea por ahí. Y si no, igual no importa.

Bueno bueno, luego comprobamos si la coordenada es un número, para ello utilicé una función que encontré en internet.

Finalmente, la última prueba es que las coordenadas estén en el rango de la LCD. Es decir, que X no sea menor que 0 ni mayor que la longitud de la pantalla. Y para Y lo mismo. ¿Ya vieron cómo las constantes que declaramos nos siguen ayudando?

Dibujar un barco

Este método también fue difícil de hacer. En las instrucciones no especificaba cómo debería ir el barco. Veamos un submarino, creo que ocupa 3 espacios. ¿Cómo los iba a ocupar? ¿Se iba a extender hacia la derecha, hacia arriba, abajo o a la izquierda?

Para eliminar todas estas dudas, hice una función que primero dibujara el barco de derecha a izquierda. Luego hacia abajo, izquierda y finalmente hacia arriba. Si no podía dibujarlo, entonces regresaba false, pues ya estaban ocupadas esas posiciones o el barco no cabía.

También es importante notar el método estaVacioEn que como su nombre lo dice, te indica si no has ocupado una coordenada.

Para dibujar:

Y el método para ver si está vacío:

Espero que el lector se dé cuenta de que las constantes siguen siendo de gran ayuda. Utilizamos de nuevo las que indican las medidas de la pantalla, y también la constante AGUA.

Paso 3: Encender y apagar LEDS

¿Ya dije difícil? bueno, este método también es difícil.

Encender un LED es una de las cosas más simples en el mundo de Arduino, de hecho es casi como el Hola mundo. La pequeña diferencia es que en este caso se tenía que utilizar un circuito integrado 74HC595, del cual ya he hablado.

Lo que tenía que estar modificando era un byte. Sí, un byte, de esos que tienen 8 bits. Y cada que encendía o apagaba un led, tenía que recorrerlo y utilizar bitSet para cambiar el valor de 0 a 1. Luego, llamaba a refrescarLeds para que por medio de shiftOut se escribiera el valor en el integrado.

Para encender determinado led:

Para apagarlo:

Y el que refresca los leds es:

Las constantes nos siguen ayudando. Y ya con esto se acaba el turno del jugador 1.

Paso 5: Alisten, Apunten, ¡Fuego!

Ahora va el jugador 2. Escuchamos la conexión serial 2, le pediremos igualmente una coordenada, reutilizando los métodos que vimos más arriba para validarlas y todo eso.

Comenzamos imprimiendo los intentos restantes. Dichos intentos son el resultado de  restar a una constante (que podemos aumentar o disminuir según sea el caso) el valor de una variable. Dicha variable son los intentos que el usuario ya ha hecho.

Si se agotan los intentos, pierde. Si no, pues no.

Vemos que hay una variable booleana para restar o no el intento. Esto es porque, si el usuario pone una coordenada mal, no se le resta intento. Tampoco si le dispara a un lugar en donde había disparado antes.

Encerramos todo en un ciclo infinito, que será roto únicamente cuando se acaben los intentos o se destruyan todos los barcos. Veamos ahora los métodos que no he explicado.

Comprobar a qué cosa se disparó

Para esto utilizamos el método disparar y obtener objetivo. El método es simple y explicativo. Simplemente te dice a qué lugar disparaste, y dependiendo de ello puedes marcar un disparo acertado, o no hacerlo. También ayuda a saber si se tiene que restar o no un intento.

Por cierto, este método es inseguro, ya que no comprueba que las coordenadas son válidas por sí mismo, sino que confía en que alguien más lo haya hecho por él. Y claro, lo hicimos nosotros antes de llamarlo.

Marcar e indicar disparos acertados o erróneos

Ya casi olvidaba que tenemos un buzzer que indica si acertamos o no. Para marcar el disparo, simplemente ponemos el valor de la constante en esa posición del arreglo. Igual por si fue erróneo.

Y hay otros dos métodos que sirven para hacer sonar al buzzer. Aquí el código de todo:

Comprobar si quedan partes de un barco

Tomando de nuevo como ejemplo al submarino, vemos que ocupa muchos espacios. Si le damos con un disparo, se hundirá una parte de él, pero no lo hemos vencido. Es decir, si ocupa 3 espacios y le damos a uno, quedarán 2 en donde seguirá vivo.

¿Y qué significa esto? que no podremos apagar el LED hasta que se haya hundido completamente. Y tampoco contará como barco derribado si no lo hundimos completamente.

Para ello, existe el método hayMasInstanciasDe, que como su nombre lo dice, comprueba si quedan todavía algunos (aunque sea “heridos”) espacios ocupados por determinado barco. Aquí el código:

Indicando victoria o fracaso

Ya para terminar todo este código, dependiendo de cómo haya jugado el jugador indicamos la victoria o su fracaso. El código es muy simple, sólo imprime mensajes para ambos jugadores.

Y el juego se queda así hasta que el Arduino se reinicia. Si quisiéramos que se repita una vez que pierde o gana, pondríamos lo que está en el setup dentro del loop.

Paso 6: código fuente completo

Para que no andes copiando y pegando parte por parte, aquí dejo el código que compone al Sketch. Recuerda que puede tener errores.

Paso 7: Circuito en fritzing

Hice el circuito para un Arduino UNO, pero se puede modificar fácilmente para cualquier otro modelo. El círculo negro a la derecha es el buzzer.

Circuito de batalla naval en Arduino, diseñado con Fritzing

Circuito de batalla naval en Arduino, diseñado con Fritzing

Conclusión

Hemos terminado. Si tuve errores en el código, o tienes algunas dudas, no dudes en comentar. Cabe mencionar que podemos utilizar una LCD de 16 x 2, o de 20 x 4, o como sea mientras se pueda controlar con el módulo I2C.

En caso de que cambiemos la medida, también tenemos que cambiar las constantes en el código fuente.

Componentes: Módulo USB a TTL y Teclado matricial en Fritzing

Introducción

Fritzing funciona para diseñar circuitos. Es un programa open source, lo que permite que tenga una comunidad.

Por defecto, Fritzing no trae el componente del USB a TTL ni el teclado matricial; pero gracias a dicha comunidad podemos conseguir uno que alguien más hizo.

Nota: puedes instalar fritzing siguiendo este tutorial

Componentes

Los componentes que veremos son el módulo usb a ttl y el componente de teclado matricial. Son los de las imágenes:

Descargar

USB a TTL

El componente está alojado en GitHub, junto con muchos otros gracias a adafruit. Lo único que tenemos que hacer es ir a esta página:

https://github.com/adafruit/Fritzing-Library/blob/master/parts/USB%20TTL%20Serial%20Cable.fzpz

Una vez ahí, simplemente hacemos click en Download:

Guardamos el archivo en un lugar accesible y listo, ya lo hemos descargado.

Teclado matricial

Igualmente este componente está alojado en GitHub. Para descargarlo seguimos este link. El proceso para descargar es el mismo que el anterior.

 

Instalar en Fritzing

Usb a ttl

Abrimos Fritzing normalmente y esperamos a que cargue. Después, en el menú seleccionamos Archivo > Abrir, como se ve en la imagen:

Buscamos el componente que descargamos anteriormente y hacemos click en Abrir:

Esperamos un instante de tiempo y podremos ver que a la derecha, en componentes > Mine ha aparecido el nuevo módulo:

Ahora ya podemos usarlo en cualquier circuito. Así se ve:

Teclado matricial

Lo mismo que hicimos para el módulo usb a ttl. Lo abrimos y se verá en los componentes:

Cuando lo agregamos a un circuito, luce así:

Guardando cambios

Independientemente de que guardemos o no el circuito, tenemos que guardar el componente que hemos utilizado. Al salir de Fritzing, se nos mostrarán 2 alertas preguntando si queremos guardar cambios.

Tenemos que hacer click en “Yes” y “Guardar” respectivamente:

Y con eso hemos agregado ambos componentes que no vienen incluidos por defecto.

Descargar, instalar y configurar fritzing

Introducción

Fritzing es un programa libre de automatización de diseño electrónico que busca ayudar a diseñadores y artistas para que puedan pasar de prototipos (usando, por ejemplo, placas de pruebas) a productos finales.

Fritzing fue creado bajo los principios de Processing y Arduino, y permite a los diseñadores, artistas, investigadores y aficionados documentar sus prototipos basados en Arduino y crear esquemas de circuitos impresos para su posterior fabricación.

Además, cuenta con un sitio web complementario que ayuda a compartir y discutir bosquejos y experiencias y a reducir los costos de fabricación. y su diseño de arte de artistas.

Personalmente lo ocupo para diseñar circuitos de Arduino, ya que tiene muchos componentes. Además, nosotros podemos crear los nuestros. Es gratuito y puede exportar a imagen, PDF, etcétera.

Descargar

Vamos a la página de descargas: Click aquí

En la siguiente página seleccionamos No donation y hacemos click en Download:

 

Ahora nos llevará a otra página en donde vamos a elegir nuestro sistema operativo. Si tenemos Windows o Linux podemos elegir entre 32 y 64 bits. Si tenemos MacOS X sólo tenemos una opción.

En mi caso la descargaré para Windows de 64 bits. Es un archivo comprimido que podemos extraer con nuestro software de compresión favorito.

Extraer

Recomiendo extraerlo en C:\fritzing para tenerlo como un programa normal

Creando acceso directo

Cuando haya terminado podemos abrir la carpeta que elegimos para extraerlo y dentro habrá una carpeta llamada fritzing.0.9.3b.64.pc o algo parecido.

Entraremos a ella y dentro veremos muchos archivos, aunque sólo nos importa el ejecutable, ya que lo demás son componentes y librerías:

Con un recuadro azul marqué la aplicación. Ahora podemos crear un acceso directo a ella o anclarlo al inicio o a la barra de herramientas.

Y finalmente podemos abrirlo:

Actualizaciones

Cuando haya actualizaciones el programa nos avisará y se actualizará. No necesitaremos intervenir mucho en el proceso. Aunque realmente casi no se actualiza, ya que la última versión estable es del 2016.

De todos modos, en cada actualización hay que asegurarnos de tener nuestros prototipos y diseños en una carpeta separada.

 

 

Configurar HC-05 bluetooth sin TTL con Arduino y modo AT

Introducción

Cuando conseguimos un módulo bluetooth HC-05 siempre será necesario cambiar el nombre y la contraseña. En casos más avanzados, vamos a necesitar incluso cambiar el modo (esclavo o maestro). Me he topado con muchos tutoriales por ahí, todos varían y algunos piden un módulo USB a TTL.

Al final de todo logré entrar en modo AT para configurar mi módulo. Y no necesité otra cosa más que un Arduino, el propio módulo y el cable con el que se suben los programas al Arduino.

Dicho esto, comencemos.

Continue reading…

Remplazar caracteres con asteriscos en Arduino

Introducción

Hoy escribí una entrada sobre cómo remplazar caracteres o enmascarar una cadena en C++. Pensé que así como funcionó en CPP funcionaría en Arduino, pero no fue así. Así que me puse a leer la documentación de los Strings en Arduino y me di cuenta de que no son muchas cosas las que cambian.

Diseño de la función

La función es muy sencilla. En mi caso usé setCharAt, que es toma como parámetros la posición de la cadena en donde queremos poner el carácter y el carácter. Sabiendo eso podemos usar un ciclo for que vaya desde 0 hasta la longitud de la cadena (para esto podemos usar cadena.length() ) e ir llamando a setCharAt con el contador del ciclo y el carácter de remplazo.

Pero bueno, vayamos al código, que hablar es de mal gusto.

String enmascarar(String cadenaOriginal, char mascara) {
  for (int x = 0; x < cadenaOriginal.length(); x++)
    cadenaOriginal.setCharAt(x, mascara);
  return cadenaOriginal;
}

Ejemplo

Ahora vamos a probar la función. Voy a imprimir por medio del monitor serial. Remplazaré con asteriscos, con puntos y con guiones bajos.

void setup()
{
  Serial.begin(9600);
  Serial.println("enmascarar(\"Hola mundo\", '*') --> " + enmascarar("Hola mundo", '*'));
  Serial.println("enmascarar(\"Ya quedó\", '_') --> " + enmascarar("Ya quedó", '_'));
  Serial.println("enmascarar(\"A ver\", '.') --> " + enmascarar("A ver", '.'));
}
void loop(){}

String enmascarar(String cadenaOriginal, char mascara) {
  for (int x = 0; x < cadenaOriginal.length(); x++)
    cadenaOriginal.setCharAt(x, mascara);
  return cadenaOriginal;

}

La salida será la siguiente:

enmascarar("Hola mundo", '*') --> **********
enmascarar("Ya quedó", '_') --> _________
enmascarar("A ver", '.') --> .....

Así podemos por ejemplo poner esos caracteres al ingresar una contraseña.