8x32 PI : interface web, matrice verticale, compatibilité Raspbian 11

- Interface web (serveur_web.py, Flask, gabarits dans web/) : dépôt de GIF,
  PNG animés et séquences de PNG, aperçus animés, ordre de lecture,
  activer / désactiver, corbeille, « jouer maintenant » et « suivante ».
  Service systemd vitrine-web, activé par installer.sh.
- Lecteur : réagit aux demandes de la page web (post-it dans
  /run/vitrine-pixels), écrit l'animation en cours, éteint la matrice
  proprement sur SIGTERM.
- Matrice posée debout : réglage ROTATION (90 = image 8 × 32) dans
  matrice.py ; images couchées tournées automatiquement ; exemples,
  animations codées, aperçus et page web adaptés au format vertical.
- Compatibilité Raspbian 11 Bullseye : partition /boot, Pillow 8.1, Flask 1.1.
- Doc : nouveau guide INTERFACE_WEB.md ; README, GUIDE_KRITA, MODE_VITRINE,
  CODER_EN_PYTHON mis à jour (format 8 × 32, adresses Wi-Fi / câble du Pi).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bach
2026-10-08 16:08:18 +02:00
co-authored by Claude Opus 5.5
parent 98fca33142
commit 2d4b0d5a5f
63 changed files with 1592 additions and 156 deletions
+62 -22
View File
@@ -68,10 +68,25 @@ from PIL import Image
# 1. RÉGLAGES
# =============================================================================
# Taille de la matrice : 32 colonnes (largeur) × 8 lignes (hauteur).
LARGEUR = 32
HAUTEUR = 8
NB_LEDS = LARGEUR * HAUTEUR # = 256
# Le PANNEAU, tel qu'il est fabriqué : 32 colonnes × 8 lignes de LED,
# câblées en serpentin par colonnes de 8. Ça ne change jamais.
COLONNES_PANNEAU = 32
LIGNES_PANNEAU = 8
NB_LEDS = COLONNES_PANNEAU * LIGNES_PANNEAU # = 256
# ROTATION : comment le panneau est POSÉ dans la vitrine, en degrés.
# 0 = couché (à l'horizontale) : l'image fait 32 de large × 8 de haut ;
# 90 = debout (à la verticale) : l'image fait 8 de large × 32 de haut.
# (180 et 270 existent aussi : couché à l'envers, debout dans l'autre sens.
# Pour remettre l'image à l'endroit, INVERSER_X / INVERSER_Y suffisent.)
ROTATION = 90
# Taille de l'IMAGE à dessiner (dans Krita, dans le code…), selon la rotation.
# Tout le reste du programme utilise LARGEUR et HAUTEUR.
if ROTATION in (90, 270):
LARGEUR, HAUTEUR = LIGNES_PANNEAU, COLONNES_PANNEAU # 8 × 32 : debout
else:
LARGEUR, HAUTEUR = COLONNES_PANNEAU, LIGNES_PANNEAU # 32 × 8 : couché
# Broche du Raspberry Pi reliée (à travers l'adaptateur 74AHCT125) au fil
# VERT (DIN) de la matrice. Ce numéro est le numéro « GPIO », pas le numéro
@@ -101,10 +116,11 @@ LUMINOSITE = 40
# (ex. : alimentation d'établi limitée à 3 A → 2500 mA ici).
COURANT_MAX_MA = 2500
# ORIENTATION de la matrice (voir test_matrice.py).
# SENS de l'image (voir test_matrice.py), une fois ROTATION choisie.
# Le pixel ROUGE du test doit être EN HAUT À GAUCHE, le VERT EN HAUT À DROITE.
# - rouge et vert inversés (gauche/droite) → INVERSER_X = True
# - rouge et vert en bas au lieu d'en haut → INVERSER_Y = True
# - les deux à la fois (image à l'envers) → les deux à True
INVERSER_X = False
INVERSER_Y = False
@@ -159,35 +175,57 @@ def teinte(t, saturation=255, valeur=255):
# 3. DE LA POSITION (x, y) AU NUMÉRO DE LED
# =============================================================================
#
# Pour dessiner, on pense en coordonnées :
# x = colonne, de 0 (à gauche) à 31 (à droite)
# y = ligne, de 0 (en haut) à 7 (en bas)
# Pour dessiner, on pense en coordonnées, sur l'IMAGE telle qu'on la voit :
# x = colonne, de 0 (à gauche) à LARGEUR - 1 (à droite)
# y = ligne, de 0 (en haut) à HAUTEUR - 1 (en bas)
# Le point (0, 0) est en haut à gauche, comme dans Krita.
#
# Mais les LED sont numérotées dans l'ordre de la chaîne, qui fait des
# zigzags (« serpentin ») : elle descend la 1re colonne, remonte la 2e…
# Pour trouver la LED correspondante, trois étapes :
#
# x=0 x=1 x=2 x=3
# y=0 0 15 16 31 …
# y=1 1 14 17 30
# … … … … …
# y=7 7 → 8 23 → 24
# 1. Retourner l'image si besoin (INVERSER_X / INVERSER_Y).
#
# C'est la même fonction que dans matrice_8x32.ino, traduite en Python.
# 2. Tourner : passer de la position sur l'IMAGE à la position sur le
# PANNEAU (px de 0 à 31, py de 0 à 7, panneau vu couché). Exemple,
# panneau debout (ROTATION = 90, tourné d'un quart de tour vers la
# droite) : le haut de l'image correspond à la gauche du panneau, et la
# droite de l'image au haut du panneau.
#
# 3. Le serpentin : les LED sont numérotées dans l'ordre de la chaîne, qui
# descend la 1re colonne du panneau, remonte la 2e…
#
# px=0 px=1 px=2 px=3
# py=0 0 15 16 31 …
# py=1 1 14 17 30
# … … … … …
# py=7 7 → 8 23 → 24
#
# L'étape 3 est la même fonction que dans matrice_8x32.ino.
def XY(x, y):
"""Renvoie le numéro (0 à 255) de la LED placée en colonne x, ligne y."""
"""Renvoie le numéro (0 à 255) de la LED qui affiche le pixel (x, y) de l'image."""
# 1. Retourner.
if INVERSER_X:
x = (LARGEUR - 1) - x
if INVERSER_Y:
y = (HAUTEUR - 1) - y
if x % 2 == 0:
# Colonne paire : la chaîne DESCEND.
return x * HAUTEUR + y
# 2. Tourner : position (px, py) sur le panneau couché.
if ROTATION == 90:
px, py = y, (LIGNES_PANNEAU - 1) - x
elif ROTATION == 180:
px, py = (COLONNES_PANNEAU - 1) - x, (LIGNES_PANNEAU - 1) - y
elif ROTATION == 270:
px, py = (COLONNES_PANNEAU - 1) - y, x
else:
px, py = x, y
# 3. Le serpentin.
if px % 2 == 0:
# Colonne paire du panneau : la chaîne DESCEND.
return px * LIGNES_PANNEAU + py
else:
# Colonne impaire : la chaîne REMONTE.
return x * HAUTEUR + (HAUTEUR - 1 - y)
return px * LIGNES_PANNEAU + (LIGNES_PANNEAU - 1 - py)
# =============================================================================
@@ -316,7 +354,9 @@ class MatriceReelle:
class Simulateur:
"""Une fenêtre sur l'ordinateur qui imite la matrice (256 ronds)."""
TAILLE = 26 # taille d'un pixel à l'écran, en points
# Taille d'un pixel à l'écran, en points : plus petit si l'image est
# haute (panneau debout), pour que la fenêtre tienne dans l'écran.
TAILLE = min(26, 640 // max(LARGEUR, HAUTEUR))
MARGE = 4 # espace entre deux pixels
def __init__(self, titre="Simulateur — matrice 8 × 32"):