Lopaka Basics 04: datos en vivo

Dos pantallas. La misma placa, el mismo sensor, los mismos números. Pero una de ellas parpadea cada vez que un número cambia. Ralentízalo y puedes ver por qué: toda la imagen se borra y después se pinta de vuelta, pieza a pieza.
En este episodio descubrirás qué hace loop(), por qué parpadea una pantalla en color cuando un OLED no lo hace, y las tres reglas que lo detienen. Después convertirás los números de un diseño de Lopaka en variables, los alimentarás desde un sensor de temperatura y humedad y lo ejecutarás en un ESP32 simulado sin un solo parpadeo.
Lo que vas a crear: un monitor de habitación en color de 320×240 con números de temperatura y humedad en vivo y una barra de humedad, actualizado dos veces por segundo. Lo que necesitas: un navegador. Ejecutamos el código en un ESP32 simulado con una pantalla ILI9341 y un sensor DHT22; una placa real es opcional.
setup() y loop()
Cada sketch de Arduino tiene dos partes:
setup()se ejecuta una vez, al encender.loop()se ejecuta después, una y otra vez, mientras la placa tenga energía.
Los datos en vivo viven en loop(): lee el sensor, convierte los números en texto, dibújalos y repite. Cada vuelta dibuja la pantalla otra vez.

Por qué parpadea una pantalla en color
El OLED dibuja primero en memoria
El OLED de los primeros episodios nunca parpadeó. Su biblioteca, U8g2, dibuja primero en memoria: un búfer de fotogramas de un kilobyte para 128 × 64 píxeles, como viste en el episodio 1. Solo la imagen terminada va a la pantalla. El modo por páginas hace lo mismo en ocho tiras de 128 bytes, una tras otra.

Un fotograma en color necesitaría 150 KB
Una pantalla en color de 320×240 guarda dos bytes por píxel, así que un fotograma ocupa 320 × 240 × 2 = 153.600 bytes, unos 150 KB. Eso es casi la mitad de la RAM que un ESP32 da a tu sketch. Así que TFT_eSPI pinta directo en la pantalla, como Adafruit GFX en el episodio 3.

Ves cada paso
Cuando la biblioteca pinta directo en la pantalla, ves cada paso del dibujo. Si el sketch limpia la pantalla al inicio de cada vuelta, la pantalla se pone negra primero y la imagen vuelve pieza a pieza.

En nuestra prueba (ESP32 en el simulador, SPI a 27 MHz), limpiar la pantalla de 320×240 llevó 48 milisegundos y dibujar el tablero otros 68. Casi 50 milisegundos de negro en cada actualización basta para que tu ojo lo note.

Tres reglas contra el parpadeo
- No limpies la pantalla dentro de
loop(). Nada defillScreen()en cada vuelta. - Dale a cada número en vivo un color de fondo. Sin él, los dígitos nuevos se dibujan sobre los viejos y se amontonan en un desastre. Con él, cada dígito se dibuja en una caja del color de fondo que borra lo que había debajo.
- Mantén otras formas fuera de debajo de un número en vivo. Una tarjeta rellena bajo el número se pintaría sobre él en cada vuelta y el número parpadearía. Una tarjeta con contorno no toca los píxeles tras los dígitos.
Lopaka se encarga de las dos primeras.

Paso 1: diseña el monitor de habitación
Un proyecto TFT_eSPI nuevo
Haz clic en + New Project y elige la plataforma TFT_eSPI, M5, Lovyan. TFT_eSPI es una de las bibliotecas de gráficos más comunes para pantallas en color en el ESP32. Pon el Title en «Room Monitor» y el Screen Size en 320×240, mantén el Background Color negro y haz clic en Save.

Dibuja el tablero
Dibuja la pantalla igual que la vez anterior: un título, dos tarjetas con contorno, dos etiquetas, dos números grandes, un símbolo de grado, una barra para la humedad, un pie y una marca roja LIVE. Estos son los valores que usamos:
| Layer | Tool | Values in the inspector | Font | Colour |
|---|---|---|---|---|
Living room | text | X 12, Y 30 | FreeSansBold12pt7b | #FFFFFF |
| temperature card | rectangle | X 12, Y 46, W 142, H 120, R 10 | #FF8200 | |
| humidity card | rectangle | X 166, Y 46, W 142, H 120, R 10 | #4C8DFF | |
Temperature | text | X 24, Y 72 | FreeSans9pt7b | #FF8200 |
Humidity | text | X 178, Y 72 | FreeSans9pt7b | #4C8DFF |
23.5 | text | X 24, Y 140 | FreeSansBold24pt7b | #FFFFFF |
48% | text | X 178, Y 140 | FreeSansBold24pt7b | #FFFFFF |
C | text | X 132, Y 122 | FreeSansBold12pt7b | #FFFFFF |
| degree sign | ellipse | X 124, Y 104, RX 3, RY 3 | #FFFFFF | |
| bar | rectangle, Filled | X 12, Y 182, W 142, H 18 | #4C8DFF | |
| empty part of the bar | rectangle, Filled | X 154, Y 182, W 154, H 18 | #1F2937 | |
Updated every 0.5 s | text | X 12, Y 228 | FreeSans9pt7b | #6B7280 |
| LIVE dot | ellipse, Filled | X 260, Y 16, RX 4, RY 4 | #EF4444 | |
LIVE | text | X 272, Y 27 | FreeSans9pt7b | #EF4444 |
El primer texto en un proyecto TFT_eSPI empieza con la fuente pequeña Adafruit 5x7 y cada texto posterior empieza con la última fuente que elegiste.
Las tarjetas son contornos, siguiendo la regla 3. La barra son dos rectángulos uno al lado del otro, la parte llena y la parte vacía, para que ningún píxel de la barra se pinte dos veces. Si dibujaras toda la barra oscura y la parte azul encima, la parte azul parpadearía en cada vuelta.
La Y de un texto
Mira el número grande de temperatura en el código:
tft.drawString("23.5", 24, 104);El inspector dice Y 140, el código dice 104. Como en U8g2, la Y del inspector es la línea base, la línea sobre la que se apoyan los dígitos. El drawString de TFT_eSPI toma en su lugar la parte superior del texto, así que Lopaka convierte el número por ti. Los colores también se convierten: #FF8200 se convierte en 0xFC00 en RGB565, el formato de color de dos bytes del episodio 3.
Ahora mismo estos números son texto simple. Nunca cambiarán.
Paso 2: las capas se convierten en variables
Nombra la capa
Para hacer que un número esté en vivo, conviértelo en una variable. Primero, nombra la capa: haz doble clic en string 6, el texto 23.5, en el panel Capas y renómbrala a temp.
Haz clic en el botón de variable
Selecciona la capa y haz clic en el botón variable en el extremo derecho de su campo de texto en el inspector (su tooltip dice Add variable). El botón se vuelve naranja y dos cosas cambian en el código:
const char* temp_text = "23.5";// temp
tft.drawString(temp_text, 24, 104);Lopaka declara una variable al inicio del código, con el nombre de la capa y el texto como su valor inicial. La llamada de dibujo ahora usa la variable en lugar del texto fijo. Tu sketch puede poner cualquier texto en temp_text y la próxima llamada a la función de dibujo lo mostrará.

Humedad y la barra
Haz lo mismo para la humedad y dale a la barra tamaños variables:
- Renombra
string 7, el texto48%, ahumy haz clic en el botón de variable junto a su texto:hum_text. - Renombra la barra llena a
hum_bary haz clic en el botón de variable junto a W:hum_bar_w. - Renombra la parte vacía de la barra a
hum_resty haz clic en los botones de variable junto a X y W:hum_rest_xyhum_rest_w.
La barra obtiene un ancho variable y su parte vacía una posición y un ancho variables, para que el sketch siga pintando cada píxel de la barra una vez, sea cual sea la humedad.
int hum_bar_w = 142;
int hum_rest_w = 154;
int hum_rest_x = 154;
const char* hum_text = "48%";
const char* temp_text = "23.5";// hum_bar
tft.fillRect(12, 182, hum_bar_w, 18, 0x4C7F);
// hum_rest
tft.fillRect(hum_rest_x, 182, hum_rest_w, 18, 0x1946);Un texto se convierte en un const char*, un número como un ancho se convierte en un int. El nombre de la capa y la propiedad forman el nombre de la variable: hum_bar + w.

Paso 3: sin parpadeo
Un fondo tras cada número
Selecciona temp y pon su color Back en #000000, negro como la pantalla. Haz lo mismo para hum. El código ahora pone dos colores para el texto, los dígitos y la caja tras ellos:
// temp
tft.setTextColor(0xFFFF, 0x0);
tft.setFreeFont(&FreeSansBold24pt7b);
tft.drawString(temp_text, 24, 104);
// hum
tft.drawString(hum_text, 178, 104);0xFFFF es blanco, 0x0 es negro. El número de humedad usa los mismos colores, así que Lopaka no repite la llamada. Esa es la regla 2.

Desactiva Clear/Fill display
Ahora la regla 1. En la lista de interruptores junto al código, encuentra Clear/Fill display. En un proyecto TFT_eSPI nuevo está activado y la función de dibujo empieza llenando toda la pantalla:
void drawScreen_1(void) {
tft.fillScreen(0x0);Esa sola línea causa el parpadeo: la función se ejecuta en cada vuelta de loop(), así que la pantalla se pone negra en cada vuelta. Desactiva Clear/Fill display y la línea desaparece.

Desactiva Include fonts
Un interruptor más: desactiva Include fonts. TFT_eSPI ya trae estas FreeFonts consigo, así que el sketch no necesita archivos de fuente.
Paso 4: tu propio loop
Escribe alrededor del bloque generado
El resto son unas líneas tuyas. Puedes escribir todo el sketch aquí mismo en el panel de código. Lopaka mantiene tu código alrededor de su bloque generado, entre // [BEGIN lopaka generated] y // [END lopaka generated], y permanece cuando cambias el diseño o recargas la página.
Haz clic sobre la línea BEGIN y escribe los includes y los objetos:
#include <TFT_eSPI.h>
#include <DHTesp.h>
TFT_eSPI tft;
DHTesp dht;
char t[8], h[8];Después, bajo la línea END, añade setup() y loop():
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextPadding(94);
dht.setup(27, DHTesp::DHT22);
}
void loop() {
TempAndHumidity d = dht.getTempAndHumidity();
snprintf(t, sizeof t, "%.1f", d.temperature);
snprintf(h, sizeof h, "%.0f%%", d.humidity);
temp_text = t;
hum_text = h;
hum_bar_w = d.humidity * 2.96;
hum_rest_x = 12 + hum_bar_w;
hum_rest_w = 296 - hum_bar_w;
drawScreen_1();
delay(500);
}
Qué hace setup()
setup() arranca la pantalla, la gira a horizontal con setRotation(1) y la pinta de negro una vez, después arranca el sensor en el pin 27. El único fillScreen aquí está bien: se ejecuta al encender, no en cada vuelta.
setTextPadding(94) le da a cada número en vivo una caja fija de 94 píxeles de ancho. TFT_eSPI llena el fondo del texto solo con el ancho del texto nuevo. Cuando «23.5» cambia a un valor más estrecho como «9.8», el extremo derecho de los dígitos viejos quedaría en la pantalla. 94 píxeles es el ancho de un valor de dos dígitos como «48%» en esta fuente, y la caja se detiene antes del símbolo de grado.
Qué hace loop()
loop() lee temperatura y humedad, las escribe en las variables de texto, convierte la humedad en el ancho de la barra y llama a la función de dibujo. Después espera medio segundo y vuelve:
snprintfconvierte un número en texto:"%.1f"mantiene un decimal, así que 23,46 se convierte en «23.5», y"%.0f%%"da «48%».%%imprime el símbolo de porcentaje.temp_text = t;apunta la variable a ese texto. Los búferestyhse declaran fuera deloop(), así que el texto sigue ahí cuando la función de dibujo lo lee.- La barra mide 296 píxeles al 100%, de x = 12 a 308, así que un uno por ciento son 2,96 píxeles. La parte vacía empieza donde termina la parte llena y ocupa el resto.
delay(500)da dos actualizaciones por segundo.
Paso 5: ejecútalo
En el simulador
Haz clic en Copy sobre el código. Copia todo el panel, tu código incluido. En el simulador del episodio 1:
- Crea un proyecto ESP32 nuevo.
- Añade una pantalla ILI9341 y conéctala: VCC y LED a 3V3, GND a GND, CS a 15, RST a 2, D/C a 0, MOSI a 13, SCK a 14, MISO a 12.
- Añade un sensor DHT22: VCC a 3V3, GND a GND, SDA a 27.
- En el Library Manager, añade TFT_eSPI y DHT sensor library for ESPx (la biblioteca con
DHTesp.h). - Pega el código en
sketch.inoy pulsa Play.
TFT_eSPI lee sus pines de un archivo de ajustes en la carpeta de la biblioteca. El simulador usa los ajustes predeterminados de la biblioteca: un ILI9341 en estos pines de ESP32.
Haz clic en el DHT22 y arrastra sus sliders. Los números y la barra siguen a los sliders sin un solo parpadeo.

El código completo del botón Copy, con el bloque de Lopaka en el centro:
#include <TFT_eSPI.h>
#include <DHTesp.h>
TFT_eSPI tft;
DHTesp dht;
char t[8], h[8];
// [BEGIN lopaka generated]
int hum_bar_w = 142;
int hum_rest_w = 154;
int hum_rest_x = 154;
const char* hum_text = "48%";
const char* temp_text = "23.5";
void drawScreen_1(void) {
// string 1
tft.setTextColor(0xFFFF);
tft.setTextSize(1);
tft.setFreeFont(&FreeSansBold12pt7b);
tft.drawString("Living room", 12, 12);
// rect 2
tft.drawRoundRect(12, 46, 142, 120, 10, 0xFC00);
// rect 3
tft.drawRoundRect(166, 46, 142, 120, 10, 0x4C7F);
// string 4
tft.setTextColor(0xFC00);
tft.setFreeFont(&FreeSans9pt7b);
tft.drawString("Temperature", 24, 58);
// string 5
tft.setTextColor(0x4C7F);
tft.drawString("Humidity", 178, 58);
// temp
tft.setTextColor(0xFFFF, 0x0);
tft.setFreeFont(&FreeSansBold24pt7b);
tft.drawString(temp_text, 24, 104);
// hum
tft.drawString(hum_text, 178, 104);
// string 8
tft.setTextColor(0xFFFF);
tft.setFreeFont(&FreeSansBold12pt7b);
tft.drawString("C", 132, 104);
// ellipse 9
tft.drawEllipse(127, 107, 3, 3, 0xFFFF);
// hum_bar
tft.fillRect(12, 182, hum_bar_w, 18, 0x4C7F);
// hum_rest
tft.fillRect(hum_rest_x, 182, hum_rest_w, 18, 0x1946);
// string 12
tft.setTextColor(0x6B90);
tft.setFreeFont(&FreeSans9pt7b);
tft.drawString("Updated every 0.5 s", 12, 214);
// ellipse 13
tft.fillEllipse(264, 20, 4, 4, 0xEA28);
// string 14
tft.setTextColor(0xEA28);
tft.drawString("LIVE", 272, 13);
}
// [END lopaka generated]
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextPadding(94);
dht.setup(27, DHTesp::DHT22);
}
void loop() {
TempAndHumidity d = dht.getTempAndHumidity();
snprintf(t, sizeof t, "%.1f", d.temperature);
snprintf(h, sizeof h, "%.0f%%", d.humidity);
temp_text = t;
hum_text = h;
hum_bar_w = d.humidity * 2.96;
hum_rest_x = 12 + hum_bar_w;
hum_rest_w = 296 - hum_bar_w;
drawScreen_1();
delay(500);
}Compilado para un ESP32, el sketch usa el 24% del espacio de programa y el 6% de la RAM.
El mismo sketch con Clear/Fill display
La pantalla dividida de abajo ejecuta el mismo sketch dos veces: a la izquierda con Clear/Fill display activado, a la derecha el nuestro. La única diferencia es la línea tft.fillScreen(0x0); al inicio de drawScreen_1(). La pantalla izquierda está capturada a mitad de un redibujo, con solo el primer dígito de vuelta. A la derecha, solo cambian los dígitos.

En una placa real
Instala TFT_eSPI y DHT sensor library for ESPx desde el Library Manager en el IDE de Arduino, cablea la pantalla y el sensor igual y sube el código. Cosas que comprobar en hardware real:
- Ajustes de TFT_eSPI. La biblioteca toma el controlador de pantalla y los pines de
User_Setup.hen su propia carpeta. El predeterminado es un ILI9341 con los pines de arriba. Para otra pantalla u otros pines, cambia ese archivo, no el sketch. - Pines de arranque. En un ESP32 real, GPIO 0 y GPIO 12 también deciden cómo arranca la placa. Si la placa no arranca con la pantalla conectada, mueve D/C y MISO a otros pines en
User_Setup.h. - El sensor. Un DHT22 da una lectura nueva como máximo cada dos segundos. La biblioteca devuelve la última lectura entre medias, así que la pantalla sigue redibujando dos veces por segundo. Si los números muestran
nan, el sensor no respondió: comprueba su cableado.
Resumen
loop()redibuja tu pantalla para siempre: lee, crea texto, dibuja, repite.- Nombra una capa, obtén una variable: el botón de variable convierte un texto o un tamaño en una variable con el nombre de la capa y tu loop la alimenta.
- Sin borrado más un fondo significa sin parpadeo: desactiva Clear/Fill display y dale a cada número en vivo un color Back.
Próximo episodio: From Browser to Board. Sacamos un diseño de la pantalla y lo llevamos a una placa real, y repasamos cinco razones por las que una pantalla nueva se queda en negro. Lee el episodio 5 o míralo en YouTube.
¿Listo para probar? Abre Lopaka y haz que tu propia pantalla cobre vida.