Skip to Content

Redibujo Paginado de Pantalla de Papel Electrónico

Redibujo Paginado de Pantalla de Papel Electrónico

En este tutorial aprenderás cómo realizar una actualización por páginas de una pantalla E-Paper con un Arduino y por qué suele ser necesaria cuando se usan microcontroladores con poca memoria.

En comparación con ordenadores personales o teléfonos móviles, los microcontroladores como el Arduino tienen una memoria mucho más limitada. Esta memoria restringida puede dificultar el manejo de imágenes grandes o gráficos complejos. Las pantallas E-Paper suelen requerir una cantidad significativa de memoria para almacenar todo el contenido de la pantalla. Cuando quieres actualizar la pantalla, puede que no tengas suficiente RAM para contener la imagen completa.

La actualización por páginas ayuda a resolver este problema. En lugar de cargar todo el contenido de la pantalla en memoria de una vez, divides la pantalla en secciones más pequeñas o «páginas». Luego puedes cargar y actualizar una página a la vez. Este método reduce el uso de memoria y permite actualizaciones más fluidas.

En las siguientes secciones aprenderás cómo realizar una actualización por páginas para una pantalla E-Paper usando un Arduino Uno.

Partes necesarias

Necesitarás una pantalla E-Paper. Para este tutorial he elegido una pantalla monocroma de 2.9 pulgadas con una resolución de 296×128 píxeles. Sin embargo, también podrías usar una pantalla E-Paper de otro tamaño.

En cuanto al microcontrolador, cualquier Arduino o ESP32/ESP8266 funcionará, pero para apreciar realmente la necesidad de las actualizaciones por páginas, te sugiero usar un Arduino Uno. Su memoria limitada hace que las actualizaciones por páginas sean esenciales, incluso para pantallas E-Paper pequeñas.

Pantalla E-Paper de 2.9″

Arduino

Arduino Uno

USB Data Sync cable Arduino

Cable USB para Arduino UNO

Dupont wire set

Juego de cables Dupont

Half_breadboard56a

Protoboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Qué es una actualización por páginas

La actualización por páginas es una técnica usada en programación gráfica, especialmente en entornos con poca memoria como los microcontroladores. Permite actualizar una pantalla dibujando el contenido en «páginas» o secciones separadas.

Veamos un ejemplo concreto. La pantalla E-Paper de 2.9″ que usaremos en este tutorial tiene una resolución de 128×296 píxeles. Suponiendo que cada píxel es de 1 bit para una pantalla monocroma (blanco y negro), esto significa que necesitamos (128×296)/8 = 4736 bytes para almacenar toda la matriz de píxeles (imagen, fotograma) en RAM.

Normalmente, la imagen a mostrar se escribe primero en un búfer de pantalla dentro de la RAM del microcontrolador y luego se envía al controlador de la pantalla E-Paper para actualizar el contenido mostrado. Sin embargo, un Arduino Uno solo tiene 2KB de SRAM = 2048 bytes, lo que significa que no podríamos preparar una imagen completa en RAM. En otras palabras, en un Arduino Uno no podemos crear un búfer de pantalla lo suficientemente grande para contener una imagen de 128×296.

Actualización por páginas

Aquí es donde el Actualización por páginas entra en juego. En lugar de preparar y enviar toda la imagen de una vez, dividimos la imagen en secciones más pequeñas llamadas «páginas» que caben en la RAM del microcontrolador. Luego procesamos una página tras otra, las enviamos al controlador E-Paper, que ensambla la imagen y refresca la pantalla cuando la imagen está completa. Mira la siguiente imagen para ilustrarlo:

Paged Redraw of an Image
Actualización por páginas de una imagen

Si usas la GxEPD2 biblioteca, puedes ver este proceso reflejado en el código. Para una actualización por páginas, primero llamas a firstPage() para iniciar la actualización. Luego llamas repetidamente a nextPage() en un bucle hasta que todas las páginas se transfieren a la pantalla.

  epd.firstPage();
  do {
    ...  // Graphics code
  } while (epd.nextPage());

Dentro del bucle, defines las operaciones gráficas que quieres realizar para crear el contenido/imagen a mostrar. La actualización por páginas es obviamente ineficiente, ya que tienes que crear el mismo contenido varias veces, pero las pantallas E-Paper son lentas para actualizarse, incluso para una actualización parcial (< 0.3 segundos), por lo que esto realmente no importa.

Altura del búfer

Aunque una página podría ser una sección rectangular arbitraria, la GxEPD2 biblioteca usa páginas en forma de franjas. La longitud de la franja es igual al ancho de la pantalla y la altura depende de la memoria disponible. En el caso de una pantalla monocroma, puedes calcular la altura h de una franja o del búfer de pantalla como sigue,

h = buff / (width / 8)

donde buff es el tamaño en bytes del búfer de pantalla que puedes permitirte y width es el ancho de la pantalla.

Para pantallas E-Paper con 4 niveles de gris, o 3 o 4 colores necesitamos 2 bits por píxel y la fórmula se convierte en:

h = (buff / 2) / (width / 8)

Un Arduino tiene 2048 bytes de RAM, así que la altura máxima h para la pantalla en modo retrato sería

h = 2048 / (128 / 8) = 128 píxeles

Sin embargo, el búfer de pantalla y el código comparten la misma memoria y no podemos usar los 2048 bytes completos. Si usas la mitad de la memoria para el búfer de pantalla, obtenemos

h = 1024 / (128 / 8) = 64 píxeles,

lo que significa que con una altura de pantalla de 296 píxeles, tendríamos 296 / 64 = 5 páginas.

Dependiendo del tamaño del código y la RAM disponible de tu microcontrolador, tendrás que ajustar el tamaño del búfer. Si obtienes el siguiente mensaje de error al compilar el código, sabes que el tamaño de tu búfer es demasiado grande:

Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint. 
data section exceeds available space in board.
Compilation error: data section exceeds available space in board

En las siguientes secciones conectamos la pantalla E-Paper al Arduino y escribimos algo de código para demostrar cómo se implementa una actualización por páginas.

Instalación de la biblioteca GxEPD2 para E-Paper

Antes de poder dibujar o escribir en la pantalla E-Paper necesitamos instalar dos bibliotecas. La Adafruit_GFX biblioteca es una biblioteca gráfica básica que proporciona un conjunto común de primitivas gráficas (texto, puntos, líneas, círculos, etc.). Y la GxEPD2 biblioteca proporciona el software controlador gráfico para controlar una pantalla E-Paper vía SPI.

Abre el Gestor de Bibliotecas, busca «Adafruit_GFX» y «GxEPD2» y pulsa «INSTALL». Tras la instalación, las bibliotecas deberían aparecer en el Gestor de Bibliotecas como sigue.

Adafruit_GFX and GxEPD2 libraries in Library Manager
Bibliotecas Adafruit_GFX y GxEPD2 en el Gestor de Bibliotecas

Conexión de la pantalla E-Paper al Arduino

El diagrama de conexiones a continuación muestra cómo conectar la interfaz SPI de la pantalla E-Paper a un Arduino. La mayoría de los pines pueden configurarse libremente, pero para las líneas DIN y CLK necesitamos usar pines específicos de SPI. En el caso de un Arduino Uno, DIN está en el pin 11 y CLK en el pin 13.

Connecting E-Paper to Arduino Uno via SPI
Conexión de E-Paper a Arduino Uno vía SPI

La siguiente tabla lista todas las demás conexiones que debes hacer.

Pantalla E-PaperArduino UNO
CS/SS4
SCL/SCK/CLK13
SDA/DIN/MOSI11
BUSY7
RES/RST6
DC5
VCC3.3V
GNDG

Actualización por páginas para E-Paper con Arduino Uno

Vamos a dibujar la siguiente imagen de prueba en la pantalla E-Paper:

Test Picture
Imagen de prueba

A continuación encuentras el código correspondiente. Échale un vistazo rápido primero, y luego discutiremos los detalles.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

  epd.hibernate();
}

void loop() {}

Comenzamos incluyendo la biblioteca gráfica requerida y definiendo algunas constantes para los pines SPI. Recuerda que los pines DIN y CLK son fijos y específicos para tu microcontrolador, mientras que los otros pueden cambiarse.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

Luego definimos el tamaño del búfer de pantalla BUFF y la altura de la página HIGHT. La altura se calcula mediante una macro que toma el objeto de pantalla EPD como parámetro y usa su propiedad de ancho (EPD::WIDTH) para determinar la altura de la página.

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

El cálculo sigue la fórmula que discutimos antes. Para una pantalla de tres colores usarías la siguiente fórmula en su lugar:

#define HEIGHT(EPD) ((BUFF / 2) / (EPD::WIDTH / 8))

Ahora podemos usar la macro HIGHT para crear el objeto de pantalla epd. Esto parece complejo, pero simplemente toma las constantes de pines, la altura y un tipo de pantalla como parámetros plantilla para crear la pantalla.

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

Si tu pantalla no muestra nada o muestra texto/imágenes corruptas, entonces o la pantalla no está correctamente cableada o se ha elegido un tipo de pantalla incorrecto.

La Readme for GxEPD2 biblioteca lista todas las pantallas soportadas y puedes encontrar los detalles en los archivos de cabecera, por ejemplo GxEPD2.h. Busca el controlador específico para tu pantalla. Y quizá echa un vistazo al Interfacing Arduino with E-ink Display tutorial, donde conectamos una pantalla E-Paper de tres colores a un Arduino y un ESP32.

Función setup

En la función setup, primero configuramos algunos parámetros gráficos como la orientación (retrato), el tamaño del texto, el color del texto y el modo de refresco (ventana completa).

  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

También existe un «refresco parcial», pero no lo usaremos aquí. Si quieres saber más sobre esto, echa un vistazo al tutorial Partial Refresh of e-Paper Display.

Finalmente, llegamos a la parte donde se realiza la actualización por páginas. Comenzamos llamando a firstPage() para iniciar la actualización. Luego dibujamos repetidamente el rectángulo, triángulo y texto en un do-while loop hasta que nextPage() devuelve falso.

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

Este bucle esencialmente llena el búfer de pantalla con el contenido de una página, envía esa página a la pantalla E-Paper, donde el controlador une las páginas hasta que la imagen está completa.

No verás la pantalla actualizándose página por página. En cambio, el controlador de la pantalla espera hasta que todas las páginas han sido recibidas y luego realiza una actualización completa de la pantalla. Mira el breve vídeo a continuación:

Full refresh with Paged Redraw
Refresco completo con actualización por páginas

Por cierto, si quieres saber la altura real de página que está usando la pantalla, puedes llamar a la siguiente función en la función setup para averiguarlo:

  Serial.println(epd.pageHeight());

Función loop

La función loop en este ejemplo está vacía, ya que no hay actualización periódica de contenido.

void loop() {}

Pero digamos que quieres implementar un Digital Clock on e-Paper Display, la función loop contendría el código para la actualización por páginas en lugar de la función setup.

Función de dibujo

Si tienes aplicaciones más complejas, es mejor usar una función de dibujo en lugar de la do-while loop para la actualización por páginas. La biblioteca GxEPD2 te permite definir una función de dibujo y luego llamar a epd.drawPaged(...) para dibujo por páginas. Aquí tienes el esquema del código:

void draw(const void* pv) {
  ... // Graphics code
}

void setup() {
  ...
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

La función de dibujo draw() toma un puntero pv, que te permite pasar parámetros cuando llamas a epd.drawPaged(draw, 0). Pero puedes simplemente pasar 0 como puntero de parámetro si no necesitas eso.

El uso de funciones de dibujo para actualizaciones por páginas hace el código más legible y extensible. Aquí está nuestro código de prueba de nuevo, pero ahora usando una función de dibujo en lugar de la do-while loop:

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// SCL(SCK)=13,
// SDA(MOSI)=11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
  epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));


void draw(const void* pv) {
  epd.fillScreen(GxEPD_WHITE);
  epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
  epd.fillTriangle(100, 120, 180, 120, 140, 60, GxEPD_BLACK);
  epd.setCursor(200, 60); epd.print("TEST");
}

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

¡Y eso es todo! Ahora sabes qué es una actualización por páginas y cómo implementarla.

Conclusiones

En este tutorial aprendiste cómo realizar una actualización por páginas de una pantalla E-Paper monocroma con un Arduino Uno. Si tienes una pantalla E-Paper tricolor en lugar de una monocroma o un ESP32, echa un vistazo al Interfacing Arduino with E-ink Display.

Si quieres aprender más sobre refresco parcial vs completo, te sugiero nuestro tutorial Partial Refresh of e-Paper Display.

Además, una combinación de actualización por páginas, y refresco parcial y completo de pantallas E-Paper de diferentes tamaños se usa en los tutoriales Temperature Plotter on e-Paper Display, Digital Clock on e-Paper Display y Analog Clock on e-Paper Display. Si necesitas ejemplos de aplicaciones, los encontrarás allí.

Si tienes cualquier otra pregunta, no dudes en preguntar en la sección de comentarios.

¡Feliz bricolaje ; )