Projet : boutons de contrôle et joystick pour un setup de sim racing

Bonsoir,

Je travaille sur un projet de volant FFB pour le sim racing sur PC. J'aimerais y ajouter un ensemble de boutons de contrôle.

J'ai constaté que des projets similaires existent sur internet et utilisent principalement des cartes de contrôle de type joystick (par exemple : https://fr.aliexpress.com/item/1005007243505395.html).

Je me demande s'il serait possible de réaliser quelque chose de similaire en utilisant une carte Arduino. Cette solution m'intéresse particulièrement car je souhaiterais aller au-delà des simples boutons. J'envisage d'ajouter :

  • Un joystick pour le contrôle,
  • Des boutons pour les commandes "Entrée" et "Échap".

Cela me permettrait, à terme, de ne plus avoir besoin d'utiliser le clavier ou la souris de mon PC pendant mes sessions de gaming.

Pensez-vous que ce projet est réalisable avec une carte Arduino ?

dans l'absolu oui, il vous faudra un arduino compatible avec la bibliothèque Arduino Joystick Library, c'est à dire qui peut se présenter sur le bus USB de votre PC comme un appareil HID.

1 Like

About

An Arduino library that adds one or more joysticks to the list of HID devices an Arduino Leonardo or Arduino Micro can support.

D’accord, j’ai un Arduino Leonardo sous la main .. Est-ce que tu peux me donner plus de détails, ou est-ce que c’est trop simple à programmer ?
Merci

Le Leonardo est OK

Des exemples il y en pas mal dans le répertoire de la bibliothèque

1 Like

Après avoir discuté avec différentes IA à propos du programme, j'ai obtenue ce modèle. J'aimerais avoir votre avis là dessus :

#include <Joystick.h>
#include <Bounce2.h>

// Création de l'instance Joystick
Joystick_ Joystick(
  JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_GAMEPAD, 
  16,  // Nombre de boutons
  0,   // Pas de boutons à hat switch
  true, true, false, false, false, false, false, false, false, false, false
);

// Broches des boutons
const int buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1, A2, A3};
const int joystickClick = A4; // Clic du joystick
const int calibrateButton = A5; // Bouton dédié pour recalibrer

// Joystick analogique
const int joystickX = A6;
const int joystickY = A7;

// Variables pour la calibration du joystick
int xMin = 1023, xMax = 0;
int yMin = 1023, yMax = 0;

// État des boutons
bool buttonStates[16];

// Instances de Bounce pour la gestion des rebonds
Bounce debouncers[16];
Bounce joystickClickDebouncer;
Bounce calibrateDebouncer;

// Durée pour distinguer un appui court/long (en ms)
const unsigned long longPressDuration = 500;
unsigned long clickPressStart = 0;
unsigned long previousMillis = 0;
const int interval = 10;

void setup() {
  // Initialisation des boutons avec Bounce2
  for (int i = 0; i < 16; i++) {
    debouncers[i].attach(buttonPins[i], INPUT_PULLUP);
    debouncers[i].interval(10); // Délai anti-rebond
  }
  joystickClickDebouncer.attach(joystickClick, INPUT_PULLUP);
  joystickClickDebouncer.interval(10);
  calibrateDebouncer.attach(calibrateButton, INPUT_PULLUP);
  calibrateDebouncer.interval(10);

  // Calibration initiale du joystick
  calibrateJoystick();

  // Initialisation du joystick HID
  Joystick.begin();
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Lecture des boutons classiques
    for (int i = 0; i < 16; i++) {
      debouncers[i].update();
      bool state = !debouncers[i].read(); // Bouton pressé = LOW
      if (state != buttonStates[i]) {
        buttonStates[i] = state;
        Joystick.setButton(i, state);
      }
    }

    // Lecture du clic du joystick (appui court/long)
    joystickClickDebouncer.update();
    if (joystickClickDebouncer.fell()) {
      clickPressStart = millis();
    }
    if (joystickClickDebouncer.rose()) {
      unsigned long pressDuration = millis() - clickPressStart;
      if (pressDuration < longPressDuration) {
        // Appui court : Entrée
        Joystick.setButton(16, true);
        delay(50); // Simuler un appui bref
        Joystick.setButton(16, false);
      } else {
        // Appui long : Échap
        Joystick.setButton(17, true);
        delay(50);
        Joystick.setButton(17, false);
      }
    }

    // Lecture du bouton de recalibration
    calibrateDebouncer.update();
    if (calibrateDebouncer.fell()) {
      calibrateJoystick();
    }

    // Lecture des axes du joystick
    int xValue = analogRead(joystickX);
    int yValue = analogRead(joystickY);

    // Normalisation des valeurs avec calibration
    int xAxis = map(xValue, xMin, xMax, -127, 127);
    int yAxis = map(yValue, yMin, yMax, -127, 127);

    // Limiter les valeurs dans la plage -127 à 127
    xAxis = constrain(xAxis, -127, 127);
    yAxis = constrain(yAxis, -127, 127);

    // Mettre à jour les axes du joystick
    Joystick.setXAxis(xAxis);
    Joystick.setYAxis(yAxis);
  }
}

// Fonction de calibration du joystick
void calibrateJoystick() {
  xMin = 1023; xMax = 0;
  yMin = 1023; yMax = 0;
  for (int i = 0; i < 100; i++) {
    int xValue = analogRead(joystickX);
    int yValue = analogRead(joystickY);
    if (xValue < xMin) xMin = xValue;
    if (xValue > xMax) xMax = xValue;
    if (yValue < yMin) yMin = yValue;
    if (yValue > yMax) yMax = yValue;
    delay(10);
  }
}

selon les IA, les boutons 'Entrée' et 'Échap' sont définis comme un bouton classiques à programmer (avec un spécification suivant la durée d'appui). Il serait également possible d'utiliser la bibliothèque Keyboard pour les définir directement, mais cela présente un risque de conflit entre les bibliothèques Keyboard et Joystick.

Vos avis sont les bienvenus.

Merci

Les IA pensent que vous utilisez les touches enter et escape d’un clavier. Est-ce le cas ?

J'ai un problème à ce niveau. L'IA a initialement proposé d'utiliser la bibliothèque keyboard pour définir cette touche, mais elle m'a également déconseillé cette solution, car elle mentionne qu'il pourrait y avoir un conflit entre la bibliothèque keyboard et Joystick.

Je ne sais pas trop quoi en penser. Je fais moyennement confiance à ces IA, surtout en version gratuite. Sinon, il faudra que je branche le tout et que je teste par moi-même

L’IA hallucine fréquemment…

Vous n’avez pas répondu si ce sont les touches clavier sue vous voulez envoyer ?

Oui, je confirme .. C'est pour cette raison que j'aimerais effectuer une vérification, même si la véritable validation consiste à programmer, tester, et améliorer.

Par ailleurs, l'IA a changé d'avis : il est possible d'utiliser les deux bibliothèques sans conflit, en adoptant une autre bibliothèque, HID-Project.


#include <HID-Project.h>
#include <HID-Settings.h>

const int buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1, A2, A3};
const int joystickClick = A4;
const int joystickX = A6;
const int joystickY = A7;
const int calibrateButton = A5;
const int buzzerPin = 14;

int xMin = 1023, xMax = 0;
int yMin = 1023, yMax = 0;

const unsigned long longPressDuration = 500;
const unsigned long debounceDelay = 50;

unsigned long lastDebounceTime[16] = {0};
unsigned long lastJoystickDebounceTime = 0;
unsigned long lastCalibrateDebounceTime = 0;

bool buttonStates[16];
bool lastButtonStates[16];
bool lastArrowStates[4] = {false, false, false, false}; // UP, DOWN, LEFT, RIGHT
bool lastJoystickClickState = HIGH;
bool lastCalibrateButtonState = HIGH;

void setup() {
  for (int i = 0; i < 16; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    buttonStates[i] = HIGH;
    lastButtonStates[i] = HIGH;
  }
  pinMode(joystickClick, INPUT_PULLUP);
  pinMode(calibrateButton, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);

  BootKeyboard.begin();
  Joystick.begin();

  calibrateJoystick();
}

void loop() {
  readButtons();
  handleJoystickClick();
  handleCalibrateButton();
  handleJoystickMovement();
  delay(10);
}

void readButtons() {
  for (int i = 0; i < 16; i++) {
    bool reading = digitalRead(buttonPins[i]);
    if (reading != lastButtonStates[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != buttonStates[i]) {
        buttonStates[i] = reading;
        Joystick.setButton(i, !buttonStates[i]);
      }
    }
    lastButtonStates[i] = reading;
  }
}

void handleJoystickClick() {
  bool reading = digitalRead(joystickClick);
  if (reading != lastJoystickClickState) {
    lastJoystickDebounceTime = millis();
  }
  if ((millis() - lastJoystickDebounceTime) > debounceDelay) {
    if (reading == LOW && lastJoystickClickState == HIGH) {
      unsigned long pressStart = millis();
      while (digitalRead(joystickClick) == LOW) {}
      unsigned long pressDuration = millis() - pressStart;
      if (pressDuration < longPressDuration) {
        BootKeyboard.press(KEY_RETURN);
        delay(50);
        BootKeyboard.release(KEY_RETURN);
      } else {
        BootKeyboard.press(KEY_ESC);
        delay(50);
        BootKeyboard.release(KEY_ESC);
      }
    }
  }
  lastJoystickClickState = reading;
}

void handleCalibrateButton() {
  bool reading = digitalRead(calibrateButton);
  if (reading != lastCalibrateButtonState) {
    lastCalibrateDebounceTime = millis();
  }
  if ((millis() - lastCalibrateDebounceTime) > debounceDelay) {
    if (reading == LOW && lastCalibrateButtonState == HIGH) {
      calibrateJoystick();
    }
  }
  lastCalibrateButtonState = reading;
}

void handleJoystickMovement() {
  int xValue = analogRead(joystickX);
  int yValue = analogRead(joystickY);

  int xAxis = map(xValue, xMin, xMax, -127, 127);
  int yAxis = map(yValue, yMin, yMax, -127, 127);

  xAxis = constrain(xAxis, -127, 127);
  yAxis = constrain(yAxis, -127, 127);

  bool arrowStates[4] = {
    yAxis < -50, // UP
    yAxis > 50,  // DOWN
    xAxis < -50, // LEFT
    xAxis > 50   // RIGHT
  };

  const uint8_t arrowKeys[4] = {KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW};

  for (int i = 0; i < 4; i++) {
    if (arrowStates[i] != lastArrowStates[i]) {
      if (arrowStates[i]) {
        BootKeyboard.press(arrowKeys[i]);
      } else {
        BootKeyboard.release(arrowKeys[i]);
      }
      lastArrowStates[i] = arrowStates[i];
    }
  }
}

void calibrateJoystick() {
  xMin = 1023; xMax = 0; yMin = 1023; yMax = 0;
  for (int i = 0; i < 100; i++) {
    int xValue = analogRead(joystickX);
    int yValue = analogRead(joystickY);
    xMin = min(xMin, xValue);
    xMax = max(xMax, xValue);
    yMin = min(yMin, yValue);
    yMax = max(yMax, yValue);
    delay(10);
  }

  if (xMin >= xMax || yMin >= yMax) {
    buzzError();
  } else {
    buzzSuccess();
  }
}

void buzzSuccess() {
  tone(buzzerPin, 1000, 1000); // 1 kHz pendant 1 seconde
}

void buzzError() {
  for (int i = 0; i < 3; i++) {
    tone(buzzerPin, 500, 200); // 500 Hz pendant 200 ms
    delay(400); // Pause de 200 ms entre les buzz
  }
}

c'est (à priori) bon là ???

Cela reste tout de même une aide impressionnante, cette IA

Avez vous testé ?

prochainement ...

C’est pas un bug mais vous ne respectez pas les type dans vos tableaux d’états des boutons. Vous déclarez des bool et mettez HIGH ou LOW dedans. Ça fera partie des petites modifications à envisager.

1 Like

Franchement, cette IA, c'est parfois n'importe quoi... Heureusement qu'elle s'auto-corrige après une réclamation. Après plusieurs itérations, voici donc le code qui passe l'étape de compilation :

#include <HID-Project.h> // Inclure la bibliothèque HID-Project
#include <HID-Settings.h> // Inclure les paramètres HID

const int buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // 12 boutons
const int joystickClick = A2; // Clic du joystick
const int joystickX = A0;     // Axe X
const int joystickY = A1;     // Axe Y
const int calibrateButton = A3; // Bouton de calibration
const int buzzerPin = A4;      // Buzzer

int xMin = 1023, xMax = 0;
int yMin = 1023, yMax = 0;

const unsigned long longPressDuration = 500;
const unsigned long debounceDelay = 50;

unsigned long lastDebounceTime[12] = {0}; // 12 boutons
unsigned long lastJoystickDebounceTime = 0;
unsigned long lastCalibrateDebounceTime = 0;

bool buttonStates[12]; // 12 boutons
bool lastButtonStates[12]; // 12 boutons
bool lastArrowStates[4] = {false, false, false, false}; // UP, DOWN, LEFT, RIGHT
bool lastJoystickClickState = HIGH;
bool lastCalibrateButtonState = HIGH;

void setup() {
  for (int i = 0; i < 12; i++) { // 12 boutons
    pinMode(buttonPins[i], INPUT_PULLUP);
    buttonStates[i] = HIGH;
    lastButtonStates[i] = HIGH;
  }
  pinMode(joystickClick, INPUT_PULLUP);
  pinMode(calibrateButton, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);

  Gamepad.begin(); // Initialisation du joystick avec HID-Project
  Keyboard.begin(); // Initialisation du clavier

  calibrateJoystick();
}

void loop() {
  readButtons();
  handleJoystickClick();
  handleCalibrateButton();
  handleJoystickMovement();
  delay(10);
}

void readButtons() {
  for (int i = 0; i < 12; i++) { // 12 boutons
    bool reading = digitalRead(buttonPins[i]);
    if (reading != lastButtonStates[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != buttonStates[i]) {
        buttonStates[i] = reading;
        if (buttonStates[i] == LOW) {
          Gamepad.press(i + 1); // Utilisez Gamepad pour les boutons
        } else {
          Gamepad.release(i + 1); // Relâcher le bouton
        }
      }
    }
    lastButtonStates[i] = reading;
  }
}

void handleJoystickClick() {
  bool reading = digitalRead(joystickClick);
  if (reading != lastJoystickClickState) {
    lastJoystickDebounceTime = millis();
  }
  if ((millis() - lastJoystickDebounceTime) > debounceDelay) {
    if (reading == LOW && lastJoystickClickState == HIGH) {
      unsigned long pressStart = millis();
      while (digitalRead(joystickClick) == LOW) {
        // Attendre que le bouton soit relâché
      }
      unsigned long pressDuration = millis() - pressStart;
      if (pressDuration < longPressDuration) {
        Keyboard.press(KEY_RETURN); // Utilisez Keyboard pour émuler la touche Entrée
        delay(50);
        Keyboard.release(KEY_RETURN);
      } else {
        Keyboard.press(KEY_ESC); // Utilisez Keyboard pour émuler la touche Échap
        delay(50);
        Keyboard.release(KEY_ESC);
      }
    }
  }
  lastJoystickClickState = reading;
}

void handleCalibrateButton() {
  bool reading = digitalRead(calibrateButton);
  if (reading != lastCalibrateButtonState) {
    lastCalibrateDebounceTime = millis();
  }
  if ((millis() - lastCalibrateDebounceTime) > debounceDelay) {
    if (reading == LOW && lastCalibrateButtonState == HIGH) {
      calibrateJoystick();
    }
  }
  lastCalibrateButtonState = reading;
}

void handleJoystickMovement() {
  int xValue = analogRead(joystickX);
  int yValue = analogRead(joystickY);

  int xAxis = map(xValue, xMin, xMax, -32767, 32767); // HID-Project utilise des valeurs 16 bits
  int yAxis = map(yValue, yMin, yMax, -32767, 32767);

  xAxis = constrain(xAxis, -32767, 32767);
  yAxis = constrain(yAxis, -32767, 32767);

  Gamepad.xAxis(xAxis); // Mettre à jour l'axe X du joystick
  Gamepad.yAxis(yAxis); // Mettre à jour l'axe Y du joystick

  bool arrowStates[4] = {
    yAxis < -16384, // UP
    yAxis > 16384,  // DOWN
    xAxis < -16384, // LEFT
    xAxis > 16384   // RIGHT
  };

  const uint8_t arrowKeys[4] = {KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW};

  for (int i = 0; i < 4; i++) {
    if (arrowStates[i] != lastArrowStates[i]) {
      if (arrowStates[i]) {
        Keyboard.press(arrowKeys[i]); // Utilisez Keyboard pour émuler les touches fléchées
      } else {
        Keyboard.release(arrowKeys[i]);
      }
      lastArrowStates[i] = arrowStates[i];
    }
  }
}

void calibrateJoystick() {
  xMin = 1023; xMax = 0; yMin = 1023; yMax = 0;
  for (int i = 0; i < 100; i++) {
    int xValue = analogRead(joystickX);
    int yValue = analogRead(joystickY);
    xMin = min(xMin, xValue);
    xMax = max(xMax, xValue);
    yMin = min(yMin, yValue);
    yMax = max(yMax, yValue);
    delay(10);
  }

  if (xMin >= xMax || yMin >= yMax) {
    buzzError();
  } else {
    buzzSuccess();
  }
}

void buzzSuccess() {
  tone(buzzerPin, 1000, 1000); // 1 kHz pendant 1 seconde
}

void buzzError() {
  for (int i = 0; i < 3; i++) {
    tone(buzzerPin, 500, 200); // 500 Hz pendant 200 ms
    delay(400); // Pause de 200 ms entre les buzz
  }
}

J'ai donné le même code à une autre IA, et elle a proposé des améliorations que je trouve intéressantes :

#include <HID-Project.h> // Inclure la bibliothèque HID-Project
#include <HID-Settings.h> // Inclure les paramètres HID
#include <Bounce2.h> // Inclure la bibliothèque Bounce2 pour le débounce

// Déclaration des broches
const int buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // 12 boutons
const int joystickClick = A2; // Clic du joystick
const int calibrateButton = A4; // Bouton de calibration
const int buzzerPin = A3;      // Buzzer
const int joystickX = A0;     // Axe X
const int joystickY = A1;     // Axe Y

// Variables pour la calibration du joystick
int xMin = 1023, xMax = 0;
int yMin = 1023, yMax = 0;

// Constantes pour la gestion des appuis longs et du débounce
const unsigned long longPressDuration = 500; // Durée pour un appui long
const unsigned long debounceDelay = 50; // Délai de débounce

// Variables pour le débounce des boutons
Bounce debouncer[12]; // Débounceurs pour les 12 boutons
Bounce joystickClickDebouncer; // Débounceur pour le clic du joystick
Bounce calibrateButtonDebouncer; // Débounceur pour le bouton de calibration

// États des boutons
bool buttonStates[12]; // États actuels des 12 boutons
bool lastButtonStates[12]; // États précédents des 12 boutons
bool lastArrowStates[4] = {false, false, false, false}; // États précédents des directions (UP, DOWN, LEFT, RIGHT)
bool lastJoystickClickState = HIGH; // État précédent du clic du joystick
bool lastCalibrateButtonState = HIGH; // État précédent du bouton de calibration

// Seuil pour détecter les directions du joystick
const int joystickThreshold = 16384; // Seuil configurable

// Fonction pour émettre un son de succès (1 bip très long)
void buzzSuccess() {
  tone(buzzerPin, 1000, 2000); // 1 kHz pendant 2 secondes
}

// Fonction pour émettre un son d'erreur (3 bips courts)
void buzzError() {
  for (int i = 0; i < 3; i++) {
    tone(buzzerPin, 500, 200); // 500 Hz pendant 200 ms
    delay(400); // Pause de 200 ms entre les bips
  }
}

// Fonction pour émettre un son d'avertissement (4 bips : long, court, long, court)
void buzzWarning() {
  tone(buzzerPin, 1000, 500); // 1 kHz pendant 500 ms (long)
  delay(600); // Pause de 600 ms
  tone(buzzerPin, 500, 200); // 500 Hz pendant 200 ms (court)
  delay(400); // Pause de 400 ms
  tone(buzzerPin, 1000, 500); // 1 kHz pendant 500 ms (long)
  delay(600); // Pause de 600 ms
  tone(buzzerPin, 500, 200); // 500 Hz pendant 200 ms (court)
}

void setup() {
  // Initialisation des broches
  for (int i = 0; i < 12; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    debouncer[i].attach(buttonPins[i], INPUT_PULLUP);
    debouncer[i].interval(debounceDelay); // Configurer le délai de débounce
    buttonStates[i] = HIGH;
    lastButtonStates[i] = HIGH;
  }
  pinMode(joystickClick, INPUT_PULLUP);
  joystickClickDebouncer.attach(joystickClick, INPUT_PULLUP);
  joystickClickDebouncer.interval(debounceDelay);

  pinMode(calibrateButton, INPUT_PULLUP);
  calibrateButtonDebouncer.attach(calibrateButton, INPUT_PULLUP);
  calibrateButtonDebouncer.interval(debounceDelay);

  pinMode(buzzerPin, OUTPUT);

  // Initialisation du joystick et du clavier
  Gamepad.begin();
  Keyboard.begin();

  // Calibration initiale du joystick
  calibrateJoystick();
}

void loop() {
  // Lire les boutons
  readButtons();

  // Gérer le clic du joystick
  handleJoystickClick();

  // Gérer le bouton de calibration
  handleCalibrateButton();

  // Gérer le mouvement du joystick
  handleJoystickMovement();

  // Petit délai pour éviter une boucle trop rapide
  delay(10);
}

// Fonction pour lire l'état des boutons avec débounce
void readButtons() {
  for (int i = 0; i < 12; i++) {
    debouncer[i].update(); // Mettre à jour le débounceur
    bool reading = debouncer[i].read();
    if (reading != lastButtonStates[i]) {
      buttonStates[i] = reading;
      if (buttonStates[i] == LOW) {
        Gamepad.press(i + 1); // Appuyer sur le bouton
      } else {
        Gamepad.release(i + 1); // Relâcher le bouton
      }
    }
    lastButtonStates[i] = reading;
  }
}

// Fonction pour gérer le clic du joystick
void handleJoystickClick() {
  joystickClickDebouncer.update(); // Mettre à jour le débounceur
  bool reading = joystickClickDebouncer.read();
  if (reading != lastJoystickClickState) {
    lastJoystickClickState = reading;
    if (reading == LOW) {
      unsigned long pressStart = millis();
      while (joystickClickDebouncer.read() == LOW) {
        // Attendre que le bouton soit relâché
      }
      unsigned long pressDuration = millis() - pressStart;
      if (pressDuration < longPressDuration) {
        Keyboard.press(KEY_RETURN); // Appui court : touche Entrée
        delay(50);
        Keyboard.release(KEY_RETURN);
      } else {
        Keyboard.press(KEY_ESC); // Appui long : touche Échap
        delay(50);
        Keyboard.release(KEY_ESC);
      }
    }
  }
}

// Fonction pour gérer le bouton de calibration
void handleCalibrateButton() {
  calibrateButtonDebouncer.update(); // Mettre à jour le débounceur
  bool reading = calibrateButtonDebouncer.read();
  if (reading != lastCalibrateButtonState) {
    lastCalibrateButtonState = reading;
    if (reading == LOW) {
      calibrateJoystick();
    }
  }
}

// Fonction pour gérer le mouvement du joystick
void handleJoystickMovement() {
  int xValue = analogRead(joystickX);
  int yValue = analogRead(joystickY);

  // Mapper les valeurs analogiques vers une plage de -32767 à 32767
  int xAxis = map(xValue, xMin, xMax, -32767, 32767);
  int yAxis = map(yValue, yMin, yMax, -32767, 32767);

  // Limiter les valeurs pour éviter les dépassements
  xAxis = constrain(xAxis, -32767, 32767);
  yAxis = constrain(yAxis, -32767, 32767);

  // Mettre à jour les axes du joystick
  Gamepad.xAxis(xAxis);
  Gamepad.yAxis(yAxis);

  // Détecter les directions du joystick
  bool arrowStates[4] = {
    yAxis < -joystickThreshold, // UP
    yAxis > joystickThreshold,  // DOWN
    xAxis < -joystickThreshold, // LEFT
    xAxis > joystickThreshold   // RIGHT
  };

  // Touches fléchées correspondantes
  const uint8_t arrowKeys[4] = {KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW};

  // Gérer les touches fléchées
  for (int i = 0; i < 4; i++) {
    if (arrowStates[i] != lastArrowStates[i]) {
      if (arrowStates[i]) {
        Keyboard.press(arrowKeys[i]); // Appuyer sur la touche fléchée
      } else {
        Keyboard.release(arrowKeys[i]); // Relâcher la touche fléchée
      }
      lastArrowStates[i] = arrowStates[i];
    }
  }
}

// Fonction pour calibrer le joystick
void calibrateJoystick() {
  xMin = 1023; xMax = 0; yMin = 1023; yMax = 0;
  for (int i = 0; i < 100; i++) {
    int xValue = analogRead(joystickX);
    int yValue = analogRead(joystickY);
    xMin = min(xMin, xValue);
    xMax = max(xMax, xValue);
    yMin = min(yMin, yValue);
    yMax = max(yMax, yValue);
    delay(10);
  }

  // Vérifier que les valeurs sont valides
  if (xMin == 1023 || xMax == 0 || yMin == 1023 || yMax == 0) {
    buzzWarning(); // Joystick non détecté ou non bougé (4 bips : long, court, long, court)
  } else if (xMin >= xMax || yMin >= yMax) {
    buzzError(); // Erreur de calibration (3 bips courts)
  } else {
    buzzSuccess(); // Calibration réussie (1 bip très long)
  }
}

il passe aussi l'etape de compilation

Je teste ça demain

LE code qui fonctionne

#include <HID-Project.h>
#include <Bounce2.h>

// Broches des boutons
const int buttonPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
Bounce debouncer[12];

// Broches du joystick
const int joystickXPin = A0; // Axe X du joystick
const int joystickYPin = A1; // Axe Y du joystick
const int joystickButtonPin = A2; // Bouton du joystick

// Variables pour le joystick
int joystickXValue = 0;
int joystickYValue = 0;
bool joystickButtonState = false;
bool joystickButtonPressed = false;
unsigned long joystickButtonPressTime = 0;
const int joystickThreshold = 512; // Seuil pour détecter les directions
const unsigned long longPressDuration = 1000; // Durée pour un clic long (1 seconde)

void setup() {
  // Initialisation des boutons
  for (int i = 0; i < 12; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    debouncer[i].attach(buttonPins[i]);
    debouncer[i].interval(10);
  }

  // Initialisation du joystick
  pinMode(joystickButtonPin, INPUT_PULLUP);

  // Initialisation des périphériques HID
  Gamepad.begin();
  Keyboard.begin();
}

void loop() {
  // Gestion des boutons
  bool stateChanged = false;
  for (int i = 0; i < 12; i++) {
    debouncer[i].update();
    if (debouncer[i].fell()) {
      Gamepad.press(i + 1);
      stateChanged = true;
    } else if (debouncer[i].rose()) {
      Gamepad.release(i + 1);
      stateChanged = true;
    }
  }
  if (stateChanged) {
    Gamepad.write();
  }

  // Gestion du joystick
  joystickXValue = analogRead(joystickXPin);
  joystickYValue = analogRead(joystickYPin);

  // Direction X (gauche/droite)
  if (joystickXValue < joystickThreshold - 200) {
    Keyboard.press(KEY_LEFT_ARROW);
  } else if (joystickXValue > joystickThreshold + 200) {
    Keyboard.press(KEY_RIGHT_ARROW);
  } else {
    Keyboard.release(KEY_LEFT_ARROW);
    Keyboard.release(KEY_RIGHT_ARROW);
  }

  // Direction Y (haut/bas)
  if (joystickYValue < joystickThreshold - 200) {
    Keyboard.press(KEY_UP_ARROW);
  } else if (joystickYValue > joystickThreshold + 200) {
    Keyboard.press(KEY_DOWN_ARROW);
  } else {
    Keyboard.release(KEY_UP_ARROW);
    Keyboard.release(KEY_DOWN_ARROW);
  }

  // Gestion du bouton du joystick
  bool currentButtonState = digitalRead(joystickButtonPin) == LOW;
  if (currentButtonState && !joystickButtonPressed) {
    // Bouton pressé
    joystickButtonPressed = true;
    joystickButtonPressTime = millis();
  } else if (!currentButtonState && joystickButtonPressed) {
    // Bouton relâché
    joystickButtonPressed = false;
    unsigned long pressDuration = millis() - joystickButtonPressTime;

    if (pressDuration < longPressDuration) {
      // Clic court : Entrée
      Keyboard.press(KEY_RETURN);
      delay(50); // Petit délai pour simuler un appui
      Keyboard.release(KEY_RETURN);
    } else {
      // Clic long : Échap
      Keyboard.press(KEY_ESC);
      delay(50); // Petit délai pour simuler un appui
      Keyboard.release(KEY_ESC);
    }
  }
}

Simple et léger, sans calibration ni autres ajustements. Avez-vous des suggestions d'améliorations ?

Salut esloch,

Très beau projet.

Ton programme semble plutôt bien, as-tu fait des tests et des essais ?
Le choix d'une Léonardo avec son ATmega32u4 est un bon choix pour la communication USB.

Ta variable "joystickButtonPin" est sur la broche A2, pour ma part j'ai plutôt tendance à choisir une broche digitale pour mes boutons et interrupteurs.

Sur certains projets, les broches analogiques se font rares, perso je les garde pour les entrées analogiques seulement. Pour autant, ta solution est viable et fonctionnera bien quand même.

1 Like

étant donné qu'il a cela pour ses boutons

il est un peu coincé ... Donc son choix est logique :slight_smile:

Toutes Les broches sont digitales - certaines ont la capacité d'être multiplexées vers l'entrée de l'ADC, donc il n'y a pas vraiment de "viable" ou "quand même" qui ne tienne à mon avis. Il fait avec les broches qu'il a et c'est même pertinent de grouper toutes les broches qui vont au joystick ensemble.

1 Like

J'ai ajouté quelques diodes leds pour faire joli .. j'ai utilisé tous les pins disponibles ou presque .. il me reste seulement la pin 0 encore libre .. les tests actuels sont concluants .. je suis en mode modélisation du support des boutons et du joystick .. je vous donne des nouvelles bientôt.

J'ai une question : pour faire de bonnes connexions avec l'Arduino, est-ce qu'il vaut mieux dessouder les connecteurs femelles sur la carte et souder directement mes fils ? Ou bien il y a d'autres solutions ? je pense a utiliser des connecteurs male par exemple .. J'aimerais des solutions fiables mais simples.

Merci !

Salut J-M-L

Effectivement je n'avais pas regardé cette partie du code :sweat_smile:
Oui, rapprocher les pins du joystick sera plus simple pour le câblage ou le routage s'il fait un PCB.
J'ai tendance à me compliquer les tâches parfois, juste pour suivre les conseils que l'on m'a donnés en cours de programmation / électronique.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.