¿Cómo funcionan las pantallas?
Cuando ves una pantalla, ya sea la de tu teléfono, una televisión o una computadora, lo que estás viendo son miles (o millones) de pequeños puntos de luz llamados píxeles.
Cada píxel está formado por tres subpíxeles de color: Rojo, Verde y Azul (RGB). Combinando esos tres colores con distintas intensidades, pueden mostrar casi cualquier color.
Esto ocurre miles de veces por segundo para darte una imagen fluida. Pero hoy no usaremos una pantalla a color, sino algo más simple y curioso…
¿Qué veremos hoy? Una Pantalla LCD
La pantalla LCD 16x2 no muestra gráficos complejos, pero sí podemos enviar texto y dibujar pequeños sprites pixel por pixel, como si fueran caritas o personajes miniatura.
Es ideal para mostrar mensajes, valores de sensores o incluso para crear mini videojuegos como Jumpman.
¿Qué es una pantalla LCD?
Una pantalla LCD (Liquid Crystal Display) utiliza cristales líquidos que cambian su forma cuando reciben voltaje.
Estos cristales bloquean o dejan pasar luz, formando texto o dibujos.
📌 La pantalla que usaremos tiene:
16 columnas y 2 filas para texto
Cada carácter es una celda de 5x8 píxeles blancos
Un LED blanco detrás (backlight) para que se vea
El Controlador HD44780
Este chip es el cerebro de la pantalla. Recibe las instrucciones del Arduino y decide qué mostrar, dónde mover el cursor y qué carácter personalizar. Gracias a este chip, podemos controlar la pantalla con solo unos cuantos comandos en el código.
¿Qué pines tiene la pantalla LCD?
¡Son muchos pines! Pero no te preocupes, existe un módulo que lo simplifica…
El módulo I2C para LCD te permite controlar todos esos pines con solo 2 cables desde el Arduino. Además, ya incluye un potenciómetro para el contraste.
I2C (Inter-Integrated Circuit) es un protocolo de comunicación que permite conectar muchos dispositivos usando solo dos líneas:
Un dispositivo es el Maestro (por ejemplo, Arduino), y los demás son Esclavos (por ejemplo, una pantalla LCD, un sensor, etc.).
Cada esclavo tiene una dirección única que el maestro usa para enviarle comandos específicos.
El Arduino envía datos por SDA, y SCL lleva el ritmo para que todo esté sincronizado. Así puedes conectar varias pantallas o sensores en las mismas dos líneas.
El módulo I2C para LCD te permite controlar todos esos pines con solo 2 cables desde el Arduino. Además, ya incluye un potenciómetro para el contraste.
El potenciómetro es una resistencia variable.
Lo usamos para ajustar el contraste de la pantalla LCD (qué tan visibles se ven los caracteres).
Si el contraste es muy bajo: la pantalla se ve vacía.
Si es muy alto: todo parece encendido.
Actividad 1: “¡Hola mundo!”
Vamos a conectar la pantalla y escribir nuestro primer mensaje.
Arduino UNO
LCD 16x2
Resistencia 220Ω
Protoboard
Jumpers
Sin módulo I2C
Código de programación:
Conexiones:
#include <LiquidCrystal.h>
// RS, E, D4, D5, D6, D7
LiquidCrystal lcd(0, 1, 2, 3, 4, 5);
void setup() {
lcd.begin(16, 2); // 16 columnas, 2 filas
lcd.print("Hola Mundo!");
}
void loop() {
// No es necesario nada aquí para esta actividad
}
Video con explicación:
Con módulo I2C
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Dirección I2C por defecto suele ser 0x27 o 0x3F
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
lcd.init(); // Inicializar pantalla
lcd.backlight(); // Encender luz de fondo
lcd.print("Hola Mundo!");
}
void loop() {
// No es necesario nada aquí para esta actividad
}
Algunos módulos LCD vienen con diferentes direcciones I2C, como 0x27, 0x3F, u otras. Si tu pantalla no muestra nada o aparece en blanco, puede ser que estés usando la dirección incorrecta.
Usa este programa que revisa todas las posibles direcciones y te dice cuál está conectada.
Abre el Monitor Serial (9600 baudios).
Sube el código al Arduino.
Verás un mensaje como:
Dispositivo I2C encontrado en dirección: 0x27!
Usa esa dirección (0x27, 0x3F, etc.) en tu código LCD.
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Escaneando dispositivos I2C...");
}
void loop() {
byte error, address;
int nDevices = 0;
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("Dispositivo I2C encontrado en dirección: 0x");
if (address < 16)
Serial.print("0");
Serial.print(address, HEX);
Serial.println(" !");
nDevices++;
}
}
if (nDevices == 0)
Serial.println("No se encontraron dispositivos I2C\n");
else
Serial.println("Escaneo terminado\n");
delay(5000); // Espera 5 segundos antes de volver a escanear
}
Actividad 2: ¡Diseña tu personaje!
La pantalla LCD permite hasta 8 caracteres personalizados, que tú dibujas con código binario.
Arduino UNO
LCD 16x2
Resistencia 220Ω
Protoboard
Jumpers
Sin módulo I2C
Código de programación:
Conexiones:
#include <LiquidCrystal.h>
// RS, E, D4, D5, D6, D7
LiquidCrystal lcd(11, 9, 6, 5, 4, 3);
// Carácter personalizado (ejemplo: una carita feliz)
byte customChar[] = {
B00000,
B00000,
B01010,
B00000,
B10001,
B01110,
B00000,
B00000
};
void setup() {
lcd.begin(16, 2); // Inicializa LCD de 16 columnas y 2 filas
lcd.createChar(0, customChar); // Carga el carácter personalizado en la posición 0
lcd.setCursor(0, 0); // Coloca el cursor en la esquina superior izquierda
lcd.write(byte(0)); // Muestra el carácter personalizado
}
void loop() {
// Nada aquí por ahora
}
Si quieres saber cómo poder hacer tus propios caracteres personalizados, revisa este apartado donde te lo explicamos paso a paso: Crear caracteres personalizados en una LCD
Video con explicación
Con módulo I2C
Código de programación:
Conexiones:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Dirección I2C común (puede variar, revisa con escáner si no funciona)
LiquidCrystal_I2C lcd(0x27, 16, 2); // (dirección, columnas, filas)
// Carácter personalizado
byte customChar[] = {
B00000,
B00000,
B01010,
B00000,
B10001,
B01110,
B00000,
B00000
};
void setup() {
lcd.init(); // Inicializa la pantalla
lcd.backlight(); // Enciende la retroiluminación
lcd.createChar(0, customChar); // Crea sprite
lcd.setCursor(0, 0); // Cursor a la esquina superior izquierda
lcd.write(byte(0)); // Muestra el sprite
}
void loop() {
// Nada aquí por ahora
}
Actividad 3: ¡Jugamos Jumpman!
Vamos a usar un botón para controlar un personaje que corre por la pantalla y salta obstáculos.
Arduino UNO
LCD 16x2
Resistencia 220Ω
Push button
Protoboard
Jumpers
Sin módulo I2C
Conexiones:
Código de programación:
#include <LiquidCrystal.h>
#define PIN_BUTTON 2
#define PIN_AUTOPLAY 1
#define PIN_READWRITE 10
#define PIN_CONTRAST 12
#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.' // Use the '.' character for the head
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' ' // User the ' ' character
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7
#define HERO_HORIZONTAL_POSITION 1 // Horizontal position of hero on screen
#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2
#define HERO_POSITION_OFF 0 // Hero is invisible
#define HERO_POSITION_RUN_LOWER_1 1 // Hero is running on lower row (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2 // (pose 2)
#define HERO_POSITION_JUMP_1 3 // Starting a jump
#define HERO_POSITION_JUMP_2 4 // Half-way up
#define HERO_POSITION_JUMP_3 5 // Jump is on upper row
#define HERO_POSITION_JUMP_4 6 // Jump is on upper row
#define HERO_POSITION_JUMP_5 7 // Jump is on upper row
#define HERO_POSITION_JUMP_6 8 // Jump is on upper row
#define HERO_POSITION_JUMP_7 9 // Half-way down
#define HERO_POSITION_JUMP_8 10 // About to land
#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 // (pose 2)
LiquidCrystal lcd(11, 9, 6, 5, 4, 3);
static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;
void initializeGraphics(){
static byte graphics[] = {
// Run position 1
B01100,
B01100,
B00000,
B01110,
B11100,
B01100,
B11010,
B10011,
// Run position 2
B01100,
B01100,
B00000,
B01100,
B01100,
B01100,
B01100,
B01110,
// Jump
B01100,
B01100,
B00000,
B11110,
B01101,
B11111,
B10000,
B00000,
// Jump lower
B11110,
B01101,
B11111,
B10000,
B00000,
B00000,
B00000,
B00000,
// Ground
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
// Ground right
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
// Ground left
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
};
int i;
// Skip using character 0, this allows lcd.print() to be used to
// quickly draw multiple characters
for (i = 0; i < 7; ++i) {
lcd.createChar(i + 1, &graphics[i * 8]);
}
for (i = 0; i < TERRAIN_WIDTH; ++i) {
terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
terrainLower[i] = SPRITE_TERRAIN_EMPTY;
}
}
// Slide the terrain to the left in half-character increments
//
void advanceTerrain(char* terrain, byte newTerrain){
for (int i = 0; i < TERRAIN_WIDTH; ++i) {
char current = terrain[i];
char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1];
switch (current){
case SPRITE_TERRAIN_EMPTY:
terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
break;
case SPRITE_TERRAIN_SOLID:
terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_RIGHT:
terrain[i] = SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_LEFT:
terrain[i] = SPRITE_TERRAIN_EMPTY;
break;
}
}
}
bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
bool collide = false;
char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
byte upper, lower;
switch (position) {
case HERO_POSITION_OFF:
upper = lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_LOWER_1:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_RUN1;
break;
case HERO_POSITION_RUN_LOWER_2:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_RUN2;
break;
case HERO_POSITION_JUMP_1:
case HERO_POSITION_JUMP_8:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_JUMP;
break;
case HERO_POSITION_JUMP_2:
case HERO_POSITION_JUMP_7:
upper = SPRITE_JUMP_UPPER;
lower = SPRITE_JUMP_LOWER;
break;
case HERO_POSITION_JUMP_3:
case HERO_POSITION_JUMP_4:
case HERO_POSITION_JUMP_5:
case HERO_POSITION_JUMP_6:
upper = SPRITE_JUMP;
lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_UPPER_1:
upper = SPRITE_RUN1;
lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_UPPER_2:
upper = SPRITE_RUN2;
lower = SPRITE_TERRAIN_EMPTY;
break;
}
if (upper != ' ') {
terrainUpper[HERO_HORIZONTAL_POSITION] = upper;
collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true;
}
if (lower != ' ') {
terrainLower[HERO_HORIZONTAL_POSITION] = lower;
collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true;
}
byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1;
// Draw the scene
terrainUpper[TERRAIN_WIDTH] = '\0';
terrainLower[TERRAIN_WIDTH] = '\0';
char temp = terrainUpper[16-digits];
terrainUpper[16-digits] = '\0';
lcd.setCursor(0,0);
lcd.print(terrainUpper);
terrainUpper[16-digits] = temp;
lcd.setCursor(0,1);
lcd.print(terrainLower);
lcd.setCursor(16 - digits,0);
lcd.print(score);
terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
return collide;
}
// Handle the button push as an interrupt
void buttonPush() {
buttonPushed = true;
}
void setup(){
pinMode(PIN_READWRITE, OUTPUT);
digitalWrite(PIN_READWRITE, LOW);
pinMode(PIN_CONTRAST, OUTPUT);
digitalWrite(PIN_CONTRAST, LOW);
pinMode(PIN_BUTTON, INPUT);
digitalWrite(PIN_BUTTON, HIGH);
pinMode(PIN_AUTOPLAY, OUTPUT);
digitalWrite(PIN_AUTOPLAY, HIGH);
// Digital pin 2 maps to interrupt 0
attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING);
initializeGraphics();
lcd.begin(16, 2);
}
void loop(){
static byte heroPos = HERO_POSITION_RUN_LOWER_1;
static byte newTerrainType = TERRAIN_EMPTY;
static byte newTerrainDuration = 1;
static bool playing = false;
static bool blink = false;
static unsigned int distance = 0;
if (!playing) {
drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
if (blink) {
lcd.setCursor(0,0);
lcd.print("Press Start");
}
delay(250);
blink = !blink;
if (buttonPushed) {
initializeGraphics();
heroPos = HERO_POSITION_RUN_LOWER_1;
playing = true;
buttonPushed = false;
distance = 0;
}
return;
}
// Shift the terrain to the left
advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
// Make new terrain to enter on the right
if (--newTerrainDuration == 0) {
if (newTerrainType == TERRAIN_EMPTY) {
newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
newTerrainDuration = 2 + random(10);
} else {
newTerrainType = TERRAIN_EMPTY;
newTerrainDuration = 10 + random(10);
}
}
if (buttonPushed) {
if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
buttonPushed = false;
}
if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
playing = false; // The hero collided with something. Too bad.
} else {
if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
heroPos = HERO_POSITION_RUN_LOWER_1;
} else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_RUN_UPPER_1;
} else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_JUMP_5;
} else if (heroPos == HERO_POSITION_RUN_UPPER_2) {
heroPos = HERO_POSITION_RUN_UPPER_1;
} else {
++heroPos;
}
++distance;
digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW);
}
delay(50);
}
Video con explicación:
Con módulo I2C
Conexiones:
Código de programación:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define PIN_BUTTON 2
#define PIN_AUTOPLAY 1
#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.'
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' '
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7
#define HERO_HORIZONTAL_POSITION 1
#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2
#define HERO_POSITION_OFF 0
#define HERO_POSITION_RUN_LOWER_1 1
#define HERO_POSITION_RUN_LOWER_2 2
#define HERO_POSITION_JUMP_1 3
#define HERO_POSITION_JUMP_2 4
#define HERO_POSITION_JUMP_3 5
#define HERO_POSITION_JUMP_4 6
#define HERO_POSITION_JUMP_5 7
#define HERO_POSITION_JUMP_6 8
#define HERO_POSITION_JUMP_7 9
#define HERO_POSITION_JUMP_8 10
#define HERO_POSITION_RUN_UPPER_1 11
#define HERO_POSITION_RUN_UPPER_2 12
// Cambia la dirección si no es 0x27 (prueba con 0x3F si no funciona)
LiquidCrystal_I2C lcd(0x27, 16, 2);
static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;
void initializeGraphics() {
static byte graphics[] = {
// Run position 1
B01100,B01100,B00000,B01110,B11100,B01100,B11010,B10011,
// Run position 2
B01100,B01100,B00000,B01100,B01100,B01100,B01100,B01110,
// Jump
B01100,B01100,B00000,B11110,B01101,B11111,B10000,B00000,
// Jump lower
B11110,B01101,B11111,B10000,B00000,B00000,B00000,B00000,
// Ground
B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111,
// Ground right
B00011,B00011,B00011,B00011,B00011,B00011,B00011,B00011,
// Ground left
B11000,B11000,B11000,B11000,B11000,B11000,B11000,B11000,
};
for (int i = 0; i < 7; ++i) {
lcd.createChar(i + 1, &graphics[i * 8]);
}
for (int i = 0; i < TERRAIN_WIDTH; ++i) {
terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
terrainLower[i] = SPRITE_TERRAIN_EMPTY;
}
}
void advanceTerrain(char* terrain, byte newTerrain) {
for (int i = 0; i < TERRAIN_WIDTH; ++i) {
char current = terrain[i];
char next = (i == TERRAIN_WIDTH - 1) ? newTerrain : terrain[i + 1];
switch (current) {
case SPRITE_TERRAIN_EMPTY:
terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
break;
case SPRITE_TERRAIN_SOLID:
terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_RIGHT:
terrain[i] = SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_LEFT:
terrain[i] = SPRITE_TERRAIN_EMPTY;
break;
}
}
}
bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
bool collide = false;
char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
byte upper, lower;
switch (position) {
case HERO_POSITION_OFF: upper = lower = SPRITE_TERRAIN_EMPTY; break;
case HERO_POSITION_RUN_LOWER_1: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_RUN1; break;
case HERO_POSITION_RUN_LOWER_2: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_RUN2; break;
case HERO_POSITION_JUMP_1:
case HERO_POSITION_JUMP_8: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_JUMP; break;
case HERO_POSITION_JUMP_2:
case HERO_POSITION_JUMP_7: upper = SPRITE_JUMP_UPPER; lower = SPRITE_JUMP_LOWER; break;
case HERO_POSITION_JUMP_3:
case HERO_POSITION_JUMP_4:
case HERO_POSITION_JUMP_5:
case HERO_POSITION_JUMP_6: upper = SPRITE_JUMP; lower = SPRITE_TERRAIN_EMPTY; break;
case HERO_POSITION_RUN_UPPER_1: upper = SPRITE_RUN1; lower = SPRITE_TERRAIN_EMPTY; break;
case HERO_POSITION_RUN_UPPER_2: upper = SPRITE_RUN2; lower = SPRITE_TERRAIN_EMPTY; break;
}
if (upper != ' ') {
terrainUpper[HERO_HORIZONTAL_POSITION] = upper;
collide = (upperSave != SPRITE_TERRAIN_EMPTY);
}
if (lower != ' ') {
terrainLower[HERO_HORIZONTAL_POSITION] = lower;
collide |= (lowerSave != SPRITE_TERRAIN_EMPTY);
}
byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1;
terrainUpper[TERRAIN_WIDTH] = '\0';
terrainLower[TERRAIN_WIDTH] = '\0';
char temp = terrainUpper[16 - digits];
terrainUpper[16 - digits] = '\0';
lcd.setCursor(0, 0);
lcd.print(terrainUpper);
terrainUpper[16 - digits] = temp;
lcd.setCursor(0, 1);
lcd.print(terrainLower);
lcd.setCursor(16 - digits, 0);
lcd.print(score);
terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
return collide;
}
void buttonPush() {
buttonPushed = true;
}
void setup() {
pinMode(PIN_BUTTON, INPUT_PULLUP);
pinMode(PIN_AUTOPLAY, OUTPUT);
digitalWrite(PIN_AUTOPLAY, HIGH);
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON), buttonPush, FALLING);
lcd.init();
lcd.backlight();
initializeGraphics();
}
void loop() {
static byte heroPos = HERO_POSITION_RUN_LOWER_1;
static byte newTerrainType = TERRAIN_EMPTY;
static byte newTerrainDuration = 1;
static bool playing = false;
static bool blink = false;
static unsigned int distance = 0;
if (!playing) {
drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
if (blink) {
lcd.setCursor(0, 0);
lcd.print("Press Start ");
}
delay(250);
blink = !blink;
if (buttonPushed) {
initializeGraphics();
heroPos = HERO_POSITION_RUN_LOWER_1;
playing = true;
buttonPushed = false;
distance = 0;
}
return;
}
advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
if (--newTerrainDuration == 0) {
if (newTerrainType == TERRAIN_EMPTY) {
newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
newTerrainDuration = 2;
} else {
newTerrainType = TERRAIN_EMPTY;
newTerrainDuration = 10 + random(10);
}
}
if (buttonPushed) {
if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
buttonPushed = false;
}
if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
playing = false;
} else {
if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8)
heroPos = HERO_POSITION_RUN_LOWER_1;
else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) &&
terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY)
heroPos = HERO_POSITION_RUN_UPPER_1;
else if (heroPos >= HERO_POSITION_RUN_UPPER_1 &&
terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY)
heroPos = HERO_POSITION_JUMP_7;
else
heroPos++;
}
distance++;
delay(100);
}
Quiz de repaso
Presentación Clase 9: Control de Servomotores
1. ¿Qué pasa si quemo plástico con el cautín?
Al quemar plástico, liberas humos tóxicos que te hacen daño. ¡Evítalo! Si se pega plástico a la punta, limpia con una esponja húmeda o lana metálica.
2. ¿Qué hago si me quemo con el cautín?
Si eres menor de edad, avisa a un adulto.
Enfría la zona con agua corriente por 10 minutos.
Cubre con una gasa estéril.
Consulta al médico si es grave (ampollas grandes o mucho dolor).
3. ¿Por qué mi cautín cambia de color en la punta?
Oxidación: Límpialo con frecuencia y mantén la punta estañada.
Temperatura alta: No sobrecalientes. Ajusta entre 350-400 °C.
Residuos: Usa estaño de calidad con flux y limpia tras cada uso.
4. ¿Cómo saber si mi cautín es de buena calidad?
Un buen cautín:
Mantiene la temperatura estable.
Permite ajustar la temperatura (idealmente entre 200-450 °C).
Tiene puntas reemplazables y de buena conducción térmica.
7. ¿Cómo ajusto la temperatura de mi cautín?
350-400 °C: Ideal para soldaduras estándar.
300-350 °C: Para componentes sensibles.
Si tu cautín no tiene ajuste, considera uno que lo permita.
8. ¿Qué hacer si mi cautín no calienta bien?
Limpia la punta, puede estar oxidada.
Revisa el cable y las conexiones.
Si sigue fallando, reemplaza el elemento calefactor o el cautín.