Ir al contenido

Lopaka Basics 05: del navegador a la placa ​

Un módulo OLED SSD1306 cableado por GND, VCC, SCL y SDA, mostrando el tablero matutino con FRI 24 OCT, 07:45 y Sunny 23C

Dibujaste una pantalla, subiste el código y la pantalla se queda en negro. La pantalla rara vez está rota. Suele ser una de cinco cosas, y cuando corriges la correcta, la pantalla se enciende.

En este episodio llevarás el tablero matutino del episodio 2 del navegador a una placa: instalarás la biblioteca en el IDE de Arduino, crearás el sketch alrededor del código de Lopaka y comprobarás cuánta memoria usa. Después recorrerás las cinco razones por las que una pantalla nueva se queda en negro, y la corrección para cada una.

Lo que vas a crear: un sketch de Arduino que muestra las pantallas Morning y Night del episodio 2 en un OLED SSD1306 de 128×64, más una lista de comprobación para una pantalla en negro. Lo que necesitas: un navegador, el IDE de Arduino gratuito y el proyecto Morning Dashboard del episodio 2. Un Arduino UNO y un módulo OLED son opcionales; ejecutamos el sketch en el simulador del episodio 1.

Cómo habla la placa con la pantalla ​

Cuatro cables: energía, reloj y datos ​

Un OLED pequeño como el nuestro necesita cuatro cables. Dos son energía: VCC y GND. Los otros dos son un bus llamado I2C:

  • SCL es el reloj. La placa envía un tic constante en él.
  • SDA lleva los datos, un bit por tic.

El bus I2C entre un Arduino UNO y el OLED: el reloj SCL marca sobre los bits de datos SDA, que deletrean la dirección 0x3C; un sensor en 0x76 y un reloj en 0x68 comparten los mismos dos cables, y solo la pantalla responde a 0x3C

Cada mensaje empieza con una dirección ​

Otras piezas pueden compartir los mismos dos cables: un sensor de temperatura, un reloj en tiempo real y la pantalla pueden colgar todos de SCL y SDA. Así que cada mensaje empieza con una dirección, y solo la pieza con esa dirección responde.

La mayoría de estas pantallas responden a 0x3C. Algunas responden a 0x3D. En muchos módulos una resistencia diminuta en la parte trasera pone la dirección, junto a una etiqueta como «IIC ADDRESS SELECT 0x78 / 0x7A». Esos dos números son las mismas direcciones duplicadas: 0x3C × 2 = 0x78 y 0x3D × 2 = 0x7A. Tenlo presente para la razón 2 de abajo.

La parte trasera del módulo OLED: la etiqueta IIC ADDRESS SELECT con una resistencia en el lado 0x78, y la nota 0x78 = 0x3C × 2

Las pantallas en color más grandes usan SPI ​

Las pantallas en color como el TFT del episodio 4 suelen usar SPI en su lugar: más cables (MOSI, SCK, CS, DC y RST), mucho más rápido, y un cable de selección, CS, en lugar de una dirección.

Un Arduino UNO junto al TFT en color del episodio 4, con los cables SPI MOSI, SCK, CS, DC y RST; más cables, mucho más rápido

Paso 1: copia el código desde Lopaka ​

Abre el Morning Dashboard del episodio 2: un OLED de 128×64 con la biblioteca U8g2 y dos pantallas, Morning y Night.

Lopaka escribe las funciones de dibujo, una por pantalla: drawMorning() y drawNight(). El sketch alrededor de ellas, con la configuración de pantalla y loop(), lo escribes una vez en el IDE de Arduino.

Haz clic en Copy en la pestaña Morning del panel de código, después cambia a la pestaña Night y copia también eso. Copy toma el código de la pestaña abierta. El código generado es el mismo que en el episodio 2.

El Morning Dashboard en Lopaka con las pantallas Morning y Night en el lienzo; el panel de código muestra la pestaña Morning con la función drawMorning, el botón Copy y Declare as PROGMEM activado

Paso 2: instala U8g2 ​

En el IDE de Arduino, abre el Library Manager (el ícono de libros a la izquierda), busca U8g2 y haz clic en INSTALL en la biblioteca U8g2 de oliver. El episodio usó la versión 2.36.19.

Paso 3: elige la placa ​

Haz clic en Select Board arriba, elige Select other board and port, escribe uno y elige Arduino UNO. Sin una placa conectada, el IDE aún puede compilar el sketch; solo necesitas el puerto para subirlo.

Paso 4: crea el sketch ​

Un sketch nuevo ya tiene setup() y loop(). Pega el código de Lopaka arriba y después añade tres líneas: incluye la biblioteca, crea la pantalla y arráncala en setup(). En loop(), limpia el búfer, dibuja y envía el búfer a la pantalla:

cpp
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// Paste the Lopaka code here: drawMorning() and drawNight()

void setup() {
  // put your setup code here, to run once:
  u8g2.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  u8g2.clearBuffer();
  if (millis() / 3000 % 2) drawNight(); else drawMorning();
  u8g2.sendBuffer();
}

setup() se ejecuta una vez, cuando la placa arranca. loop() se ejecuta una y otra vez. La línea if muestra cada pantalla durante tres segundos, como en el episodio 2, y el orden limpiar, dibujar, enviar es el búfer de fotogramas del episodio 1.

El IDE con el sketch terminado: el final de drawNight, después setup con u8g2.begin() y loop con clearBuffer, la línea millis y sendBuffer

La línea del constructor describe tu pantalla ​

La línea larga bajo el #include es el constructor. Describe tu pantalla y cada parte de su nombre significa algo:

  • SSD1306: el chip controlador en el módulo.
  • 128X64: el tamaño en píxeles.
  • NONAME: un módulo genérico, no una marca concreta.
  • F: el modo de búfer. F es un búfer de fotogramas completo, 1.024 bytes de RAM. 1 dibuja la pantalla por páginas y necesita solo 128 bytes.
  • HW_I2C: el cableado, I2C por hardware en los pines SDA y SCL de la placa.

U8G2_R0 significa sin rotación y U8X8_PIN_NONE dice que no hay un pin de reinicio cableado. La biblioteca U8g2 tiene un constructor para cada combinación de chip, tamaño, búfer y cableado. Elige el equivocado y la pantalla se queda en negro.

El constructor U8G2_SSD1306_128X64_NONAME_F_HW_I2C dividido en tarjetas: chip controlador, tamaño, F para un búfer completo de 1 KB e I2C por hardware; una línea es tu pantalla, y una línea equivocada significa una pantalla en negro

Paso 5: verifica y ejecuta ​

Verifica ​

Haz clic en Verify, la marca de verificación arriba a la izquierda. El IDE compila el sketch e informa:

Sketch uses 15394 bytes (47%) of program storage space. Maximum is 32256 bytes.
Global variables use 1600 bytes (78%) of dynamic memory, leaving 448 bytes for local variables. Maximum is 2048 bytes.

El sketch ocupa menos de la mitad de la flash y más de tres cuartos de la RAM. Solo el búfer de fotogramas son 1.024 de los 2.048 bytes del UNO; todo lo demás ocupa 576. Vigila ese 78%: vuelve en la razón 5.

La salida de Verify: 15394 bytes (47%) de flash y 1600 bytes (78%) de memoria dinámica, y una barra de los 2.048 bytes de RAM del UNO con el búfer de fotogramas de 1.024 bytes ocupando la mitad

Sube el código, o ejecútalo en el simulador ​

Con una placa conectada, elige su puerto en el mismo diálogo Select Board y haz clic en Upload, la flecha junto a Verify.

¿Sin placa? Abre el proyecto de simulador del episodio 1: un Arduino UNO con una pantalla SSD1306 cableada GND a GND, VCC a 5V, SDA a A4 y SCL a A5, y la biblioteca U8g2. Pega el sketch y pulsa Play. La pantalla muestra la pantalla Morning y cambia a Night cada tres segundos.

El simulador ejecutando el sketch: un Arduino UNO cableado al OLED con SDA a A4 y SCL a A5, y la pantalla mostrando 07:45 y Sunny 23C

Cinco razones por las que una pantalla se queda en negro ​

Las comprobaciones van de fuera hacia dentro: cables, dirección, chip, código y memoria. En cada una rompemos la configuración que funciona en el simulador, vemos la pantalla ponerse negra y la arreglamos.

1. Los cables ​

En un UNO, SDA va a A4 y SCL va a A5. Intercámbialos y la placa está hablando con nadie: la pantalla se queda en negro.

Comprueba también energía y tierra. Otras placas usan otros pines: en la mayoría de placas ESP32, SDA es el pin 21 y SCL es el pin 22.

Razón 1, cables: los cables SDA y SCL intercambiados en A4 y A5, etiquetados como intercambiados, hablando con nadie, y el OLED se queda en negro

2. La dirección ​

La mayoría de los módulos responden a 0x3C, algunos a 0x3D. U8g2 habla a 0x3C a menos que le digas otra cosa. Si el módulo está en 0x3D, nada responde y la pantalla se queda en negro.

Para averiguar cuál tienes, ejecuta un sketch escáner I2C. Pregunta a cada dirección del bus e imprime las que responden:

cpp
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("Scanning I2C...");
  for (byte a = 1; a < 127; a++) {
    Wire.beginTransmission(a);
    if (Wire.endTransmission() == 0) {
      Serial.print("Found a device at 0x");
      Serial.println(a, HEX);
    }
  }
  Serial.println("Done.");
}

void loop() {}

Súbelo y abre el Serial Monitor a 9600 baudios.

Razón 2, dirección: el sketch escáner I2C ejecutándose, con Found a device at 0x3D en el Serial Monitor

Después pon la dirección en tu sketch de tablero, antes de begin():

cpp
void setup() {
  u8g2.setI2CAddress(0x3D * 2);
  u8g2.begin();
}

U8g2 quiere la dirección duplicada, así que 0x3D se convierte en 0x3D * 2, 0x7A: el mismo número impreso en la parte trasera del módulo.

La corrección en setup: u8g2.setI2CAddress(0x3D * 2) antes de u8g2.begin(), U8g2 lo quiere duplicado: 0x7A, y el OLED muestra el tablero otra vez

3. El chip ​

Dos módulos pueden parecer exactamente iguales y usar controladores distintos. La mayoría de las pantallas pequeñas de 0,96 pulgadas usan el SSD1306. Muchas de 1,3 pulgadas usan el SH1106. Ejecuta código SSD1306 en un SH1106 y obtienes una pantalla en blanco o una franja de ruido en el borde.

La corrección es el nombre del chip en el constructor:

cpp
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

Razón 3, chip: un módulo SSD1306 de 0,96 pulgadas con una imagen limpia junto a un módulo SH1106 de 1,3 pulgadas ejecutando código SSD1306, con una franja de ruido en su borde derecho

4. El código ​

U8g2 dibuja en un búfer en memoria y solo sendBuffer() mueve la imagen a la pantalla. Olvida esa línea, u olvida u8g2.begin() en setup(), y la pantalla se queda oscura. El sketch sigue compilando sin error, así que comprueba primero estas líneas:

cpp
u8g2.begin();          // in setup()
u8g2.clearBuffer();    // in loop()
drawMorning();
u8g2.sendBuffer();

Razón 4, código: el fotograma se dibuja en el búfer de fotogramas de RAM, pero con begin y sendBuffer tachados nunca llega a la pantalla, que se queda en negro; Done compiling, sin error

5. La memoria ​

¿Recuerdas ese 78%? Un UNO tiene 2.048 bytes de RAM y el búfer de fotogramas ocupa la mitad.

Lopaka mantiene las imágenes en flash con PROGMEM: el interruptor Declare as PROGMEM junto al código está activado por defecto y los arrays reciben U8X8_PROGMEM. Desactívalo y las imágenes se mueven a la RAM. Solo la luna son 48 × 48 / 8 = 288 bytes y las cuatro imágenes del tablero ocupan 416. Ahora el sketch usa 2.016 bytes, el 98% de la RAM, con 32 bytes libres para todo lo que el programa hace mientras se ejecuta. Sigue compilando, pero la pantalla se deshace: en el simulador, el texto desaparece y las imágenes se rompen.

Razón 5, memoria: la RAM del UNO al 98%, búfer de fotogramas 1.024, todo lo demás 576 e imágenes 416, para 2.016 bytes; la salida de Verify informa 2016 bytes (98%) y el OLED muestra un tablero roto

Deja PROGMEM activado. Si aún te falta RAM, cambia a modo por páginas: pon un 1 en lugar de la F en el constructor y el búfer se reduce de 1.024 bytes a 128.

cpp
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

En modo por páginas la biblioteca dibuja la pantalla en ocho tiras, una tras otra, así que loop() también cambia. En lugar de clearBuffer() y sendBuffer(), el dibujo va dentro de un bucle firstPage() / nextPage():

cpp
void loop() {
  u8g2.firstPage();
  do {
    if (millis() / 3000 % 2) drawNight(); else drawMorning();
  } while (u8g2.nextPage());
}

Con el modo por páginas, el mismo tablero usa 704 bytes, el 34% de la RAM.

Modo por páginas: la RAM del UNO al 34%; los constructores U8G2_SSD1306_128X64_NONAME_F_HW_I2C para un búfer completo y U8G2_SSD1306_128X64_NONAME_1_HW_I2C para modo por páginas, búfer de 1.024 a 128 bytes

Resumen ​

  1. Lopaka dibuja la pantalla, el IDE crea el sketch alrededor: instala U8g2, elige la placa, pega las funciones de dibujo y añade el include, el constructor, begin() y el bucle limpiar, dibujar, enviar.
  2. Una línea de constructor describe tu pantalla: chip, tamaño, búfer y cableado.
  3. ¿Pantalla en negro? Comprueba cinco cosas: los cables, la dirección, el chip, el código y la memoria.

Próximo episodio: Screens & Menus. Más de una pantalla, con menús y un botón. Lee el episodio 6 o míralo en YouTube.

¿Listo para probar? Abre Lopaka y pon tu pantalla en una placa real.