Boite à bouton à plus de 25 boutons

Bonjour,

Je tente de créer une boite à bouton pour interagir avec mon ordinateur.

J'ai trouvé le code suivant sur le net, qui fonctionne très bien, mais j'avoue que je ne comprend pas tout...

#include <Keypad.h>
#include <Joystick.h>

#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 25
#define NUMROWS 5
#define NUMCOLS 5


byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4}, //boutons de la 1ere ligne. Bien respecter les numéros au niveau du schéma car ça ne fonctionne pas si les boutons sont intervertis
  {5,6,7,8,9}, //boutons de la 2eme ligne
  {10,11,12,13,14}, //boutons de la 3eme ligne
  {15,16,17,18,19}, //boutons de la 4eme ligne
  {20,21,22,23,24}, //boutons de la 5eme ligne
};

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

rotariesdef rotaries[NUMROTARIES] {
  {0,1,24,25,0},
  {2,3,26,27,0},
  {4,5,28,29,0},
  {6,7,30,31,0},
};

#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0

#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
  // R_START (00)
  {R_START_M,            R_CW_BEGIN,     R_CCW_BEGIN,  R_START},
  // R_CCW_BEGIN
  {R_START_M | DIR_CCW, R_START,        R_CCW_BEGIN,  R_START},
  // R_CW_BEGIN
  {R_START_M | DIR_CW,  R_CW_BEGIN,     R_START,      R_START},
  // R_START_M (11)
  {R_START_M,            R_CCW_BEGIN_M,  R_CW_BEGIN_M, R_START},
  // R_CW_BEGIN_M
  {R_START_M,            R_START_M,      R_CW_BEGIN_M, R_START | DIR_CW},
  // R_CCW_BEGIN_M
  {R_START_M,            R_CCW_BEGIN_M,  R_START_M,    R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif

// A modifier
byte rowPins[NUMROWS] = {0,1,2,3,4}; 
byte colPins[NUMCOLS] = {5,6,7,8,9}; 

Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS); 

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 32, 0,
  false, false, false, false, false, false,
  false, false, false, false, false);

void setup() {
  Joystick.begin();
  rotary_init();}

void loop() { 

  CheckAllEncoders();

  CheckAllButtons();

}

void CheckAllButtons(void) {
      if (buttbx.getKeys())
    {
       for (int i=0; i<LIST_MAX; i++)   
        {
           if ( buttbx.key[i].stateChanged )   
            {
            switch (buttbx.key[i].kstate) {  
                    case PRESSED:
                    case HOLD:
                              Joystick.setButton(buttbx.key[i].kchar, 1);
                              break;
                    case RELEASED:
                    case IDLE:
                              Joystick.setButton(buttbx.key[i].kchar, 0);
                              break;
            }
           }   
         }
     }
}


void rotary_init() {
  for (int i=0;i<NUMROTARIES;i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
    #ifdef ENABLE_PULLUPS
      digitalWrite(rotaries[i].pin1, HIGH);
      digitalWrite(rotaries[i].pin2, HIGH);
    #endif
  }
}


unsigned char rotary_process(int _i) {
   unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
  rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
  return (rotaries[_i].state & 0x30);
}

void CheckAllEncoders(void) {
  for (int i=0;i<NUMROTARIES;i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
}

Je l'ai modifié ainsi pour pouvoir gérer une matrice plus importante (idéalement 5x6, voir plus).
Il n'y a pas d'erreur à la compilation, mais j'aimerai savoir s'il n'y a rien qui vous choque dans cette modification. Sachant que je n'ai absolument pas besoin de gérer les potentiomètres

#include <Keypad.h>
#include <Joystick.h>

#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 30
#define NUMROWS 6
#define NUMCOLS 5


byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4}, //boutons de la 1ere ligne. Bien respecter les numéros au niveau du schéma car ça ne fonctionne pas si les boutons sont intervertis
  {5,6,7,8,9}, //boutons de la 2eme ligne
  {10,11,12,13,14}, //boutons de la 3eme ligne
  {15,16,17,18,19}, //boutons de la 4eme ligne
  {20,21,22,23,24}, //boutons de la 5eme ligne
  {25,26,27,28,29},
};

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

rotariesdef rotaries[NUMROTARIES] {
  {0,1,24,25,0},
  {2,3,26,27,0},
  {4,5,28,29,0},
  {6,7,30,31,0},
};

#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0

#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
  // R_START (00)
  {R_START_M,            R_CW_BEGIN,     R_CCW_BEGIN,  R_START},
  // R_CCW_BEGIN
  {R_START_M | DIR_CCW, R_START,        R_CCW_BEGIN,  R_START},
  // R_CW_BEGIN
  {R_START_M | DIR_CW,  R_CW_BEGIN,     R_START,      R_START},
  // R_START_M (11)
  {R_START_M,            R_CCW_BEGIN_M,  R_CW_BEGIN_M, R_START},
  // R_CW_BEGIN_M
  {R_START_M,            R_START_M,      R_CW_BEGIN_M, R_START | DIR_CW},
  // R_CCW_BEGIN_M
  {R_START_M,            R_CCW_BEGIN_M,  R_START_M,    R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif

// A modifier
byte rowPins[NUMROWS] = {0,1,2,3,4,10}; 
byte colPins[NUMCOLS] = {5,6,7,8,9}; 

Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS); 

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 32, 0,
  false, false, false, false, false, false,
  false, false, false, false, false);

void setup() {
  Joystick.begin();
  rotary_init();}

void loop() { 

  CheckAllEncoders();

  CheckAllButtons();

}

void CheckAllButtons(void) {
      if (buttbx.getKeys())
    {
       for (int i=0; i<LIST_MAX; i++)   
        {
           if ( buttbx.key[i].stateChanged )   
            {
            switch (buttbx.key[i].kstate) {  
                    case PRESSED:
                    case HOLD:
                              Joystick.setButton(buttbx.key[i].kchar, 1);
                              break;
                    case RELEASED:
                    case IDLE:
                              Joystick.setButton(buttbx.key[i].kchar, 0);
                              break;
            }
           }   
         }
     }
}


void rotary_init() {
  for (int i=0;i<NUMROTARIES;i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
    #ifdef ENABLE_PULLUPS
      digitalWrite(rotaries[i].pin1, HIGH);
      digitalWrite(rotaries[i].pin2, HIGH);
    #endif
  }
}


unsigned char rotary_process(int _i) {
   unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
  rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
  return (rotaries[_i].state & 0x30);
}

void CheckAllEncoders(void) {
  for (int i=0;i<NUMROTARIES;i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
}

Qu'en pensez-vous ?

Merci d'avance.

qu'il faut lire les recommandations listées dans "Les bonnes pratiques du Forum Francophone” pour poster le code avec les balises de code et pas une balise [details] :wink:

Je me doutais bien que j'avais oublié un truc... j'avais pourtant lu, mais j'ai voulu faire trop vite...
C'est corrigé. Merci :wink:

1 Like

les N° de pins de vos rotaries (c'est quoi? un encodeur rotatif?) sont dans ce tableau

et il semble que vos boutons sont dans cet autre tableau

mais des pins sont en commun... c'est bizarre, non ?

Les rotaries sont des potentiomètres. Ce code prévoit qu'il y en ait 4.
Mais je ne le comprends pas suffisamment pour comprendre sur quels PINS il me faut les brancher. (Si je réussi à les brancher, c'est un bonus.) Mais j'avoue que je n'ai rien compris sur les rotaries....

Concernant les boutons, ils ont effectivement des PINS en communs.
C'est un branchement matriciel qui permet d'outrepasser la limite des emplacements physiques du Leonardo tout en "consommant" un minimum de PINS. J'ai annoté des commentaires sur ce que je comprenais.

Cela fonctionne sur ce schéma. (Les numéro des PINS ne sont pas les bons)

ça marche très bien, et ça permet de doubler (voir plus) le nombre de bouton. En revanche, il ne faut pas appuyer sur plus de 3 boutons à la fois.

J'essai de comprendre ce codage pour être autonome sur la matrice et pouvoir gérer un maximum de boutons.

Le code attend des encodeurs rotatifs

Ce que vous montrez correspond plutôt à cette partie du code

C’est un KeyPad

Postez le schéma de votre montage

En effet, il s'agit d'encodeurs rotatifs. J'ai utilisé à tort le terme "potentiomètres".

La partie de code que je montre correspond en effet à un keypad.
Mon but est de créer un Keypad plus gros, qui va au delà de 9.
Le tout réagit comme une boite à boutons une fois relié à un pc.

(Je poste le schéma dans la journée)

nettoyez le code et virez tout ce qui ne sert pas... ce sera beaucoup plus simple

Edit, j'ai dit une connerie.

on peut avoir des keypad rectangulaires

Cool, comment tu les branches?

en lignes et colonnes


(source https://arduinogetstarted.com/tutorials/arduino-keypad)

Le deuxième c'est un carré :slight_smile: , mais oui je suis con, rien ne n'empêche de te raccorder sur n'importe qu'elle ligne ou colonne, tant que la combinaison est utilisé une seul fois :crazy_face:

Oui c'est pour montrer que les 2 sont possibles

mais non mais non, parfois on s'emporte :slight_smile:

Oui et des fois on est con mais on essaye de s'améliorer petit à petit :joy:

J'ai déjà essayé de virer les parties inutiles, mais je n'y parviens pas : je ne suis pas assez doué en programmation pour y parvenir sans créer de nouvelles erreurs.

De plus.... Si je parviens à adapter le code pour prendre en charge un nombre plus important de bouton, j'aimerai par la suite pouvoir utiliser les encodeurs rotatifs... Donc, ils me serviront.

Du coup, je n'ai pas trop compris, ce que tu voulais, tu semble indiquer que cela marche bien?

Voilà le schéma actuel du projet (j'utilise un Leonardo).
ça fonctionne très bien.
Au final, comme l'a dit JML ça n'est qu'un keypad...

Ce que je souhaiterai, c'est augmenter le nombre de touche de ce keypad de 25 touches à 36, voir 49 boutons en mettant en services les PINS 10, 11, 12, 13.

J'aimerai conserver les PINS analogiques pour faire fonctionner les encodeurs rotatifs, même si je ne sais pas encore comment ils vont fonctionner, ni comment ils vont se brancher.

Je me permettais de demander un petit coup de main, pour comprendre ce codage, afin que je puisse l'adapter et y ajouter une ou deux colonnes / lignes.

Le seul inconvénient de cette utilisation matricielle est le fait que tu ne peux pas appuyer sur plus de 3 boutons à la fois (environs), sans quoi, ça risque de créer des appuis non désirés, ou de la non reconnaissance de boutons.