Integrar CrowPanel E-paper en HA

Integrar CrowPanel E-paper en HA

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.

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:

*Algún precio puede haber cambiado desde la ultima revision

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:

  1. Conecta la pantalla a tu ordenador a través del puerto USB-C.
  2. 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”).
  3. 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.
  4. Pulsa en “Skip” y haz clic en el enlace “Edit” del bloque correspondiente al dispositivo que acabas de crear.
  5. 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!!");
  1. 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.
  2. Cuando termine, selecciona la opción “Modern format” para descargar el fichero ‘.bin’ correspondiente.
  3. Ahora ve a la página de ESPHome y pulsa en “Connect”. En la ventana emergente selecciona tu placa y pulsa en “Conectar”.
  4. Ahora pulsa en “Install” y selecciona el fichero ‘.bin’ obtenido en el paso 7. De nuevo, pulsa en “Install”.
  5. 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.
  6. 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());
      }