Comment le robot « pense » : du joystick sur le téléphone jusqu'aux roues. Pas besoin de savoir programmer — le code est en Python, en français, et chaque morceau important est expliqué.
1
Vue d'ensemble
l'idée en une image
Le robot n'est pas autonome : c'est nous qui le pilotons au téléphone. Le téléphone affiche un joystick et, 20 fois par seconde, il envoie deux nombres à l'ESP32 par Wi-Fi : la vitesse de gauche et la vitesse de droite. L'ESP32 vérifie d'abord ses capteurs de bord, puis applique la commande aux moteurs et au flipper.
On envoieTéléphone
joystick + bouton flipper
Wi-Fi · 20×/s
Le cerveauESP32
lit, décide, sécurise
il agit
Ça bougeMoteurs + servos
4 roues · flipper
En boucle : les 7 capteurs de bord renvoient en permanence l'info « sol noir = bord » à l'ESP32. S'ils voient le bord, le cerveau reprend la main tout seul et ignore le pilote — c'est ce qui empêche le robot de sortir.
2
Les 4 fichiers
qui fait quoi
Tout le robot tient en 4 fichiers, environ 430 lignes, tout en français (noms, fonctions, commentaires).
boot.py3 lignes
Le tout premier fichier lancé quand l'ESP32 s'allume. Il ne fait qu'une chose : démarrer le programme principal. C'est le bouton « ON ».
main.py~130 lignes
Le chef d'orchestre. Il crée le Wi-Fi, sert la page web au téléphone, reçoit les commandes du joystick et surveille la connexion (sécurité).
robot.py~150 lignes
Le « corps » du robot. Il sait faire bouger les moteurs, lever le flipper et lire les capteurs. Chaque action est une petite fonction au nom clair.
page.html~140 lignes
La télécommande affichée sur le téléphone : le joystick, le bouton FLIPPER et le bouton STOP. C'est elle qui envoie les ordres au robot.
3
Le robot crée son propre Wi-Fi
Pas besoin de box ni d'internet
main.py · demarrer_wifi()
def demarrer_wifi():
ap = network.WLAN(network.AP_IF) # l'ESP32 devient une borne Wi-Fi
ap.active(True)
ap.config(essid="RobotSumo", # nom du réseau créé
authmode=network.AUTH_OPEN) # ouvert, sans mot de passe
print("Wi-Fi prêt :", ap.ifconfig()[0])
En clairL'ESP32 ne se connecte pas à un réseau : il fabrique le sien, appelé « RobotSumo ». Le téléphone s'y connecte directement, comme à un Wi-Fi normal.
AP_IF = mode « point d'accès » : l'ESP32 joue le rôle de la box.
Réseau ouvert : pas de mot de passe, on se connecte en 2 secondes le jour du combat.
Le robot est ensuite joignable à l'adresse 192.168.4.1.
4
Le serveur web : il écoute le téléphone
Une boucle qui ne se bloque jamais
main.py · boucle_serveur()
serveur.settimeout(0.05) # ne pas rester bloqué à attendre
while True:
verifier_watchdog() # 1. sécurité : commande reçue récemment ?
try:
connexion, _ = serveur.accept() # 2. un téléphone nous parle ?
traiter_requete(connexion, page_html) # 3. on lui répond
connexion.close()
except OSError:
pass # personne ne parle → on continue la boucle
En clairLe robot tourne en boucle, encore et encore. À chaque tour il fait deux choses : il vérifie sa sécurité, puis il regarde si le téléphone lui a envoyé quelque chose.
settimeout(0.05) : la clé du truc. Le robot n'attend pas indéfiniment qu'on lui parle — il « jette un œil » 50 ms max, puis passe à la suite. Comme ça la sécurité tourne en parallèle.
Sans ça, si personne n'envoyait de commande, le robot resterait figé à attendre, sécurité comprise.
Deux types de messages reçus
main.py · traiter_requete()
# Cas 1 : le téléphone demande la télécommande
if methode == "GET" and chemin == "/":
repondre(connexion, page_html) # on envoie la page HTML
# Cas 2 : le téléphone envoie une commande de joystick
if methode == "POST" and chemin == "/cmd":
commande = json.loads(corps) # ex : {"gauche": 80, "droite": 80}
executer_commande(commande)
En clairLe téléphone parle au robot avec des messages web très simples.
GET / = « donne-moi la télécommande » → le robot renvoie page.html.
POST /cmd = « voici où j'ai mis le joystick » → un petit texte JSON, ex. {"gauche": 80, "droite": 80}.
5
Du joystick aux roues
le morceau que le prof adore
Le robot n'a pas de volant : il tourne comme un char, en faisant aller un côté plus vite que l'autre. Toute la magie est dans 4 lignes qui transforment la position du doigt en deux vitesses.
Mélanger « avancer » et « tourner »
page.html · maj_joystick()
const avant = -dy / R * 100; // haut/bas du joystick → avancer/reculer
const tourner = dx / R * 100; // gauche/droite du joystick → tourner
vitesseGauche = avant + tourner; // roue gauche
vitesseDroite = avant - tourner; // roue droite
En clairdx et dy = de combien le doigt s'éloigne du centre. On en tire deux idées : « à quel point j'avance » et « à quel point je tourne », puis on les additionne pour chaque roue.
Doigt tout en haut : tourner = 0 → gauche et droite à la même vitesse → le robot va tout droit.
Doigt en haut à droite : on ajoute à gauche, on enlève à droite → la roue gauche pousse plus fort → le robot vire à droite.
Doigt à fond à gauche : un côté part en avant, l'autre en arrière → le robot pivote sur place.
Côté robot : on regarde la sécurité d'abord
main.py · executer_commande()
def executer_commande(commande):
robot.derniere_commande_ms = time.ticks_ms() # « j'ai reçu un ordre »
if robot.reflexe_bord(): # AU BORD ? on gère ça en priorité…
return # …et on IGNORE le joystick
if "gauche" in commande and "droite" in commande:
robot.piloter(commande["gauche"], commande["droite"])
if commande.get("flipper") is True:
robot.donner_coup_de_flipper()
En clairQuand une commande arrive, le robot ne l'applique pas tout de suite. Il vérifie d'abord le bord. S'il est au bord, il fait son réflexe de sécurité et return : la commande du pilote est jetée. Sinon, il bouge et, si on a appuyé sur FLIPPER, il donne un coup.
6
Faire tourner les moteurs
Un pourcentage devient de la puissance
robot.py · _piloter_cote()
def _piloter_cote(self, cote, pourcent):
if pourcent > 90: pourcent = 90 # jamais plus de 90 % (sécurité)
if pourcent < -90: pourcent = -90
duty = int(abs(pourcent) * 65535 / 100) # % → puissance envoyée au moteur
if pourcent >= 0:
pwm_avance.duty_u16(duty) # nombre positif → on avance
pwm_recule.duty_u16(0)
else:
pwm_avance.duty_u16(0)
pwm_recule.duty_u16(duty) # nombre négatif → on recule
En clairOn reçoit un nombre entre -100 et +100. Le signe donne le sens (avant/arrière), la taille donne la puissance. Le robot envoie cette puissance sur l'un des deux fils du driver de moteur (BTS7960).
PWM = on allume/éteint le moteur très vite ; plus on le laisse « allumé », plus il est puissant. duty_u16 va de 0 (arrêt) à 65535 (à fond).
La limite à 90 % protège les drivers des à-coups de courant au démarrage.
Chaque côté a 2 fils : un pour avancer, un pour reculer. On n'en allume qu'un à la fois.
ANGLE_FLIPPER_BAS_US = 700 # impulsion courte → lame en bas
ANGLE_FLIPPER_HAUT_US = 2000 # impulsion longue → lame en haut
def donner_coup_de_flipper(self):
self.flipper_haut() # on lève la lame
time.sleep_ms(600) # on la garde en l'air 0,6 s
self.flipper_bas() # on redescend
En clairUn servomoteur se commande par la durée d'un petit signal répété : court = lame baissée, long = lame levée. Le « coup de flipper » est juste : lever, attendre un peu, baisser.
Les deux servos reçoivent le même ordre en même temps → la lame monte droit.
Au démarrage, la lame est forcée en bas : pas de mauvaise surprise au réveil.
8
Les 4 sécurités
pourquoi le robot ne sort jamais
Quatre garde-fous indépendants. Même si le pilote fait une bêtise, le robot se protège tout seul.
1
Le bord est prioritaire
def reflexe_bord(self):
if self.bord_detecte(): # un capteur voit le sol noir ?
self.reculer(60) # on recule
time.sleep_ms(200)
self.tourner_droite(60) # on se réoriente
self.stop()
return True # « j'ai pris la main »
return False
Vérifié avant chaque commande du pilote. Si un capteur voit le bord, le robot recule et tourne tout seul, et le joystick est ignoré. C'est l'anti-suicide.
2
Coupure auto (0,5 s)
def verifier_watchdog():
# plus aucune commande depuis 0,5 s ?
if time.ticks_diff(time.ticks_ms(),
robot.derniere_commande_ms) > 500:
robot.stop() # on coupe les moteurs
Si le téléphone se déconnecte, plante ou sort de portée, plus aucune commande n'arrive. Au bout d'une demi-seconde, le robot s'arrête seul au lieu de partir tout droit.
3
Puissance plafonnée à 90 %
VITESSE_MAX_POURCENT = 90
if pourcent > 90: pourcent = 90
if pourcent < -90: pourcent = -90
On ne demande jamais 100 % aux moteurs. Cette marge protège les drivers (BTS7960) des pics de courant au démarrage et fiabilise toute l'électronique.
4
Flipper baissé au démarrage
def __init__(self):
...
self.flipper_bas() # lame forcée en bas dès l'allumage
Dès que le robot s'allume, la lame est mise en position basse. Pas de réveil avec la lame en l'air, qui pourrait gêner ou être dangereuse.
9
Questions que le prof pourrait poser
Les réponses courtes à avoir en tête. Touchez une question pour voir la réponse.
Pourquoi MicroPython et pas du C / Arduino ?
Parce que c'est lisible par toute l'équipe. Le code est court et en français, donc n'importe lequel d'entre nous peut l'expliquer. Pour un robot piloté à la main, MicroPython est largement assez rapide.
Comment le robot ne sort-il jamais de l'arène ?
La fonction reflexe_bord() est testée avant toute commande du pilote. Si un capteur de bord voit le sol noir, le robot recule, tourne, et ignore le joystick. La sécurité passe toujours avant le pilote.
Et si le Wi-Fi coupe en plein combat ?
Un watchdog surveille l'heure de la dernière commande. Sans nouvelle pendant 500 ms, les moteurs s'arrêtent automatiquement. Le robot ne part pas tout seul dans le décor.
Comment le joystick devient-il un mouvement ?
On calcule « avancer » (haut/bas) et « tourner » (gauche/droite), puis : gauche = avant + tourner et droite = avant − tourner. Faire aller un côté plus vite que l'autre fait tourner le robot, comme un char.
Pourquoi limiter la puissance à 90 % ?
Pour protéger les drivers de moteurs (BTS7960) des pics de courant au démarrage. On garde une marge de sécurité plutôt que de tout risquer pour 10 % de vitesse en plus.
Le robot est-il autonome ?
Non : il est piloté au smartphone. Seules les sécurités sont automatiques (réflexe de bord et arrêt d'urgence). Le radar est lu par le code mais la stratégie reste manuelle.
Comment le téléphone communique avec le robot ?
L'ESP32 sert une page web (la télécommande). Le téléphone envoie la position du joystick par des requêtes POST /cmd en JSON, 20 fois par seconde. Pas d'app à installer.
Pourquoi un serveur « non-bloquant » ?
Avec settimeout(0.05), le robot n'attend jamais bloqué qu'on lui parle. Il garde du temps pour faire tourner ses sécurités en parallèle de l'écoute du téléphone.