Le réveil de la manette
Chevalier du câble DupontApprenti Sith du câble Dupont ★★ 🎮 Utilise la manette, Luc… euh, Nathan.🎮 Désormais, c'est une machine qui obéit à Nathan. Le haut commandement approuve.
📡 Transmission de l'Alliance · R2-DuPont
« Cette fois, tes mains entrent réellement dans la boucle : axes, boutons, commandes. Un conseil : quand tu pousses la manette à droite et que tout part à gauche, ce n'est pas la Force. C'est probablement une valeur inversée. »
🛰️ Haut commandement · Opération Contre-Circuit
« L'Alliance veut empêcher toute prise de contrôle direct du système. Tu vas faire exactement l'inverse. Active l'interface. Teste les axes. Teste les boutons. Prends les commandes. À partir de maintenant, c'est une machine qui obéit à Nathan. Le haut commandement considère cela comme une amélioration. »
Objectif : coupler la manette PS4 à l'ESP32 en Bluetooth, puis tester toutes ses commandes une par une : 4 boutons de face, 4 flèches, 4 gâchettes, 2 joysticks (et leurs clics), SHARE, OPTIONS et PS. Chacune fait réagir quelque chose : la LED bleue de la carte, la barre lumineuse de la manette, ses vibrations et le moniteur série. Toujours aucun câblage !
🧭 Notions de la mission : Le PWM · Anatomie d'un programme · Tableaux, boucles, fonctions · Le temps
Équipement de mission¶
🧰 Matériel
- ESP32 (sur son support ou non)
- Câble USB-C de données
- Manette PS4 chargée, et son câble de recharge (micro-USB)
💻 Logiciels
- Arduino IDE 2 (mission 0)
- Cartes « ESP32 + Bluepad32 » de Ricardo Quesada
🌐 Connexion internet
Oui : environ 200 Mo pour Bluepad32
🔓 Missions à réussir avant
Mission 0 réussie
🛠️ Outils
Aucun
⏱️ Durée
45 min à 1 h
Comment ça marche ?¶
L'ESP32 a le Bluetooth intégré, comme une console. Un logiciel gratuit, Bluepad32, lui apprend à parler aux manettes de jeu : la manette se connecte directement à la carte, sans téléphone ni ordinateur au milieu. Plusieurs fois par seconde, elle envoie l'état de tous ses boutons et joysticks, et notre programme décide quoi en faire.
Pourquoi notre carte convient
La manette PS4 utilise le Bluetooth « classique ». Notre ESP32 (ESP-WROOM-32) le gère. Ce n'est pas le cas de toutes les cartes : les ESP32-S3 et ESP32-C3, par exemple, ne font que le Bluetooth basse consommation et ne savent pas parler à une manette PS4.
Étape 1 · Préparer la manette¶
- Charger la manette avec son câble, branché sur un chargeur ou un ordinateur. Pendant la charge, la barre lumineuse pulse doucement en orange.
- Éloigner la PS4 (ou la débrancher) si une console est dans la maison : sinon, la manette risque de se reconnecter à elle au lieu de l'ESP32.
Étape 2 · Ajouter Bluepad32 à l'IDE¶
On ajoute une deuxième adresse dans les préférences, à côté de celle d'Espressif installée à la mission 0.
- Ouvrir Fichier → Préférences.
- À droite du champ « URL de gestionnaire de cartes supplémentaires », cliquer sur la petite icône de fenêtre : une liste s'ouvre, avec une adresse par ligne.
-
Garder la ligne d'Espressif et ajouter, sur une nouvelle ligne, l'adresse officielle de Bluepad32 :
-
Valider avec OK, puis encore OK.
Étape 3 · Installer les cartes « ESP32 + Bluepad32 »¶
- Ouvrir le gestionnaire de cartes : Outils → Carte → Gestionnaire de carte…
- Chercher bluepad32.
- Installer « esp32_bluepad32 » par Ricardo Quesada (l'auteur de Bluepad32).
C'est un gros téléchargement (environ 200 Mo) : Bluepad32 arrive avec sa propre copie des outils ESP32. Pas d'inquiétude, les cartes « esp32 » de la mission 0 restent installées à côté.
Étape 4 · Choisir la bonne carte¶
Dans le menu Outils :
| Réglage | Choix | Remarque |
|---|---|---|
| Carte | ESP32 + Bluepad32 Arduino → ESP32 Dev Module | Le même modèle qu'à la mission 0, mais dans la famille Bluepad32 |
| Port | COM3 (Windows) ou /dev/ttyUSB0 (Linux) | Comme à la mission 0 |
| Tous les autres | Laisser par défaut |
Le piège de la mission 1
Si tu restes sur esp32 → ESP32 Dev Module (la carte de la mission 0), la vérification échoue avec :
Bluepad32 n'existe que dans la famille « ESP32 + Bluepad32 Arduino ». À partir d'ici, toutes les missions se téléversent avec cette carte.
Étape 5 · Premier contact avec la manette¶
On commence par l'exemple fourni avec Bluepad32, pour vérifier que tout fonctionne avant d'écrire notre propre programme.
- Ouvrir Fichier → Exemples → Bluepad32_ESP32 → Controller (dans la partie « Exemples pour ESP32 Dev Module », plus bas dans la liste).
- Téléverser (la flèche →). Si le téléversement n'arrive pas à se connecter, maintenir le bouton BOOT de la carte au moment où « Connecting… » s'affiche, comme à la mission 0.
- Ouvrir le moniteur série (la loupe en haut à droite) et régler la vitesse sur 115200 bauds.
- Mettre la manette en mode couplage : maintenir SHARE puis PS enfoncés ensemble pendant environ 3 secondes, jusqu'à ce que la barre lumineuse clignote très vite (deux flashs rapprochés, qui se répètent).
- Au bout de quelques secondes, la barre lumineuse devient fixe et le moniteur série annonce que la manette est connectée.
- Appuyer sur des boutons, bouger les joysticks : des lignes de nombres défilent.
Lire ce que dit la manette¶
Chaque ligne du moniteur série est une « photo » de la manette. Voici comment la déchiffrer :
| Dans la ligne | Ce que c'est | Valeurs |
|---|---|---|
dpad |
les 4 flèches | 0 si rien n'est appuyé ; chaque flèche allume un « bit » : ↑ = 1, ↓ = 2, → = 4, ← = 8 |
buttons |
✕ ○ □ △, L1, R1, L2, R2, L3, R3 | un nombre qui change selon les boutons enfoncés |
axis L |
joystick gauche (X puis Y) | environ −508 à +512, 0 au centre |
axis R |
joystick droit (X puis Y) | pareil |
brake |
gâchette L2, enfoncée à moitié ou à fond | 0 à 1020 : plus on appuie, plus c'est grand |
throttle |
gâchette R2 | 0 à 1020 |
misc |
PS, SHARE, OPTIONS | un nombre qui change selon le bouton |
Le Y est à l'envers
Pour les joysticks, pousser vers le haut donne un Y négatif, vers le bas un Y positif. C'est une habitude héritée des écrans d'ordinateur, où l'on compte les lignes de haut en bas.
Les gâchettes sont des boutons et des curseurs
L2 et R2 existent deux fois : comme boutons (appuyé ou non), et comme curseurs brake / throttle qui mesurent jusqu'où on appuie. C'est ce qui permettra plus tard d'accélérer doucement ou à fond.
Étape 6 · Le banc d'essai Endor-Pad¶
Place à notre programme. Il transforme la manette en tableau de bord : chaque commande a un effet, et une check-list compte les commandes testées. Quand les 19 sont validées… surprise.
Qui fait quoi¶
| Commande | Effet | Barre lumineuse |
|---|---|---|
| ✕ | LED bleue : clignotement lent | bleue |
| ○ | LED bleue : clignotement rapide | rouge |
| △ | LED bleue : SOS en morse | verte |
| □ | LED bleue : « respiration » (s'allume et s'éteint doucement) | rose |
| ↑ / ↓ | le scénario accélère / ralentit (de 25 % à 400 %) | |
| ← / → | scénario précédent / suivant | |
| L1 | mode pilote : la LED bleue brille selon le joystick gauche poussé vers le haut | jaune |
| R1 | extinction de la LED | presque éteinte |
| L2 | fait vibrer le gros moteur, plus ou moins fort selon l'appui | |
| R2 | fait vibrer le petit moteur, plus ou moins fort | |
| Joystick droit | la barre lumineuse prend la couleur de l'arc-en-ciel qui correspond à sa direction | 🌈 |
| Joystick gauche | pilote la LED en mode pilote (L1) | |
| L3 / R3 (clic sur un joystick) | affiche les valeurs exactes de ce joystick | |
| SHARE | active / coupe le radar : les valeurs des joysticks et gâchettes défilent 4 fois par seconde | |
| OPTIONS | affiche le bilan : ce qui reste à tester, le scénario, la batterie | |
| PS | petite vibration « bzzz » |
Pour valider un joystick, il faut le pousser au bout dans ses 4 directions (↑ ↓ ← →).
Le code¶
- Fichier → Nouveau croquis, effacer tout ce qui est écrit.
- Déplier le programme ci-dessous et le copier (bouton de copie en haut à droite du bloc), ou télécharger le fichier.
- Enregistrer : Fichier → Enregistrer sous… → mission1_endor_pad.
Le programme · cliquer pour l'afficher
// Mission 1 · Endor-Pad : le banc d'essai de la manette
// Chaque bouton, flèche, gâchette et joystick fait réagir quelque chose :
// la LED bleue de l'ESP32, la barre lumineuse de la manette, ses vibrations
// et le moniteur série. Quand les 19 commandes sont validées : célébration !
#include <Bluepad32.h>
// ================= Réglages =================
const int LED = 2; // LED bleue intégrée (GPIO 2)
const int ZONE_MORTE = 100; // joystick en dessous de 100 : considéré au repos
const int SEUIL_DIRECTION = 400; // joystick poussé presque au bout (max ~510)
// ================= La manette =================
ControllerPtr manette = nullptr;
// ================= Les scénarios de la LED bleue =================
// 0 éteint, 1 lent, 2 rapide, 3 SOS, 4 respiration, 5 pilote (au joystick)
const int NB_SCENARIOS = 6;
const char* NOMS_SCENARIOS[NB_SCENARIOS] = {
"éteint", "clignotement lent", "clignotement rapide", "SOS", "respiration", "pilote"
};
int scenario = 0;
int vitesse = 100; // en % : 100 = normal, 200 = deux fois plus vite
unsigned long horloge = 0; // horloge des scénarios (avance selon la vitesse)
unsigned long dernierTemps = 0;
// ================= La check-list des commandes =================
enum {
T_CROIX, T_ROND, T_TRIANGLE, T_CARRE,
T_HAUT, T_BAS, T_GAUCHE, T_DROITE,
T_L1, T_R1, T_L2, T_R2, T_L3, T_R3,
T_SHARE, T_OPTIONS, T_PS,
T_STICK_G, T_STICK_D,
NB_TESTS
};
const char* NOMS_TESTS[NB_TESTS] = {
"✕", "○", "△", "□",
"flèche ↑", "flèche ↓", "flèche ←", "flèche →",
"L1", "R1", "L2", "R2", "L3 (clic joystick gauche)", "R3 (clic joystick droit)",
"SHARE", "OPTIONS", "PS",
"joystick gauche (4 directions)", "joystick droit (4 directions)"
};
bool teste[NB_TESTS];
int nbTestes = 0;
// Directions atteintes par chaque joystick : 1 haut, 2 bas, 4 gauche, 8 droite
int directionsG = 0;
int directionsD = 0;
// ================= Barre lumineuse et vibrations =================
uint8_t couleurR = 0, couleurG = 0, couleurB = 255; // couleur choisie aux boutons
uint8_t envoyeR = 1, envoyeG = 1, envoyeB = 1; // dernière couleur envoyée
unsigned long dernierEnvoiCouleur = 0;
unsigned long dernierEnvoiVibration = 0;
unsigned long finCelebration = 0;
// Radar (SHARE) : affiche en continu les valeurs des joysticks et des gâchettes
bool radar = false;
unsigned long dernierRadar = 0;
// ================= Mémoire des boutons (pour détecter l'appui) =================
bool avant[NB_TESTS];
// Renvoie true UNIQUEMENT au moment où le bouton vient d'être enfoncé.
bool vientDAppuyer(int numero, bool appuye) {
bool nouveau = appuye && !avant[numero];
avant[numero] = appuye;
return nouveau;
}
// ------------------------------------------------------------------
void celebrer() {
Serial.println("\n🎉🎉🎉 MANETTE 100 % OPÉRATIONNELLE ! 🎉🎉🎉");
Serial.println("Mission 1 réussie. Que la Force soit avec toi, Nathan !");
finCelebration = millis() + 4000; // 4 secondes d'arc-en-ciel
manette->playDualRumble(0, 800, 255, 255); // grosse vibration
}
void valider(int numero) {
if (teste[numero]) return; // déjà validé : rien à faire
teste[numero] = true;
nbTestes++;
Serial.printf(" ✔ Nouveau : %s (%d/%d)\n", NOMS_TESTS[numero], nbTestes, NB_TESTS);
if (nbTestes == NB_TESTS) celebrer();
}
void afficherBilan() {
Serial.printf("\n📋 Bilan : %d/%d commandes validées\n", nbTestes, NB_TESTS);
for (int i = 0; i < NB_TESTS; i++) {
if (!teste[i]) Serial.printf(" ☐ reste à tester : %s\n", NOMS_TESTS[i]);
}
Serial.printf(" Scénario : %s, vitesse %d %%, batterie %d %%\n",
NOMS_SCENARIOS[scenario], vitesse, manette->battery() * 100 / 255);
}
void choisirScenario(int s, uint8_t r, uint8_t g, uint8_t b) {
scenario = s;
couleurR = r; couleurG = g; couleurB = b;
Serial.printf(" → LED : %s\n", NOMS_SCENARIOS[scenario]);
}
// Couleur de l'arc-en-ciel : angle de 0 à 359 degrés → rouge, vert, bleu
void arcEnCiel(int angle, uint8_t &r, uint8_t &g, uint8_t &b) {
angle = (angle % 360 + 360) % 360;
int zone = angle / 60;
int x = (angle % 60) * 255 / 60; // monte de 0 à 255 dans la zone
switch (zone) {
case 0: r = 255; g = x; b = 0; break;
case 1: r = 255 - x; g = 255; b = 0; break;
case 2: r = 0; g = 255; b = x; break;
case 3: r = 0; g = 255 - x; b = 255; break;
case 4: r = x; g = 0; b = 255; break;
default: r = 255; g = 0; b = 255 - x; break;
}
}
// ================= Connexion / déconnexion =================
void onConnectedController(ControllerPtr ctl) {
if (manette != nullptr) {
Serial.println("Une manette est déjà connectée : la nouvelle est ignorée.");
return;
}
manette = ctl;
Serial.printf("\n🎮 Manette connectée : %s\n", ctl->getModelName().c_str());
Serial.println("Teste chaque bouton, flèche, gâchette et joystick.");
Serial.println("OPTIONS = bilan, SHARE = radar des joysticks.");
envoyeR = envoyeG = envoyeB = 1; // force le renvoi de la couleur
}
void onDisconnectedController(ControllerPtr ctl) {
if (manette == ctl) {
manette = nullptr;
Serial.println("\n❌ Manette déconnectée");
}
}
// ================= Lecture des boutons =================
void lireBoutons() {
// --- Les 4 boutons de face : un scénario + une couleur chacun ---
if (vientDAppuyer(T_CROIX, manette->a())) {
Serial.println("Bouton ✕"); valider(T_CROIX);
choisirScenario(1, 0, 0, 255); // bleu
}
if (vientDAppuyer(T_ROND, manette->b())) {
Serial.println("Bouton ○"); valider(T_ROND);
choisirScenario(2, 255, 0, 0); // rouge
}
if (vientDAppuyer(T_TRIANGLE, manette->y())) {
Serial.println("Bouton △"); valider(T_TRIANGLE);
choisirScenario(3, 0, 255, 0); // vert
}
if (vientDAppuyer(T_CARRE, manette->x())) {
Serial.println("Bouton □"); valider(T_CARRE);
choisirScenario(4, 255, 0, 160); // rose
}
// --- Les flèches : ↑ ↓ changent la vitesse, ← → changent de scénario ---
uint8_t f = manette->dpad();
if (vientDAppuyer(T_HAUT, f & DPAD_UP)) {
vitesse = min(vitesse + 25, 400);
Serial.printf("Flèche ↑ : vitesse %d %%\n", vitesse); valider(T_HAUT);
}
if (vientDAppuyer(T_BAS, f & DPAD_DOWN)) {
vitesse = max(vitesse - 25, 25);
Serial.printf("Flèche ↓ : vitesse %d %%\n", vitesse); valider(T_BAS);
}
if (vientDAppuyer(T_GAUCHE, f & DPAD_LEFT)) {
Serial.println("Flèche ←"); valider(T_GAUCHE);
choisirScenario((scenario + NB_SCENARIOS - 1) % NB_SCENARIOS, couleurR, couleurG, couleurB);
}
if (vientDAppuyer(T_DROITE, f & DPAD_RIGHT)) {
Serial.println("Flèche →"); valider(T_DROITE);
choisirScenario((scenario + 1) % NB_SCENARIOS, couleurR, couleurG, couleurB);
}
// --- Les boutons de tranche ---
if (vientDAppuyer(T_L1, manette->l1())) {
Serial.println("L1 : mode pilote, la LED suit le joystick gauche"); valider(T_L1);
choisirScenario(5, 255, 160, 0); // jaune
}
if (vientDAppuyer(T_R1, manette->r1())) {
Serial.println("R1 : extinction"); valider(T_R1);
choisirScenario(0, 20, 20, 20); // presque éteinte
}
if (vientDAppuyer(T_L2, manette->l2())) {
Serial.println("L2 : appuie plus ou moins fort… (gros moteur)"); valider(T_L2);
}
if (vientDAppuyer(T_R2, manette->r2())) {
Serial.println("R2 : appuie plus ou moins fort… (petit moteur)"); valider(T_R2);
}
// --- Clic sur les joysticks : affiche leurs valeurs exactes ---
if (vientDAppuyer(T_L3, manette->thumbL())) {
Serial.printf("L3 : joystick gauche X=%d Y=%d\n", manette->axisX(), manette->axisY());
valider(T_L3);
}
if (vientDAppuyer(T_R3, manette->thumbR())) {
Serial.printf("R3 : joystick droit X=%d Y=%d\n", manette->axisRX(), manette->axisRY());
valider(T_R3);
}
// --- Les boutons du milieu ---
if (vientDAppuyer(T_OPTIONS, manette->miscStart())) {
Serial.println("OPTIONS"); valider(T_OPTIONS);
afficherBilan();
}
if (vientDAppuyer(T_SHARE, manette->miscSelect())) {
radar = !radar;
Serial.printf("SHARE : radar %s\n", radar ? "activé" : "désactivé"); valider(T_SHARE);
}
if (vientDAppuyer(T_PS, manette->miscSystem())) {
Serial.println("PS : bzzz !"); valider(T_PS);
manette->playDualRumble(0, 300, 200, 0); // petite vibration
}
}
// ================= Lecture des joysticks =================
// Note : pour Y, vers le HAUT = négatif, vers le BAS = positif.
int directionsAtteintes(int x, int y) {
int d = 0;
if (y < -SEUIL_DIRECTION) d |= 1; // haut
if (y > SEUIL_DIRECTION) d |= 2; // bas
if (x < -SEUIL_DIRECTION) d |= 4; // gauche
if (x > SEUIL_DIRECTION) d |= 8; // droite
return d;
}
void suivreJoystick(int x, int y, int &directions, int numero, const char* nom) {
int nouvelles = directionsAtteintes(x, y) & ~directions;
if (nouvelles == 0) return;
directions |= nouvelles;
Serial.printf("Joystick %s : %s%s%s%s (%d/4 directions)\n", nom,
(nouvelles & 1) ? "↑ " : "", (nouvelles & 2) ? "↓ " : "",
(nouvelles & 4) ? "← " : "", (nouvelles & 8) ? "→ " : "",
__builtin_popcount(directions));
if (directions == 15) valider(numero);
}
void lireJoysticks() {
suivreJoystick(manette->axisX(), manette->axisY(), directionsG, T_STICK_G, "gauche");
suivreJoystick(manette->axisRX(), manette->axisRY(), directionsD, T_STICK_D, "droit");
// Radar : 4 fois par seconde, toutes les valeurs d'un coup
if (radar && millis() - dernierRadar > 250) {
dernierRadar = millis();
Serial.printf("📡 G X=%4d Y=%4d | D X=%4d Y=%4d | L2=%4d R2=%4d\n",
manette->axisX(), manette->axisY(), manette->axisRX(), manette->axisRY(),
manette->brake(), manette->throttle());
}
}
// ================= Barre lumineuse et vibrations =================
void piloterManette() {
unsigned long maintenant = millis();
// Couleur à afficher : arc-en-ciel (célébration), joystick droit, ou bouton
uint8_t r = couleurR, g = couleurG, b = couleurB;
int rx = manette->axisRX(), ry = manette->axisRY();
if (maintenant < finCelebration) {
arcEnCiel(maintenant / 4, r, g, b); // défile tout seul
} else if (abs(rx) > 2 * ZONE_MORTE || abs(ry) > 2 * ZONE_MORTE) {
int angle = atan2(-ry, rx) * 180 / PI; // direction du joystick
arcEnCiel(angle, r, g, b); // = une couleur
}
// On n'envoie la couleur que si elle a changé, et pas plus de 20 fois/s
if ((r != envoyeR || g != envoyeG || b != envoyeB) && maintenant - dernierEnvoiCouleur > 50) {
manette->setColorLED(r, g, b);
envoyeR = r; envoyeG = g; envoyeB = b;
dernierEnvoiCouleur = maintenant;
}
// Gâchettes : plus on appuie, plus ça vibre (L2 = gros moteur, R2 = petit)
int fort = manette->brake() / 4; // 0 à 255
int doux = manette->throttle() / 4; // 0 à 255
if ((fort > 10 || doux > 10) && maintenant - dernierEnvoiVibration > 100) {
manette->playDualRumble(0, 150, doux, fort);
dernierEnvoiVibration = maintenant;
}
}
// ================= La LED bleue =================
void jouerScenario() {
unsigned long maintenant = millis();
horloge += (maintenant - dernierTemps) * vitesse / 100; // l'horloge avance plus ou moins vite
dernierTemps = maintenant;
unsigned long t = horloge;
int lumiere = 0;
if (maintenant < finCelebration) {
lumiere = (maintenant / 60) % 2 ? 255 : 0; // célébration : stroboscope
} else {
switch (scenario) {
case 1: lumiere = (t / 800) % 2 ? 255 : 0; break; // lent
case 2: lumiere = (t / 120) % 2 ? 255 : 0; break; // rapide
case 3: { // S O S : ... --- ...
const char* sos = "1010100011101110111000101010000000";
lumiere = sos[(t / 150) % strlen(sos)] == '1' ? 255 : 0;
break;
}
case 4: { // respiration
int v = (t / 6) % 512;
lumiere = v < 256 ? v : 511 - v;
break;
}
case 5: { // pilote : joystick gauche vers le haut
int y = manette ? -manette->axisY() : 0;
lumiere = y > ZONE_MORTE ? map(y, ZONE_MORTE, 508, 0, 255) : 0;
lumiere = constrain(lumiere, 0, 255);
break;
}
}
}
analogWrite(LED, lumiere);
}
// ================= Programme principal =================
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
BP32.setup(&onConnectedController, &onDisconnectedController);
// Oublie les manettes déjà couplées : il faudra refaire SHARE + PS à chaque démarrage.
// Une fois que tout marche, mets cette ligne en commentaire (//) :
// un simple appui sur PS suffira alors pour reconnecter la manette.
BP32.forgetBluetoothKeys();
Serial.println("Endor-Pad prêt. Maintiens SHARE + PS pour coupler la manette.");
}
void loop() {
BP32.update();
if (manette && manette->isConnected()) {
lireBoutons();
lireJoysticks();
piloterManette();
}
jouerScenario();
delay(10);
}
Comment il est construit¶
Le programme paraît long, mais il est fait de petits blocs qui ont chacun un seul travail :
| Bloc | Son travail |
|---|---|
onConnectedController / onDisconnectedController |
Bluepad32 les appelle tout seul quand la manette arrive ou s'en va. On garde la manette dans la variable manette. |
vientDAppuyer |
Répond « oui » une seule fois, au moment précis où un bouton s'enfonce. Sans elle, un appui d'une demi-seconde serait compté 50 fois ! |
lireBoutons |
Regarde chaque bouton et chaque flèche, et déclenche son effet. |
lireJoysticks |
Note les directions atteintes par les joysticks, et gère le radar. |
piloterManette |
Choisit la couleur de la barre lumineuse et fait vibrer la manette. |
jouerScenario |
Allume la LED bleue selon le scénario choisi. |
valider / afficherBilan |
Tiennent la check-list à jour, et lancent la célébration à 19/19. |
Ce qu'on apprend ici
- Les événements : on réagit au moment où un bouton s'enfonce, pas tant qu'il est enfoncé (
vientDAppuyer). - Le temps sans bloquer : grâce à
millis(), la LED clignote pendant que le programme continue de lire la manette. Avecdelay(800), la manette serait ignorée pendant presque une seconde à chaque clignotement. - Les tableaux :
teste[]etNOMS_TESTS[]rangent les 19 commandes dans des cases numérotées, qu'on parcourt avec une bouclefor. - Ne pas saturer le Bluetooth : on n'envoie une nouvelle couleur que si elle a changé, et au plus 20 fois par seconde. La manette n'aime pas être bombardée d'ordres.
🎓 Académie : Le temps · Tableaux, boucles, fonctions · Le PWM
Étape 7 · Téléverser et jouer¶
- Vérifier (✓), puis téléverser (→).
- Ouvrir le moniteur série à 115200 bauds.
- Coupler la manette : SHARE + PS jusqu'au clignotement rapide.
- Tester les commandes une par une, en regardant le moniteur série.
Voici à quoi ressemble le début d'une partie :
Endor-Pad prêt. Maintiens SHARE + PS pour coupler la manette.
🎮 Manette connectée : DualShock 4
Teste chaque bouton, flèche, gâchette et joystick.
OPTIONS = bilan, SHARE = radar des joysticks.
Bouton ✕
✔ Nouveau : ✕ (1/19)
→ LED : clignotement lent
Flèche ↑ : vitesse 125 %
✔ Nouveau : flèche ↑ (2/19)
Joystick droit : ↑ (1/4 directions)
Joystick droit : → (2/4 directions)
Le nom exact affiché après « Manette connectée » dépend de la manette.
Pourquoi faut-il recoupler la manette à chaque démarrage ?
La ligne BP32.forgetBluetoothKeys(); du setup() fait oublier à l'ESP32 les manettes déjà connues. C'est pratique pour les premiers essais, mais il faut refaire SHARE + PS à chaque redémarrage. Quand tout marche, mets cette ligne en commentaire (ajoute // devant) et téléverse à nouveau : un simple appui sur PS suffira alors pour reconnecter la manette.
Si ça coince¶
| Symptôme | Cause probable | Solution |
|---|---|---|
Bluepad32.h: No such file or directory |
Mauvaise carte choisie | Outils → Carte → ESP32 + Bluepad32 Arduino → ESP32 Dev Module (étape 4) |
| La barre lumineuse ne clignote pas vite | Mode couplage pas activé | Maintenir SHARE d'abord, puis PS, pendant 3 secondes |
| La manette clignote puis s'éteint | Batterie vide, ou couplage trop lent | Recharger, puis recommencer le couplage |
| La manette s'allume mais va sur la PS4 | La console est à portée | Éteindre ou débrancher la PS4 |
| Rien dans le moniteur série | Mauvaise vitesse | Régler 115200 bauds en bas du moniteur série |
| Des caractères bizarres à l'écran | Mauvaise vitesse | Même solution : 115200 bauds |
| « Failed to connect to ESP32 » au téléversement | La carte n'est pas prête | Maintenir BOOT pendant « Connecting… » |
| Un joystick n'arrive jamais à 4/4 | Pas poussé assez loin | Pousser tout au bout ; vérifier les valeurs avec le radar (SHARE) |
| Pas de vibrations ni de couleurs | Manette compatible non officielle | Certaines copies n'ont pas de moteurs ou de barre pilotable : les boutons marchent, c'est l'essentiel |
Défis bonus¶
Les solutions sont rangées dans des holocrons scellés : essaie d'abord, l'indice est là si tu bloques, et le code d'accès du maître de l'atelier ouvre la solution.
Défi 1 · Ton prénom en morse¶
Ajoute un 7ᵉ scénario qui écrit NATHAN en morse sur la LED bleue : N = —·, A = ·—, T = —, H = ····.
Indice
Inspire-toi de la chaîne sos du scénario SOS, où 1 = allumé et 0 = éteint. En morse, un trait dure trois fois plus longtemps qu'un point, et on laisse un silence plus long entre deux lettres.
Défi 2 · La manette qui bat comme un cœur¶
🎓 Académie : Le temps
Quand on appuie sur PS, la manette vibre « boum-boum… boum-boum… boum-boum ».
Indice
Piège : un nouvel ordre playDualRumble annule celui en cours. Il faut donc envoyer chaque « boum » à son moment, en mesurant le temps avec millis()… et sans delay(), qui bloquerait la lecture de la manette.
Défi 3 · Tes propres couleurs¶
Change les couleurs de la barre lumineuse pour les boutons de face.
Indice
Chaque couleur est un mélange de rouge, vert et bleu de 0 à 255 : (255, 128, 0) donne de l'orange. Cherche choisirScenario dans lireBoutons().
Validation¶
- Bluepad32 installé, la carte « ESP32 Dev Module » de la famille Bluepad32 est sélectionnée
- L'exemple Controller affiche les valeurs de la manette
- Le banc d'essai annonce « Manette connectée »
- La barre lumineuse change de couleur (boutons de face et joystick droit)
- Les gâchettes L2 et R2 font vibrer la manette, plus ou moins fort
- 19/19 et la célébration arc-en-ciel 🎉