Skip to content

new features : partial refresh, gallery thumbnail, SD file read and thumbnail generator#2

Open
pierrehenrymuller wants to merge 9 commits into
GuySie:mainfrom
pierrehenrymuller:main
Open

new features : partial refresh, gallery thumbnail, SD file read and thumbnail generator#2
pierrehenrymuller wants to merge 9 commits into
GuySie:mainfrom
pierrehenrymuller:main

Conversation

@pierrehenrymuller

Copy link
Copy Markdown

Why

What

Public driver additions, with no change to the existing art-frame path:

  • Render/push split: render_framebuffer() + flush_zone(x, y, w, h, mode = 1) (and the refresh_zone() convenience) for partial-area DU updates with no full-screen flash. full_refresh() is kept for boot and the periodic deghost.
  • show_sd_image(path): load a 1872x1404 4bpp .raw straight from the microSD card (shared SPI bus) and push it in GC16, no network needed.
  • Thumbnail cache: make_thumbnail(), sd_file_exists(), draw_sd_thumbnail() for a paginated 3x3 gallery.
  • SD access via SdFat in SHARED_SPI mode so it coexists with the IT8951 CS on the shared bus.
  • README: a reference section per feature plus copy-pasteable examples (clock zone refresh, a touch button calling a Home Assistant action, a paginated thumbnail grid, touch photo navigation).

Alternatives considered

  • Keep a full-screen GC16 per change: too slow and flashes the whole panel, unusable for a once-a-minute clock.
  • Keep downloading images from HA (existing online_image path): needs the network and HA up. The SD path keeps a photo frame working offline.
  • A separate SD component: would fight the IT8951 over the shared SPI CS. Embedding SdFat SHARED_SPI in the driver keeps CS coordination in one place.

Tradeoffs and impact

  • DU partial updates accumulate ghosting, mitigated by a periodic full_refresh() (documented).
  • SdFat adds a library dependency and a small amount of flash.
  • No behavior change for the existing art-frame config: every addition is a new public method.

pierrehenrymuller and others added 9 commits June 13, 2026 13:00
Pourquoi: en mode live alimente sur secteur, rafraichir tout l'ecran en GC16
(flash ~0.5-1s) a chaque changement de capteur est lent et visuellement penible.
Le materiel IT8951 sait deja rafraichir un sous-rectangle avec une waveform
rapide (DU mode 1, sans flash) mais le composant cablait update() en plein ecran.

Approche: exposer deux methodes publiques sans casser l'existant.
- full_refresh(): comportement historique (INIT mode 0 + GC16 mode 2), utilise au
  boot et pour le deghost nocturne; reset le compteur de partiels.
- refresh_zone(x,y,w,h,mode=1): re-rend le framebuffer complet (pour refleter les
  valeurs courantes) puis ne pousse que le rectangle demande en DU. Conversion des
  coordonnees logiques vers panneau (framebuffer miroir en X) et alignement x/w sur
  4px (contrainte 4bpp). Garde-fou: full_refresh force tous les 60 partiels pour
  purger le ghosting.
update() delegue desormais a full_refresh(). Extraction de wake_panel_/sleep_panel_
pour mutualiser la sequence reveil/sommeil de l'IT8951.

Alternatives ecartees: chemin 1bpp partiel (alignement 16px plus contraignant et
re-packing complexe) -> on reutilise le slice direct du framebuffer 4bpp, byte-order
identique au chemin plein ecran. Pas de patch du flux de sommeil (le panneau dort
toujours entre rafraichissements: ~10ms de reveil, image bistable conservee).

Impact: aucune regression sur le mode plein ecran; nouvelles methodes opt-in
appelables depuis un lambda ESPHome. Securite: bornage/clamp des rectangles,
buffer de ligne borne a 936 octets (largeur max 1872px en 4bpp).
Pourquoi: en mode live, refresh_zone() re-rendait le framebuffer complet
(do_update_, lambda ~3.3s a cause des ecritures PSRAM) a CHAQUE zone. Un tick
multi-zones bloquait la boucle principale ~12s (3 zones), gelant WiFi/API.

Approche: separer le rendu (couteux, 1x) du push (rapide, par zone).
- render_framebuffer(): execute la lambda une fois (remplit le framebuffer RAM).
- flush_zone(x,y,w,h,mode=1): pousse seulement le rectangle, SANS re-rendre
  (suppose le framebuffer a jour). Coeur deplace depuis l'ancien refresh_zone.
- refresh_zone(): devient render_framebuffer() + flush_zone() (zone isolee/event).
Le YAML appelle render_framebuffer() une fois puis flush_zone() par zone -> un
seul rendu par tick au lieu de N.

Impact: tick multi-zones passe de ~N*3.3s a ~3.3s + N*0.4s. Pas de changement de
comportement par zone; aucune regression plein ecran.
Mesure live: chaque flush_zone (DU) coute ~2.4s (sequencage d'alim EPD par appel
DPY_AREA, inherent au panneau 10"), independamment de la taille -> N zones = N*2.4s.
Le rendu (do_update_) coutait ~3.3s a cause de fill() qui bouclait pixel par pixel
(~2.6M appels draw_absolute_pixel) pour l'auto-clear.

Corrections:
- fill(Color) override en memset sur le framebuffer 4bpp (nibble*0x11). L'auto-clear
  par frame devient quasi instantane -> rendu ~0.3s.
- Consequence strategie: un DU PLEIN ECRAN (flush_zone(0,0,1872,1404,1)) coute ~le
  meme qu'une zone (~2.4s) et reste sans flash. Donc 1 DU plein ecran >> N zones.
  Le YAML rafraichit tout en un seul DU au lieu de decouper en zones.
- MAX_PARTIALS_BEFORE_FULL 60 -> 180 (avec un DU/minute, GC16 toutes les ~3h au
  lieu de chaque heure).

Impact: tick live ~0.3s (rendu) + ~2.4s (DU) au lieu de ~17s. Pas de regression
plein ecran GC16 (deghost). fill() override est transparent pour le reste.
Phase A photos: lire un fichier .raw (1872x1404 4bpp, format exact du
framebuffer) depuis la carte SD et l'afficher en 16 gris (GC16).

- SD sur le bus SPI PARTAGE avec l'IT8951 (un seul maitre Arduino SPI, CS
  distincts: ecran=10, SD=14). Alim SD = GPIO39 (SD_EN) activee au setup.
- setup(): SD.begin(14, SPI, 10MHz) apres l'init IT8951; flag sd_ok_.
- show_sd_image(path): SD.open + lecture par blocs dans framebuffer_ (1.3 Mo),
  puis upload 4bpp + INIT + GC16. Reutilise wake/sleep_panel_.

Risque connu (a valider sur hard): coexistence SD.h / Arduino SPI direct de
l'IT8951 sur le meme bus. CS coordonnes, transactions sequentielles.
Pourquoi: le mode photo n'offrait qu'une navigation lineaire (suivante/precedente).
Pour selectionner directement une image il faut une grille de miniatures cliquables.
Generer une miniature a chaque affichage serait lent (lecture de tout le .raw plein
ecran ~1,3 Mo par vignette); d'ou un cache SD.

Approche: trois methodes publiques sur le driver IT8951.
- make_thumbnail(src,dst): charge le .raw plein ecran dans le framebuffer (reutilise
  la boucle de lecture de show_sd_image), sous-echantillonne en nearest d'un facteur
  THUMB_FACTOR=4 (468x351) puis ecrit un .raw miniature 4bpp row-major LOGIQUE.
- draw_sd_thumbnail(path,dx,dy): blit de la miniature dans le framebuffer au coin
  logique (dx,dy), sans declencher de refresh (a appeler depuis le lambda d'affichage).
- sd_file_exists(path): test du cache (genere une seule fois par image).
Helpers get_pixel_logical_/set_pixel_nibble_ pour rester en coordonnees logiques
(le framebuffer stocke l'image inversee en X comme draw_absolute_pixel_internal),
ce qui rend generation et blit symetriques: le mirror s'annule.

Alternatives ecartees: generation hors-appareil via outil + recopie SD (impose de
regenerer/recopier la carte a chaque ajout de photo); sous-echantillon a la volee
sans cache (trop lent, relit toute la source a chaque rendu de menu).

Compromis: nearest (pas de moyenne de bloc) -> vignettes un peu crues mais generation
rapide; ~82 Ko par miniature sur SD. La generation ecrase le framebuffer (chargement
de la source) -> make_thumbnail doit etre appele AVANT un rendu, jamais pendant.

Impact: aucune regression sur show_sd_image (boucle de lecture inchangee, reutilisee).
Bus SPI SD partage deja gere. Teste sur le modele E1003.
The driver added several public methods on top of the upstream art-frame
flow (render_framebuffer/flush_zone/refresh_zone for flash-free partial
updates, show_sd_image for network-free rendering from the SD card, and
make_thumbnail/draw_sd_thumbnail/sd_file_exists for a cached gallery).
None of these were documented, so adopters had to read the header to
discover them.

Add a reference section per feature plus an 'Interactive examples' section
with copy-pasteable YAML: clock zone refresh, a touch button calling a HA
action, a paginated thumbnail grid, and touch photo navigation.
Documentation only, no code change; keeps the PR series self-explanatory
for upstream review.
@pierrehenrymuller pierrehenrymuller changed the title The E1003 driver only supported the art-frame flow (one full-screen GC16 refresh per wake, then deep sleep). Building an always-on, interactive dashboard on this panel needs three things the driver did not expose: flash-free partial updates, rendering images without a Home Assistant download, and a cached thumbnail gallery for a picture menu. new features : partial refresh, gallery thumbnail, SD file read and thumbnail generator Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant