From ff1628efeb9099f8a50fc55148dc454073cb453a Mon Sep 17 00:00:00 2001 From: Artemia Date: Sun, 18 Oct 2026 14:55:16 +0200 Subject: [PATCH] =?UTF-8?q?Passe=20=C3=A0=20Quarto=20:=20projet,=20gabarit?= =?UTF-8?q?=20PDF=20(Typst)=20et=20module=203=20en=20.qmd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le Markdown remplace le .odt binaire comme source : modifications lisibles dans git, PDF produit par 'quarto render'. Le gabarit reprend la charte des supports (titres bleus, encadrés, code coloré, en-tête et pied de page). Conversion vérifiée : 865 mots, aucun écart avec le .odt. Co-Authored-By: Claude Opus 5.5 --- .gitignore | 8 ++ _quarto.yml | 25 +++++ modele/code.theme | 217 +++++++++++++++++++++++++++++++++++++++++ modele/entete.typ | 43 ++++++++ modules/03-pwm-adc.qmd | 162 ++++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+) create mode 100644 _quarto.yml create mode 100644 modele/code.theme create mode 100644 modele/entete.typ create mode 100644 modules/03-pwm-adc.qmd diff --git a/.gitignore b/.gitignore index 329aee3..c6b8252 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,11 @@ secrets.h # Environnement Python .venv/ __pycache__/ + +/.quarto/ +**/*.quarto_ipynb + +# Sorties Quarto (PDF générés) +_output/ +.quarto/ +modules/*.typ diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..ac80fc4 --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,25 @@ +project: + type: default + output-dir: _output + render: + - modules/*.qmd + +lang: fr +subtitle: "Formation ESP32-S3 avec Arduino Framework" + +crossref: + fig-title: Figure + fig-prefix: figure + +format: + typst: + papersize: a4 + margin: + x: 2cm + y: 2.2cm + mainfont: "Segoe UI" + fontsize: 10pt + codefont: "Consolas" + toc: false + highlight-style: modele/code.theme + include-in-header: modele/entete.typ diff --git a/modele/code.theme b/modele/code.theme new file mode 100644 index 0000000..1cb8314 --- /dev/null +++ b/modele/code.theme @@ -0,0 +1,217 @@ +{ + "custom-styles": { + "INI Files": { + "Section": { + "selected-text-color": "#6f42c1", + "text-color": "#6f42c1" + } + }, + "Python": { + "Builtin Function": { + "selected-text-color": "#6f42c1", + "text-color": "#6f42c1" + }, + "Import": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "Special Variable": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + } + }, + "Rust": { + "Attribute": { + "selected-text-color": "#24292e", + "text-color": "#24292e" + }, + "Macro": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "Self": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "Trait": { + "selected-text-color": "#24292e", + "text-color": "#24292e" + } + }, + "XML": { + "Element": { + "selected-text-color": "#22863a", + "text-color": "#22863a" + } + } + }, + "editor-colors": { + "BackgroundColor": "#ffffff", + "BracketMatching": "#bef5cb", + "CodeFolding": "#f6f8fa", + "CurrentLine": "#f6f8fa", + "CurrentLineNumber": "#24292e", + "IconBorder": "#ffffff", + "IndentationLine": "#d7dbe0", + "LineNumbers": "#c7c2bc", + "MarkBookmark": "#8be9fd", + "MarkBreakpointActive": "#ff5555", + "MarkBreakpointDisabled": "#bd93f9", + "MarkBreakpointReached": "#f1fa8c", + "MarkError": "#b31d28", + "MarkExecution": "#44475a", + "MarkWarning": "#e36209", + "ModifiedLines": "#d73a49", + "ReplaceHighlight": "#50fa7b", + "SavedLines": "#28a745", + "SearchHighlight": "#ffea7f", + "Separator": "#e1e4e8", + "SpellChecking": "#ff5555", + "TabMarker": "#d1d5da", + "TemplateBackground": "#23241e", + "TemplateFocusedPlaceholder": "#22231d", + "TemplatePlaceholder": "#22231d", + "TemplateReadOnlyPlaceholder": "#262721", + "TextSelection": "#dee6fc", + "WordWrapMarker": "#e1e4e8" + }, + "metadata": { + "copyright": [ + "SPDX-FileCopyrightText: 2020 GitHub Inc.", + "SPDX-FileCopyrightText: 2020 Waqar Ahmed " + ], + "license": "SPDX-License-Identifier: MIT", + "name": "GitHub Light", + "revision": 2 + }, + "text-styles": { + "Alert": { + "bold": true, + "selected-text-color": "#ff5555", + "text-color": "#ff5555" + }, + "Annotation": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "Attribute": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "BaseN": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "BuiltIn": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "Char": { + "selected-text-color": "#032f62", + "text-color": "#032f62" + }, + "Comment": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "CommentVar": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "Constant": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "ControlFlow": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "DataType": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "DecVal": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "Documentation": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "Error": { + "selected-text-color": "#ff5555", + "text-color": "#ff5555", + "underline": true + }, + "Extension": { + "bold": true, + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "Float": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "Function": { + "selected-text-color": "#6f42c1", + "text-color": "#6f42c1" + }, + "Import": { + "selected-text-color": "#032f62", + "text-color": "#032f62" + }, + "Information": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "Keyword": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "Normal": { + "selected-text-color": "#24292e", + "text-color": "#24292e" + }, + "Operator": { + "selected-text-color": "#24292e", + "text-color": "#24292e" + }, + "Others": { + "selected-text-color": "#6f42c1", + "text-color": "#6f42c1" + }, + "Preprocessor": { + "selected-text-color": "#d73a49", + "text-color": "#d73a49" + }, + "RegionMarker": { + "selected-text-color": "#6a737d", + "text-color": "#6a737d" + }, + "SpecialChar": { + "selected-text-color": "#005cc5", + "text-color": "#005cc5" + }, + "SpecialString": { + "selected-text-color": "#032f62", + "text-color": "#032f62" + }, + "String": { + "selected-text-color": "#032f62", + "text-color": "#032f62" + }, + "Variable": { + "selected-text-color": "#e36209", + "text-color": "#e36209" + }, + "VerbatimString": { + "selected-text-color": "#032f62", + "text-color": "#032f62" + }, + "Warning": { + "selected-text-color": "#ff5555", + "text-color": "#ff5555" + } + }, + "background-color": "#f6f8fa" +} diff --git a/modele/entete.typ b/modele/entete.typ new file mode 100644 index 0000000..943d0b9 --- /dev/null +++ b/modele/entete.typ @@ -0,0 +1,43 @@ +// Note : Quarto décale les titres d'un niveau (## -> niveau 1, ### -> niveau 2). +// Gabarit commun des supports : couleurs, titres, en-tête et pied de page. +#let bleu-formation = rgb("#1a5fb4") +#let gris-doux = rgb("#6b7280") + +#set page( + header: context { + if counter(page).get().first() > 1 [ + #set text(size: 8pt, fill: gris-doux) + Formation ESP32-S3 avec Arduino Framework + #h(1fr) + #document.title + #v(-4pt) + #line(length: 100%, stroke: 0.4pt + gris-doux) + ] + }, + footer: context [ + #set text(size: 8pt, fill: gris-doux) + #h(1fr) + #counter(page).display("1 / 1", both: true) + ], +) + +#show heading.where(level: 1): it => block(above: 1.6em, below: 0.8em, width: 100%)[ + #set text(size: 14pt, weight: "semibold", fill: bleu-formation) + #it.body + #v(-6pt) + #line(length: 100%, stroke: 0.6pt + bleu-formation.lighten(60%)) +] +#show heading.where(level: 2): set text(size: 11.5pt, weight: "semibold", fill: bleu-formation) +#show heading.where(level: 2): set block(above: 1.3em, below: 0.6em) + +// Code : le fond et les couleurs viennent du thème modele/code.theme +#show raw: set text(size: 8.5pt) + +// Tableaux : en-tête bleu +#set table( + stroke: 0.5pt + rgb("#d0d7de"), + fill: (_, y) => if y == 0 { bleu-formation } else if calc.even(y) { rgb("#f6f8fa") }, +) +#show table.cell.where(y: 0): set text(fill: white, weight: "bold") + +#show figure.caption: set text(size: 8.5pt, style: "italic", fill: gris-doux) diff --git a/modules/03-pwm-adc.qmd b/modules/03-pwm-adc.qmd new file mode 100644 index 0000000..48a453f --- /dev/null +++ b/modules/03-pwm-adc.qmd @@ -0,0 +1,162 @@ +--- +title: "Module 3 — Signaux analogiques : PWM & ADC" +--- + +## Objectifs du module + +- Comprendre le principe du PWM (modulation de largeur d'impulsion) et son usage sur ESP32-S3 via l'API LEDC. +- Réaliser un variateur de luminosité et un contrôle de servomoteur. +- Comprendre le fonctionnement du convertisseur analogique-numérique (ADC) et ses limites. +- Lire un potentiomètre et convertir la valeur brute en unité physique. + +## 1. Qu'est-ce que le PWM ? + +Le PWM (Pulse Width Modulation) simule une sortie analogique en faisant varier rapidement le rapport cyclique (duty cycle) d'un signal carré : plus la portion « HIGH » est longue par rapport à la période, plus la valeur moyenne perçue (tension, luminosité, vitesse) est élevée. C'est la technique utilisée pour faire varier la luminosité d'une LED ou piloter un servomoteur. + +![Le rapport cyclique fixe la tension moyenne perçue par la charge (LED, moteur)](../schemas/svg/m03-pwm-rapport-cyclique.svg){#fig-m03-pwm-rapport-cyclique} + +## 2. Le PWM sur ESP32-S3 : l'API LEDC + +Contrairement à un Arduino Uno où `analogWrite()` suffit, l'ESP32 utilise un contrôleur matériel dédié appelé LEDC (LED Control), plus flexible (fréquence et résolution configurables) mais nécessitant une configuration explicite. Depuis les versions récentes du core Arduino-ESP32, une API simplifiée `analogWrite()` est également disponible et gère LEDC en interne — nous verrons les deux approches. + +### 2.1 Approche simplifiée : `analogWrite()` + +```cpp +#define LED_PIN 4 + +void setup() { + // Rien de particulier a configurer avec l'API simplifiee +} + +void loop() { + for (int duty = 0; duty <= 255; duty++) { + analogWrite(LED_PIN, duty); // 0 = eteint, 255 = pleine puissance + delay(10); + } + for (int duty = 255; duty >= 0; duty--) { + analogWrite(LED_PIN, duty); + delay(10); + } +} +``` + +### 2.2 Approche explicite : API LEDC (contrôle fin) + +Utile lorsqu'on a besoin d'une fréquence ou d'une résolution précise (par exemple pour piloter un servomoteur, qui attend une fréquence de 50 Hz). + +![Commande d'un servomoteur : la largeur de l'impulsion (1 à 2 ms) fixe l'angle](../schemas/svg/m03-servo-50hz.svg){#fig-m03-servo-50hz} + +```cpp +#define LED_PIN 4 +#define PWM_FREQ 5000 // 5 kHz +#define PWM_RES 8 // resolution 8 bits (0-255) + +void setup() { + ledcAttach(LED_PIN, PWM_FREQ, PWM_RES); +} + +void loop() { + for (int duty = 0; duty <= 255; duty++) { + ledcWrite(LED_PIN, duty); + delay(10); + } +} +``` + +::: {.callout-note title="Version du core Arduino-ESP32"} +L'API LEDC a changé entre les versions 2.x et 3.x du core Arduino-ESP32 (l'ancienne API utilisait des numéros de canal explicites via ledcSetup/ledcAttachPin). Vérifiez la version installée dans le gestionnaire de cartes si le code ci-dessus ne compile pas, et adaptez-vous à la documentation correspondante. +::: + +## 3. Exercice : variateur de luminosité avec potentiomètre + +Nous allons combiner PWM et lecture analogique : la position d'un potentiomètre définit la luminosité d'une LED. + +![Câblage de l'exercice : potentiomètre sur GPIO1 (ADC1), LED sur GPIO4 (PWM)](../schemas/svg/m03-montage-variateur.svg){#fig-m03-montage-variateur} + +## 4. Le convertisseur analogique-numérique (ADC) + +L'ADC convertit une tension continue (0 à 3,3 V) en une valeur numérique. L'ESP32-S3 dispose d'un ADC 12 bits, soit une résolution de 4096 valeurs possibles (0 à 4095). + +![Conversion analogique-numérique puis mise à l'échelle avec map()](../schemas/svg/m03-adc-quantification.svg){#fig-m03-adc-quantification} + +| Caractéristique | Valeur | +|---|---| +| Résolution native | 12 bits (0 à 4095) | +| Plage de tension | 0 à 3,3 V (selon atténuation configurée) | +| Unité ADC utilisable en Wi-Fi actif | ADC1 uniquement (ADC2 partagé avec le Wi-Fi) | +| Fonction de lecture | `analogRead(pin)` | + +::: {.callout-warning title="Limitation importante : ADC2 et Wi-Fi"} +L'ADC2 de l'ESP32 partage des ressources internes avec le module Wi-Fi. Si le Wi-Fi est actif (voir Module 7), les lectures sur les broches ADC2 deviennent peu fiables voire impossibles. Pour tout projet connecté utilisant l'analogique, privilégiez les broches de l'ADC1 (généralement GPIO1 à GPIO10 sur l'ESP32-S3). +::: + +### 4.1 Lire un potentiomètre + +```cpp +#define POT_PIN 1 // Broche ADC1 + +void setup() { + Serial.begin(115200); +} + +void loop() { + int valeurBrute = analogRead(POT_PIN); // 0 a 4095 + float tension = valeurBrute * (3.3 / 4095.0); // conversion en volts + Serial.printf("Brut: %d Tension: %.2f V\n", valeurBrute, tension); + delay(200); +} +``` + +### 4.2 Exercice combiné : variateur de LED par potentiomètre + +Utilisez la valeur lue sur le potentiomètre (0-4095) pour piloter la luminosité d'une LED en PWM (0-255) en convertissant l'échelle avec la fonction `map()`. + +```cpp +#define POT_PIN 1 +#define LED_PIN 4 + +void setup() { + ledcAttach(LED_PIN, 5000, 8); + Serial.begin(115200); +} + +void loop() { + int brut = analogRead(POT_PIN); + int duty = map(brut, 0, 4095, 0, 255); + ledcWrite(LED_PIN, duty); + Serial.printf("Potentiometre: %d -> PWM: %d\n", brut, duty); + delay(50); +} +``` + +## 5. Aller plus loin : appliquer le PWM à la LED NeoPixel + +Pour rappel (Module 2), la LED NeoPixel ne se pilote pas avec analogWrite ou ledcWrite : elle utilise un protocole numérique dédié (WS2812) géré par la bibliothèque Adafruit_NeoPixel, dont la fonction `setBrightness()` joue un rôle équivalent à celui du PWM pour l'intensité globale. Reliez les deux notions : adaptez l'exercice du variateur pour que ce soit cette fois la luminosité de la LED NeoPixel qui varie selon le potentiomètre. + +```cpp +#include +#define POT_PIN 1 +#define NEOPIXEL_PIN 48 + +Adafruit_NeoPixel pixel(1, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + pixel.begin(); + pixel.setPixelColor(0, pixel.Color(255, 255, 255)); +} + +void loop() { + int brut = analogRead(POT_PIN); + int luminosite = map(brut, 0, 4095, 0, 255); + pixel.setBrightness(luminosite); + pixel.show(); + delay(50); +} +``` + +## Checklist de fin de module + +- [ ] Je sais expliquer le principe du duty cycle en PWM. +- [ ] Je sais configurer une sortie PWM avec analogWrite ou l'API LEDC. +- [ ] Je connais la résolution et les limites de l'ADC de l'ESP32-S3 (notamment ADC2 + Wi-Fi). +- [ ] J'ai réalisé le variateur de luminosité piloté par potentiomètre, sur LED classique et sur NeoPixel.