Dedicado a los que buscan unir tecnología y decoración, hoy vamos a integrar CrowPanel E-paper en HA, una pantalla de tinta electrónica.
Índice
Pantallas de tinta electrónica
Hace mucho tiempo que tenía ganas de hacer algún proyecto con alguna pantalla de tinta electrónica o «e-Ink». Simplemente creo son capaces de aportarte todas las ventajas de cualquier pantalla, pero además tienen la capacidad de integrarse más fácilmente en tu decoración. Esto se debe a que funcionan de manera diferente a las pantallas tradicionales (LCD, LED, OLED…) porque están diseñadas para imitar la apariencia del papel impreso, ofreciendo una experiencia de lectura más cómoda y con menor consumo de energía.
En conclusión, me parecen un componente perfecto para crear dispositivos que no tengan ese aspecto tan «tecnológico». Por ejemplo, puedes integrarlas perfectamente en un marco tradicional y nadie se dará cuenta.
En mi caso voy a utilizar la CrowPanel ESP32 4.2” E-paper de Elecrow, una pantalla de 4,2 pulgadas (400×300 pixels). Se trata de una pantalla con todos los componentes presoldados sobre una placa ESP32-S3 (por lo que es perfecta para usarla con proyectos como Bermuda). Además incorpora un puerto para conectarle una batería, una ranura para tarjetas de memoria y controles físicos.

Cabe destacar que, a día de hoy, no tiene soporte oficial con ESPHome. No obstante, hemos conseguido «engañar» a ESPHome estudiando el diagrama de componentes del dispositivo y utilizando el componente de Waveshare E-Paper Display.
Requisitos previos
Para integrar CrowPanel E-paper en HA previamente necesitas:
- Una pantalla CrowPanel ESP32 4.2” E-paper.
- Un cable USB-C para alimentar la placa de DATOS (con un cable de carga no vas a poder instalar el software).
- Haber instalado ESPHome en Home Assistant.
Configuración de ESPHome
Ahora tenemos que ‘flashear’ la pantala con ESPHome y la configuración correspondiente, que vamos a extraer directamente de la documentación sobre el componente Waveshare E-Paper Display y el PR#6209. Para ello sigue estos pasos:
- Conecta la pantalla a tu ordenador a través del puerto USB-C.
- En Home Assistant, accede al complemento de ESPHome desde el menú lateral y pulsa en “New device”. Pulsa en “Continue” y dale un nombre (por ejemplo, “ePaperDisplay”).
- Pulsa en “Next” y a continuación selecciona la opción “ESP32-S3” como tipo de dispositivo. Verás que en el fondo se ha creado un nuevo dispositivo.
- Pulsa en “Skip” y haz clic en el enlace “Edit” del bloque correspondiente al dispositivo que acabas de crear.
- Añade las siguientes líneas al final del código.
captive_portal:
external_components:
- source: github://pr#6209
components: [ waveshare_epaper ]
output:
- platform: gpio
pin: 7
id: pin_7
inverted: false
switch:
- platform: output
name: "Pin 7 High"
output: pin_7
restore_mode: ALWAYS_ON
font:
- file: "gfonts://Roboto"
id: font1
size: 30
spi:
clk_pin: GPIO12
mosi_pin: GPIO11
display:
- platform: waveshare_epaper
model: 4.20in-v2
full_update_every: 30
reset_duration: 200ms
update_interval: 60s
cs_pin: GPIO10
dc_pin: GPIO46
busy_pin: GPIO48
reset_pin: GPIO47
lambda: |-
it.print(0, 0, id(font1), "Hola Aguacateros!!");
- Cuando hayas terminado de editar el código pulsa en “Save” e “Install”. Selecciona la opción “Manual download” y espera a que el código se compile.
- Cuando termine, selecciona la opción “Modern format” para descargar el fichero ‘.bin’ correspondiente.
- Ahora ve a la página de ESPHome y pulsa en “Connect”. En la ventana emergente selecciona tu placa y pulsa en “Conectar”.
- Ahora pulsa en “Install” y selecciona el fichero ‘.bin’ obtenido en el paso 7. De nuevo, pulsa en “Install”.
- Vuelve a Home Assistant y ve a Configuración > Dispositivos y servicios. Lo normal es que tu dispositivo haya sido descubierto y aparezca en la parte superior, esperando únicamente a que pulses el botón de “Configurar”. De lo contrario pulsa en el botón de “Añadir integración”, busca “ESPHome” e introduce la IP de tu placa en el campo ‘Host’. Como siempre, te recomiendo que asignes una IP fija en tu router para evitar fallos en el futuro si esta cambia.
- Tras esperar unos segundos deberías ver el siguiente mensaje en pantalla. A partir de aquí, los resultados dependen de tu creatividad!

Ejemplo práctico
Si acabas de integrar CrowPanel E-paper en HA y necesitas un poco de inspiración, a continuación te dejo el código de un panel simple para mostrar algunos datos de Home Assistant (temperatura, humedad, fecha, calendario…).
substitutions:
# Device customization
# Personalización del dispositivo
name: epaperdisplay
friendly_name: ePaperDisplay
# Entities / Entidades
sensor_temperature: sensor.openweathermap_temperature
sensor_home_temperature: sensor.home_temperatura
sensor_home_humidity: sensor.home_humedad
calendar: calendar.aguacatec
# Other settings / Otros ajustes
allowed_characters: " ¿?¡!#%'()+,-./:°0123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyzáéíóú"
################################################################################################################
esphome:
name: ${name}
friendly_name: ${friendly_name}
esp32:
board: esp32-s3-devkitc-1
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "gNwcUkjsdfddfDRVkgu+3xz/Q4dTWDksa383="
ota:
- platform: esphome
password: "b6sadads59505590eeca9babfc5f8"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Epaperhack Fallback Hotspot"
password: "YvOjasddsa8G"
captive_portal:
external_components:
- source: github://pr#6209
components: [ waveshare_epaper ]
font:
- file: "gfonts://Kanit"
id: font_data
size: 20
glyphs: ${allowed_characters}
- file: "gfonts://Lobster"
id: font_quote1
size: 45
glyphs: ${allowed_characters}
- file: "gfonts://Rowdies"
id: font_quote2
size: 30
glyphs: ${allowed_characters}
- file: "gfonts://Exo 2"
id: font_calendar
size: 18
glyphs: ${allowed_characters}
- file: "gfonts://Raleway"
id: font_date
size: 16
glyphs: ${allowed_characters}
image:
- file: mdi:weather-partly-cloudy
id: icon_temperature
resize: 30x30
type: BINARY
transparency: chroma_key
- file: mdi:home-thermometer
id: icon_home_temperature
resize: 30x30
type: BINARY
transparency: chroma_key
- file: mdi:water-percent
id: icon_humidity
resize: 30x30
type: BINARY
transparency: chroma_key
- file: mdi:calendar
id: icon_calendar
resize: 60x60
type: BINARY
transparency: chroma_key
output:
- platform: gpio
pin: 7
id: pin_7
inverted: false
sensor:
- platform: homeassistant
id: sensor_temperature
entity_id: ${sensor_temperature}
internal: true
- platform: homeassistant
id: sensor_home_temperature
entity_id: ${sensor_home_temperature}
internal: true
- platform: homeassistant
id: sensor_home_humidity
entity_id: ${sensor_home_humidity}
internal: true
spi:
clk_pin: GPIO12
mosi_pin: GPIO11
switch:
- platform: output
name: "Pin 7 High"
output: pin_7
restore_mode: ALWAYS_ON
internal: true
text_sensor:
- platform: homeassistant
id: calendar_event
entity_id: ${calendar}
attribute: message
internal: true
- platform: homeassistant
id: calendar_time
entity_id: ${calendar}
attribute: start_time
internal: true
time:
- platform: homeassistant
id: esptime
display:
- platform: waveshare_epaper
model: 4.20in-v2
full_update_every: 30
reset_duration: 200ms
update_interval: 3600s # Se actualiza cada hora
cs_pin: GPIO10
dc_pin: GPIO46
busy_pin: GPIO48
reset_pin: GPIO47
lambda: |-
it.image(5, 5, id(icon_temperature));
it.printf(40, 6, id(font_data), "%.0f°C", id(sensor_temperature).state);
it.image(80, 5, id(icon_home_temperature));
it.printf(115, 6, id(font_data), "%.0f°C", id(sensor_home_temperature).state);
it.image(155, 5, id(icon_humidity));
it.printf(190, 6, id(font_data), "%.0f%%", id(sensor_home_humidity).state);
it.strftime(310, 6, id(font_data), "%d/%m/%y", id(esptime).now());
it.printf(200, 120, id(font_quote1), TextAlign::CENTER, "La vida es breve!!");
it.printf(200, 170, id(font_quote2), TextAlign::CENTER, "automatiza tus tareas");
it.image(5, 240, id(icon_calendar));
if (id(calendar_time).state.length() >= 16) {
std::string date = id(calendar_time).state.substr(8, 2); // Extrae el día (DD)
std::string month = id(calendar_time).state.substr(5, 2); // Extrae el mes (MM)
std::string hour = id(calendar_time).state.substr(11, 2); // Extrae la hora (HH)
std::string minute = id(calendar_time).state.substr(14, 2); // Extrae los minutos (MM)
// Mapea el número del mes a su nombre en español
std::map<std::string, std::string> month_names = {
{"01", "enero"}, {"02", "febrero"}, {"03", "marzo"}, {"04", "abril"},
{"05", "mayo"}, {"06", "junio"}, {"07", "julio"}, {"08", "agosto"},
{"09", "septiembre"}, {"10", "octubre"}, {"11", "noviembre"}, {"12", "diciembre"}
};
std::string month_name = month_names[month]; // Obtiene el nombre del mes
// Imprime el evento y la fecha formateada
it.printf(70, 250, id(font_calendar), "%s", id(calendar_event).state.c_str());
it.printf(70, 270, id(font_date), "Tendrá lugar el %s de %s, a las %s:%sh", date.c_str(), month_name.c_str(), hour.c_str(), minute.c_str());
}

