🎓 Cours : Architecture d'un projet Arduino Web Blink

  • 0 comments

Comprendre comment les fichiers communiquent entre eux


🎯 Objectif du cours Comprendre comment 5 fichiers travaillent ensemble pour créer une application qui contrôle une LED depuis un navigateur web.


📁 1. La carte d'identité de chaque fichier

Imagine un restaurant 🍽️ :

  • Le client passe commande depuis sa table (interface web)

  • Le serveur transmet la commande en cuisine (Python)

  • Le cuisinier prépare le plat (Arduino)

C'est exactement ce qui se passe ici !


📄 index.html + app.js + style.cssLa Salle du Restaurant (Interface utilisateur)

Fichiers, Rôles et Responsabilités :

  • index.html — Structure la page web et affiche le bouton LED.

  • app.js — Gère les interactions : écoute les clics et met à jour l'affichage.

  • style.css — Habille le bouton en rouge ou vert selon l'état de la LED.


📄 main.pyLe Serveur de salle (Logique Software)

Rôle et Responsabilités :

  • Pont entre le web et l'Arduino — Reçoit les messages du navigateur et envoie des ordres à l'Arduino.

  • Gestionnaire d'état — Mémorise l'état de la LED (allumée ou éteinte).

  • Diffuseur d'informations — Informe tous les clients connectés de l'état actuel.


📄 sketch.cppLa Cuisine (Logique Matériel)

Rôle et Responsabilité : Contrôle physique — Allume/éteint réellement la LED sur la carte. Exécuteur d'ordres — Attend des instructions de Python via le Bridge.


🔗 2. Comment la communication transite entre les fichiers

┌─────────────────────────────────────────────────────────────┐
│                    NAVIGATEUR WEB                           │
│                                                             │
│   index.html          app.js                               │
│   [Bouton LED]  ───►  [Clic détecté]                       │
│                            │                               │
│                     socket.emit('toggle_led')              │
└────────────────────────────┼────────────────────────────────┘
                             │  📡 WebSocket (Internet)
                             ▼
┌─────────────────────────────────────────────────────────────┐
│                    CARTE ARDUINO (Linux)                    │
│                                                             │
│   main.py                                                  │
│   [toggle_led_state()]                                     │
│        │  Reçoit le message                                │
│        │  Inverse led_is_on                                │
│        │                                                   │
│        ├──► Bridge.call("set_led_state", led_is_on)        │
│        │         │  🔌 Bridge (RPC interne)                │
│        │         ▼                                         │
│        │    sketch.cpp                                     │
│        │    [set_led_state()]                              │
│        │    [digitalWrite → LED 💡]                        │
│        │                                                   │
│        └──► ui.send_message('led_status_update', ...)      │
│                  │  📡 WebSocket                           │
└──────────────────┼──────────────────────────────────────────┘
                   ▼
┌─────────────────────────────────────────────────────────────┐
│                    NAVIGATEUR WEB                           │
│   app.js reçoit 'led_status_update'                        │
│   [updateLedStatus() → bouton change de couleur 🟢/🔴]     │
└─────────────────────────────────────────────────────────────┘

💡 Le Bridge, c'est quoi ? C'est un traducteur entre Python et l'Arduino. Python parle un langage, l'Arduino en parle un autre. Le Bridge fait la traduction en temps réel. C'est ce qu'on appelle un RPC(Remote Procedure Call) = "appel de fonction à distance".


🚀 3. Le détail du code à l'initialisation

Voyons ce qui se passe dans les premières secondes après le démarrage.

Étape 1️⃣ — L'Arduino démarre (sketch.cpp)

void setup() {
    // On configure la broche de la LED comme une sortie
    pinMode(LED_BUILTIN, OUTPUT);

    // ⚠️ Attention : sur Arduino, HIGH = LED éteinte !
    // C'est une logique inversée (active LOW)
    digitalWrite(LED_BUILTIN, HIGH);  // → LED éteinte au démarrage

    // On démarre le pont de communication avec Python
    Bridge.begin();

    // On "enregistre" la fonction set_led_state
    // Python pourra l'appeler par son nom "set_led_state"
    Bridge.provide("set_led_state", set_led_state);
}

🧠 À retenir : L'Arduino s'allume, éteint la LED, puis dit au Bridge : "Si quelqu'un demande set_led_state, appelle ma fonction !"


Étape 2️⃣ — Python démarre (main.py)

# L'état de la LED est mémorisé ici (dans la RAM de la carte)
led_is_on = False  # ← Par défaut : éteinte

# On crée l'interface web (le serveur WebSocket démarre)
ui = WebUI()

# On "abonne" nos fonctions aux messages qui peuvent arriver
# Si le navigateur envoie 'toggle_led' → appelle toggle_led_state()
ui.on_message('toggle_led', toggle_led_state)

# Si le navigateur envoie 'get_initial_state' → appelle on_get_initial_state()
ui.on_message('get_initial_state', on_get_initial_state)

# On lance l'application (boucle infinie d'écoute)
App.run()

🧠 À retenir : Python démarre son serveur et attend des messages. C'est comme un répondeur téléphonique qui associe chaque numéro à une action.


Étape 3️⃣ — Le navigateur se connecte (app.js)

// Quand la page est chargée
document.addEventListener('DOMContentLoaded', () => {
    initSocketIO();
    ledButton.addEventListener('click', handleLedClick);
});

function initSocketIO() {
    // Quand la connexion est établie avec Python...
    socket.on('connect', () => {
        // On demande l'état actuel de la LED
        // (pour afficher le bon état si on recharge la page)
        socket.emit('get_initial_state', {});
    });
}

🧠 À retenir : Le navigateur se connecte et demande immédiatement l'état de la LED pour être synchronisé.


🗓️ Chronologie complète de l'initialisation

T=0s   Arduino démarre → LED éteinte → Bridge prêt
T=1s   Python démarre → Serveur WebSocket actif → Abonnements enregistrés
T=2s   Navigateur ouvre la page → Connexion WebSocket établie
T=2s   Navigateur envoie 'get_initial_state'
T=2s   Python répond avec { led_is_on: false, status_text: "LED IS OFF" }
T=2s   Navigateur affiche le bouton en rouge "LED IS OFF" ✅

🖱️ 4. Le comportement lors d'une interaction (clic sur le bouton)

Suivons le voyage d'un simple clic de la souris jusqu'à la LED !

🔴 → 🟢 : L'utilisateur clique sur le bouton


ÉTAPE 1 : app.js détecte le clic

function handleLedClick() {
    // On envoie un message "toggle_led" au serveur Python
    // Le {} signifie qu'on n'envoie pas de données supplémentaires
    socket.emit('toggle_led', {});
    // ⚠️ On n'attend PAS la réponse ici, elle viendra plus tard
}

ÉTAPE 2 : main.py reçoit le message

def toggle_led_state(client, data):
    global led_is_on

    # On inverse l'état : False → True ou True → False
    led_is_on = not led_is_on

    # On ordonne à l'Arduino d'allumer/éteindre la LED
    # C'est ici que Python "traverse" vers le matériel
    Bridge.call("set_led_state", led_is_on)

    # On informe TOUS les navigateurs connectés du nouvel état
    # (utile si plusieurs personnes ont la page ouverte en même temps !)
    ui.send_message('led_status_update', get_led_status())

ÉTAPE 3 : sketch.cpp reçoit l'ordre

void set_led_state(bool state) {
    // Si state = true (LED doit s'allumer)
    //   → on met LOW (logique inversée !)
    // Si state = false (LED doit s'éteindre)
    //   → on met HIGH
    digitalWrite(LED_BUILTIN, state ? LOW : HIGH);
    //                         ↑
    //                  Opérateur ternaire :
    //                  condition ? si_vrai : si_faux
}

⚠️ Erreur fréquente des débutants : Sur beaucoup de cartes Arduino, la LED intégrée fonctionne en logique inversée :

  • HIGH = LED éteinte

  • LOW = LED allumée

C'est contre-intuitif mais c'est une caractéristique du hardware !


ÉTAPE 4 : app.js reçoit la confirmation

socket.on('led_status_update', (message) => {
    updateLedStatus(message);
});

function updateLedStatus(status) {
    const isOn = status.led_is_on;

    // On change l'apparence du bouton (classe CSS)
    ledButton.className = isOn ? 'led-on' : 'led-off';

    // ⚠️ BUG TROUVÉ DANS LE CODE ORIGINAL !
    // 'LED IS ONB' devrait être 'LED IS ON' (lettre B en trop)
    ledText.textContent = isOn ? 'LED IS ONB' : 'LED IS OFF';
    //                                    ↑
    //                              Faute de frappe !
}

🗓️ Chronologie complète d'un clic

👆 Clic utilisateur
   │
   ▼ app.js
   socket.emit('toggle_led')
   │
   │ ~~~~ WebSocket ~~~~
   │
   ▼ main.py
   led_is_on = True
   Bridge.call("set_led_state", True)
   │                    │
   │                    │ ~~~~ Bridge RPC ~~~~
   │                    │
   │                    ▼ sketch.cpp
   │                    digitalWrite(LED_BUILTIN, LOW)
   │                    💡 LED s'allume physiquement !
   │
   ui.send_message('led_status_update', {...})
   │
   │ ~~~~ WebSocket ~~~~
   │
   ▼ app.js
   updateLedStatus()
   🟢 Bouton devient vert "LED IS ON"

Merci d'avoir suivi ce cours

0 comments

Rejoindreor login to leave a comment