Hace ya bastante tiempo he hecho un pequeño juego con una pantalla OLED de 4D Systems llamado ArduGame ya que siempre me ha llamado bastante la atención este tipo de cosas. De hecho también hace ya un tiempo que estaba pensando en hacer una nueva versión un poco más elaborada y con mejor acabado, o eso espero. Tras unos días investigando sobre cómo podría montar una pequeña consola de juegos y sus características, decidí basarme en una pequeña pantalla OLED de Adafruit. Y aquí comienza la historia y que aún se está desarrollando. La meta de todo esto es hacerla lo más pequeña posible utilizando componentes de superficie (SMD) y hacer tanto el hardware como el software. Actualmente tengo hecho gran parte del hardware y el software con un pequeño juego retro tipo Galaxian y antes de fabricar la PCB, me gustaría compartir este proyecto con vosotros y que todo aquel que quiera aportar ideas o sugerencias lo pueda hacer. No vaya ser que se me escape algo! Además siempre está bien escuchar opiniones que no sean la de uno mismo ;)
Como no puede ser de otra forma, todo el código fuente y los archivos Eagle de la placa están disponibles de forma libre para todo aquel que quiera hacerse una réplica o simplemente curiosear el fuente. A continuación os detallo el estado actual del proyecto y por supuesto, espero vuestro comentarios!!
MicroGame
El corazón del proyecto es una pantalla OLED de 1.3 pulgadas de Adafruit, basada en el chip SSD1306 que puede funcionar tanto por I2C como por SPI.
La ventaja de esta pantalla es que Adafruit dispone de una completa librería que permite programarla sin reinventar la rueda, cosa que me ahorra bastante trabajo. La desventaja es que la librería es bastante pesada y ocupa unos 10kb y por lo tanto me quedan unos 20kb más o menos para programar un juego medianamente decente.
Especificaciones
El controlador principal que controla la pantalla y el resto de componentes es un microcontrolador ATmega32U4, el mismo que utiliza el Arduino Leonardo que además tiene un puerto de conexión USB directamente por hardware. Esto hace que pueda hacer la consola totalmente compatible y así poder programarla con el IDE de Arduino directamente por USB sin problema, además de ahorrarme un conversor serie/USB tipo FTDI.
Hasta el momento, las especificaciones y características que me he puesto como meta son las siguientes:
- Compatible con Arduino
- Alimentación por baterías (3,6V)
- Recargable por USB
- Sonidos
- Lo más pequeña posible (Actualmente unos 8x4cm)
- Uso de componentes de superficie SMD (que sean fáciles de encontrar!)
- Juego propio tipo Galaxian con puntuación permanente en EEPROM
Creo que no me dejo nada y además no quiero meterle demasiadas cosas para no complicar en exceso el circuito. Hablando del circuito, es realmente sencillo y es muy similar al Arduino Fio de Spakfun tal y como se puede ver en el esquema:
Esquema de la placa. Haz click para agrandar
Todo el sistema, dado que es portatil y se alimenta con una sola batería de botón, funciona a 3,3V y con un consumo aproximado rondando los 100mA aunque depende en gran medida de la cantidad de pixels encendidos en la pantalla. El microcontrolador por su lado funciona a 8MHz y no a 16MHz debido a la baja tensión de alimentación. Perdemos un poco de velocidad pero ahorramos en consumo y según la ficha técnica de Atmel, es la velocidad correcta para esta alimentación ya que a una mayor frecuencia se considera overlocking y puede hacer que no funcione correctamente.
Alimentación
La alimentación como ya he dicho es de 3,3V y proviene de una única batería de botón tipo CR2450 de 3.6V (160mAh) que además es recargable. Un circuito integrado especializado MCP73831 de Microchip se ocupa de la recarga de la batería cuando se conecta la placa por USB. Por otro lado un regulador MIC5219 de Micrel se encarga de proporcionar unos 3,3V estables y de forma bastante eficiente. Este regulador es una pequeña maravilla y a pesar de su pequeño tamaño, es capaz de proporcionar hasta 500mA con un error de tan solo 1%. Ambos integrados se pueden conseguir de forma gratuita con el programa de samples de ambas empresas. Yo me hice con una decena de cada. Un interruptor se encarga de cortar la alimentación del circuito pero está situado de manera que podamos recargar igualmente por USB estando apagado. Eso sí, será necesario encender dicho interruptor para poder programar el Atmega32U4 como es lógico.
La placa PCB
Por el momento tengo el tamaño que quiero para la placa y los componentes presentados sobre ella, sin embargo aún no he trazado las pistas ya que igual hago algunas modificaciones de última hora. Ahora se presenta en Eagle tal que así:
Placa PCB hecha con Eagle Cad (Click para agrandar)
En la parte central como es obvio está la pantalla que se conecta directamente a la placa con sus 8 pines. La placa es de dos caras pero sobre la superior (top) solo he dejado los pulsadores, la pantalla, el interruptor de encendido y algunos LEDs indicadores. Todo lo demás se encuentra sobre la parte posterior (bottom) para que no estorbe y la parte superior quede lo más limpia posible. Estoy bastante contento con la disposición general aunque estoy un poco preocupado por la pila que queda sobre la parte derecha ya que puede entorpecer un poco con los dedos de la mano derecha al jugar. Realmente, creo que no tengo mucho más donde ponerla así que probablemente se quede ahí.
Para asegurarme que todos los encapsulados son correctos, he impreso los pads sobre papel y colocado los componentes por encima y parece que todo cuadra sin problema. Una vez comprobado y finalizado el proceso de hacer las pistas, la enviaré a fabricar para que quede perfecta. Eso tomará por lo menos unas 3 semanas, así que hay que tomarlo con calma.
Código fuente
Dado que la placa tardará al menos 3 semanas en venir, me queda entonces algo de tiempo para jugar un poco y hacer un pequeño juego decente para cargar. Aquí tampoco podemos hacer un Halo, así que me decanté por uno de mis juegos favoritos, el Galaxian, un clásico de los arcade de mediados de los 80. Es un juego bastante fácil de hacer y los gráficos se pueden optimizar bastante. Actualmente el sprite de la nave mide unos 10x7 pixeles y los enemigos unos 5x5 pixeles. Todo eso se almacena en la memoria flash con PROGMEM y utilizando 1 byte por cada ocho píxeles, de esta forma aprovechamos mucho más el espacio ya que para un sprite de 5x5 solo ocupamos 5 bytes. He decidido hacer un conjunto de clases para gestionar de forma fácil todos los elementos del juego. La clase Battleship gestiona la nave del jugador, Enemy los enemigos, Starfield el mapa de estrellas y Collider se encarga de detectar las colisiones. Me queda alguna por hacer como por ejemplo la clase Scoreboard que gestione las puntuaciones.
Todo el código está montado de forma que no se bloquee en ningún momento el juego (nada de delay!) para poder refrescar la pantalla lo más rápido posible. Una cosa interesante es el movimiento de los enemigos, en total 5 distintos, que se calcula mediante un delta en el frame actual. Así si por algún motivo nos saltamos frames, el enemigo estará en la posición que tiene que estar. Eso es imprescindible ya que más adelante, cuando haga la parte del sonido será necesario no bloquear el juego. Para la parte del sonido tengo pensado utilizar las funciones tone() y notone() que al ser asíncronas permiten reproducir pitidos sin bloquear nada. Creo que midiendo los tiempos entre notas e hilando fino en el blucle principal para actualizarlo rápido, podría generar una música de fondo simultanea con un solo canal. Haré unas pruebas en los próximos días a ver qué sale.
Las clases dan mucho juego (valga la redundancia) ya que se pueden hacer cosas chulas como definir el tipo de movimiento de los enemigos, cada cuanto disparan, cuantos disparos hacen falta para matarlos o cuantos puntos conseguimos por matar un enemigo. De esta forma se pueden ajustar estos parámetros para afinar el juego y hacerlo coerente.
Aun hay muchas optimizaciones que hacer, como por ejemplo el uso de tablas precalculadas para el seno y coseno que actualmente utilizo tal cual y hace perder bastantes ciclos de reloj.
Primeras pruebas
Si vale, venga, que sois unos impacientes! :) Aquí os dejo unos vídeos del pequeño juego en su estado actual. Le faltan bastantes cosas aún pero va pillando forma. Actualmente la gestión de la nave funciona y tanto los enemigos como las colisiones también. Aquí teneis un par de pruebas que he hecho con un Arduino UNO.
Prueba de sprites y nave
Prueba de colisiones con enemigos
Conclusiones y recursos
Bueno, no es que esté terminado pero creo que lo tengo bastante avanzado. Si quereis cotillear el código fuente o la placa, podéis verlo en github. Por supuesto estaré encantado de leer vuestras opiniones o mejoras que podría implementar y os seguiré comentando cómo va evolucionando este divertido proyecto.