Écho lors de l'activation d'un boutons

Bonjour,
j'utilise une carte lenorado pour réaliser un contrôleur de jeux ayant plus d'une centaine de fonctions (j’utilise 3 cartes).
J'ai 5 types de commande :

  • axes : pas de problème pour ces derniers.
  • bouton poussoir instantané : pas de problème (sortie 0 à 5 dans mon code)
  • bouton a bascule à 2 positions : active durant un court instant une sortie quand on active l’interrupteur, et une autre quant on le désactive. (sortie 6 à 11)
  • bouton a bascule à 3 positions : idem version que le 2 positions, mais avec une 3ème. (sortie 12 à 29)
  • encodeur rotatif : active une sortie ou une autre suivant le sens de rotation (sortie 30 et 31).

Mes 3 problème :

  • dans le cas des interrupteur bascule, j'ai une redondance ou un éco quant j'actionne un boutons, me faisant clignoter 2 fois la sortie (je n'arrive pas a comprendre pourquoi).

  • dans le cas de l'encodeur rotatif, j'aimerais qu'il soit moins sensible. certaine fois, il envoies sur les 2 sortie l'une après l'autre. (problème encore pas trop gênant)

  • Dernier problème, je trouve la réaction des interrupteur aléatoire. certaine fois, quant j'actionne un interrupteur, je n'ai pas de réaction, et si je le réactive, sa fonctionne.

voilà. ci quelqu'un a des idées de pourquoi sa fonctionne mal, je suis preneur.
merci d'avance.

ci-dessous le code complet :

#include <Keypad.h>
#include <Joystick.h>
#include <Encoder.h>
#include <Toggle.h> 
#include "Joystick.h"

#define ENABLE_PULLUPS
#define NUMROWS_IMP 1 // nombre de colones de la matrice bouton
#define NUMCOLS_IMP 6 // nombre de ligne de la matrice bouton
#define NUMROWS_ON 4
#define NUMCOLS_ON 6
#define NUMROWS_OF 4
#define NUMCOLS_OF 6

int nb_bts_impulsion=6;

byte buttons_IMP[NUMROWS_IMP][NUMCOLS_IMP] = { // numéro bouton controleur -1
  {0,1,2,3,4,5},        // bts impulsions (6)
};
byte rowPinsIMP[NUMROWS_IMP] = {6}; // numéro des broche ligne
byte colPinsIMP[NUMCOLS_IMP] = {0,1,2,3,4,5}; // numéro des broche colone
Keypad butonsIMP = Keypad( makeKeymap(buttons_IMP), rowPinsIMP, colPinsIMP, NUMROWS_IMP, NUMCOLS_IMP); 

byte butons_ON[NUMROWS_ON][NUMCOLS_ON] = {
  {27,28},              // 1bts 3 voies (nb 1+3+2=6)
  {6,8,10},             // 3bts 2 voies (nb 3)
  {12,13,15,16,18,19},  // 3bts 3 voies
  {21,22,24,25},        // 2bts 3 voies
};
byte rowPinsON[NUMROWS_ON] = {7,8,9,10};
byte colPinsON[NUMCOLS_ON] = {0,1,2,3,4,5};
Keypad butonsON = Keypad( makeKeymap(butons_ON), rowPinsON, colPinsON, NUMROWS_ON, NUMCOLS_ON); 
byte butons_OF[NUMROWS_OF][NUMCOLS_OF] = {
  {29,29},              // 1bts 3 voies
  {7,9,11},             // 3bts 2 voies
  {14,14,17,17,20,20},  // 3bts 3 voies
  {23,23,26,26},        // 2bts 3 voies
};
byte rowPinsOF[NUMROWS_OF] = {7,8,9,10};
byte colPinsOF[NUMCOLS_OF] = {0,1,2,3,4,5};
Keypad butonsOF = Keypad( makeKeymap(butons_OF), rowPinsOF, colPinsOF, NUMROWS_OF, NUMCOLS_OF); 

// déclaration des variable pour l'encodeur 1
const byte encodeurCLKPin1 = 12; // signal de mouvement encodeur 1
const byte encodeurDTPin1  = 13; // signal sens de rotation encodeur 1
Encoder encodeur1(encodeurDTPin1, encodeurCLKPin1);
Toggle encodeurBouton1;
enum EncodeurMouvement1 {IDENTIQUE1, PLUS1, MOINS1};

void setup() {
  Joystick.begin();
  Joystick.setXAxis (false);
  Joystick.setYAxis (false);
  Joystick.setZAxis(false);
}

void loop() { 
  CheckButtons();
  CheckAxes();
  CheckEncodeur1();
  }

void CheckButtons(void) {
  if (butonsIMP.getKeys())
  {
    for (int i=0; i<nb_bts_impulsion; i++)   
    {
      if ( butonsIMP.key[i].stateChanged ) 
      {
        switch (butonsIMP.key[i].kstate) {  
          case PRESSED:
          case HOLD:
                Joystick.setButton(butonsIMP.key[i].kchar, 1);
                delay(20);
                break;
          case RELEASED:
          case IDLE:
                Joystick.setButton(butonsIMP.key[i].kchar, 0);
                break;
        }
      }
    }
  }
  if (butonsON.getKeys())
  {
    for (int i=0; i<LIST_MAX; i++)   
    {
      if ( butonsON.key[i].stateChanged ) 
      {
        switch (butonsON.key[i].kstate) {  
          case PRESSED:
          case HOLD:
                Joystick.setButton(butonsON.key[i].kchar, 1);
                delay(20);
                Joystick.setButton(butonsON.key[i].kchar, 0);
                break;
          case RELEASED:
          case IDLE:
                break;
        }
      }
    }
  }
  if (butonsOF.getKeys())
  {
    for (int i=0; i<LIST_MAX; i++)
    {
      if ( butonsOF.key[i].stateChanged )   
      {  
        switch (butonsOF.key[i].kstate) {  
          case RELEASED:
          case IDLE:
                Joystick.setButton(butonsOF.key[i].kchar, 1); 
                //Fixe l'état (0 ou 1) du bouton spécifié. si le numéro est 0 : la valeur est 1 si l'on appuie sur le bouton et 0 si le bouton est relâché.
                delay(20);
                Joystick.setButton(butonsOF.key[i].kchar, 0);
                break;
          case PRESSED:
          case HOLD :
                break;
        }
      }
    }
  }
}

void CheckAxes(void) {
  Joystick.setXAxisRotation(analogRead (A0)/2.845); // axe X rota
  Joystick.setYAxisRotation(analogRead (A1)/2.845); // axe Y rota
  Joystick.setRudder(analogRead (A2)/4.01); // Palonnier
  Joystick.setThrottle(analogRead (A3)/4.01); // Manette de gaz
}

EncodeurMouvement1 detection() {
  static long prevPosition1;
  long newPosition1 = encodeur1.read() >> 0; // divise par 4 as the rotary sends 4 ticks per click
  long delta = newPosition1 - prevPosition1;
  prevPosition1 = newPosition1;
  if (delta > 0) return PLUS1;
  else if (delta < 0) return MOINS1;
  return IDENTIQUE1;
}
void CheckEncodeur1() {
  switch (detection()) {
    case PLUS1:  Joystick.setButton(30,1); delay(20); break;
    case MOINS1: Joystick.setButton(31,1); delay(20); break;
    default: break;
  }
  Joystick.setButton(30,0);
  Joystick.setButton(31,0);
}

Une partie du code ressemble a du code que j’ai écrit

Pour l’encodeur

Vous avez conservé le commentaire mais mis 0 pour le décalage. Êtes vous sûr qu’il n’y a qu’un seul tick par click ?

Sinon les delay(20); dans les boucles for ne sont pas top - ça risque de créer une latence visible si vous en avez beaucoup qui se déclenchent.

Pourquoi utiliser la bibliothèque keypad pour des toggle (boutons ON et OFF) - ils sont vraiment câblés en matrice ?

Oui, j'ai laissé le commentaire pour le décalage de l'encodeur afin de me rappeler que c'est à cette endroit que je fait varier le nombre de tick par click. mais si non, j'ai bien un tick par click.

pour les delay(20);, auriez vous une autre solutions pour accélérer le code tout en gardant un temps de réponse assez long afin que l'activation du bouton soit prise en compte ?
une suppression de la fonction delay, et la remise à "0" de la sortie dans le loop pourrais améliorer le programme ? (J'ai effectivement un problème de latence.)

Oui, je suis câblé en matrice. j'ai 30 sortie câblé sur 10 broches.

petite image de la console avec les boutons que je viens de fabriquer.

ça s'applique donc à

                Joystick.setButton(butonsON.key[i].kchar, 1);
                delay(20);
                Joystick.setButton(butonsON.key[i].kchar, 0);

Avec une petite machine à états, quand vous activez la pin (Joystick.setButton(butonsON.key[i].kchar, 1);) , vous mettez à jour une entrée d'un tableau avec la valeur de millis() à ce moment là et la valeur de butonsON.key[i].kchar sous forme d'un int8_t (-128 à +127) et à chaque tour de la loop vous parcourez ce tableau pour voir si le bouton doit être désactivée (les 20ms sont écoulées), si oui vous le faites (avec un Joystick.setButton(tableau[i].pin, 0); ) et remettez son N° de pin à -1 pour dire que cette pin n'est pas activée.

où un truc du genre ➜ vous passez en asynchrone ainsi, le code n'attend jamais

impressionnant !

ça contrôle quoi ? simulateur de vol ?

simulateur d'un F18-C.

Vos plaques gravées vous les faites vous même ?

elles viennent de chez "tekcreations.space". mais j'ai une société sur Auch (dans le gers) qui à la machine pour les fabriquer en gravure laser (je pense que tout bonne société d'impression et découpe numérique est équipé de ce type de machine pour peu qu'on leur passe les fichier en vectorisé (type dxf)

Alors tu m'a perdu un peu (beaucoup).
Dans un premier temps (en attendant d'y voir plus clair), j'ai ré-initialisé l'ensemble des variables dans le loop. Je me suis tâté à mettre la fonction : Joystick.setButton(butons.key[i] == 1) et remettre a "0" uniquement si la valeur n'est pas 1. mais comme sa fait une opération de controle, je pense qu'elle prend plus de temps que tout simplement faire une boucle qui remet tout à 0

  for (int i=6; i<30; i++)  {
    Joystick.setButton(butons.key[i].kchar, 0);
  }

le butons.key[i] ou tableau[i], c'est pas le même principe de fonctionnement ?

Comment on passe en asynchrone ? j'ai jamais trouvé comment faire

Imaginons que vous ayez un bouton et une led associée et quand vous appuyez sur le bouton la LED s'allume et que au bout d'un certain temps elle s'éteint.

Si vous faites un delay() après avoir allumé (comme vous le faites), vous êtes coincé et le code s'arrête et ne vérifie pas les autres boutons. C'est pas top.

Donc il faut faire autrement : le principe est d'enregistrer quelque part que vous avez une action à effectuer plus tard et dans la loop vous regardez si c'est le moment d'effectuer cette action.

Souvent on va grouper dans une structure ou une classe tous les attributs nécessaires au même endroit. De plus comme on peut associer des fonctions à la structure, ça facilite la programmation.

Voici à quoi ressemblerait l'exemple ci dessus codé avec ce principe

click to see the code
enum Etat {ETEINT, ALLUME};

struct BoutonLed {
  const byte brocheLed;
  const byte brocheBouton;
  const unsigned long dureeEclairage;
  Etat etat;
  unsigned long debut;

  void begin() {
    pinMode(brocheLed, OUTPUT);                       // LED en sortie
    pinMode(brocheBouton, INPUT_PULLUP);              // bouton en entrée
  }

  void checkState() {
    if (digitalRead(brocheBouton) == LOW) {           // si le bouton est appuyé
      digitalWrite(brocheLed, HIGH);                  // on allume la LED
      debut = millis();                               // et on note le moment
      etat = ALLUME;                                  // et l'état
    }

    if (etat == ALLUME) {                             // si la LED est allumée
      if (millis() - debut >= dureeEclairage) {       // depuis trop longtemps
        digitalWrite(brocheLed, LOW);                 // on éteint la LED
        etat = ETEINT;                                // et on note l'état
      }
    }
  }
};

BoutonLed elements[] = {
  { 1,  2, 3000, ETEINT, 0},
  { 6,  7, 5000, ETEINT, 0},
  {11, 12,  500, ETEINT, 0},
};

void setup() {
  for (auto& unElement : elements) unElement.begin();
}

void loop() {
  for (auto& unElement : elements) unElement.checkState();
}

vous voyez que je regroupe dans la structure plusieurs attributs:

  • la broche du bouton et de la Led
  • un temps d'attente pour la durée avant extinction
  • un état
  • une variable qui permet de mémoriser quand la LED a été activée

j'associe deux fonctions à la structure, begin() qui établit le mode des broches et checkState() qui gère la logique du couple bouton + led.

le code de checkState() est assez simple à lire :

  • si le bouton est appuyé on allume la led et enregistre cet état et quand ça s'est produit
  • si la led est allumée, on regarde si ça fait trop longtemps et si oui on l'éteint.

On peut définir de manière statique les éléments de notre sketch

BoutonLed elements[] = {
  { 1,  2, 3000, ETEINT, 0}, // led, bouton, durée, état initial, 0
  { 6,  7, 5000, ETEINT, 0},
  {11, 12,  500, ETEINT, 0},
};

et avec cette approche, le code du setup et de la loop se réduisent à leur plus simple expression, on parcourt tous les éléments et on leur demande une action (j'utilise la "for range loop" qui permet de parcourir un tableau)

void setup() {
  for (auto& unElement : elements) unElement.begin();
}

void loop() {
  for (auto& unElement : elements) unElement.checkState();
}