Skip to main content

XIAO ePaper Display Board EE04 con PlatfromIO

Introducción a PlatformIO

PlatformIO se presenta como un ecosistema de desarrollo potente y altamente extensible diseñado para sistemas embebidos. Integra sin problemas el soporte para una amplia gama de placas de desarrollo y microcontroladores, ofreciendo una flexibilidad sin igual. Lo que distingue a PlatformIO es su notable escalabilidad: incluso si tu placa específica no está soportada nativamente, su arquitectura permite definiciones de placas personalizadas de manera sencilla.

Crucialmente, PlatformIO cierra la brecha para los desarrolladores familiarizados con Arduino, permitiendo la compilación e implementación de código estilo Arduino simplemente incluyendo las librerías relevantes.

Preparación del Hardware

Necesitas preparar una XIAO ePaper Display Board EE04 junto con pantallas del tamaño soportado.

XIAO ePaper Display Board EE04

La XIAO ePaper Display Board EE04 soporta pantallas de diferentes tamaños y números de pines. La pantalla de tinta de 7.5 pulgadas de 800×480 con 24 pines utilizada en este tutorial es un ejemplo.

Enlace: Enlace de pantallas soportadas

Descargar Vscode

Descarga según el sistema que estés usandoVscode

Instalar PlatformIO

Abre VSCode, haz clic en Extensions, luego busca PlatformIO y selecciona instalar. Después de que se complete la instalación, reinicia VSCode.

Nuevo Proyecto

  • Abre la interfaz PIO Home y selecciona New Project
  • Name: Nombra tu proyecto
  • Board: Selecciona Seeed Studio XIAO ESP32S3
  • Framework: Selecciona Ardunio
  • Location: La ruta de los archivos del proyecto puede establecerse como una ruta personalizada o se puede seleccionar la ruta predeterminada.
  • Haz clic en "Finish" y espera a que se complete la creación. Luego, abre el archivo del proyecto en el espacio de trabajo.

Agregar la librería Seeed GFX

tip

Esta librería tiene la misma función que la librería TFT y no es compatible con ella. Si has instalado la librería TFT u otras librerías de pantalla similares, por favor desinstálalas primero.

Usaremos la librería Seeed_GFX, que proporciona soporte integral para varios dispositivos de pantalla de Seeed Studio.

Paso 1. Descarga la librería Seeed_GFX desde GitHub:


Paso 2. Mueve los archivos extraídos al directorio lib de los archivos del proyecto.

Paso 3. Agregar archivo driver.h

Herramienta de Configuración Seeed GFX

  • En la página de la herramienta, selecciona la especificación de pantalla que estás usando. Aquí, la opción seleccionada es una pantalla de papel electrónico monocromática de 7.5 pulgadas.
  • Selecciona la XIAO ePaper Display Board EE04 para la placa controladora, y luego se generará el código del controlador correspondiente.
#define BOARD_SCREEN_COMBO 502 // 7.5 inch monochrome ePaper Screen (UC8179)
#define USE_XIAO_EPAPER_DISPLAY_BOARD_EE04
tip

Si haces la elección incorrecta, la pantalla no mostrará nada. Así que por favor asegúrate del tipo de tus dispositivos o componentes.

  • Crea una nueva carpeta driver bajo el directorio lib del archivo del proyecto PlatfromIO, luego agrega el archivo driver.h. Copia el código del archivo de encabezado generado y presiona Ctrl + S para guardar.

Cargar

Copia el código a main.cpp, haz clic en Build, y después de completarse, carga.

#include "TFT_eSPI.h"

#ifdef EPAPER_ENABLE // Only compile this code if the EPAPER_ENABLE is defined in User_Setup.h
EPaper epaper;
#endif

void setup()
{
#ifdef EPAPER_ENABLE
epaper.begin();
epaper.fillScreen(TFT_WHITE);

epaper.fillCircle(25, 25, 15, TFT_BLACK);
epaper.fillRect(epaper.width() - 40, 10, 30, 30, TFT_BLACK);

for (int i = 0; i < epaper.height() / 80; i++)
{
epaper.setTextSize(i + 1);
epaper.drawLine(10, 70 + 60 * i, epaper.width() - 10, 70 + 60 * i, TFT_BLACK);
epaper.drawString("Hello EE04", 10, 80 + 60 * i);
}

epaper.update(); // update the display

#endif
}

void loop()
{
// put your main code here, to run repeatedly:
}
  • Demostración del efecto

Botón de usuario

La EE04 cuenta con tres botones programables por el usuario que pueden utilizarse para varios propósitos de control. Esta sección demuestra cómo leer los estados de los botones y responder a las pulsaciones de botones usando Arduino.

En la EE04, los tres botones están conectados al ESP32-S3:

  • KEY1 (GPIO2_D1/A1)
  • KEY2 (GPIO3_D2/A2)
  • KEY3 (GPIO5_D4/A4)

Todos los botones son activos en bajo, lo que significa que leen LOW cuando se presionan y HIGH cuando se liberan.

Ejemplo Básico de Lectura de Botones

Este ejemplo demuestra cómo detectar pulsaciones de botones e imprimir mensajes en el monitor serie.


#include <Arduino.h>
// reTerminal E Series - Button Test
// Based on hardware schematic

// Define button pins according to schematic
const int BUTTON_KEY0 = 2; // KEY0 - GPIO2
const int BUTTON_KEY1 = 3; // KEY1 - GPIO3
const int BUTTON_KEY2 = 5; // KEY2 - GPIO5

// Button state variables
bool lastKey0State = HIGH;
bool lastKey1State = HIGH;
bool lastKey2State = HIGH;

void setup() {
// Initialize serial communication
Serial.begin(115200);
while (!Serial) {
delay(10); // Wait for serial port to connect
}

Serial.println("=================================");
Serial.println("Press any button to see output");
Serial.println();

// Configure button pins as inputs
// Hardware already has pull-up resistors, so use INPUT mode
pinMode(BUTTON_KEY0, INPUT_PULLUP);
pinMode(BUTTON_KEY1, INPUT_PULLUP);
pinMode(BUTTON_KEY2, INPUT_PULLUP);

// Read initial states
lastKey0State = digitalRead(BUTTON_KEY0);
lastKey1State = digitalRead(BUTTON_KEY1);
lastKey2State = digitalRead(BUTTON_KEY2);

Serial.println("Setup complete. Ready to detect button presses...");
}

void loop() {
// Read current button states
bool key0State = digitalRead(BUTTON_KEY0);
bool key1State = digitalRead(BUTTON_KEY1);
bool key2State = digitalRead(BUTTON_KEY2);

// Check KEY1
if (key0State != lastKey0State) {
if (key0State == LOW) {
Serial.println("KEY0 (GPIO2) pressed!");
} else {
Serial.println("KEY0 (GPIO2) released!");
}
lastKey0State = key0State;
delay(50); // Debounce delay
}

// Check KEY2
if (key1State != lastKey1State) {
if (key1State == LOW) {
Serial.println("KEY1 (GPIO3) pressed!");
} else {
Serial.println("KEY1 (GPIO3) released!");
}
lastKey1State = key1State;
delay(50); // Debounce delay
}

// Check KEY3
if (key2State != lastKey2State) {
if (key2State == LOW) {
Serial.println("KEY2 (GPIO5) pressed!");
} else {
Serial.println("KEY2 (GPIO5) released!");
}
lastKey2State = key2State;
delay(50); // Debounce delay
}

delay(10); // Small delay to prevent excessive CPU usage
}

Explicación del código:

  • Análisis de funciones principales
  1. pinMode(pin, mode)

    • Función: Configura el modo del pin.
    • El modo INPUT_PULLUP se utiliza aquí para habilitar la resistencia pull-up interna. Esto hace que el pin produzca un nivel alto (HIGH) por defecto cuando el botón no está presionado, y produzca un nivel bajo (LOW) cuando el botón está presionado (ya que se conecta a tierra).
  2. digitalRead(pin)

    • Función: Lee el estado del nivel (HIGH o LOW) del pin especificado.
    • Se utiliza en el bucle para obtener el estado actual del botón en tiempo real, lo que ayuda a determinar si el botón ha sido activado.
  3. Serial.begin(baud) y Serial.println()

    • El primero inicializa la comunicación serie (con una velocidad de baudios de 115200), mientras que el segundo envía información de texto al puerto serie. Esto se utiliza para mostrar el estado del botón en el monitor.
  4. delay(ms)

    • Función: Pausa el programa durante un número específico de milisegundos.
    • Se utiliza en dos escenarios aquí: primero, para esperar la conexión del puerto serie en setup(); segundo, para retrasar 50ms después de que cambie el estado del botón. Esto logra el anti-rebote por hardware "esperando a que desaparezca la vibración", evitando activaciones falsas.
  • Análisis de lógica principal
  1. Detección por comparación de estados

    • Registra el "estado anterior" de cada botón (como lastKey0State), y lee el "estado actual" en el bucle.
    • Si el "estado actual ≠ estado anterior", indica que el botón ha sido activado (presionado o liberado).
  2. Juicio de acción del botón

    • Cuando el estado cambia de HIGH a LOW: se determina como "presionado" (muestra "pressed").
    • Cuando el estado cambia de LOW a HIGH: se determina como "liberado" (muestra "released").
    • Después de cada cambio de estado, actualiza el "estado anterior" al estado actual, que sirve como referencia para la siguiente comparación.
  3. Ejecución del bucle

    • La función loop() se ejecuta en un bucle infinito, ejecutando repetidamente el proceso de "leer el estado → comparar el estado → mostrar el resultado" para lograr detección en tiempo real.
  • Demostración del efecto:

El Monitor Serie puede mostrar el estado del puerto serie.

Voltaje de la batería

  • La placa de pantalla ePaper XIAO EE04 está alimentada por una batería de litio de 3.7V - 4.2V. Además, hay una interfaz ADC para que puedas medir el voltaje y monitorear el voltaje de la batería en tiempo real.
  • El pin de medición ADC es A0 (GPIO1), y el pin de habilitación ADC es D5 (GPIO_6).

Conectar:


El programa para monitorear el voltaje de la batería. Es solo para referencia.

#include <Arduino.h>

#define BATTERY_ADC A0 // Battery voltage ADC pin
#define ADC_EN 6 // ADC enable pin
#define VOLTAGE_DIVIDER_RATIO 2.0 // Voltage divider ratio (adjust based on your resistor values) ((R1+R2)/R2)

// Function to read battery voltage
static float readBatteryVoltage() {
int sum = 0;
// Read multiple samples for better accuracy
for (int i = 0; i < 10; i++) {
sum += analogRead(BATTERY_ADC);
delay(2);
}
int adcValue = sum / 10;

// Calculate actual battery voltage
// Formula: voltage = (ADC_value / 4095) * 3.3V * divider_ratio
float voltage = (adcValue / 4095.0) * 3.3 * VOLTAGE_DIVIDER_RATIO;

return voltage;
}

void setup() {
Serial.begin(115200);
delay(1000);

analogReadResolution(12); // Set ADC resolution to 12 bits
pinMode(BATTERY_ADC, INPUT);

pinMode(ADC_EN, OUTPUT);
digitalWrite(ADC_EN, HIGH); // Enable ADC
}

void loop() {
float batteryVoltage = readBatteryVoltage();

Serial.print("Battery Voltage: ");
Serial.print(batteryVoltage, 2);
Serial.println(" V");

delay(500); // Read every 0.5 seconds
}

Explicación del código:

  • Funciones principales:

    • Adquisición de voltaje de batería: Leer el voltaje de batería dividido por resistencias a través del pin ADC (el voltaje de la batería puede exceder el voltaje de referencia ADC de Arduino, requiriendo división primero).
    • Optimización de precisión: Reducir la interferencia de ruido del circuito promediando múltiples muestras.
    • Conversión de voltaje: Convertir la señal digital del ADC al voltaje real de la batería (considerando la relación de división de voltaje y el voltaje de referencia).
    • Salida serie: Imprimir periódicamente el voltaje medido a través del puerto serie para que dispositivos externos (ej., computadora) puedan verlo.
  • Funciones principales y roles

    • setup() (Función de inicialización)

      • Rol: Se ejecuta una vez al inicio del programa para configurar hardware y parámetros.
      • Operaciones clave:
        • Serial.begin(115200): Inicializar comunicación serie (velocidad de baudios 115200) para salida de datos de voltaje.
        • analogReadResolution(12): Establecer resolución ADC a 12-bit (rango de lectura: 0~4095) para mayor precisión.
        • pinMode(BATTERY_ADC, INPUT): Establecer pin de detección de batería (A0) en modo entrada para señales analógicas.
        • pinMode(ADC_EN, OUTPUT) & digitalWrite(ADC_EN, HIGH): Habilitar módulo ADC (para uso de bajo consumo: encender solo al medir).
    • loop() (Función de bucle principal)

      • Rol: Se ejecuta repetidamente después de la inicialización para detección periódica de voltaje y salida.
      • Operaciones clave:
        • Llamar readBatteryVoltage() para obtener el voltaje actual de la batería.
        • Usar Serial.print()/Serial.println() para imprimir voltaje formateado (2 decimales, ej., "Battery Voltage: 3.82 V").
        • delay(500): Establecer intervalo de 0.5 segundos entre mediciones.
    • readBatteryVoltage() (Función de medición principal)

      • Rol: Leer señales ADC, optimizar resultados y convertir a voltaje real.
      • Operaciones clave:
        • Muestreo promedio: Leer ADC 10 veces, sumar, luego promediar (reducir ruido).
        • analogRead(BATTERY_ADC): Leer voltaje analógico del pin A0 (devuelve 0~4095).
        • delay(2): Intervalo de 2ms entre muestras para estabilidad.
        • Cálculo de voltaje: Usar fórmula (adcValue / 4095.0) * 3.3 * VOLTAGE_DIVIDER_RATIO para obtener voltaje real de batería.
        • Devolver el voltaje calculado (tipo float) para que loop() lo use.
  • Demostración del efecto:

Demo de panel de control

EE04 te permite realizar varios diseños creativos, como paneles de control y visualización de imágenes. Al combinarse con botones, permite cambiar entre múltiples páginas. Aquí hay un ejemplo de un panel de control.

tip

En esta rutina, la operación de dibujar el panel de control se lleva a cabo basándose en la biblioteca LVGL.

Documentación oficial de LVGL: LVGL docs

Hardware

Necesitas preparar una placa de pantalla ePaper XIAO EE04 y una pantalla de 7.5 pulgadas con una resolución de 800*480.

Placa de pantalla ePaper XIAO EE04

Enlace: Enlace de pantalla

Software

  • Agregar la biblioteca LVGL. En la herramienta Library de la interfaz PIO Home, busca LVGL, y luego selecciona agregar la biblioteca al proyecto actual. Se recomienda que elijas una versión de LVGL 9.0 o superior.
  • En el directorio lib
    • Crear una carpeta dashboard luego crear los archivos dashboard_ui.cpp y dashboard_ui.h. Estos archivos se utilizan principalmente para almacenar el código de dibujo para LGVL.
    • Crear la carpeta e1001_display, y agregar los archivos del controlador de pantalla e1001_display.c y e1001_display.h
    • Crear la carpeta lvgl_conf y agregar el archivo de configuración lv_conf.h de LVGL.

Código de referencia completo: EE04_Dashboard_ui.zip

Código main.cpp
/*
* Seeed reTerminal E1001 Multi-UI Demo
* - All hardware I/O and e-paper driver work happens here.
* - All runtime parameters and debug logs live here.
* - dashboard_ui.cpp is a pure LVGL UI layer (no driver calls, no runtime params).
*
* UI switching:
* KEY0 (GPIO2) → Vehicle Dashboard
* KEY1 (GPIO3) → Smart Home
* KEY2 (GPIO5) → Super Mario (default)
*/

#include <TFT_eSPI.h>
#include <lvgl.h>

#include "dashboard_ui.h" // Pure UI layer
#include "e1001_display.h" // E1001 e-paper driver (init/refresh in this file only)

/* ============ Global driver object ============ */
static e1001_driver_t e1001_driver;

/* ============ Current UI ============ */
static UIType current_ui = UI_SUPER_MARIO;

/* ============ Smart Home runtime parameters (tuned here) ============ */
static String smh_location = "New York";
static String smh_weather = "Sunny";
static float smh_temperature = 22.5f;
static int smh_humidity = 45;
static float smh_batt_voltage = 12.4f;
static int smh_batt_capacity = 85;
static int smh_wifi_signal = 4; // 0..4

/* ============ Vehicle runtime parameters (tuned here) ============ */
static int veh_speed = 85; // km/h
static int veh_rpm = 2800; // RPM
static int veh_fuel = 75; // %
static int veh_engine_temp = 82; // °C
static char veh_gear = 'D'; // gear char
static long veh_odometer = 86531; // km
static bool veh_seatbelt = true; // indicator example

/* ---------------------------------------------------------------
* LVGL periodic tick (moved here from dashboard_ui.cpp)
* --------------------------------------------------------------- */
void ui_update_loop()
{
lv_timer_handler();
delay(50);
}

/* ---------------------------------------------------------------
* Rebuild the active screen for a given UI type (moved here)
* --------------------------------------------------------------- */
void load_ui(UIType type)
{
lv_obj_clean(lv_scr_act());
current_ui = type;

switch (type)
{
case UI_VEHICLE_DASHBOARD:
create_vehicle_dashboard_ui();
break;
case UI_SMART_HOME:
create_smarthome_ui();
break;
case UI_SUPER_MARIO:
default:
create_supermario_ui();
break;
}
}

/* ---------------------------------------------------------------
* Helper to apply Smart Home params to the active UI (if loaded)
* --------------------------------------------------------------- */
static void apply_smarthome_params()
{
update_temperature(smh_temperature);
update_humidity(smh_humidity);
update_battery_voltage(smh_batt_voltage);
update_battery_capacity(smh_batt_capacity);
update_wifi_signal(smh_wifi_signal);
update_weather_status(smh_weather.c_str());
update_location(smh_location.c_str());

// Example to-do placeholders (UI has checkboxes already)
add_todo_item("Water plants");
add_todo_item("Check security");
add_todo_item("Update firmware");
add_todo_item("Check smart plugs");
}

/* -------------------------------------------------------------
* Helper to apply Vehicle params to the active UI (if loaded)
* ------------------------------------------------------------- */
static void apply_vehicle_params()
{
update_speed_gauge(veh_speed);
update_rpm_gauge(veh_rpm);
update_fuel_level(veh_fuel);
update_engine_temp(veh_engine_temp);
update_gear_position(veh_gear);
update_odometer(veh_odometer);
set_warning_indicator(0, veh_seatbelt);
}

/* -------------------------------------------------------------
* Switch UI (rebuilds the UI and applies current parameters)
* ------------------------------------------------------------- */
static void switch_ui(UIType next_ui)
{
if (next_ui == current_ui)
return;

current_ui = next_ui;
load_ui(current_ui);

if (current_ui == UI_VEHICLE_DASHBOARD)
{
Serial.println("[UI] Loaded Vehicle Dashboard");
apply_vehicle_params();
}
else if (current_ui == UI_SMART_HOME)
{
Serial.println("[UI] Loaded Smart Home");
apply_smarthome_params();
}
else
{
Serial.println("[UI] Loaded Super Mario");
}

// Trigger an e-paper refresh immediately after rebuilding UI
e1001_display_refresh(&e1001_driver);
}

/* -------------------------------------------------------------
* Setup
* ------------------------------------------------------------- */
void setup()
{
Serial.begin(115200);
Serial.println("LVGL Multi-UI + E1001 e-paper demo starting...");

// Configure keys (active LOW due to INPUT_PULLUP)
pinMode(BUTTON_KEY0, INPUT_PULLUP);
pinMode(BUTTON_KEY1, INPUT_PULLUP);
pinMode(BUTTON_KEY2, INPUT_PULLUP);

// Initialize e-paper (includes LVGL/timer/display config handled by your driver)
Serial.println("Initializing E1001 e-paper driver...");
e1001_display_init(&e1001_driver);
Serial.println("E1001 init done.");

// Default UI: Super Mario
current_ui = UI_SUPER_MARIO;
load_ui(current_ui);
Serial.println("Default UI created: Super Mario");

e1001_display_refresh(&e1001_driver);
}

/* -------------------------------------------------------------
* Loop: button-based UI switching + LVGL ticks + e-paper refresh
* ------------------------------------------------------------- */
void loop()
{
// Handle UI switching (debounced)
if (digitalRead(BUTTON_KEY0) == LOW)
{ // Vehicle
switch_ui(UI_VEHICLE_DASHBOARD);
delay(300);
}
else if (digitalRead(BUTTON_KEY1) == LOW)
{ // Smart Home
switch_ui(UI_SMART_HOME);
delay(300);
}
else if (digitalRead(BUTTON_KEY2) == LOW)
{ // Super Mario
switch_ui(UI_SUPER_MARIO);
delay(300);
}

// Drive LVGL internal timers only (no driver I/O in UI layer)
ui_update_loop();

// Check if e-paper refresh is needed (driver logic stays here)
if (e1001_display_should_refresh(&e1001_driver))
{
Serial.println("Refreshing e-paper display...");
e1001_display_refresh(&e1001_driver);
Serial.println("Display refresh complete.");
}
}

Demostración del Efecto:

Presionar los botones en la placa EE04 puede cambiar a la interfaz de usuario correspondiente:

  • KEY1: Panel de Control del Vehículo
  • KEY2: Panel de Control SmartHome
  • KEY3: Super Mario
  • Por defecto: Super Mario
Super MarioVehículoSmartHome

Haz clic en el enlace, busca el título Resources y selecciona el archivo de carcasa 3D que deseas imprimir.
Recursos de archivos de carcasa 3D

Soporte Técnico y Discusión del Producto

¡Gracias por elegir nuestros productos! Estamos aquí para brindarte diferentes tipos de soporte para asegurar que tu experiencia con nuestros productos sea lo más fluida posible. Ofrecemos varios canales de comunicación para satisfacer diferentes preferencias y necesidades.

Loading Comments...