Files
vitrine-pixels/8x32 PI/outils/schemas.py
T
bachandClaude Opus 5.5 2d4b0d5a5f 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>
2026-10-08 16:08:18 +02:00

455 lines
22 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Dessine les schémas du dossier « schemas » :
- branchement.svg : Raspberry Pi + 74AHCT125 + matrice + alimentation
- branchement_sans_puce.svg : solution A sans puce (matrice en 4,4 V)
- branchement_transistor.svg : solution B sans puce (transistor inverseur)
- brochage_74ahct125.svg : la puce adaptatrice de niveau, patte par patte
- chaine_krita_matrice.svg : le trajet d'une animation, de Krita à la matrice
Usage : python3 outils/schemas.py"""
import os
ICI = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SORTIE = os.path.join(ICI, "schemas")
ROUGE, NOIR, BLANC_FIL, VERT = "#d62828", "#1a1a1a", "#bdbdbd", "#2a9d3a"
JAUNE_GPIO = "#e0a800"
class Svg:
def __init__(self, l, h, titre):
self.l, self.h = l, h
self.out = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{l}" height="{h}" '
f'viewBox="0 0 {l} {h}" font-family="Helvetica, Arial, sans-serif">',
f' <title>{titre}</title>',
' <defs><marker id="fleche" viewBox="0 0 10 10" refX="8" refY="5" '
'markerWidth="7" markerHeight="7" orient="auto"><path d="M1 1 L9 5 L1 9 Z" '
'fill="context-stroke"/></marker>'
'<marker id="flecheg" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" '
'markerHeight="6" orient="auto"><path d="M1 1 L9 5 L1 9 Z" fill="#555"/></marker>'
'<marker id="flechec" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" '
'markerHeight="7" orient="auto"><path d="M1 1 L9 5 L1 9 Z" fill="#cfcfcf"/></marker>'
'</defs>',
f' <rect width="{l}" height="{h}" fill="#ffffff"/>']
def add(self, s):
self.out.append(" " + s)
def rect(self, x, y, w, h, fill, rx=0, stroke=None, sw=1):
st = f' stroke="{stroke}" stroke-width="{sw}"' if stroke else ""
self.add(f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{rx}" fill="{fill}"{st}/>')
def texte(self, x, y, s, taille=13, c="#333", ancre="start", gras=False, mono=False):
g = ' font-weight="bold"' if gras else ""
m = ' font-family="monospace"' if mono else ""
self.add(f'<text x="{x}" y="{y}" font-size="{taille}" fill="{c}" '
f'text-anchor="{ancre}"{g}{m}>{s}</text>')
def fil(self, d, c, w=5):
self.add(f'<path d="{d}" stroke="{c}" stroke-width="{w}" fill="none" '
'stroke-linecap="round" stroke-linejoin="round"/>')
def point(self, x, y, c, r=5):
self.add(f'<circle cx="{x}" cy="{y}" r="{r}" fill="{c}"/>')
def save(self, nom):
self.out.append("</svg>")
with open(os.path.join(SORTIE, nom), "w") as f:
f.write("\n".join(self.out) + "\n")
def hop_h(x_croise, sens=1, r=7):
"""Petit pont (demi-cercle) pour un fil horizontal qui en enjambe un autre.
sens = 1 : on va vers la droite ; -1 : vers la gauche."""
return (f"H{x_croise - sens * r} a{r} {r} 0 0 {1 if sens == 1 else 0} "
f"{2 * r * sens} 0")
# =============================================================================
# 1. BRANCHEMENT
# =============================================================================
def _matrice_et_alimentation(s, tension="5.00 V"):
"""Partie commune à tous les montages : matrice vue de dos, sortie DOUT,
alimentation d'établi + condensateur, connecteur DIN et sa fiche."""
# ---------- matrice (vue de dos) ----------
s.texte(60, 38, "Matrice WS2812B 8 × 32 — vue de dos (côté fils)", 20, "#222", gras=True)
s.add('<pattern id="pix" x="60" y="55" width="30.625" height="30.625" '
'patternUnits="userSpaceOnUse"><rect x="9" y="9" width="12" height="12" rx="1" '
'fill="#2b2b2b"/></pattern>')
s.rect(60, 55, 980, 245, "#141414", 6)
s.rect(60, 55, 980, 245, "url(#pix)", 6)
s.add('<line x1="760" y1="150" x2="340" y2="150" stroke="#cfcfcf" stroke-width="3" '
'marker-end="url(#flechec)"/>')
s.texte(550, 135, "les données entrent par DIN et traversent les 256 LED", 15, "#e6e6e6", "middle")
for x, t in [(110, "5V"), (150, "GND"), (194, "DOUT"), (490, "5V"), (550, "GND"),
(880, "5V"), (910, "GND"), (940, "DIN")]:
s.texte(x, 268, t, 12, "#cfcfcf", "middle")
# ---------- sortie DOUT ----------
for x, c in [(110, ROUGE), (150, BLANC_FIL), (190, VERT)]:
s.fil(f"M{x} 282 V340", c)
s.rect(96, 340, 108, 24, "#222", 3)
s.texte(150, 390, "Sortie DOUT : ne rien brancher", 14, "#555", "middle")
s.texte(150, 408, "(sert à chaîner un 2e panneau)", 13, "#888", "middle")
# ---------- alimentation de la matrice (fils du milieu) ----------
s.fil("M490 282 V440 H530 V586", ROUGE, 6)
s.fil("M550 282 V420 H590 V586", NOIR, 6)
s.add('<line x1="490" y1="352" x2="506" y2="352" stroke="#777" stroke-width="2"/>')
s.add('<line x1="534" y1="352" x2="550" y2="352" stroke="#777" stroke-width="2"/>')
s.rect(506, 340, 28, 24, "#1f4e9c", 5)
s.rect(524, 340, 10, 24, "#c9d6ee")
s.texte(529, 356, "−", 13, "#1f4e9c", "middle", True)
s.texte(474, 340, "Condensateur 1000 µF", 14, "#1f4e9c", "end", True)
s.texte(474, 358, "bande « − » côté GND (fil noir)", 13, "#1f4e9c", "end")
s.texte(474, 375, "patte + côté 5V (fil rouge)", 13, "#1f4e9c", "end")
# ---------- alimentation d'établi ----------
s.rect(300, 500, 330, 210, "#e9ecef", 10, "#adb5bd", 2)
s.texte(465, 526, "Alimentation d'établi", 16, "#333", "middle", True)
s.rect(318, 540, 160, 96, "#1d2b1d", 4)
s.texte(398, 578, tension, 28, "#7dff7d", "middle", True, True)
s.texte(398, 618, "3.00 A", 20, "#ffb347", "middle", mono=True)
for x, t in [(345, "tension"), (420, "courant")]:
s.add(f'<circle cx="{x}" cy="672" r="16" fill="#666"/>')
s.texte(x, 702, t, 11, "#555", "middle")
s.add(f'<circle cx="530" cy="600" r="13" fill="{ROUGE}" stroke="#7a0f0f" stroke-width="2"/>')
s.add(f'<circle cx="590" cy="600" r="13" fill="{NOIR}" stroke="#000" stroke-width="2"/>')
s.texte(530, 637, "+", 15, ROUGE, "middle", True)
s.texte(590, 637, "−", 15, NOIR, "middle", True)
s.texte(560, 660, "bornes de", 12, "#555", "middle")
s.texte(560, 675, "sortie", 12, "#555", "middle")
# ---------- entrée DIN + fiche ----------
for x, c in [(880, ROUGE), (910, BLANC_FIL), (940, VERT)]:
s.fil(f"M{x} 282 V340", c)
s.rect(866, 340, 88, 22, "#222", 3)
s.rect(870, 364, 80, 22, "#3a3a3a", 3)
s.texte(966, 358, "connecteur DIN de la matrice", 13, "#555")
s.texte(966, 381, "+ fiche fournie (rouge / blanc / vert)", 13, "#555")
def _resistance_v(s, x, y, couleurs, valeur, sous_titre=None):
"""Résistance verticale de 40 de haut, centrée en x, du haut y à y + 40."""
s.rect(x - 9, y, 18, 40, "#e8d3a8", 6, "#a88a52")
for i, c in enumerate(couleurs):
s.rect(x - 9, y + 8 + i * 8, 18, 4, c)
s.rect(x - 9, y + 32, 18, 3, "#c9a227")
s.texte(x + 18, y + 16, valeur, 13, "#7a5a1e", gras=True)
if sous_titre:
s.texte(x + 18, y + 33, sous_titre, 12, "#7a5a1e")
def _resistance_h(s, x, y, couleurs, valeur):
"""Résistance horizontale de 40 de large, centrée en y, de x à x + 40."""
s.rect(x, y - 9, 40, 18, "#e8d3a8", 6, "#a88a52")
for i, c in enumerate(couleurs):
s.rect(x + 8 + i * 8, y - 9, 4, 18, c)
s.rect(x + 32, y - 9, 3, 18, "#c9a227")
s.texte(x + 20, y - 15, valeur, 13, "#7a5a1e", "middle", True)
ANNEAUX_330 = ["#e67e22", "#e67e22", "#8b4513"] # orange-orange-marron
ANNEAUX_1K = ["#8b4513", "#1a1a1a", ROUGE] # marron-noir-rouge
def _raspberry_pi(s, x0, pas=16, y=645):
"""Carte Raspberry Pi avec son connecteur 40 pattes.
x0 = abscisse de la patte 2 ; renvoie un dictionnaire n° de patte → x."""
largeur = 19 * pas + 104
gauche = x0 - 52
s.rect(gauche, y, largeur, 230, "#1f7a3a", 12)
s.rect(x0 - 13, y + 10, 19 * pas + 26, 42, "#143d20", 3)
xs = {}
for i in range(20):
for n, yy in ((2 * i + 2, y + 21), (2 * i + 1, y + 39)): # bord : paires
xs[n] = x0 + i * pas
couleur = "#d4af37"
if n in (2, 4):
couleur = ROUGE
elif n in (6, 14):
couleur = NOIR
elif n == 12:
couleur = JAUNE_GPIO
s.rect(xs[n] - 5, yy - 5, 10, 10, couleur, 1)
s.texte(x0 - 9, y + 25, "2", 9, "#cde", "end")
s.texte(x0 - 9, y + 43, "1", 9, "#cde", "end")
s.texte(x0 + 19 * pas + 9, y + 25, "40", 9, "#cde")
s.texte(x0 + 19 * pas + 9, y + 43, "39", 9, "#cde")
centre = gauche + largeur / 2
s.texte(centre, y + 125, "Raspberry Pi", 24, "#fff", "middle", True)
s.texte(centre, y + 149, "3B+, 4 ou Zero 2 W (pas le Pi 5)", 14, "#d8f0dd", "middle")
s.texte(xs[12] + 3, y + 67, "patte 12 = GPIO 18", 12, "#ffe08a", "start", True)
s.texte(xs[6] - 2, y + 67, "patte 6 = GND", 12, "#fff", "end", True)
s.add(f'<path d="M{gauche + 30} {y + 188} l16 16 m0 -16 l-16 16" '
f'stroke="#ffb3b3" stroke-width="3"/>')
s.texte(gauche + 56, y + 201, "pattes 2 et 4 (5 V) : NE PAS relier à la matrice", 12, "#ffd6d6")
# alimentation USB du Pi
droite = gauche + largeur
s.rect(droite, y + 145, 18, 34, "#b8b8b8", 3)
s.fil(f"M{droite + 18} {y + 162} H{droite + 30} V{y + 255}", "#555")
s.texte(droite + 25, y + 273, "bloc secteur USB du Pi", 13, "#333", "end")
s.texte(droite + 25, y + 290, "(5 V, 2,5 A ou plus)", 12, "#777", "end")
return xs
def _legende(s, fils, lignes_gnd, alerte, notes=()):
s.rect(40, 470, 236, 140 + 28 * len(fils) + 18 * len(lignes_gnd) + 14 * len(notes),
"#f8f9fa", 8, "#dee2e6")
s.texte(56, 496, "Code couleur des fils", 15, "#222", gras=True)
for i, (c, t) in enumerate(fils):
y = 520 + i * 28
s.add(f'<line x1="56" y1="{y}" x2="90" y2="{y}" stroke="{c}" stroke-width="6" '
'stroke-linecap="round"/>')
s.texte(100, y + 5, t, 14)
y = 520 + len(fils) * 28 + 10
for ligne in lignes_gnd:
s.texte(56, y, ligne, 13, "#555")
y += 18
y += 6
s.texte(56, y, alerte, 13, "#b00020", gras=True)
y += 22
for ligne in notes:
s.texte(56, y, ligne, 12, "#777")
y += 14
FILS_5V = [(ROUGE, "rouge = + 5 V"), (NOIR, "noir = GND (masse)"),
(BLANC_FIL, "blanc = GND (masse)"), (VERT, "vert = données 5 V"),
(JAUNE_GPIO, "jaune = données 3,3 V")]
def branchement():
s = Svg(1200, 940, "Branchement matrice WS2812B 8×32, Raspberry Pi, 74AHCT125 "
"et alimentation d'établi")
_matrice_et_alimentation(s)
# ---------- plaque d'essai + 74AHCT125 ----------
s.rect(700, 425, 440, 190, "#f6f1e3", 8, "#d9cfb2", 2)
s.texte(1132, 608, "plaque d'essai", 12, "#a8997a", "end")
def broche_bas(n): # pattes 1 à 7 (rangée du bas), de gauche à droite
return 815 + (n - 1) * 28
def broche_haut(n): # pattes 14 à 8 (rangée du haut), de gauche à droite
return 815 + (14 - n) * 28
# fils de la fiche vers la plaque
s.fil(f"M880 386 V444 H{broche_haut(14)} V462", ROUGE) # 5V → VCC (14)
s.fil(f"M910 386 V454 {hop_h(broche_haut(14), -1)} H780 V585", BLANC_FIL) # GND → rail
s.fil(f"M940 386 V444 H1080 V470", VERT) # DIN → résistance
s.fil(f"M1080 510 V552 {hop_h(broche_bas(7), -1)} H{broche_bas(3)} V528", VERT)
# rail de masse
s.fil(f"M780 585 {hop_h(broche_bas(2), 1)} H{broche_bas(7)}", NOIR, 4)
s.fil(f"M{broche_bas(1)} 528 V585", NOIR, 4) # 1OE → GND
s.fil(f"M{broche_bas(7)} 528 V585", NOIR, 4) # GND (7)
for x in (780, broche_bas(1), broche_bas(7)):
s.point(x, 585, NOIR)
# la puce
s.rect(805, 470, 192, 50, "#222", 4)
s.add('<path d="M805 485 a10 10 0 0 1 0 20" fill="#555"/>')
s.texte(901, 500, "74AHCT125", 15, "#eee", "middle", True)
for n in range(1, 8):
s.rect(broche_bas(n) - 5, 520, 10, 8, "#b8b8b8")
s.texte(broche_bas(n), 515, str(n), 9, "#bbb", "middle")
for n in range(8, 15):
s.rect(broche_haut(n) - 5, 462, 10, 8, "#b8b8b8")
s.texte(broche_haut(n), 482, str(n), 9, "#bbb", "middle")
s.texte(broche_bas(1) - 10, 545, "1OE", 11, "#333", "end", True)
s.texte(broche_bas(2) + 6, 545, "1A", 11, JAUNE_GPIO, "start", True)
s.texte(broche_bas(3) + 6, 545, "1Y", 11, VERT, "start", True)
s.texte(broche_bas(7) + 8, 545, "GND", 11, "#333", "start", True)
_resistance_v(s, 1080, 470, ANNEAUX_330, "330 Ω")
s.texte(966, 404, "fil rouge de la fiche : il alimente", 12, ROUGE, gras=True)
s.texte(966, 418, "la puce (patte 14 = VCC, 5 V)", 12, ROUGE, gras=True)
# ---------- Raspberry Pi ----------
pattes = _raspberry_pi(s, broche_bas(2) - 80) # la patte 12 tombe sous « 1A »
s.fil(f"M{broche_bas(2)} 528 V661", JAUNE_GPIO) # GPIO 18 → 1A
s.fil(f"M780 585 V630 H{pattes[6]} V661", NOIR, 4) # GND (patte 6) ← rail
_legende(s, FILS_5V,
["Tous les GND sont reliés :", "Pi ↔ puce ↔ fil blanc ↔ matrice",
"↔ fil noir ↔ borne − de l'alim."],
"Alim. réglée sur 5,0 V maxi !",
["Un pont = les fils se croisent", "sans se toucher."])
s.save("branchement.svg")
def branchement_sans_puce():
"""Solution A : pas d'adaptateur, la matrice est alimentée en 4,4 V."""
s = Svg(1200, 940, "Branchement sans 74AHCT125 (solution A) : matrice en 4,4 V, "
"Raspberry Pi relié directement")
_matrice_et_alimentation(s, "4.40 V")
pattes_x0 = 832 # le Pi doit tenir dans la largeur du dessin
# fil rouge de la fiche : isolé
s.fil("M880 386 V420", ROUGE)
s.rect(872, 418, 16, 16, "#f2c94c", 2)
s.texte(864, 431, "fil rouge : NON branché, isolé (scotch)", 13, "#b00020", "end", True)
# fil vert → 330 Ω → patte 12 ; fil blanc → patte 6
s.fil("M940 386 V440 H948 V470", VERT)
_resistance_v(s, 948, 470, ANNEAUX_330, "330 Ω", "(300 à 500 Ω)")
pattes = _raspberry_pi(s, pattes_x0, pas=14)
s.fil(f"M948 510 V620 H{pattes[12]} V661", JAUNE_GPIO)
s.fil(f"M910 386 V600 H{pattes[6]} V661", BLANC_FIL)
# encadré d'explication
s.rect(660, 455, 230, 120, "#fff8e1", 8, "#f2c94c", 2)
for i, (t, gras) in enumerate([("Pourquoi 4,4 V ?", True),
("Le « 1 » doit dépasser 70 %", False),
("de la tension des LED :", False),
("0,7 × 4,4 V ≈ 3,1 V", True),
("→ les 3,3 V du Pi suffisent.", False)]):
s.texte(675, 480 + i * 20, t, 13, "#7a5a00", gras=gras)
_legende(s, [(ROUGE, "rouge = + 4,4 V"), (NOIR, "noir = GND (masse)"),
(BLANC_FIL, "blanc = GND (masse)"), (VERT, "vert = données"),
(JAUNE_GPIO, "jaune = données 3,3 V")],
["Tous les GND sont reliés :", "Pi ↔ fil blanc ↔ matrice",
"↔ fil noir ↔ borne − de l'alim."],
"Alim. réglée sur 4,4 V !",
["Entre 4,2 et 4,5 V.", "Fil de données : moins de 30 cm."])
s.save("branchement_sans_puce.svg")
def branchement_transistor():
"""Solution B : un transistor NPN (2N3904) en inverseur, matrice en 5 V."""
s = Svg(1200, 940, "Branchement sans 74AHCT125 (solution B) : transistor 2N3904 "
"en adaptateur de niveau")
_matrice_et_alimentation(s)
s.rect(700, 425, 440, 190, "#f6f1e3", 8, "#d9cfb2", 2)
s.texte(1132, 608, "plaque d'essai", 12, "#a8997a", "end")
XC = 1030 # verticale du collecteur et de l'émetteur
XB = 1013 # barre de base du transistor
# rouge (5 V) → résistance de tirage 1 kΩ → nœud du collecteur
s.fil(f"M880 386 V440 H{XC} V455", ROUGE)
_resistance_v(s, XC, 455, ANNEAUX_1K, "1 kΩ", "tirage")
# vert (DIN) ← nœud du collecteur
s.fil(f"M940 386 V400 H1100 V505 H{XC}", VERT)
s.fil(f"M{XC} 495 V520", VERT)
s.point(XC, 505, VERT)
# transistor NPN
s.add(f'<circle cx="{XC - 8}" cy="540" r="24" fill="#fff" stroke="#333" stroke-width="2"/>')
s.add(f'<line x1="{XB}" y1="526" x2="{XB}" y2="554" stroke="#333" stroke-width="4"/>')
s.fil(f"M{XB} 534 L{XC} 520", "#333", 3)
s.fil(f"M{XB} 546 L{XC} 560 V585", "#333", 3)
s.add(f'<path d="M{XC - 9} 550 L{XC} 560 L{XC - 12} 561 Z" fill="#333"/>')
s.texte(XC + 10, 524, "C", 12, "#333", gras=True)
s.texte(XC + 10, 570, "E", 12, "#333", gras=True)
s.texte(XB - 4, 528, "B", 12, "#333", "end", True)
s.texte(XC + 26, 545, "2N3904", 13, "#333", gras=True)
s.texte(XC + 26, 561, "(NPN)", 12, "#555")
# base ← 1 kΩ ← GPIO 18
pattes_x0 = 843 - 80
_resistance_h(s, 930, 540, ANNEAUX_1K, "1 kΩ")
s.fil(f"M970 540 H{XB}", JAUNE_GPIO, 4)
s.fil(f"M843 661 V540 {hop_h(910, 1)} H930", JAUNE_GPIO)
# masses : blanc → rail → émetteur et Pi
s.fil(f"M910 386 V585 H{XC}", BLANC_FIL)
s.point(XC, 585, NOIR)
s.point(910, 585, BLANC_FIL)
pattes = _raspberry_pi(s, pattes_x0)
s.fil(f"M910 585 V630 {hop_h(843, -1)} H{pattes[6]} V661", NOIR, 4)
s.texte(712, 448, "fil rouge (5 V) : seulement", 11, ROUGE, gras=True)
s.texte(712, 461, "vers la résistance de tirage", 11, ROUGE, gras=True)
_legende(s, FILS_5V,
["Tous les GND sont reliés :", "Pi ↔ émetteur ↔ fil blanc",
"↔ matrice ↔ borne − de l'alim."],
"Dans matrice.py :",
["INVERSER_SIGNAL = True", "(le transistor inverse le signal)",
"Vérifier l'ordre E-B-C du", "transistor sur sa datasheet !"])
s.save("branchement_transistor.svg")
# =============================================================================
# 2. BROCHAGE DU 74AHCT125
# =============================================================================
def brochage():
s = Svg(820, 470, "Brochage du 74AHCT125 (vu de dessus)")
s.texte(410, 36, "74AHCT125 vu de DESSUS (encoche à gauche)", 20, "#222", "middle", True)
s.rect(250, 150, 320, 110, "#222", 6)
s.add('<path d="M250 185 a20 20 0 0 1 0 40" fill="#555"/>')
s.add('<circle cx="275" cy="240" r="6" fill="#555"/>')
s.texte(410, 212, "74AHCT125", 24, "#eee", "middle", True)
bas = {1: ("1OE", "→ GND", "#333"), 2: ("1A", "← GPIO 18", JAUNE_GPIO),
3: ("1Y", "→ 330 Ω → DIN", VERT), 4: ("2OE", "", "#aaa"), 5: ("2A", "", "#aaa"),
6: ("2Y", "", "#aaa"), 7: ("GND", "→ GND", "#333")}
haut = {14: ("VCC", "← 5 V", ROUGE), 13: ("4OE", "", "#aaa"), 12: ("4A", "", "#aaa"),
11: ("4Y", "", "#aaa"), 10: ("3OE", "", "#aaa"), 9: ("3A", "", "#aaa"),
8: ("3Y", "", "#aaa")}
for n, (nom, role, c) in bas.items():
x = 280 + (n - 1) * 43
s.rect(x - 8, 260, 16, 26, "#b8b8b8")
s.texte(x, 252, str(n), 12, "#bbb", "middle")
s.texte(x, 306, nom, 14, c, "middle", True)
if role:
y = 338 + {1: 0, 2: 22, 3: 44, 7: 0}[n] * 1
s.add(f'<line x1="{x}" y1="312" x2="{x}" y2="{y - 13}" stroke="{c}" stroke-width="1.5"/>')
s.texte(x, y, role, 13, c, "middle", True)
for n, (nom, role, c) in haut.items():
x = 280 + (14 - n) * 43
s.rect(x - 8, 124, 16, 26, "#b8b8b8")
s.texte(x, 168, str(n), 12, "#bbb", "middle")
s.texte(x, 114, nom, 14, c, "middle", True)
if role:
s.texte(x, 92, role, 13, c, "middle", True)
s.texte(40, 445, "On n'utilise qu'une des 4 « portes » (la n° 1). Pattes en gris : non branchées "
"(on peut relier 4, 10 et 13 au 5 V pour les désactiver).", 13, "#555")
s.save("brochage_74ahct125.svg")
# =============================================================================
# 3. DE KRITA À LA MATRICE
# =============================================================================
def chaine():
s = Svg(1200, 330, "De Krita à la matrice : le trajet d'une animation")
etapes = [
("#7b3fa0", "1. Dessiner", ["Krita", "document 8 × 32 px", "(vertical), sans flou"]),
("#b5418f", "2. Exporter", ["GIF animé", "ou séquence de PNG", "(Rendu de l'animation)"]),
("#c9682a", "3. Vérifier", ["verifier_animation.py", "+ simulateur", "sur l'ordinateur"]),
("#b08900", "4. Déposer", ["dossier « animations »", "carte SD ou réseau", "(01_, 02_… = ordre)"]),
("#1f7a3a", "5. Lire", ["Raspberry Pi", "lecteur.py : gamma,", "luminosité, serpentin"]),
("#2f6fc4", "6. Afficher", ["adaptateur 3,3 → 5 V", "(ou matrice en 4,4 V)", "puis 256 LED WS2812B"]),
]
larg, ecart = 170, 28
for i, (c, titre, lignes) in enumerate(etapes):
x = 30 + i * (larg + ecart)
s.rect(x, 60, larg, 190, "#ffffff", 12, c, 3)
s.rect(x, 60, larg, 46, c, 12)
s.rect(x, 90, larg, 16, c)
s.texte(x + larg / 2, 90, titre, 18, "#fff", "middle", True)
for j, t in enumerate(lignes):
s.texte(x + larg / 2, 140 + j * 26, t, 14 if j == 0 else 13,
"#222" if j == 0 else "#555", "middle", j == 0)
if i < len(etapes) - 1:
s.add(f'<line x1="{x + larg + 4}" y1="155" x2="{x + larg + ecart - 4}" y2="155" '
'stroke="#555" stroke-width="3" marker-end="url(#flecheg)"/>')
s.texte(30, 36, "De Krita à la matrice", 22, "#222", gras=True)
s.add('<path d="M115 270 V292 H1003 V270" stroke="#aaa" stroke-width="2" fill="none" '
'stroke-dasharray="6 5"/>')
s.texte(559, 316, "Étapes 1 à 4 : l'équipe design, sur n'importe quel ordinateur. "
"Étapes 5 et 6 : automatiques, dans la vitrine.", 14, "#555", "middle")
s.save("chaine_krita_matrice.svg")
if __name__ == "__main__":
os.makedirs(SORTIE, exist_ok=True)
branchement()
branchement_sans_puce()
branchement_transistor()
brochage()
chaine()
print("Schémas écrits dans", SORTIE)