Seeed Sudio XIAO 7.5 e-Ink Panel con ESP32 - come programmarlo con ESPHome

Home Assistant

Scritto da Vincenzo Caputo

Ancora alle prese con ESPHome!

Tantissimi di voi apprezzano questo filone di contenuti ed hanno manifestato apertamente il desiderio che continuino ad uscirne numerosi.

E noi non ci facciamo pregare ed eccoci con un nuovo interessantissimo contenuto.

Seeed Studio, che già mi aveva mandato un dispositivo molto interessante di cui ho realizzato una guida che vi linko qui sotto, questa volta mi ha inviato un Pannello e-Ink già fornito di ESP32 e di batteria da 2000mAh, il tutto racchiuso in un case stampato in 3D. 

Prima di continuare vi lascio il link al precedente articolo nel caso ve lo siate perso.

ReSpeaker Lite 2-Mic Array Voice Kit, integrazione in Home Assistant con ESPHome

Intanto mi piacerebbe spendere 2 parole sulla tecnologia e-Ink utilizzata da questo pannello.

In italiano "carta elettronica", comunemente identificata come e-ink (che sta per electrophoretic ink) o  chiamato anche e-paper, è una tecnologia progettata per imitare l'aspetto dell'inchiostro su un normale foglio di carta.

A differenza di uno schermo a cristalli liquidi, che usa una luce posteriore al display per illuminare i pixel, tale tipologia di display riflette la luce ambientale esattamente come un foglio di carta.

Il vantaggio di utilizzare questo tipo di tecnologia risiede nel bassissimo consumo energetico.

Infatti il pannello e-Ink consuma energia solo per cambiare il contenuto dello schermo (ogni tot secondi in base a come lo impostiamo) 

Questo permette di realizzare dispositivi leggeri e a elevata autonomia, dato che l'energia è necessaria solo per cambiare il contenuto dello schermo.

Il rovescio della medaglia c'è naturalmente, ed è quello di non avere un aggiornamento in tempo reale delle informazioni (che in alcuni casi è necessario), oppure la mancanza di colori.

Ma per alcuni casi il suo utilizzo è ideale.

Fatta questa breve premessa sulla tecnologia, parliamo del prodotto in questione.

In tanto qui di seguito vi lascio il link al sito del produttore:

https://www.seeedstudio.com/XIAO-7-5-ePaper-Panel-p-6416.html?srsltid=AfmBOopELizGqXEbZv8_L14L_MLWhbcAgyl91E0E65bhMYTsL2K9yjVj

https://s.click.aliexpress.com/e/_oD2QTwD

Seeed Studio non ha fatto altro che prendere un pannello e-Ink e metterlo dentro un case stampato in 3D insieme ad un esp32 ed una batteria da 2000mAh.

In realtà altro non si tratta che di un display Waveshare ufficilmente supportato da ESPHome di cui potete raggiungere la pagina ufficiale al seguente link:

https://esphome.io/components/display/waveshare_epaper.html#waveshare-e-paper-display

Quindi in teoria potreste anche costruirvi da soli una soluzione acquistando i componenti separatamente.

Questa di Seeed Studio rimane comunque una soluzione molto interessante perché abbastanza economica e già pronta all'uso.

Nella parte posteriore è presente uno switch che permette di passare dal funzionamento a batteria al funzionamento ad alimentazione diretta tramite cavo USB-C. Sono presenti inoltre i due pulsanti standard presenti su tutti gli ESP32: Boot e Reset.

Il tutto è sormontato da un'aletta che funge da coperchio posteriore e anche da sostegno/supporto da tavolo.

Cosa è possibile realizzare con questo display?

Possiamo appunto programmarlo con ESPHome creando una dashboard che legge i dati direttamente da Home Assistant e ce li mostra sul display per avere in casa un colpo d'occhio sulle cose principali che vogliamo monitorare, senza la necessità di consultare lo smartphone o il PC.

Il display è da 7,5 pollici ed è dotato di batteria da 2000mAh che permette di usare (limitatamente all'autonomia della batteria) il dispositivo anche in luoghi dove non vi sia alimentazione elettrica.

Il produttore dichiara fino a 3 mesi con una singola carica in modalità deep sleep (intervallo di aggiornamento di 6 ore).

A questo punto vorrei fare una precisazione: ho già detto in apertura che questo display non si aggiorna in tempo reale come un normale display lcd e il tempo di aggiornamento va specificato all'interno del codice (come vedremo più avanti).

Inoltre, durante le fasi di aggiornamento dell'immagine, lampeggia un paio di volte (bianco-nero) per ridisegnare la pagina, procedura che dura circa 2 secondi.

Comprenderete come non sia possibile ottenere un aggiornamento dei dati per esempio una volta al secondo, ma sarebbe poco confortevole anche farlo aggiornare ogni 5 secondi, perché starebbe sempre a lampeggiare. Diciamo che farlo aggiornare sotto il mezzo minuto ha veramente poco senso.

Questo non rappresenta un problema quando il dato che vogliamo mostrare è un dato che varia molto lentamente, come può essere la temperatura esterna della nostra abitazione magari monitorata da uno Shelly BLU H&T, oppure un calendario, o tutti gli altri dati che possiamo tranquillamente monitorare ogni tot minuti senza che questo rappresenti un problema.

Nella foto che potete osservare in copertina mi sono spinto su una casistica che è al limite dell'utilizzabile con questo display, cioè il monitoraggio del fotovoltaico.

Alcuni dei dati mostrati, come la batteria di accumulo, possono essere letti e aggiornati tranquillamente ogni 2 o 3 minuti invece, la potenza prodotta in un dato momento dal fotovoltaico, può essere molto diversa anche da un secondo all'altro (esempio se passa una nuvola che mette in ombra i pannelli).

Magari un dato meglio rappresentabile da questo display sarebbe stato quello dei kWh prodotti durante la giornata (fino a quel dato momento) o i kWh consumati dall'abitazione sempre fino a quel momento.

Ma ho voluto portare l'esempio fatto in questo modo proprio per farvi capire a quali tipologie di applicazioni si rivolge questo prodotto.

COME PROGRAMMARLO

Passiamo ora alla parte pratica spiegando come fare praticamente a programmarlo.

Come pre-requisiti naturalmente c'è quello di avere un Home Assistant installato e che abbiate installati anche gli add-on ESPHome Device Builder e l'integrazione ESPHome.

Intanto dovrete aggiungerlo ad Home Assistant come un qualsiasi ESP32 nuovo. A tal proposito ho realizzato un breve video che potete guardare al seguente link.

A questo punto entrate nella programmazione del dispositivo ed aggiungete, dopo la voce Captive Portal, il seguente codice:

# define font to display words
font:
  - file: "gfonts://Inter@700"
    id: font1
    size: 24

# define SPI interface
spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: GPIO4
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: 30s
    lambda: |-
      it.print(0, 0, id(font1), "Hello World!");

Cliccate poi su install e poi sulla modalità di installazione che preferite, vi consiglio quella USB finche non finite il grosso della configurazione perché è quella più rapida.

Se tutto va a buon fine otterrete sul vostro display la scritta Hello World!

Questo piccolo esempio serve a mostrare come scrivere qualcosa a schermo.

Se ci fate caso, nel codice che abbiamo incollato in ESPHome Builder, abbiamo nella prima parte il font usato con la dimensione del carattere (size 24). Per esempio basterà aumentare quel numero per ottenere un carattere più grande.

L'ultima riga invece rappresenta l'informazione di quello che vogliamo venga scritto e dove vogliamo che venga scritto.

it.print(0, 0, id(font1), "Hello World!");

i numeri 0, 0, non sono altro che le coordinate dello schermo, rispettivamente x e y.

In effetti la nostra frase è stata scritta in alto a sinistra dove si trova lo 0 delle X e lo 0 delle Y.

Se volessimo postare la scritta più a destra basterebbe incrementare la prima cifra (es. 50, 0,).

Ora passiamo ad un altro esempio.

Incollate, sempre sotto la voce captive portal il seguente codice.

spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    model: 7.50inv2
    cs_pin: GPIO3
    dc_pin: GPIO5
    reset_pin: GPIO2
    busy_pin: GPIO4
    update_interval: 5min
    lambda: |-
      it.rectangle(10, 10, 100, 50);
      it.rectangle(150, 10, 50, 50);
      it.circle(250, 35, 25);

      it.filled_rectangle(10, 80, 100, 50);
      it.filled_rectangle(150, 80, 50, 50);
      it.filled_circle(250, 105, 25);

Installate come avete fatto in precedenza e al termine dovreste ottenere a schermo queste figure.

Questa volta abbiamo disegnato delle figure geometriche invece che del testo, questo aiuta a rendere la grafica più accattivante perché potrete ad esempio incorniciare un testo oppure creare delle divisioni all'interno della pagina del vostro pannello.

Prendiamo come esempio questa riga per spiegare il codice installato: it.rectangle(10,10,100, 50);

I primi due numeri (10, 10,) altro non sono che le coordinate esattamente come abbiamo visto nel testo.

Gli altri due numeri invece rappresentano la larghezza del rettangolo (100) e la sua altezza (50).

Variando questi numeri potrete ottenere la grafica che preferite.

Passiamo ora ad un ulteriore esempio.

Adesso cercheremo di mostrare dei dati di Home Assistant nel display.

Per prima cosa bisogna aggiungere il dispositivo dentro l'integrazione ESPHome.

A questo punto, sempre all'interno dell'editor del codice di ESPHome Builder, incollate dopo la voce Captive portal il seguente codice:

# Define font to show info
font:
  - file: "gfonts://Inter@700"
    id: myFont
    size: 24

# Get info from HA, as string format
text_sensor:
  - platform: homeassistant
    entity_id: weather.forecast_home
    id: myWeather
    internal: true
  - platform: homeassistant
    entity_id: weather.forecast_home
    id: myTemperature
    attribute: "temperature"
    internal: true

# Get info from HA, as float format
sensor:
  - platform: homeassistant
    entity_id: weather.forecast_home
    id: myPressure
    attribute: "pressure"
    internal: true

# Display info via SPI
spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: GPIO4
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: 30s
    lambda: |-
      //print info in log
      ESP_LOGD("epaper", "weather: %s", id(myWeather).state.c_str());
      ESP_LOGD("epaper", "temperature: %s", id(myTemperature).state.c_str());
      ESP_LOGD("epaper", "pressure: %.1f", id(myPressure).state);
      //display info in epaper screen
      it.printf(100, 100, id(myFont), "%s", id(myWeather).state.c_str());
      it.printf(100, 150, id(myFont), "%s", id(myTemperature).state.c_str());
      it.printf(100, 200, id(myFont), "%.1f", id(myPressure).state);

Questo serve mostrare a schermo alcune informazioni meteo.

Prima di installare questo codice assicuratevi che in nome dell'entità delle previsioni meteo del vostro Home Assistant sia uguale a quella scritta nel codice, altrimenti cambiatela.

Nel mio caso ad esempio l'entità per le previsioni meteo è weather.casa_2 come leggete nell'immagine qui di seguito.

Al di la del fatto che riusciate a visualizzare a schermo le informazioni meteo, questo esempio serve a mostrare come leggere e stampare i dati di qualsiasi entità presente in Home Assistant.

Il risultato quindi dovrebbe essere più o meno quello che vedete nell'immagine seguente:

A questo punto abbiamo fatto gran parte del lavoro che avete visto nella mia realizzazione.

Avviamo mostrato come generare e posizionare le scritte, come realizzare e posizionare le figure geometriche, e come leggere e posizionare i valori di un'entità di Home Assistant.

Ma se notate manca ancora un piccolo passaggio... le icone!

Per usare la icone dovrete installare un ulteriore add-on.

Dopo averlo installato e avviato dovrete scaricare sul vostro PC il seguente file zip.

SCARICA

Il file zip contiene una cartella chiamata font_ttf con all'interno 2 files (materialdesignicons-webfont.ttf e 

Montserrat-Black.ttf)

Seguendo l'esempio riportato sull'immagine seguente, andate sulla barra laterale su Studio Code Server e sotto la voce esphome create una cartella di nome font. All'interno copiate i due file estratti in precedenza dal file zip.

Potrete letteralmente trascinali dentro col mouse dal file manager del vostro sistema operativo.

A questo punto provate questo codice:

font:
  - file: 'fonts/materialdesignicons-webfont.ttf'  #here is the directory to save ttf file
    id: font_mdi_large
    size: 200        # big size icon
    glyphs: &mdi-weather-glyphs
      - "\U000F0595" # weather cloudy
      - "\U000F0592" # weather hail
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medium   # small size icon
    size: 40
    glyphs: *mdi-weather-glyphs

spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: GPIO4
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: 30s
    lambda: |-
      it.printf(100, 200, id(font_mdi_medium), TextAlign::CENTER, "\U000F0595");
      it.printf(400, 200, id(font_mdi_large), TextAlign::CENTER, "\U000F0592");

Se tutto va bene dovreste ottenere un'immagine come qui di seguto:

Come fare ad ottenere altre icone?

Potete recarvi al seguente sito web e cercare tra migliaia di icone diverse.

https://pictogrammers.com/library/mdi/

Quando avrete trovato quella che fa per voi potrete semplicemente copiarne il codice come vedete nella seguente immagine:

e incollare tale codice al posto di quello dell'esempio precedente.

A questo punto dovrete solo armarvi di pazienza e posizionare i vari elementi facendo prove e modifiche in corso d'opera.

Una volta capito il meccanismo non ci vuole molto per realizzare grafiche anche abbastanza complesse come quella fatta da me.

Nel caso vogliate usare il mio codice come spunto (naturalmente dovrete aggiornalo con le vostre entità) ve lo lascio qui di seguito:

# Define font to show info
font:
  - file: "gfonts://Inter@700"
    id: myFont
    size: 24
  - file: "gfonts://Inter@700"
    id: myFont_T
    size: 36
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_medium   # small size icon
    size: 40
    glyphs: &mdi-weather-glyphs
      - "\U000F12A6" # battery
      - "\U000F0A72" # solar pv power
      - "\U000F02DC" # house
      - "\U000F0D3E" # grid
      - "\U000F0B6C" # car_batt
      - "\U000F0D64" # car_km
      - "\U000F050F" # term
      - "\U000F058E" # Humid


# Get info from HA, as string format
text_sensor:
  - platform: homeassistant
    entity_id: sensor.battery_state_of_charge
    id: charge
    internal: true
  - platform: homeassistant
    entity_id: sensor.pv_power
    id: power
    internal: true
  - platform: homeassistant
    entity_id: sensor.house_consumption
    id: house
    internal: true
  - platform: homeassistant
    entity_id: sensor.on_grid_export_power
    id: grid
    internal: true
  - platform: homeassistant
    entity_id: sensor.jarvis_battery
    id: tesla_batt
    internal: true
  - platform: homeassistant
    entity_id: sensor.jarvis_range
    id: tesla_range
    internal: true
  - platform: homeassistant
    entity_id: sensor.car_evinfo_battery_stateofcharge
    id: cinq_batt
    internal: true
  - platform: homeassistant
    entity_id: sensor.car_evinfo_battery_totalrange
    id: cinq_range
    internal: true
  - platform: homeassistant
    entity_id: sensor.bthome_sensor_d954_temperature
    id: temp_ext
    internal: true
  - platform: homeassistant
    entity_id: sensor.bthome_sensor_d954_humidity
    id: humid_ext
    internal: true


# Get info from HA, as float format
sensor:
  - platform: homeassistant
    entity_id: weather.casa
    id: myPressure
    attribute: "pressure"
    internal: true

# Display info via SPI
spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: GPIO4
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: 30s
    lambda: |-
      //print info in log
      ESP_LOGD("epaper", "weather: %s", id(charge).state.c_str());
      ESP_LOGD("epaper", "temperature: %s", id(power).state.c_str());
      ESP_LOGD("epaper", "pressure: %.1f", id(house).state);
      //display info in epaper screen
      it.printf(50, 150, id(myFont), "%s", id(charge).state.c_str());
      it.printf(50, 200, id(myFont), "%s", id(power).state.c_str());
      it.printf(50, 250, id(myFont), "%s", id(house).state.c_str());
      it.printf(50, 300, id(myFont), "%s", id(grid).state.c_str());
      it.print(80, 150, id(myFont), "% Battery Charge");
      it.print(300, 30, id(myFont_T), "MissingTech");
      it.printf(30, 165, id(font_mdi_medium), TextAlign::CENTER, "\U000F12A6");
      it.print(130, 200, id(myFont), "W PV Power");
      it.printf(30, 215, id(font_mdi_medium), TextAlign::CENTER, "\U000F0A72");
      it.print(130, 250, id(myFont), "W Consumo");
      it.printf(30, 265, id(font_mdi_medium), TextAlign::CENTER, "\U000F02DC");
      it.rectangle(280, 25, 270, 55);
      it.print(130, 300, id(myFont), "W Rete in-out");
      it.printf(30, 315, id(font_mdi_medium), TextAlign::CENTER, "\U000F0D3E");
      it.filled_rectangle(400, 100, 5, 370);
      it.printf(450, 150, id(myFont), "%s", id(tesla_batt).state.c_str());
      it.print(490, 150, id(myFont), "% batteria Tesla");
      it.printf(430, 165, id(font_mdi_medium), TextAlign::CENTER, "\U000F0B6C");
      it.printf(450, 200, id(myFont), "%s", id(tesla_range).state.c_str());
      it.print(530, 200, id(myFont), "km autonomia Tesla");
      it.printf(430, 215, id(font_mdi_medium), TextAlign::CENTER, "\U000F0D64");
      it.printf(450, 250, id(myFont), "%s", id(cinq_batt).state.c_str());
      it.print(490, 250, id(myFont), "% batteria 500e");
      it.printf(430, 265, id(font_mdi_medium), TextAlign::CENTER, "\U000F0B6C");
      it.printf(450, 300, id(myFont), "%s", id(cinq_range).state.c_str());
      it.print(510, 300, id(myFont), "km autonomia 500e");
      it.printf(430, 315, id(font_mdi_medium), TextAlign::CENTER, "\U000F0D64");
      it.print(80, 80, id(myFont_T), "Fotovoltaico");
      it.print(450, 80, id(myFont_T), "Auto Elettriche");
      it.printf(50, 430, id(myFont), "%s", id(temp_ext).state.c_str());
      it.print(100, 430, id(myFont), "°Temperatura in giardino");
      it.printf(30, 445, id(font_mdi_medium), TextAlign::CENTER, "\U000F050F");
      it.printf(450, 430, id(myFont), "%s", id(humid_ext).state.c_str());
      it.print(480, 430, id(myFont), "% umidita' in giardino");
      it.printf(430, 445, id(font_mdi_medium), TextAlign::CENTER, "\U000F058E");
      it.filled_rectangle(10, 340, 820, 3);
      it.print(60, 360, id(myFont_T), "Temp. Esterna");
      it.print(450, 360, id(myFont_T), "Umidita' Esterna");

Sempre da incollare dopo captive portal.

Vi lascio ora al video dove potrete vedere il dispositivo in funzione direttamente dal nostro canale YouTube MissingTech.

Buona visione!

Produrre e aggiornare contenuti su vincenzocaputo.com richiede molto tempo e lavoro. Se il contenuto che hai appena letto è di tuo gradimento e vuoi supportarmi, clicca uno dei link qui sotto per fare una donazione.

Vincenzo Caputo

Nato a Matera, il 1° novembre 1977. Sono da sempre appassionato di tecnologia e ho un'esperienza lavorativa ventennale nel settore IT. Mi piace sperimentare e cercare sempre nuove soluzioni e soprattutto mi piace comunicare le mie esperienze agli altri.


Vai ai commenti