Waveshare ESP32-S3 AMOLED: Trasformiamolo in una Mini Dashboard Domotica con ESPHome e LVGL

Waveshare ESP32-S3 AMOLED: Trasformiamolo in una Mini Dashboard Domotica con ESPHome e LVGL

di Vincenzo Caputo

15 Febbraio 2026

Home Assistant Guide

Vincenzo Caputo

Vi ricordate il piccolo gioiello della Waveshare di cui abbiamo parlato qualche tempo fa? Sì, parlo del display circolare ESP32-S3 Touch AMOLED da 1.75 pollici. Nel precedente articolo avevamo visto come configurarlo per visualizzare dati statici usando la grafica vettoriale. Un ottimo risultato estetico, ma limitato.

Waveshare ESP32-S3 AMOLED: Trasformiamolo in una Mini Dashboard Domotica con ESPHome e LVGL

Il dispositivo utilizzati lo potete acquistare su Amazon al seguenti link:

Waveshare ESP32-S3 1.75inch AMOLED Round Touch Display Development Board(with Case, No GPS), 32-Bit LX7 2-Core Processor, 466x466, QSPI Interface, Dual Digital Microphones Array, ESP32 With Display

Prezzo intero: 0,00€ | Prezzo scontato: 45,99€

Oppure sul sito del produttore:

https://www.waveshare.com/product/arduino/displays/amoled/esp32-s3-touch-amoled-1.75.htm

Oggi alziamo l'asticella. E di parecchio.

Mi sono chiesto: perché usare questo dispositivo solo come visualizzatore passivo, quando ha un touchscreen capacitivo e un processore dual-core che scalpita? L'idea è quella di trasformare questo piccolo display in una vera e propria Dashboard Domotica da parete, un'alternativa compatta, elegante (e dai consumi irrisori) ai classici tablet che spesso appendiamo in giro per casa.

Tablet Android vs. Microcontrollore: Perché complicarsi la vita?

Qualcuno potrebbe chiedersi: “Ma perché scrivere centinaia di righe di codice YAML per un display da 1.75 pollici (o magari per uno più grande da 7 o 10 pollici) quando posso comprare un tablet Android economico da appendere al muro?”

La domanda è legittima, ma la risposta sta tutta in una parola: Affidabilità.

Chi usa un tablet come dashboard conosce bene i problemi cronici: la batteria che si gonfia se lasciata sempre in carica, l'app di Home Assistant che si chiude in background, gli aggiornamenti di Android che rallentano il dispositivo nel tempo, le notifiche indesiderate e la necessità di configurare app esterne (come Fully Kiosk Browser) per bloccare l'interfaccia. Un tablet è un dispositivo generalista forzato a fare un lavoro specifico.

Un dispositivo basato su ESP32, invece, è un "Elettrodomestico".

  • Manutenzione Zero: Una volta flashato, il software non cambia. Non ci sono aggiornamenti del sistema operativo che "rompono" le cose il venerdì sera.

  • Boot Istantaneo: Se va via la corrente, al riavvio la dashboard è operativa in meno di 2 secondi.

  • Sicurezza e Privacy: Niente account Google, niente tracciamento, niente app in background. Fa solo quello per cui è stato programmato.

  • Reattività: L'interfaccia non deve passare attraverso i layer di Android. Il tocco dialoga quasi direttamente con l'hardware.

I Contro? Ovviamente ci sono. Perderete la possibilità di usare il dispositivo per altro (niente YouTube mentre cucinate, niente videochiamate) e la configurazione iniziale è decisamente più ostica del "scarica l'app e fai login". Inoltre, gestire grafiche complesse su schermi molto grandi (oltre i 7-10 pollici) con un ESP32 richiede molta ottimizzazione del codice per evitare scatti, cosa che un processore mobile gestisce ad occhi chiusi.

Da Grafica Vettoriale a LVGL: Il Salto di Qualità

Nel progetto precedente usavamo primitive grafiche (linee, cerchi) disegnate direttamente sul display. Funziona, ma creare pulsanti interattivi, slider o menu multipagina è un incubo di programmazione.

Per questo progetto siamo passati a LVGL (Light and Versatile Graphics Library) integrato in ESPHome. LVGL è un motore grafico che permette di creare interfacce "simil-smartphone": pulsanti che cambiano colore quando li premi, ghiere che ruotano al tocco, transizioni tra pagine.

Tuttavia, non è stato tutto rose e fiori. Gestire un display AMOLED 466x466 pixel richiede molta memoria e potenza. Durante i test abbiamo affrontato:

  1. Lag nello scrolling: Le animazioni a trascinamento (swipe) erano troppo pesanti per il bus SPI, creando rallentamenti.

  2. Artefatti grafici: Linee bianche o "tagli" sui bordi dovuti all'arrotondamento software del display circolare.

La soluzione? Abbiamo ottimizzato il codice eliminando le animazioni superflue e creando un sistema di navigazione a pulsanti "istantaneo" che carica le schermate dalla PSRAM in un lampo. Abbiamo inoltre forzato lo sfondo nero "puro" (spegnendo i pixel dell'AMOLED) per integrare perfettamente l'interfaccia con la cornice hardware, eliminando ogni difetto visivo.

Tuttavia, per chi cerca una soluzione "Set and Forget" (installalo e dimenticatene), la strada del microcontrollore è, a mio avviso, la vincente.

Cosa Abbiamo Realizzato: 3 Schermate per il Controllo Totale

Il firmware che vi propongo oggi trasforma il Waveshare in un controller a 3 pagine:

  1. Termostato "Nest Style": Una ghiera arancione e grigia reattiva al tocco per impostare la temperatura, con visualizzazione chiara di setpoint e temperatura attuale.

  2. Controllo Luci: Una pagina dedicata con pulsanti grandi e comodi per gestire tre punti luce (nel mio caso due canali di uno Shelly 2.5 e uno Shelly Wave).

  3. Gestione Tapparelle: Pulsanti "Heavy Duty" per alzare, abbassare e fermare le tapparelle, con grafica ad alto contrasto.

Waveshare ESP32-S3 AMOLED: Trasformiamolo in una Mini Dashboard Domotica con ESPHome e LVGL

Prima di Iniziare: I Pre-requisiti

Per replicare questo progetto non serve essere programmatori esperti, ma occorre avere l'ambiente pronto. Ecco la lista della spesa (e delle configurazioni):

  1. L'Hardware: Ovviamente, il Waveshare ESP32-S3 Touch AMOLED 1.75". Se lo avete appena tolto dalla scatola e non sapete come collegarlo o flasharlo la prima volta, fate un salto alla mia guida precedente per il setup iniziale oppure guardate questo video su MissingTech per la prima attivazione di un ESP32 

https://www.youtube.com/watch?v=xMgDSIG3vuM&t=4s

  1. Home Assistant & ESPHome: Do per scontato che abbiate già un'istanza di Home Assistant operativa e l'Add-on di ESPHome installato e aggiornato all'ultima versione (questo è cruciale per il supporto LVGL e driver recenti).

  2. Le Vostre Entità (La parte più importante!): Il codice che vedrete tra poco è un "abito su misura". Io l'ho cucito sulla mia casa, ma voi dovete adattarlo alla vostra. Prima di copiare il codice, andate negli Strumenti per Sviluppatori di Home Assistant e segnatevi gli Entity ID esatti di:

    • Un termostato (es. climate.sala, climate.corridoio).

    • Una tapparella motorizzata (es. cover.finestra_cucina).

    • Tre luci o interruttori (es. light.lampadario, switch.presa_smart).

    • Senza questi ID corretti, i pulsanti sul display non faranno nulla!

  3. Connessione Internet in Compilazione: Nel codice usiamo i Google Fonts (gfonts://Montserrat) e delle componenti esterne per il driver del touch (external_components). Assicuratevi che il vostro Home Assistant sia connesso a internet durante la prima compilazione, perché dovrà scaricare queste librerie automaticamente.

Tutto pronto? Bene, passiamo alla parte divertente: il codice.

Il Codice YAML (Verificato e Pronto all'Uso)

Ecco il codice completo per ESPHome. È stato ripulito e testato. Nota: Ho inserito una sezione substitutions all'inizio. Vi basterà cambiare i nomi delle entità lì per adattare tutto il codice alla vostra casa senza dover cercare riga per riga.

substitutions:
  name: "jarvis-face"
  friendly_name: "Jarvis Face Display"

  # --- PERSONALIZZA QUI LE TUE ENTITÀ ---
  # Inserisci gli ID esatti dei tuoi dispositivi Home Assistant
  climate_entity: "climate.hallway"
  cover_entity: "cover.wave_shutter"
  light_1: "switch.shellyswitch25_40f520016471_channel_1"
  light_2: "switch.shellyswitch25_40f520016471_channel_2"
  light_3: "switch.wave_2pm_2"

esphome:
  name: "${name}"
  friendly_name: "${friendly_name}"
  platformio_options:
    board_build.flash_mode: dio
    board_build.f_flash: 80000000L
    board_build.f_cpu: 240000000L

esp32:
  variant: esp32s3
  flash_size: 16MB
  framework:
    type: esp-idf

# La PSRAM è fondamentale per LVGL su display ad alta risoluzione
psram:
  mode: octal
  speed: 80MHz

# --- CONFIGURAZIONE WIFI ---
wifi:
  ssid: "IL_TUO_SSID"      # <--- Inserisci qui il tuo WiFi
  password: "LA_TUA_PASSWORD" # <--- Inserisci qui la tua Password

api:
  id: api_server
  reboot_timeout: 0s

ota:
  platform: esphome

logger:
  level: INFO

# Importiamo i driver per il Touch CST9217 (non nativi in ESPHome)
external_components:
  - source:
      type: git
      url: https://github.com/shelson/esphome-cst9217

i2c:
  id: bus_a
  sda: GPIO15
  scl: GPIO14
  scan: true
  frequency: 400kHz

touchscreen:
  - platform: cst9217
    id: my_touch
    interrupt_pin: GPIO11
    reset_pin: GPIO40
    transform:
      swap_xy: false
      mirror_x: true
      mirror_y: true

spi:
  - id: display_qspi
    type: quad
    clk_pin: GPIO38
    data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]

display:
  - platform: mipi_spi
    id: disp1
    model: CO5300
    bus_mode: quad
    reset_pin: GPIO39
    cs_pin: GPIO12
    dimensions:
      height: 466
      width: 466
    update_interval: 33ms # ~30 FPS

# Gestione luminosità display
light:
  - platform: monochromatic
    id: display_backlight
    name: "Backlight"
    output: backlight_brightness
    restore_mode: ALWAYS_ON

output:
  - platform: template
    id: backlight_brightness
    type: float
    write_action:
      then:
        - lambda: |-
            id(disp1).set_brightness(state*255);

# Caricamento Font da Google Fonts
font:
  - file: "gfonts://Montserrat"
    id: font_huge
    size: 80
  - file: "gfonts://Montserrat"
    id: font_large
    size: 40
  - file: "gfonts://Montserrat"
    id: font_medium
    size: 26
  - file: "gfonts://Montserrat"
    id: font_small
    size: 20

# Variabile globale per memorizzare il setpoint locale prima di inviarlo ad HA
globals:
  - id: temp_target_buffer
    type: float
    initial_value: '20.0'

# =========================
# SENSORI & INTEGRAZIONE HA
# =========================
sensor:
  # Legge la temperatura attuale dal clima di casa
  - platform: homeassistant
    id: ha_current_temp
    entity_id: ${climate_entity}
    attribute: current_temperature
    on_value:
      then:
        - lambda: |-
            if (isnan(x)) return;
            std::string val = esphome::to_string(x).substr(0,4) + "°";
            lv_label_set_text(id(lbl_curr_temp), val.c_str());

  # Legge il setpoint attuale
  - platform: homeassistant
    id: ha_target_temp
    entity_id: ${climate_entity}
    attribute: temperature
    on_value:
      then:
        - lambda: |-
            if (isnan(x)) return;
            int val = (int)(x * 10);
            lv_arc_set_value(id(my_arc), val);
            std::string txt = esphome::to_string(x).substr(0,4) + "°";
            lv_label_set_text(id(lbl_target_temp), txt.c_str());

# Script per inviare la temperatura ad HA (con un piccolo delay per non floodare)
script:
  - id: send_temp_to_ha
    mode: restart
    then:
      - delay: 1s
      - homeassistant.service:
          service: climate.set_temperature
          data:
            entity_id: ${climate_entity}
            temperature: !lambda 'return lv_arc_get_value(id(my_arc)) / 10.0;'

# =========================
# INTERFACCIA GRAFICA LVGL
# =========================
lvgl:
  displays:
    - disp1
  touchscreens:
    - my_touch
  # Buffer al 20% è il "sweet spot" per evitare artefatti grafici su questo chip
  buffer_size: 20%

  # Definizione degli stili grafici riutilizzabili
  style_definitions:
    # --- STILI TERMOSTATO ---
    - id: st_arc_track
      arc_color: 0x1A1A1A # Grigio scuro (sfondo ghiera)
      arc_width: 30
      arc_rounded: true
    - id: st_arc_indicator
      arc_color: 0xFF4500 # Arancione (valore attivo)
      arc_width: 30
      arc_rounded: true
    - id: st_text_huge
      text_color: 0xFFFFFF
      text_font: font_huge

    # --- STILI BOTTONI ---
    - id: st_nav_btn
      bg_color: 0x222222
      bg_opa: 80%
      radius: 50%
      text_color: 0x888888
      text_font: font_medium

    - id: st_btn_light
      bg_color: 0x222222
      radius: 15
      border_width: 2
      border_color: 0x444444
      text_color: 0xFFFFFF
      text_font: font_medium

    - id: st_btn_shutter
      bg_color: 0x222222
      radius: 20
      border_width: 0
      text_color: 0x00D0FF # Ciano Jarvis
      text_font: font_large

    - id: st_btn_stop
      bg_color: 0x880000 # Rosso
      radius: 20
      text_color: 0xFFFFFF
      text_font: font_medium

  pages:
    # ---------------------------
    # PAGINA 1: TERMOSTATO
    # ---------------------------
    - id: pg_thermostat
      widgets:
        - obj:
            width: 466
            height: 466
            bg_color: 0x000000 # Nero assoluto per fondersi con la cornice
            radius: 0
            scrollbar_mode: "OFF"
            widgets:
              # Ghiera interattiva
              - arc:
                  id: my_arc
                  width: 420
                  height: 420
                  align: CENTER
                  min_value: 100
                  max_value: 300
                  start_angle: 135
                  end_angle: 45
                  adjustable: true
                  styles: st_arc_track
                  indicator:
                    styles: st_arc_indicator
                  on_value:
                    - lambda: |-
                        int val_int = lv_arc_get_value(id(my_arc));
                        float temp = val_int / 10.0;
                        std::string txt = esphome::to_string(temp).substr(0,4) + "°";
                        lv_label_set_text(id(lbl_target_temp), txt.c_str());
                    - script.execute: send_temp_to_ha

              # Etichette di testo
              - label:
                  id: lbl_curr_temp
                  text: "--.-°"
                  align: CENTER
                  y: -20
                  styles: st_text_huge
              - label:
                  text: "NEST THERMOSTAT"
                  align: CENTER
                  y: -75
                  text_font: font_small
                  text_color: 0xAAAAAA
              - label:
                  id: lbl_target_temp
                  text: "--.-°"
                  align: CENTER
                  y: 70
                  text_font: font_medium
                  text_color: 0xFF4500
              - label:
                  text: "SETPOINT"
                  align: CENTER
                  y: 100
                  text_font: font_small
                  text_color: 0xAAAAAA

              # Navigazione -> Vai a Luci
              - button:
                  width: 50
                  height: 50
                  align: RIGHT_MID
                  x: -65
                  styles: st_nav_btn
                  widgets:
                    - label:
                        text: ">"
                        align: CENTER
                  on_click:
                    - lvgl.page.show: pg_lights

    # ---------------------------
    # PAGINA 2: LUCI
    # ---------------------------
    - id: pg_lights
      widgets:
        - obj:
            width: 466
            height: 466
            bg_color: 0x000000
            radius: 0
            widgets:
              - label:
                  text: "LUCI SALA"
                  align: TOP_MID
                  y: 40
                  text_font: font_medium
                  text_color: 0xFFFF00

              # Pulsanti Luci (Chiamano direttamente il servizio HA)
              - button:
                  width: 220
                  height: 70
                  align: CENTER
                  y: -80
                  styles: st_btn_light
                  widgets:
                    - label:
                        text: "LUCE 1"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: switch.toggle
                        data:
                          entity_id: ${light_1}

              - button:
                  width: 220
                  height: 70
                  align: CENTER
                  y: 0
                  styles: st_btn_light
                  widgets:
                    - label:
                        text: "LUCE 2"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: switch.toggle
                        data:
                          entity_id: ${light_2}

              - button:
                  width: 220
                  height: 70
                  align: CENTER
                  y: 80
                  styles: st_btn_light
                  widgets:
                    - label:
                        text: "WAVE"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: switch.toggle
                        data:
                          entity_id: ${light_3}

              # Navigazione
              - button:
                  width: 50
                  height: 50
                  align: LEFT_MID
                  x: 65
                  styles: st_nav_btn
                  widgets:
                    - label:
                        text: "<"
                        align: CENTER
                  on_click:
                    - lvgl.page.show: pg_thermostat
              - button:
                  width: 50
                  height: 50
                  align: RIGHT_MID
                  x: -65
                  styles: st_nav_btn
                  widgets:
                    - label:
                        text: ">"
                        align: CENTER
                  on_click:
                    - lvgl.page.show: pg_shutter

    # ---------------------------
    # PAGINA 3: TAPPARELLA
    # ---------------------------
    - id: pg_shutter
      widgets:
        - obj:
            width: 466
            height: 466
            bg_color: 0x000000
            radius: 0
            widgets:
              - label:
                  text: "TAPPARELLA"
                  align: TOP_MID
                  y: 40
                  text_font: font_medium
                  text_color: 0x00D0FF

              # Comandi Tapparella
              - button:
                  width: 140
                  height: 90
                  align: CENTER
                  y: -100
                  styles: st_btn_shutter
                  widgets:
                    - label:
                        text: "▲"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: cover.open_cover
                        data:
                          entity_id: ${cover_entity}

              - button:
                  width: 140
                  height: 80
                  align: CENTER
                  y: 0
                  styles: st_btn_stop
                  widgets:
                    - label:
                        text: "STOP"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: cover.stop_cover
                        data:
                          entity_id: ${cover_entity}

              - button:
                  width: 140
                  height: 90
                  align: CENTER
                  y: 100
                  styles: st_btn_shutter
                  widgets:
                    - label:
                        text: "▼"
                        align: CENTER
                  on_click:
                    - homeassistant.service:
                        service: cover.close_cover
                        data:
                          entity_id: ${cover_entity}

              # Navigazione -> Torna a Luci
              - button:
                  width: 50
                  height: 50
                  align: LEFT_MID
                  x: 65
                  styles: st_nav_btn
                  widgets:
                    - label:
                        text: "<"
                        align: CENTER
                  on_click:
                    - lvgl.page.show: pg_lights

Considerazioni Finali

Questo progetto dimostra che anche con hardware "piccolo" si possono ottenere risultati professionali, a patto di ottimizzare bene il software. L'uso di dispositivi dedicati come questo Waveshare spesso supera i vecchi tablet Android riciclati in termini di affidabilità, estetica e soprattutto consumi.

Fatemi sapere nei commenti se proverete a replicarlo!

Restiamo Connessi

Se volete ricevere una notifica istantanea ogni volta che pubblico una nuova guida o un aggiornamento su questi progetti (e altri interessanti contenuti), vi invito a iscrivervi al mio Canale Telegram ufficiale:

Iscriviti al Canale Telegram di Vincenzo Caputo

Guarda il video su MissingTech

Volete vedere il Waveshare in azione? Nel video dedicato sul mio canale YouTube MissingTech, vi mostrerò il test dal vivo e una prova completa con la stupenda grafica di questo display touch!

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

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.

Disqus loading...