En el vasto universo de la electrónica de pasatiempo y la microprogramación, pocos proyectos combinan la funcionalidad didáctica con el encanto nostálgico como la recreación de videojuegos clásicos. El juego del dinosaurio de Google Chrome (también conocido como T-Rex Game o Dino Runner), que aparece cuando no hay conexión a Internet, es un ícono de la simplicidad y la jugabilidad adictiva.
Este proyecto, el Dino-Run I2C, lleva la esencia de ese juego al mundo físico, utilizando componentes básicos de electrónica: una placa Arduino Uno como cerebro, una pantalla LCD 16×2 con interfaz I2C para la visualización, y un simple pulsador como controlador principal. La implementación en una pantalla LCD monocromática y limitada a 16×2 caracteres es un desafío de programación minimalista, que obliga a crear animaciones y gráficos utilizando únicamente los caracteres personalizados del LCD.
🔩Funcionamiento
El Poder de I2C en la LCD
Tradicionalmente, las pantallas LCD 16×2 requerían hasta 12 cables para funcionar en modo de 4 bits, consumiendo la mayoría de los pines digitales del Arduino. La interfaz I2C (Inter-Integrated Circuit) resuelve esto a través de un pequeño adaptador que contiene un chip (a menudo un PCF8574).
- Principio: I2C es un protocolo de comunicación serial de dos hilos (SDA y SCL) que permite que múltiples dispositivos (‘esclavos’) se comuniquen con un ‘maestro’ (el Arduino) utilizando solo esos dos pines.
- Ventaja: En este proyecto, el uso de I2C libera los pines Digitales 2 al 7 para otros usos (como controlar un relé para abrir una cerradura real), permitiendo una escalabilidad del proyecto.
PINES del I2C
| Pin del I2C | Protocolo | Notas Importantes |
|---|---|---|
| VCC | Alimentación | Uso de 5V. |
| GND | Tierra | Conexión de referencia. |
| SDA | I2C | Línea de Datos Serial. |
| SCL | I2C | Línea de Reloj Serial. |
🔨Componentes
| Componente | Cantidad | Especificación | Función |
|---|---|---|---|
| Placa Arduino | 1 | Arduino Uno, Nano, etc. | Control de componentes(cerebro) |
| LCD | 1 | 1602a 16×2 | Mostrar información |
| Interfaz | 1 | I2C | Reducir pines del LCD |
| Botón | 1 | Push | Saltar dinosaurio |
| Resistencia | 1 | 10kΩ | Pull-down para el botón |
| Cables de conexión | n | Unir los componentes |
🔌Conexiones
Conexión LCD 16×2 con I2C
| LCD (I2C)Pin | Arduino Pin |
|---|---|
| GND | GND |
| VCC | 5V |
| SDA | A4 |
| SCL | A5 |
Conexión del botón(Configuración Pull-Down):
| Arduino Pin | Arduino Pin |
|---|---|
| 5v | Pin 2 (con resistencia de 10kΩ a GND) |
Para asegurar una lectura digital estable, los pulsadores se conectan en configuración Pull-Down: un extremo a 5V, el otro extremo al Pin Digital (2) de Arduino y, crucialmente, una resistencia de 10kΩ entre ese mismo pin digital y GND. Esto garantiza que el pin digital lea un estado LOW cuando el botón está abierto y HIGH (5V) solo cuando es presionado.
0️⃣Código
El corazón del proyecto reside en dos pilares de programación: el uso de la memoria CGRAM para definir los gráficos personalizados y el control del tiempo sin usar la función delay(), lo que garantiza una respuesta fluida al botón de salto.
El LCD 16×2 solo permite 8 caracteres personalizados. Usaremos 3 de ellos para el dinosaurio y el cactus. La memoria CGRAM (Character Generator RAM) permite definir cada carácter como una matriz de 5 x 8 píxeles.
Ejecuta el siguiente código, no olvidar instalar la librería “LiquidCrystal_I2C” por marcoschwartz o similar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | #include <Arduino.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> void saltar(); void iniciarJuego(); void mostrarPantallaInicial(); void moverObstaculo(); void terminarJuego(); bool chequearColision(); void actualizarPantalla(); // Definición de pines y configuración de la pantalla const int PIN_BOTON = 2; // ¡ATENCIÓN! Asegúrate de que la dirección 0x27 o 0x3F sea la correcta para tu módulo. LiquidCrystal_I2C lcd(0x27, 16, 2); // Variables del juego int dinosaurioY = 1; // Fila del dinosaurio (1: suelo, 0: saltando) int cactusX = 10; // Columna del obstáculo long tiempo_anterior = 0; int velocidad_juego = 150; // Milisegundos entre movimientos (cuanto menor, más rápido) unsigned long puntuacion = 0; bool juego_activo = false; int contadorSalto = 0; //Cronómetro para mantener al Dino en el aire int tiempoDinoSaltando = 3;//Tiempo en el que el dino estara en el aire // ----------------------------------------------------------------- // Definición de Caracteres Personalizados (CGRAM) - Matriz de 5x8 // B11111 significa que todos los 5 píxeles de esa línea están encendidos. // ----------------------------------------------------------------- // Carácter 0: Dinosaurio en el suelo (Pie Abajo) byte dino_pie_abajo[8] = { B11110, B01011, B01100, B01111, B10110, B11111, B01110, B00111 }; // Carácter 1: Dinosaurio Saltando (Cuerpo en el aire) byte dino_saltando[8] = { B00000, B11110, B01011, B01100, B01111, B10110, B11111, B01110 }; // Carácter 2: Cactus (Obstáculo) byte cactus_grafico[8] = { B00000, B00000, B00000, B00110, B00110, B01110, B01110, B00100 }; void setup() { Serial.begin(9600); // Inicialización del LCD lcd.init(); lcd.backlight(); // Carga de caracteres personalizados en la CGRAM lcd.createChar(0, dino_pie_abajo); lcd.createChar(1, dino_saltando); lcd.createChar(2, cactus_grafico); // Configuración del botón pinMode(PIN_BOTON, INPUT); mostrarPantallaInicial(); } void loop() { if (juego_activo == true) { // 1. Detección de Salto: El control de entrada debe ser constante // Si el botón es HIGH y el dino está en el suelo (dinosaurioY == 1) if (digitalRead(PIN_BOTON) == HIGH && dinosaurioY == 1) { saltar(); } // 2. Control de Velocidad y Movimiento (Non-blocking timing) // Solo mueve el cactus y actualiza la pantalla si ha pasado el tiempo necesario. if (millis() - tiempo_anterior >= velocidad_juego) { tiempo_anterior = millis(); moverObstaculo(); // Lógica de Caída: Regresa a la fila de suelo (1) después de un ciclo de salto (0) if (dinosaurioY == 0) { actualizarPantalla(); contadorSalto++; if(contadorSalto >= tiempoDinoSaltando) dinosaurioY = 1; } // 3. Chequeo de Colisión if (chequearColision()) { terminarJuego(); return; } // 4. Actualización de Puntuación y Pantalla puntuacion++; actualizarPantalla(); // 5. Aumento Progresivo de Velocidad para aumentar la dificultad if (puntuacion % 25 == 0 && velocidad_juego > 70) { velocidad_juego -= 5; } } } else { // Si el juego está inactivo (menú o Game Over), espera el botón if (digitalRead(PIN_BOTON) == HIGH) { iniciarJuego(); } } } // ----------------------------------------------------------------- // Lógica de Funciones del Juego // ----------------------------------------------------------------- void saltar() { contadorSalto = 0; dinosaurioY = 0; // Pone al dino en la fila superior (saltando) } void moverObstaculo() { // Limpiar el rastro anterior if (cactusX < 16) { lcd.setCursor(cactusX, 1); lcd.print(" "); } // Mover una posición a la izquierda cactusX--; // Reiniciar el obstáculo si sale de la pantalla if (cactusX < 0) { cactusX = 15; } } bool chequearColision() { // Colisión: Cactus en Columna 1 (donde está el dino) Y el dino en el suelo (fila 1) if (cactusX == 1 && dinosaurioY == 1) { return true; } return false; } void actualizarPantalla() { // Limpia completamente el display para el nuevo frame lcd.clear(); // Dibuja el dinosaurio int grafico_dino = (dinosaurioY == 0) ? 1 : 0; // Carácter 1 si salta, 0 si está en el suelo lcd.setCursor(1, dinosaurioY); // El dino se queda fijo en la Columna 1 lcd.write(byte(grafico_dino)); // Dibuja el obstáculo lcd.setCursor(cactusX, 1); lcd.write(byte(2)); // Carácter 2 es el cactus // Muestra la Puntuación if(puntuacion < 10) lcd.setCursor(11, 0); else if (puntuacion >= 10 && puntuacion < 99) lcd.setCursor(10, 0); else if (puntuacion >= 100 && puntuacion < 999) lcd.setCursor(9, 0); else lcd.setCursor(8, 0); lcd.print("PTS:"); lcd.print(puntuacion); } void iniciarJuego() { puntuacion = 0; cactusX = 15; dinosaurioY = 1; velocidad_juego = 150; juego_activo = true; contadorSalto = 0; tiempoDinoSaltando = 3; lcd.clear(); actualizarPantalla(); } void terminarJuego() { juego_activo = false; lcd.clear(); lcd.setCursor(3, 0); lcd.print("GAME OVER"); lcd.setCursor(2, 1); lcd.print("Score: "); lcd.print(puntuacion); delay(3000); // Pequeño delay para que el usuario pueda ver el Game Over } void mostrarPantallaInicial() { lcd.setCursor(4, 0); lcd.print("Dino-Run"); lcd.setCursor(0, 1); lcd.print("Pulsa para Jugar"); delay(1000); } |
🖌️Diseños

🎬Videos
📑Conclusión
El proyecto Dino-Run I2C es mucho más que una simple recreación; representa un ejercicio magistral de ingeniería minimalista y programación eficiente. Al integrar una placa Arduino Uno, una pantalla LCD 16×2 I2C y un solo pulsador, hemos demostrado que las experiencias de juego más adictivas pueden nacer de recursos limitados.
El éxito de este proyecto radica en tres pilares técnicos fundamentales:
- Eficiencia del I2C: La adopción del bus I2C simplificó drásticamente el cableado, liberando recursos del Arduino y haciendo el montaje más limpio y accesible.
- Ingenio Gráfico (CGRAM): Superamos la limitación de los caracteres predefinidos del LCD al utilizar la memoria CGRAM. Esto nos permitió diseñar gráficos personalizados del dinosaurio y el cactus, transformando simples bloques de píxeles en elementos visuales reconocibles.
- Lógica Reactiva: La implementación de la técnica de tiempo no bloqueante con
millis()fue crucial. Al evitar la funcióndelay(), garantizamos que la lectura del botón de salto fuera instantánea y precisa, asegurando una jugabilidad fluida que honra la velocidad y la frustración placentera del juego original de Chrome.
En última instancia, el Dino-Run I2C sirve como una poderosa herramienta de aprendizaje. Demuestra cómo la comprensión profunda de los límites del hardware puede inspirar soluciones de software creativas, transformando un puñado de componentes básicos en una experiencia de usuario completa y satisfactoria. Este proyecto es una prueba viviente de que, en el mundo de la electrónica, la limitación es a menudo la madre de la innovación.

