diff --git a/modules/00-installation.qmd b/modules/00-installation.qmd index 434e9cf..56bf678 100644 --- a/modules/00-installation.qmd +++ b/modules/00-installation.qmd @@ -68,10 +68,14 @@ Une fois le paquet installé : Si l'upload échoue avec un message « Failed to connect », maintenez le bouton BOOT appuyé, appuyez brièvement sur RESET, relâchez BOOT, puis relancez le téléversement. Ce geste force la carte en mode bootloader. ::: +![Forcer le mode téléchargement : séquence des boutons BOOT et RESET](../schemas/svg/m00-boot-reset.svg){#fig-m00-boot-reset width=90%} + ## 5. Premier programme : Blink Nous allons vérifier que toute la chaîne (code → compilation → upload → exécution) fonctionne, en faisant clignoter la LED intégrée. +![La chaîne de développement : du croquis à l'exécution sur la carte, puis retour par le moniteur série](../schemas/svg/m00-chaine-developpement.svg){#fig-m00-chaine-developpement} + ```cpp // Sur la plupart des DevKitC ESP32-S3, la LED simple (non RGB) est sur la broche 2. // Vérifiez la sérigraphie de votre carte : elle peut varier. diff --git a/modules/01-architecture.qmd b/modules/01-architecture.qmd index d67f23a..a4ba8ba 100644 --- a/modules/01-architecture.qmd +++ b/modules/01-architecture.qmd @@ -17,6 +17,8 @@ L'ESP32-S3 va plus loin qu'un microcontrôleur classique (type ATmega328 de l'Ar ## 2. Architecture interne de l'ESP32-S3 +![Architecture simplifiée de l'ESP32-S3 et de son module](../schemas/svg/m01-architecture.svg){#fig-m01-architecture} + ### 2.1 Cœurs de calcul - Double cœur Xtensa LX7 cadencé jusqu'à 240 MHz. @@ -49,10 +51,14 @@ L'ESP32-S3 dispose d'un contrôleur USB OTG natif, permettant la programmation e | Broches ADC | 10 bits, 5 entrées | 12 bits, jusqu'à 20 entrées | | PWM | Limité (Timer matériel) | LEDC — jusqu'à 8 canaux configurables | +![Arduino Uno et ESP32-S3 : ordres de grandeur (échelle logarithmique)](../schemas/svg/m01-uno-vs-s3.svg){#fig-m01-uno-vs-s3} + ::: {.callout-warning title="Point de vigilance : tension 3,3 V"} Toutes les broches GPIO de l'ESP32-S3 fonctionnent en logique 3,3 V et ne tolèrent pas 5 V en entrée. Un signal 5 V (capteur, module tiers) doit être abaissé via un diviseur de tension ou un convertisseur de niveau logique avant d'être connecté à une broche. ::: +![Abaisser un signal 5 V à 3,3 V avec un diviseur de tension](../schemas/svg/m01-diviseur-tension.svg){#fig-m01-diviseur-tension width=65%} + ## 4. Lire le brochage (pinout) de la carte Avant de câbler quoi que ce soit, il est indispensable de repérer sur votre carte : diff --git a/modules/02-gpio-neopixel.qmd b/modules/02-gpio-neopixel.qmd index 14b634a..8129602 100644 --- a/modules/02-gpio-neopixel.qmd +++ b/modules/02-gpio-neopixel.qmd @@ -35,6 +35,8 @@ Une entrée numérique non connectée (« flottante ») peut lire un état aléa - `INPUT_PULLUP` : la broche est tirée à 3,3 V par défaut (état HIGH) ; le bouton doit relier la broche à GND pour donner un état LOW à l'appui. - `INPUT_PULLDOWN` : disponible sur l'ESP32-S3 (contrairement à l'AVR classique) — la broche est tirée à 0 V par défaut. +![Bouton sur une entrée en `INPUT_PULLUP` : la résistance de tirage interne fixe l'état au repos](../schemas/svg/m02-pull-up.svg){#fig-m02-pull-up width=70%} + ```cpp #define BUTTON_PIN 5 @@ -56,10 +58,14 @@ void loop() { Un bouton mécanique génère plusieurs transitions HIGH/LOW parasites en quelques millisecondes lors de l'appui. Un simple `delay(50)` après lecture suffit pour un usage pédagogique ; pour un projet robuste, on mesure le temps écoulé avec `millis()` plutôt que d'utiliser `delay()` (voir Module 6 sur les interruptions et timers). ::: +![Rebonds d'un bouton mécanique : l'anti-rebond ne retient qu'un seul appui](../schemas/svg/m02-rebonds.svg){#fig-m02-rebonds} + ## 3. Exercice guidé : chenillard de LED Câblez 4 LED (avec résistances de 220 Ω) sur 4 GPIO libres, puis reproduisez un effet de chenillard : +![Câblage du chenillard : une résistance de 220 Ω par LED, masse commune](../schemas/svg/m02-chenillard.svg){#fig-m02-chenillard width=75%} + ```cpp const int leds[] = {4, 5, 6, 7}; const int nbLeds = 4; @@ -87,6 +93,8 @@ Contrairement à une LED classique qui ne prend que deux états (allumé/éteint Le protocole WS2812 impose un timing très précis (de l'ordre de la centaine de nanosecondes) pour distinguer un bit 0 d'un bit 1. Le générer « à la main » avec digitalWrite serait peu fiable ; on utilise donc une bibliothèque dédiée qui exploite les ressources matérielles du microcontrôleur pour respecter ce timing. +![Protocole WS2812 : trame de 24 bits et codage temporel de chaque bit](../schemas/svg/m02-ws2812.svg){#fig-m02-ws2812} + ### 4.2 Installation de la bibliothèque - Dans l'IDE : Croquis \> Inclure une bibliothèque \> Gérer les bibliothèques. diff --git a/schemas/m00_installation.py b/schemas/m00_installation.py new file mode 100644 index 0000000..85b5a76 --- /dev/null +++ b/schemas/m00_installation.py @@ -0,0 +1,53 @@ +"""Module 0 — Prérequis & installation.""" +from outils import boite, chronogramme, fleche, toile +from style import BLEU, ENCRE, GRIS, ORANGE, VERT, Figure + + +def chaine_developpement(): + fig, ax = toile(16, 5.6) + # Zones ordinateur / carte + boite(ax, 5.0, 3.0, 9.4, 4.6, couleur=GRIS, fond="#f9fafb") + boite(ax, 13.2, 3.0, 4.6, 4.6, couleur=BLEU, fond="#f5f8fe") + ax.text(0.6, 4.8, "Ordinateur — Arduino IDE", fontsize=9, color=GRIS, fontweight="bold") + ax.text(11.2, 4.8, "Carte ESP32-S3", fontsize=9, color=BLEU, fontweight="bold") + code = boite(ax, 2.0, 3.3, 2.6, 1.4, "Croquis\n(.ino)", couleur=BLEU) + comp = boite(ax, 5.6, 3.3, 2.6, 1.4, "Compilation\n✓ Vérifier", couleur=BLEU) + moni = boite(ax, 4.2, 1.4, 5.4, 0.9, "Moniteur série — 115200 bauds", couleur=VERT, taille=8.5) + flash = boite(ax, 13.2, 3.3, 3.2, 1.4, "Mémoire Flash\n(programme)", couleur=ORANGE) + cpu = boite(ax, 13.2, 1.4, 3.2, 0.9, "Exécution", couleur=ORANGE) + fleche(ax, code["d"], comp["g"]) + fleche(ax, comp["d"], flash["g"], "Téléverser →\n(câble USB)", taille=8) + fleche(ax, flash["b"], cpu["h"]) + fleche(ax, cpu["g"], moni["d"], "Serial.println()", couleur=VERT, taille=8) + ax.text(8.0, 0.1, "Le même câble USB sert au téléversement et au moniteur série (USB CDC).", + ha="center", fontsize=7.5, color=GRIS, style="italic") + return fig + + +def boot_reset(): + voies = [ + ("BOOT", [(0, 1), (1, 0), (4, 1)]), + ("RESET", [(0, 1), (2, 0), (2.6, 1)]), + ] + fig, ax, y = chronogramme(voies, 6, largeur_cm=15, hauteur_voie_cm=1.6, + couleurs={"RESET": ORANGE}) + ax.set_xticks([]) + ax.spines["bottom"].set_visible(False) + for i in range(2): + ax.text(6.05, y(i) + 1, "relâché", fontsize=7, color=GRIS, va="center") + ax.text(6.05, y(i), "appuyé", fontsize=7, color=GRIS, va="center") + etapes = [(1, "① maintenir BOOT"), (2.3, "② appui bref\nsur RESET"), (4, "③ relâcher BOOT")] + for t, txt in etapes: + ax.axvline(t, color=GRIS, lw=0.8, ls=":") + ax.text(t, 3.05, txt, ha="center", va="bottom", fontsize=8, color=ENCRE) + ax.text(5.0, -0.75, "→ la carte démarre en mode téléchargement", ha="center", fontsize=8.5, + color=VERT, fontweight="bold") + ax.set_ylim(-1.1, 4.1) + fig.tight_layout() + return fig + + +FIGURES = [ + Figure("m00-chaine-developpement", chaine_developpement), + Figure("m00-boot-reset", boot_reset), +] diff --git a/schemas/m01_architecture.py b/schemas/m01_architecture.py new file mode 100644 index 0000000..9649675 --- /dev/null +++ b/schemas/m01_architecture.py @@ -0,0 +1,85 @@ +"""Module 1 — Découverte du microcontrôleur ESP32-S3.""" +import matplotlib.pyplot as plt +import schemdraw +import schemdraw.elements as elm + +from outils import boite, fleche, toile +from style import BLEU, CM, ENCRE, GRIS, ORANGE, VERT, Figure + +schemdraw.use("matplotlib") + + +def architecture(): + fig, ax = toile(16, 8.2) + boite(ax, 6.6, 4.2, 12.6, 7.6, couleur=BLEU, fond="#f5f8fe") + ax.text(0.6, 7.6, "ESP32-S3 (puce)", fontsize=10, color=BLEU, fontweight="bold") + # Cœurs et mémoire + boite(ax, 2.6, 6.2, 3.8, 1.0, "Cœur 0\nXtensa LX7", couleur=BLEU, taille=8.5) + boite(ax, 2.6, 5.0, 3.8, 1.0, "Cœur 1\nXtensa LX7", couleur=BLEU, taille=8.5) + ax.text(2.6, 4.2, "240 MHz max · FreeRTOS", ha="center", fontsize=7.5, color=GRIS) + sram = boite(ax, 6.6, 5.6, 3.0, 2.2, "SRAM interne\n≈ 512 Ko\n(données +\ninstructions)", couleur=VERT, taille=8.5) + # Radio + boite(ax, 10.6, 6.2, 3.6, 1.0, "Wi-Fi 802.11 b/g/n\n2,4 GHz", couleur=ORANGE, taille=8) + boite(ax, 10.6, 5.0, 3.6, 1.0, "Bluetooth 5 LE\n(pas de Classic)", couleur=ORANGE, taille=8) + # Périphériques + periph = ["GPIO", "ADC 12 bits", "LEDC (PWM)", "UART", "I2C", "SPI", "Timers", "USB OTG"] + for i, p in enumerate(periph): + x = 1.35 + (i % 4) * 2.85 + yy = 2.55 if i < 4 else 1.35 + boite(ax, x, yy, 2.55, 0.9, p, couleur=GRIS, taille=8.5) + ax.text(0.6, 3.25, "Périphériques intégrés", fontsize=8, color=GRIS, fontweight="bold") + # Bus interne + ax.plot([0.7, 12.5], [3.8, 3.8], color=BLEU, lw=2.2, alpha=0.5) + # Externe + flash = boite(ax, 14.75, 5.6, 2.45, 2.2, "Flash\nexterne\n8–16 Mo\n(programme,\nfichiers)", couleur=VERT, taille=7.5) + psram = boite(ax, 14.7, 2.9, 2.3, 1.2, "PSRAM\n(option)", couleur=VERT, taille=8, alpha=0.8) + ax.text(14.7, 7.15, "sur le module", ha="center", fontsize=7.5, color=GRIS, style="italic") + fleche(ax, (12.9, 5.6), flash["g"], double=True) + fleche(ax, (12.9, 2.9), psram["g"], double=True) + return fig + + +def uno_vs_s3(): + fig, ax = plt.subplots(figsize=(16 * CM, 6.2 * CM)) + criteres = ["Fréquence (MHz)", "RAM (Ko)", "Flash (Ko)"] + uno = [16, 2, 32] + s3 = [240, 512, 8192] + y = range(len(criteres)) + ax.barh([i + 0.2 for i in y], uno, height=0.38, color=GRIS, label="Arduino Uno (ATmega328)") + ax.barh([i - 0.2 for i in y], s3, height=0.38, color=BLEU, label="ESP32-S3") + ax.set_xscale("log") + ax.set_xlim(1, 300000) + ax.set_yticks(list(y), criteres) + ax.invert_yaxis() + for i, (u, s) in enumerate(zip(uno, s3)): + ax.text(u * 1.15, i + 0.2, f"{u:g}", va="center", fontsize=8, color=ENCRE) + lib = f"{s:g}" if s < 8000 else "8192 (8 Mo)" + ax.text(s * 1.15, i - 0.2, f"{lib} × {s // u}", va="center", fontsize=8, color=BLEU, + fontweight="bold") + ax.set_xlabel("échelle logarithmique : chaque graduation = × 10") + ax.grid(axis="y", visible=False) + ax.legend(loc="lower center", bbox_to_anchor=(0.5, 1.0), ncol=2, fontsize=8, frameon=False) + fig.tight_layout() + return fig + + +def diviseur_tension(): + d = schemdraw.Drawing(show=False) + d.config(fontsize=11, unit=2.6, color=ENCRE) + d += elm.Dot(open=True).label("Signal 5 V\n(module tiers)", loc="left") + d += elm.Resistor().right().label("R1 = 10 kΩ").color(ORANGE) + d += (noeud := elm.Dot()) + d += elm.Line().right().length(2.2) + d += elm.Dot(open=True).label("GPIO\n(3,3 V max)", loc="right").color(BLEU) + d += elm.Resistor().down().at(noeud.center).label("R2 = 20 kΩ", loc="bottom").color(ORANGE) + d += elm.Ground() + d += elm.Label().at((noeud.center[0] + 1.2, noeud.center[1] - 2.2)).label( + r"$V_{GPIO} = 5\,V \times \frac{R2}{R1 + R2} \approx 3{,}3\,V$", fontsize=11, color=VERT) + return d.draw(show=False) + + +FIGURES = [ + Figure("m01-architecture", architecture), + Figure("m01-uno-vs-s3", uno_vs_s3), + Figure("m01-diviseur-tension", diviseur_tension), +] diff --git a/schemas/m02_gpio_neopixel.py b/schemas/m02_gpio_neopixel.py new file mode 100644 index 0000000..91584f2 --- /dev/null +++ b/schemas/m02_gpio_neopixel.py @@ -0,0 +1,121 @@ +"""Module 2 — Entrées/sorties numériques & LED RGB NeoPixel.""" +import matplotlib.pyplot as plt +import numpy as np +import schemdraw +import schemdraw.elements as elm + +from outils import chronogramme +from style import BLEU, CM, ENCRE, GRIS, ORANGE, ROUGE, VERT, Figure + +schemdraw.use("matplotlib") + + +def pull_up(): + d = schemdraw.Drawing(show=False) + d.config(fontsize=10.5, unit=2.4, color=ENCRE) + d += (vdd := elm.Vdd().label("3V3")) + d += (r := elm.Resistor().down().label("pull-up interne\n≈ 45 kΩ", loc="bottom").color(BLEU)) + d += (n := elm.Dot()) + d += (lecture := elm.Line().left().length(2.6).label("vers digitalRead()", loc="top", fontsize=9).color(BLEU)) + d += elm.Line().right().at(n.center).length(2.8) + d += (gpio := elm.Dot(open=True).label("GPIO5", loc="top")) + d += elm.Line().right().length(1.2) + d += elm.Button().down().label("bouton", loc="bottom").color(ORANGE) + d += elm.Ground() + d += elm.EncircleBox([vdd, r, n, lecture, gpio], padx=0.3, pady=0.5).linestyle("--").color(GRIS) + d += elm.Label().at((n.center[0] + 1.6, vdd.end[1] + 0.2)).label("à l'intérieur\nde l'ESP32-S3", color=GRIS, fontsize=9) + d += elm.Label().at((gpio.center[0] + 3.6, n.center[1] - 2.0)).label( + "relâché → HIGH (3,3 V)\nappuyé → LOW (0 V)", fontsize=10, color=VERT) + return d.draw(show=False) + + +def rebonds(): + t = [0] + niv = [1] + # Appui avec rebonds autour de 40 ms, relâchement avec rebonds autour de 140 ms + for a, b in ((40, 0), (41.2, 1), (42, 0), (43.5, 1), (44.1, 0), (140, 1), (140.8, 0), (141.9, 1), (142.5, 0), (143.2, 1)): + t.append(a) + niv.append(b) + voies = [("broche (brut)", list(zip(t, niv))), + ("état retenu", [(0, 1), (54, 0), (153, 1)])] + fig, ax, y = chronogramme(voies, 200, largeur_cm=16, hauteur_voie_cm=1.7, + graduations=range(0, 201, 25), unite="temps (ms)", + couleurs={"état retenu": VERT}) + for x0, x1, txt in ((40, 44.1, "rebonds\n(quelques ms)"), (140, 143.2, "rebonds")): + ax.axvspan(x0 - 1, x1 + 1, color=ORANGE, alpha=0.18) + ax.text((x0 + x1) / 2, y(0) + 1.25, txt, ha="center", fontsize=7.5, color=ORANGE) + ax.annotate("", xy=(54, y(1) + 0.5), xytext=(44.1, y(1) + 0.5), + arrowprops=dict(arrowstyle="<->", color=ENCRE, lw=0.9)) + ax.text(49, y(1) + 0.62, "attente", ha="center", fontsize=7, color=ENCRE) + ax.text(97, y(1) + 0.3, "un seul appui détecté", ha="center", fontsize=8, color=VERT, fontweight="bold") + ax.set_ylim(-0.5, 3.9) + fig.tight_layout() + return fig + + +def chenillard(): + d = schemdraw.Drawing(show=False) + d.config(fontsize=10.5, unit=2.2, color=ENCRE) + pins = [elm.IcPin(name=f"GPIO{g}", side="right", anchorname=f"P{g}") for g in (7, 6, 5, 4)] + ic = elm.Ic(pins=pins, size=(3.2, 5.4), pinfontsize=10).label("ESP32-S3", loc="center", fontsize=12).color(BLEU) + d += ic + couleurs = {4: ROUGE, 5: VERT, 6: BLEU, 7: ORANGE} + fins = [] + for g in (4, 5, 6, 7): + d += elm.Line().right().at(getattr(ic, f"P{g}")).length(0.6) + d += elm.Resistor().right().label("220 Ω", fontsize=9) + d += elm.LED().right().color(couleurs[g]) + d += elm.Line().right().length(0.6) + fins.append(d.here) + d += elm.Line().at(fins[0]).to(fins[-1]) + d += elm.Line().down().at(fins[-1]).length(0.6) + d += elm.Ground() + d += elm.Label().at((fins[0][0] + 0.4, (fins[0][1] + fins[-1][1]) / 2)).label("GND\ncommun", loc="right", fontsize=9, color=GRIS) + return d.draw(show=False) + + +def ws2812(): + fig = plt.figure(figsize=(16 * CM, 8.6 * CM)) + # (a) trame 24 bits + ax1 = fig.add_axes([0.04, 0.62, 0.92, 0.3]) + ax1.axis("off") + ax1.set_xlim(0, 24) + ax1.set_ylim(0, 1.6) + for k, (nom, c) in enumerate((("G", VERT), ("R", ROUGE), ("B", BLEU))): + for b in range(8): + x = k * 8 + b + ax1.add_patch(plt.Rectangle((x, 0.3), 1, 0.8, facecolor=c, alpha=0.18 + 0.06 * (7 - b) / 7, + edgecolor=c, lw=1)) + ax1.text(x + 0.5, 0.7, f"{nom}{7 - b}", ha="center", va="center", fontsize=6.5, color=ENCRE) + ax1.text(k * 8 + 4, 1.25, {"G": "Vert (8 bits)", "R": "Rouge (8 bits)", "B": "Bleu (8 bits)"}[nom], + ha="center", fontsize=8.5, color=c, fontweight="bold") + ax1.text(0, 0.02, "(a) Une couleur = 24 bits, envoyés dans l'ordre G-R-B (d'où NEO_GRB), bit de poids fort en premier", + fontsize=8, color=GRIS) + # (b) codage d'un bit + ax2 = fig.add_axes([0.08, 0.12, 0.88, 0.36]) + for x0, th, tl, nom in ((0, 0.4, 0.85, "bit 0"), (1.8, 0.8, 0.45, "bit 1")): + xs = [x0, x0, x0 + th, x0 + th, x0 + th + tl] + ys = [0, 1, 1, 0, 0] + ax2.plot(xs, ys, color=BLEU, lw=1.8) + ax2.fill_between([x0, x0 + th], 0, 1, color=BLEU, alpha=0.12) + ax2.text(x0 + (th + tl) / 2, 1.32, nom, ha="center", fontsize=9, fontweight="bold", color=ENCRE) + ax2.annotate("", xy=(x0, 1.1), xytext=(x0 + th, 1.1), arrowprops=dict(arrowstyle="<->", lw=0.8)) + ax2.text(x0 + th / 2, 1.14, f"{th} µs".replace(".", ","), ha="center", va="bottom", fontsize=7.5) + ax2.annotate("", xy=(x0 + th, -0.12), xytext=(x0 + th + tl, -0.12), arrowprops=dict(arrowstyle="<->", lw=0.8)) + ax2.text(x0 + th + tl / 2, -0.2, f"{tl} µs".replace(".", ","), ha="center", va="top", fontsize=7.5) + ax2.text(3.4, 0.55, "période d'un bit : 1,25 µs (800 kHz)\nla différence se joue à ~400 ns près :\n" + "impossible avec digitalWrite(),\nla bibliothèque utilise le matériel (RMT)", + fontsize=8, color=ENCRE, va="center") + ax2.set_xlim(-0.1, 6.2) + ax2.set_ylim(-0.5, 1.6) + ax2.axis("off") + fig.text(0.04, 0.03, "(b) Codage d'un bit : seule la durée de l'état haut change", fontsize=8, color=GRIS) + return fig + + +FIGURES = [ + Figure("m02-pull-up", pull_up), + Figure("m02-rebonds", rebonds), + Figure("m02-chenillard", chenillard), + Figure("m02-ws2812", ws2812), +] diff --git a/schemas/outils.py b/schemas/outils.py new file mode 100644 index 0000000..f4e2830 --- /dev/null +++ b/schemas/outils.py @@ -0,0 +1,129 @@ +"""Briques graphiques communes : toile de dessin, boîtes, flèches, chronogrammes, séquences.""" +import matplotlib.pyplot as plt +from matplotlib.patches import FancyBboxPatch + +from style import BLEU, CM, ENCRE, GRIS, GRIS_CLAIR + +CLAIR = { # fonds pâles associés aux couleurs de la charte + BLEU: "#e8f0fe", "#e07a1f": "#fdf0e3", "#2e8b57": "#e6f4ea", + "#c0392b": "#fce8e6", GRIS: "#f3f4f6", +} + + +def toile(largeur_cm, hauteur_cm, xmax=None, ymax=None): + """Figure sans axes, en coordonnées « cm » (1 unité = 1 cm à l'échelle d'origine).""" + fig, ax = plt.subplots(figsize=(largeur_cm * CM, hauteur_cm * CM)) + ax.set_xlim(0, xmax or largeur_cm) + ax.set_ylim(0, ymax or hauteur_cm) + ax.set_aspect("equal") + ax.axis("off") + return fig, ax + + +def boite(ax, x, y, l, h, texte="", couleur=BLEU, taille=9, gras=False, fond=None, style="round", alpha=1): + """Boîte arrondie centrée sur (x, y). Renvoie un dict des points d'ancrage.""" + fond = fond if fond is not None else CLAIR.get(couleur, "white") + ax.add_patch(FancyBboxPatch((x - l / 2, y - h / 2), l, h, + boxstyle=f"{style},pad=0,rounding_size=0.15" if style == "round" else style, + facecolor=fond, edgecolor=couleur, linewidth=1.4, alpha=alpha)) + if texte: + ax.text(x, y, texte, ha="center", va="center", fontsize=taille, color=ENCRE, + fontweight="bold" if gras else "normal", linespacing=1.3) + return {"c": (x, y), "g": (x - l / 2, y), "d": (x + l / 2, y), + "h": (x, y + h / 2), "b": (x, y - h / 2)} + + +def fleche(ax, depart, arrivee, texte="", couleur=ENCRE, taille=8, double=False, pointille=False, + cote="haut", decalage=0.18, courbe=0): + """Flèche entre deux points, avec étiquette optionnelle au milieu.""" + ax.annotate("", xy=arrivee, xytext=depart, + arrowprops=dict(arrowstyle="<|-|>" if double else "-|>", color=couleur, lw=1.3, + linestyle="--" if pointille else "-", shrinkA=0, shrinkB=0, + mutation_scale=11, connectionstyle=f"arc3,rad={courbe}")) + if texte: + mx, my = (depart[0] + arrivee[0]) / 2, (depart[1] + arrivee[1]) / 2 + vertical = abs(arrivee[0] - depart[0]) < abs(arrivee[1] - depart[1]) + if vertical: + ax.text(mx + decalage, my, texte, ha="left", va="center", fontsize=taille, color=couleur) + else: + dy = decalage if cote == "haut" else -decalage + ax.text(mx, my + dy, texte, ha="center", va="bottom" if cote == "haut" else "top", + fontsize=taille, color=couleur) + + +def chronogramme(voies, duree, largeur_cm=16, hauteur_voie_cm=1.5, graduations=None, unite="", + couleurs=None): + """Chronogramme logique. + + voies : liste de (nom, [(t, niveau), ...]) — niveau 0/1, maintenu jusqu'au point suivant. + Renvoie (fig, ax, y_voie) où y_voie(i) donne l'ordonnée de base de la voie i. + """ + n = len(voies) + fig, ax = plt.subplots(figsize=(largeur_cm * CM, (n * hauteur_voie_cm + 1.2) * CM)) + ax.set_xlim(-duree * 0.02, duree * 1.01) + ax.set_ylim(-0.6, n * 1.6) + ax.spines[["left", "top", "right"]].set_visible(False) + ax.set_yticks([]) + ax.grid(axis="y", visible=False) + if graduations is not None: + ax.set_xticks(graduations) + if unite: + ax.set_xlabel(unite) + y_voie = lambda i: (n - 1 - i) * 1.6 + for i, (nom, points) in enumerate(voies): + base = y_voie(i) + xs, ys = [], [] + for k, (t, niv) in enumerate(points): + fin = points[k + 1][0] if k + 1 < len(points) else duree + xs += [t, fin] + ys += [base + niv, base + niv] + c = (couleurs or {}).get(nom, BLEU) + ax.plot(xs, ys, color=c, lw=1.8, solid_joinstyle="miter") + ax.fill_between(xs, base, ys, color=c, alpha=0.08) + ax.text(-duree * 0.025, base + 0.5, nom, ha="right", va="center", fontsize=9, + color=ENCRE, fontweight="bold") + return fig, ax, y_voie + + +def sequence(acteurs, messages, largeur_cm=16, pas_cm=0.85, couleurs=None, notes=()): + """Diagramme de séquence. + + acteurs : liste de noms. messages : liste de (source, cible, texte[, pointillé]). + notes : liste de (index_message, acteur, texte) affichées à droite de la ligne de vie. + """ + n = len(messages) + hauteur = n * pas_cm + 2.2 + fig, ax = toile(largeur_cm, hauteur) + xs = {a: largeur_cm * (i + 0.5) / len(acteurs) for i, a in enumerate(acteurs)} + haut = hauteur - 0.6 + for a, x in xs.items(): + c = (couleurs or {}).get(a, BLEU) + boite(ax, x, haut, min(3.6, largeur_cm / len(acteurs) - 0.4), 0.8, a, couleur=c, taille=8.5, gras=True) + ax.plot([x, x], [0.3, haut - 0.4], color=GRIS, lw=1, ls=(0, (3, 3))) + for k, msg in enumerate(messages): + src, dst, texte = msg[:3] + pointille = len(msg) > 3 and msg[3] + y = haut - 1.2 - k * pas_cm + if src == dst: + ax.annotate("", xy=(xs[src] + 0.05, y - 0.3), xytext=(xs[src] + 0.05, y), + arrowprops=dict(arrowstyle="-|>", color=ENCRE, lw=1.1, + connectionstyle="arc,angleA=0,angleB=0,armA=18,armB=18,rad=4", + mutation_scale=9)) + ax.text(xs[src] + 0.75, y - 0.15, texte, fontsize=7.8, va="center", color=ENCRE) + continue + fleche(ax, (xs[src], y), (xs[dst], y), texte, pointille=pointille, taille=7.8, decalage=0.08) + for k, acteur, texte in notes: + y = haut - 1.2 - k * pas_cm + ax.text(xs[acteur] + 0.2, y, texte, fontsize=7.5, color=GRIS, style="italic", va="center") + return fig, ax + + +def legende_couleurs(ax, elements, x, y, taille=7.5): + """Petite légende manuelle : elements = [(couleur, texte), ...].""" + for i, (c, t) in enumerate(elements): + ax.add_patch(plt.Rectangle((x, y - i * 0.45 - 0.12), 0.35, 0.25, color=c)) + ax.text(x + 0.5, y - i * 0.45, t, fontsize=taille, va="center", color=ENCRE) + + +__all__ = ["toile", "boite", "fleche", "chronogramme", "sequence", "legende_couleurs", + "CLAIR", "GRIS_CLAIR"] diff --git a/schemas/png/m00-boot-reset.png b/schemas/png/m00-boot-reset.png new file mode 100644 index 0000000..9ee890e Binary files /dev/null and b/schemas/png/m00-boot-reset.png differ diff --git a/schemas/png/m00-chaine-developpement.png b/schemas/png/m00-chaine-developpement.png new file mode 100644 index 0000000..f673c02 Binary files /dev/null and b/schemas/png/m00-chaine-developpement.png differ diff --git a/schemas/png/m01-architecture.png b/schemas/png/m01-architecture.png new file mode 100644 index 0000000..1e273dd Binary files /dev/null and b/schemas/png/m01-architecture.png differ diff --git a/schemas/png/m01-diviseur-tension.png b/schemas/png/m01-diviseur-tension.png new file mode 100644 index 0000000..2d6c592 Binary files /dev/null and b/schemas/png/m01-diviseur-tension.png differ diff --git a/schemas/png/m01-uno-vs-s3.png b/schemas/png/m01-uno-vs-s3.png new file mode 100644 index 0000000..a79a6b3 Binary files /dev/null and b/schemas/png/m01-uno-vs-s3.png differ diff --git a/schemas/png/m02-chenillard.png b/schemas/png/m02-chenillard.png new file mode 100644 index 0000000..8fc4985 Binary files /dev/null and b/schemas/png/m02-chenillard.png differ diff --git a/schemas/png/m02-pull-up.png b/schemas/png/m02-pull-up.png new file mode 100644 index 0000000..d77c72e Binary files /dev/null and b/schemas/png/m02-pull-up.png differ diff --git a/schemas/png/m02-rebonds.png b/schemas/png/m02-rebonds.png new file mode 100644 index 0000000..6acacae Binary files /dev/null and b/schemas/png/m02-rebonds.png differ diff --git a/schemas/png/m02-ws2812.png b/schemas/png/m02-ws2812.png new file mode 100644 index 0000000..e2e9e1a Binary files /dev/null and b/schemas/png/m02-ws2812.png differ diff --git a/schemas/svg/m00-boot-reset.svg b/schemas/svg/m00-boot-reset.svg new file mode 100644 index 0000000..8186397 --- /dev/null +++ b/schemas/svg/m00-boot-reset.svg @@ -0,0 +1,1492 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m00-chaine-developpement.svg b/schemas/svg/m00-chaine-developpement.svg new file mode 100644 index 0000000..cd43b89 --- /dev/null +++ b/schemas/svg/m00-chaine-developpement.svg @@ -0,0 +1,2427 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m01-architecture.svg b/schemas/svg/m01-architecture.svg new file mode 100644 index 0000000..085eb67 --- /dev/null +++ b/schemas/svg/m01-architecture.svg @@ -0,0 +1,2619 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m01-diviseur-tension.svg b/schemas/svg/m01-diviseur-tension.svg new file mode 100644 index 0000000..dd0f251 --- /dev/null +++ b/schemas/svg/m01-diviseur-tension.svg @@ -0,0 +1,1110 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m01-uno-vs-s3.svg b/schemas/svg/m01-uno-vs-s3.svg new file mode 100644 index 0000000..60ce6c4 --- /dev/null +++ b/schemas/svg/m01-uno-vs-s3.svg @@ -0,0 +1,1987 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m02-chenillard.svg b/schemas/svg/m02-chenillard.svg new file mode 100644 index 0000000..50966c1 --- /dev/null +++ b/schemas/svg/m02-chenillard.svg @@ -0,0 +1,940 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m02-pull-up.svg b/schemas/svg/m02-pull-up.svg new file mode 100644 index 0000000..5370617 --- /dev/null +++ b/schemas/svg/m02-pull-up.svg @@ -0,0 +1,1712 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m02-rebonds.svg b/schemas/svg/m02-rebonds.svg new file mode 100644 index 0000000..c3daa83 --- /dev/null +++ b/schemas/svg/m02-rebonds.svg @@ -0,0 +1,1367 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/svg/m02-ws2812.svg b/schemas/svg/m02-ws2812.svg new file mode 100644 index 0000000..4473ae2 --- /dev/null +++ b/schemas/svg/m02-ws2812.svg @@ -0,0 +1,2633 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +