• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Concevez des claviers pour l'Arduino

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Vous allez découvrir ici comment créer une interface pour des entrées, c'est-à-dire comment envoyer de l'information vers la carte Arduino, et ce sans avoir besoin de la connecter à un ordinateur.

En fait, nous allons tout simplement créer un clavier de saisie. Là encore, il ne s'agit pas d'un clavier complet (quoique vous pourriez le faire par la suite) mais d'un clavier de quelques boutons.

Nous allons pour ce faire étudier deux techniques différentes : la matrice de boutons, et la série de boutons.

Concevez et utilisez une matrice de boutons

Qu'est-ce qu'une matrice de boutons ?

(Non Lukas, rien à voir avec l'acné, bien que dans votre cas, en effet on pense tout de suite à un tableau de bord d'avion)

Ce nom devrait vous évoquer quelque chose en rapport avec le chapitre précédent.

En effet, le principe va correspondre aux matrices de LED. Nous allons créer une sorte de grille de boutons que l'Arduino va parcourir comme précédemment, à l'aide d'un tableau.

Voici donc le schéma qui correspond : 

Matrice de boutons poussoir de 3x3
Matrice de 3x3 boutons poussoir

La matrice ci dessus comporte 9 boutons sur 3 lignes et trois colonnes. Je vous l'ai mis à titre indicatif pour pouvoir faire le parallèle avec le chapitre précédent.

Pour ce cours, et surtout pour des raisons à la fois techniques (montage) et financières (coût par bouton)  et sans gêner la théorie, nous allons seulement utiliser une matrice de boutons de 2 x 2 soit 4 boutons.

Voici donc le schéma théorique (les couleurs ont de l'importance pour la suite) :

Matrice de boutons poussoir de 2x2
Matrice de 2x2 boutons poussoir

Les boutons poussoirs sont repérés avec les lettres A, B, C, D. Les numéros correspondent aux numéros des pins numériques de l'Arduino.

Les contraintes techniques

Contrairement à la LED, qui a deux pattes de connexion, le bouton poussoir, qui est pourtant un dipôle, a dans sa forme commerciale, 4 pattes de connexion. Il est de plus assez large par rapport aux trous de la breadboard.

Du coup, connecter 4 boutons poussoirs sur une bread board en gardant la forme d'une matrice carré est impossible ( à moins d'utiliser plusieurs breadboards). En effet, le bouton poussoir se connecte sur une breadboard, en le mettant à cheval par dessus la ligne centrale. Nous n'allons donc pas pouvoir connecter nos boutons en carré, mais en ligne !

Voici l'image de montage : 

Matrice de 2x2 boutons poussoirs en ligne
Matrice de 2x2 boutons poussoirs en ligne

Pour vous faciliter le repérage, j'ai utilisé le même code couleur entre le schéma et l'image de montage, ainsi que le même repérage des boutons.

Vous pouvez observer que :

  • Les boutons A et B sont bien sur la ligne attachée au pin 2 (fil rouge) qui sera la ligne 0 ;

  • Les boutons C et D sont bien sur la ligne attachée au pin 3 (fil orange) qui sera la ligne 1 ;

  • Les boutons A et C sont bien sur la colonne attachée au pin 4 (fil bleu) qui sera la colonne 0 ;

  • Les boutons B et D sont bien sur la colonne attachée au pin 5 (fil noir) qui sera la colonne 1 ;

  • Chaque bouton a bien une coordonnée unique. Par exemple, le bouton A est à la ligne 0, colonne 0.

Il nous faut maintenant réaliser le programme qui va gérer les appuis sur les boutons.

Programmez d'une matrice de boutons

Nous avons vu au chapitre précédent que la programmation d'une matrice de LED se base sur une écriture de chaque coordonnée (ligne/colonne) en position haute ou basse.

La matrice de boutons procède de la même manière, mais au lieu d'écrire "(mode OUTPUT)", nous allons lire les boutons "(mode INPUT)" à chaque coordonnée.

Voici la démarche générale :

  • Les pins de lignes sont en mode OUTPUT, pour envoyer le courant ;

  • Les pins de colonne sont en mode INPUT, pour recevoir le courant ;

  • On place un pin de ligne en état Haut ;

  • On lit chaque pin de colonne ;

  • On replace le pin de ligne à l'état Bas pour préparer la lecture de la ligne suivante.

Voici donc le programme :

int ligne[2]={2,3}; //tableau pour stocker les pins des lignes
int colonne[2]={4,5}; //tableau pour stocker les pins des colonnes

void setup() {
//mise en OUTPUT et LOW des lignes et INPUT des colonnes
for (int i=0;i<2;i++){
pinMode(ligne[i], OUTPUT);
digitalWrite(ligne[i],LOW);
pinMode(colonne[i], INPUT);
}
Serial.begin(9600); //communication série
}

void loop() {
test(); //appel de la fonction test
}
//fonction de test des états des boutons

void test(){
for (int l = 1; l >=0; l--) { //boucle qui parcourt le tableau des lignes
digitalWrite(ligne[l], HIGH); //on place la ligne en HIGH
for (int c = 1; c >=0; c--) { //boucle qui parcourt le tableau des colonnes
boolean etat=digitalRead(colonne[c]); //on lit le résultat
if (etat) { // si etat==1
Serial.print(" D "); //on écrit D pour down
}
else { //sinon
Serial.print(" U "); //on écrit U pour up
}
}
digitalWrite(ligne[l], LOW); //on place la ligne en LOW
}
Serial.println(); //saut de ligne
delay(100); //petite attente
}

 

Testez le programme (il faut ouvrir le moniteur série pour voir le résultat). 

Vous voyez que lorsque vous appuyez un bouton, la lettre "D" s'affiche à la bonne position. Le délai de 100 ms permet de gérer correctement la lecture.

Vous pouvez aussi tester l'appui sur plusieurs boutons à la fois !

Il y a des combinaisons qui ne fonctionnent pas ! C'est normal ?

(Oui très chère Cunégonde, et je m'en vais de ce pas en expliquer la raison...)

Prenons l'exemple d'un appui simultané sur A et C. Pour la lecture de A, la ligne 0 est en HIGH, la ligne 1 est en LOW, et la lecture se fait sur la colonne 0.

Si l'on observe le sens du courant, on voit qu'il peut circuler du pin 2 vers le pin 4, ce qui correspond à notre pin de lecture.

Mais comme le contact C est aussi enfoncé, il se dirige aussi vers le pin 3 (de HIGH vers LOW) ! Ce qui répartit le courant entre les deux pins et ne permet pas de réaliser une mesure d'un état haut pour le pin de lecture.

Il nous faut donc empêcher le courant de "remonter" vers le pin 3. La solution est simple, nous allons ajouter à notre montage une diode (non lumineuse) qui va bloquer le courant dans ce sens.

En fait nous allons ajouter au total 4 diodes (une par bouton), ce qui va empêcher le courant de "remonter".

Et tant que nous y sommes, pour éviter des données erratiques de lecture (voir le chapitre sur les boutons poussoirs), nous allons ajouter une résistance (10 kΩ) à chaque colonne qui sera reliée au ground. Ainsi la lecture de l'état sera nette (HIGH ou LOW).

Voici donc le montage final à réaliser pour pouvoir afficher les lettres des boutons poussoirs actionnés à la manière d’un clavier à appuis multiples.

Matrice de 2x2 boutons poussoir, avec diodes et résistance pull-down
Matrice de 2x2 boutons poussoir, avec diodes et résistance pull-down

Si votre montage est correct, vous avez maintenant accès à toutes les combinaisons (soit 2^4=16 possibilités). En effet, chacun des 4 boutons peut avoir 2 états possibles (haut ou bas).

Et si nous réalisions un petit exercice ?

TP : Réalisez un coffre-fort virtuel

Je vous propose de réaliser un programme qui utilise la matrice de boutons poussoir pour saisir un code qui ouvrirait un "coffre fort virtuel". L'idée est la suivante :

  • Une combinaison est stockée dans une variable dans le programme.

  • On appuie sur les boutons pour former une combinaison et on relâche.

  • Si la combinaison est la bonne une LED  s'allume, sinon elle reste éteinte. 

  • Au bout de trois essais, la LED se met à clignoter rapidement pour indiquer l'auto-destruction du coffre-fort.

  • On appuie sur le bouton "reset" situé sur la carte Arduino pour relancer le programme.

Il vous faut donc prévoir pour réaliser le montage :

  • La matrice de boutons poussoir que l'on vient de réaliser dans ce chapitre ;

  • Une LED connectée avec la résistance qui va bien.

Du côté du programme :

  • Une variable pour le code (je vous conseille un type chaîne de caractère, exemple : "DUUD" avec D=down et U=Up) ;

  • Un moyen de lire et stocker la combinaison saisie (pour la comparer) ;

  • Une variable qui compte les essais ;

  • Une solution pour faire clignoter la diode au bout de 3 essais (sûrement avec la gestion du temps, donc l'utilisation de la fonction  millis()) ;

  • Quelques fonctions pour la lisibilité du programme (fonction de comparaison, de lecture de matrice, de clignotement... par exemple).

Je vous laisse réfléchir. Prenez votre temps, et là encore, essayez de trouver une solution par vous-mêmes avant de passer à la correction. ;)

TP : Correction

Voici la solution que je vous propose pour le code (le montage ne devrait pas vous poser de problème) :

/*
 * Coffre-Fort by Nanomaitre 2015
 * LED connectée au pin 13 avec résitance 220Ω
 * matrice de boutons poussoirs 2x2 :
 * lignes : pins 2 et 3
 * colonnes : pins 4 et 5
 */
char code[5] = "DUUD"; //chaine de caractère pour stocker la combinaison
char codeSaisi[5] = "UUUU";//chaine de caractère pour stocker la saisie
char codeActuel[5] = "UUUU"; // tableau de char pour stocker l'état des boutons
int essai = 3; // variable pour stocker le nombre d'essais
boolean ok = 0; // boolean pour stocker si code trouvé ou non
boolean etatLED = 0; // boolean pour gérer le clignotement
int pinLED = 13; // variable pour stocker le pin de la LED
int ligne[2] = {2, 3}; //tableau pour stocker les pins des lignes
int colonne[2] = {4, 5}; //tableau pour stocker les pins des colonnes
unsigned long tpsDep = millis(); //initialisation du temps de départ pour clignotement

void setup() {
  //mise en OUTPUT et LOW des lignes et INPUT des colonnes
  for (int i = 0; i < 2; i++) {
    pinMode(ligne[i], OUTPUT);
    digitalWrite(ligne[i], LOW);
    pinMode(colonne[i], INPUT);
  }
  pinMode(pinLED, OUTPUT); //mise du pin de LED en mode OUTPUT
}

void loop() {

  if (!ok && essai > 0) { // si code non trouvé et essai restants
    test(); // appel de la fonction de lecture de matrice
    if (!relache()) { // si un appui est lu
      while (!relache()) { // on répète tant qu'un bouton au moins est appuyé
        test(); //lecture de la matrice
      }
      compare(); //au relâchement, on compare avec la combinaison
    }
  }
  else if (ok) { // sinon, teste si la combinaison est trouvée
    digitalWrite(pinLED, HIGH); //on allume la LED
  }
  else { //sinon (nombre d'essai à 0
    unsigned long tpsAct = millis(); //temps actuel
    if (tpsAct - tpsDep > 100) { //si différence > 100 ms
      etatLED = !etatLED; //on inverse l'état de la LED
      digitalWrite(pinLED, etatLED); //affichage de l'état de la LED
      tpsDep = tpsAct; // réinitialisatin du temps de départ.
    }
  }
}

//fonction de test des état des boutons de la matrice
void test() {
  int pos = 0; //position dans le tableau de char
  
  for (int l = 1; l >= 0; l--) { //boucle qui parcourt le tableau des lignes
    digitalWrite(ligne[l], HIGH); //on place la ligne en HIGH
    for (int c = 1; c >= 0; c--) { //boucle qui parcourt le tableau des colonnes
      boolean etat = digitalRead(colonne[c]); //on lit le résultat
      if (etat) { // si etat==1
        codeActuel[pos] = 'D'; //on écrit D dans le tableau de char
      }
      else { //sinon
        codeActuel[pos] = 'U'; //on écrit U dans le tableau de char
      }
      pos++; //on passe à la position suivante
    }
    digitalWrite(ligne[l], LOW); //on place la ligne en LOW
  }
  if (!relache()) { //on teste si l'appui est toujours actif
    // si oui on met à jour la position saisie
    for (int t = 0; t < 4; t++)
      codeSaisi[t] = codeActuel[t];
  }
  delay(100); //petite attente
}

//fonction de comparaison entre position saisie et combinaison
void compare() {
  for (int i = 0; i < 4; i++) { // on parcours les deux tableaux
    if (code[i] != codeSaisi[i]) { // si différence
      essai--; // on enlève un essai
      return; //on sort du test
    }
  }
  //si on arrive là c'est que le code correspond
  ok = 1; //donc on ouvre le coffre
}

//fonction de test si tout relaché
boolean relache() {
  //on parcourt l'état de la matrice
  for (int i = 0; i < 4; i++) {
    if (codeActuel[i] == 'D') //si une touche est enfoncée
      return 0; //on renvoie 0
  }
  //sinon
  return 1;//on renvoie 1
}

Ce code n'est pas forcément le seul possible pour réaliser ce TP. Si vous avez réfléchi au problème, et testé vos solutions, vous avez dû remarquer que la difficulté majeur se situe dans le test d'appui et la comparaison.

En effet, l'appui des boutons est lu très rapidement, et il faut être sûr du moment où l'on compare. J'ai créé pour ce faire la fonction relâche() qui teste si la matrice ne contient aucun appui. Elle me permet de temporiser l'appui (utilisation de la condition while()) de ne récupérer que le code saisi (et non un code vide au moment du relâchement) et de tester ce code récupéré, après relâchement.

Je pense que nous avons fait le tour des matrices de boutons, voyons maintenant les boutons en série...

Les boutons en série

Je vais aborder avec vous une autre façon de récupérer les informations de plusieurs boutons tout en économisant des pins de la carte Arduino.

Le titre "les boutons en série", n'est pas tout à fait juste. En effet des boutons en série (les uns à la suite des autres) ne serviraient pas à grand-chose, car il faudrait que tous soient appuyés en même temps pour que le courant passe.

Il s'agit plutôt de faire un montage qui va, en fonction du bouton appuyé, faire varier la tension.

Mais un bouton est lu à l'état haut ou bas ? Ce n'est pas une différence de tension ?!

(Cunégonde, je me demande parfois pourquoi vous suivez ce cours... on dirait que vous savez déjà tout !)

Nous n'allons pas utiliser les pins numériques pour lire nos boutons, mais les pins analogiques – un seul pin analogique pour être précis.

Je vous rappelle que les pins analogiques de l'Arduino, sont des convertisseurs analogiques/numériques (CAN) qui peuvent convertir une tension entre 0V et 5V en nombre entre 0 et 1023. Chaque unité représentant environ 5 mV.

Il nous faut donc trouver un moyen de lire des valeurs différentes en fonction du bouton appuyé.

Nous avons vu dans le cours d'initiation comment connecter un bouton poussoir avec une résistance pull-down. Nous utilisions un pin numérique pour lire l’état du bouton poussoir. Je vous propose de faire le montage suivant, c'est le même mais en se connectant sur un pin analogique (le A0) :

 

Connexion d'un bouton poussoir avec résistance pull-down sur pin analogique
Connexion d'un bouton poussoir avec résistance pull-down sur pin analogique

Vous pouvez afficher les valeurs lues à l'aide du programme suivant :

void setup() {
  Serial.begin(9600);//communication série
}

void loop() {
  Serial.println(analogRead(A0)); //affichage de la valeur lue par le CAN 0
  delay(10); //petite attente

}

Lorsqu'on appuie sur le bouton, on lit la valeur 1023, sinon la valeur 0. Ce qui correspond aux deux états low (0V) et high (+5V) transformées en valeurs numériques par le CAN.

Et bien l'idée est d'ajouter un bouton en série avec une résistance (220Ω pour l'exemple) qui viendrait se connecter au pin A0. Voici le montage :

Deux boutons poussoirs, dont l'un en dérivation avec une résistance
Deux boutons poussoir en dérivation (dont l'un avec une résistance en série)

Si on appuie sur le bouton de droite, le +5V est relié au pin A0, le CAN indique donc 1023 (car il reçoit du +5V)

Mais, si l'on appuie sur le bouton de gauche, le courant passe à travers la résistance (220Ω) et se dirige vers le pin A0.

Le CAN va donc lire une tension inférieure à +5V et nous renverra un nombre inférieur à 1023.

Faites le test, il vous suffit d'utiliser le même programme que précédemment.

Je vous propose maintenant, en suivant le même principe  et de connecter 4 boutons. Voici le montage :

4 boutons poussoir montés en dérivation
4 boutons poussoir montés en dérivation

Que se passe-t-il si on appuie sur D ? Et bien le courant passe par D puis par les 3 résistances (qui du coup sont en série) avant d'aller rejoindre A0. La valeur de la tension sera donc encore amoindrie.

Hé, mais si j'appuie sur plusieurs boutons, la valeur reste la même !

(Quel esprit scientifique Cunégonde, toujours tester, toujours chercher !)

En effet, c'est la limite de ce montage. On peut arriver à repérer différents boutons, mais, contrairement au réseau de bouton vu plus haut, ce montage ne permet pas la lecture de plusieurs boutons appuyés simultanément.

Le programme pour les boutons en série

Je dirais qu'il n'y a qu'une difficulté et elle est matérielle. En effet, les résistances ont une marge d'erreur de 5% (bague or sur la résistance). Ce qui fait qu'on ne peut pas calculer avec certitude q'un nombre correspond à une valeur.

Dans notre cas, les valeurs affichées (à par celles du bouton A) oscillent. Nous allons donc devoir tester des intervalles de valeurs pour récupérer le bouton appuyé.

Voici un premier programme :

void setup() {
  Serial.begin(9600);//communication série
}

void loop() {
  int valeur=analogRead(A0);
  Serial.print(valeur); //affichage de la valeur lue par le CAN 0
  if (valeur>=1023){
    Serial.println(" bouton A");
  }
  else if (valeur>=999){
    Serial.println(" bouton B");
  }
  else if (valeur>=979){
    Serial.println(" bouton C");
  }
  else if (valeur>=959){
    Serial.println(" bouton D");
  }
  delay(10); //petite attente
}

J'ai volontairement fait les tests en utilisant la valeur minimale lue (en utilisant le programme plus haut) et en enlevant 1. Il m'a donc fallu noter les valeurs pour chaque appui. 

Le montage qu'on réalise lorsqu'on met ces boutons en dérivations avec une résistance en série, s'appelle un diviseur de tension. D'ailleurs, le montage avec une résistance pull-down est déjà un montage en diviseur de tension.

Le principe est le suivant. Voici un montage :

Schéma pour pont diviseur de tension
Schéma pour pont diviseur de tension

La première formule  à connaître est la suivante :

tension de sortie= (r1/(r1+r2)) x tension d'entrée

Dans notre cas, r1 vaut 10000Ω, r2 vaut (pour le bouton B) 220Ω, la tension d'entrée est de 5V, on obtient donc :

sortie=(10000/(10000+220))*5=4,8923 V

Si on mappe cette valeur entre 0 et 1023, on obtient 1000 (valeur qui correspond à peu près à celle lue par le CAN)

On peut donc calculer les intervalles de valeur (en prenant en compte +-5% pour les résistances à bande dorées) pour obtenir la fourchette de résultats.

Si plusieurs résistances sont en série, on ajoute leur valeur (pour le bouton C, r2 vaut donc 440Ω +-5%).

Ce calcul peut nous permettre de savoir à l'avance les paliers en fonction des résistances (attention, il faut utiliser des résistances de valeur identiques !) :

Voici un programme qui permet d'afficher les valeurs en fonction du nombre de boutons :

float r1=10000.0; // valeur pour R1
float r2=220.0; // valeur pour R2
float p=5.0; // pourcentage d'erreur
float ue=5000.0; // tension d'entrée en mV
int nbBoutons=4; // nombre de boutons
void setup() {
  Serial.begin(9600);//communication série
  Serial.println("*************");
  for (int t=0;t<nbBoutons;t++){
    float pr2=r2*p/100; //valeur du pourcentage
    float usmin=(r1/(r1+t*(r2+pr2)))*ue; // valeur sortie min
    float usmax=(r1/(r1+t*(r2-pr2)))*ue; // valeur sortie max
    float us=(r1/(r1+t*(r2)))*ue; // valeur sortie théorique
    //affichage
    Serial.print ("Bouton ");
    Serial.print (t);
    Serial.print (" : ");
    Serial.print (map (int(usmin),0,5000,0,1023));
    Serial.print (" < ");
    Serial.print (map (int(us),0,5000,0,1023));
    Serial.print (" < ");
    Serial.println (map (int(usmax),0,5000,0,1023));
  }
}

void loop() {
  
}

Je vous fais remarquer au passage l'utilisation du type float. C'est pour utiliser les nombres à virgules.

En utilisant les valeurs minimum de chaque palier, on peut stocker dans un tableau les valeurs à utiliser sans avoir à faire de relevés.

Voici un programme qui nous dit quel bouton est appuyé grâce à un tableau calculé à l'avance :

float r1=10000.0; // valeur pour R1
float r2=220.0; // valeur pour R2
float p=5.0; // pourcentage d'erreur
float ue=5000.0; // tension d'entrée en mV
const int nbBoutons=4; // nombre de boutons
int valeurs[nbBoutons]; // tableau de stockage des valeurs basses
void setup() {
  Serial.begin(9600);//communication série
  Serial.println("*************");
  for (int t=0;t<nbBoutons;t++){
    float pr2=r2*p/100; //valeur du pourcentage
    float usmin=(r1/(r1+t*(r2+pr2)))*ue; // valeur sortie min
    valeurs[t]=map (int(usmin),0,5000,0,1023); //ajout d'une valeur
  }
}
void loop() {
  int val=analogRead(A0); // lecture du CAN 0
  if (val){ // si différent de 0
    for (int t=0;t<nbBoutons;t++){ // parcours des valeurs
      if (val>=valeurs[t]){ //test si supérieur à valeur concernée
        // affichage
        Serial.print ("Bouton ");
        Serial.print (t);
        Serial.println (" down");
        break; //sortie de boucle
      }
    }
  }
}

On peut, typiquement, grâce à cette méthode, utiliser des claviers construits de façon personnelle en n'utilisant qu'un seul pin de l'Arduino !

En effet, en utilisant des résistances de 220Ω, on peut atteindre théoriquement une utilisation de 50 boutons ! 

Sans pouvoir, bien sûr appuyer plusieurs boutons simultanément. 

À vous de construire votre propre calculatrice, ou votre clavier de saisie (ou votre clavier utile à vos seules fins) ou de pouvoir tester plusieurs contacts de type micro-rupteurs.

Je pense que nous pouvons nous arrêter là pour le moment.

En résumé

Vous avez vu dans ce chapitre, comment créer un clavier pour votre Arduino grâce à deux méthodes :

  • La matrice de boutons : qui permet de tester l'appui simultané de plusieurs bouton, mais qui reste gourmande en pin

  • La série de boutons : qui permet de savoir quel bouton est appuyé parmi un nombre assez grand de boutons, mais qui ne gère pas les appuis simultanés.

Comme pour les matrices de LED, il est possible d'utiliser des puces de gestions d'entrée/sortie qui fonctionnent en I2C pour gérer les matrices de boutons.

Nous allons dans le chapitre suivant étudier une interface d'affichage proposée dans le commerce...

Exemple de certificat de réussite
Exemple de certificat de réussite