Utiliser un bouton de keypad comme switch ON OFF

Bonjour, je suis actuellement en train de fabriquer une sorte de stream deck avec un keypad et un Arduino Pro Micro. J'ai pu définir mes raccourcis sur tous les boutons, sauf les 3 premiers. Les trois premiers doivent me permettre de controller trois relais sous mon bureau pour allumer/éteindre mes écrans et le bandeau LED derrière.

Mais voilà le problème: Il est facile de programmer ces trois premiers boutons en tant que push button (on appuie, ça s'allume, on lâche, ça s'éteint), mais je n'arrive pas à les programmer comme switch ON OFF (on appuie, ça s'allume jusqu'à ce que l'on réappuie dessus). Les tutos sur internet montrent comment réaliser ce genre d'action basique mais uniquement dans le cas d'un bouton poussoir. Je dois implémenter cette fonctionnalité dans un "switch; case:" comme ci dessous:

switch (key) {
      case '1':
 
        break;
      case '2':

        break;
      case '3':

        break;
}

Et voici mon code:

#include <Keypad.h>
#include <Keyboard.h>

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', 'Z', 'D'},
};

/*  Screen1  Screen2    LEDs    Discord
 *  NewTab   Youtube    Twitter Gmail
 *  Paypal   RTE        ElecMap Whatsapp
 *  Banggood Aliexpress Ebay    Amazon
 */

byte rowPins[ROWS] = {2, 3, 4, 5}; 
byte colPins[COLS] = {6, 7, 8, 9 };

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

int e1R = 10;
int e2R = 16;
int lR = 14;

void setup() {
  Serial.begin(9600);
  Keyboard.begin();

  pinMode(e1R, OUTPUT);
  pinMode(e2R, OUTPUT);
  pinMode(lR, OUTPUT);
}

void sendMacroCommand(uint8_t key) {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(key);
}

void loop() {

  char key = keypad.getKey();

  if (key) {
    //Serial.println(key);
    switch (key) {
      case '1':
 
        break;
      case '2':

        break;
      case '3':

        break;
      case 'A':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F5);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '4':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F1);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '5':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F2);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '6':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F3);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'B':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F4);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '7':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F6);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '8':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F7);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '9':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F8);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'C':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F9);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '*':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F10);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '0':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F11);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'Z':
        Keyboard.press(KEY_LEFT_CTRL);
        Keyboard.write(KEY_F1);
        Keyboard.release(KEY_LEFT_CTRL);
        break;
      case 'D':
        Keyboard.press(KEY_LEFT_CTRL);
        Keyboard.write(KEY_F2);
        Keyboard.release(KEY_LEFT_CTRL);
        break;
    }

    delay(100);
    Keyboard.releaseAll();
  }
}

TimiMaker:
Les tutos sur internet montrent comment réaliser ce genre d'action basique mais uniquement dans le cas d'un bouton poussoir.

En quoi la logique de gestion de l'appui d'une touche sur un keypad diffère-t-elle de la gestion d'un bouton poussoir?
La librairie keypad t'indique si une touche est appuyée ce n'est pas très différent de lire l'état d'un bouton poussoir avec digitalRead.

Pour chaque bouton, il te faut une variable qui mémorise l'état actuel (allumé/éteint).
Si l'état est ALLUMé, un appui sur le bouton fait passer à l'état ETEINT.
Et inversement.

Sinon, plus simple, une multiprise avec interrupteur ? :slight_smile:

fdufnews:
En quoi la logique de gestion de l'appui d'une touche sur un keypad diffère-t-elle de la gestion d'un bouton poussoir?
La librairie keypad t'indique si une touche est appuyée ce n'est pas très différent de lire l'état d'un bouton poussoir avec digitalRead.

Je sais, dans ce cas, c'est le code que j'ai essayé qui ne va pas. D'habitude cela fonctionne mais ici, dans un "case" ça ne fonctionne pas (je précise que le morceau de code n'est pas présent sur celui au dessus)

TimiMaker:
Je sais, dans ce cas, c'est le code que j'ai essayé qui ne va pas. D'habitude cela fonctionne mais ici, dans un "case" ça ne fonctionne pas (je précise que le morceau de code n'est pas présent sur celui au dessus)

Les keypads sont quasi toujours organisé en matrice
il faut détecter la combinaison qui t'interesse

TimiMaker:
Je sais, dans ce cas, c'est le code que j'ai essayé qui ne va pas. D'habitude cela fonctionne mais ici, dans un "case" ça ne fonctionne pas (je précise que le morceau de code n'est pas présent sur celui au dessus)

Bonjour,

Le traitement doit être fait avant le switch (à moins que tu ne veuilles traiter différemment certaines touches).
Tu dois comparer la touche lue à la touche précédemment lue et faire la traitement si elle est différente.

D'après son code chaque touche à une fonction différente.
Lorsque l'exécution tombe dans un case c'est que la touche a été appuyé.
En gros, il suffit d'avoir pour chacun des 3 case une variable qui mémorise l'état ON/OFF et dont tu changes l'état à chaque appui.

fdufnews:
D'après son code chaque touche à une fonction différente.
Lorsque l'exécution tombe dans un case c'est que la touche a été appuyé.
En gros, il suffit d'avoir pour chacun des 3 case une variable qui mémorise l'état ON/OFF et dont tu changes l'état à chaque appui.

C'est justement ce que j'essaye de faire

Bonsoir, désolé des réponses tardives. Donc, voici le code modifié mais qui ne fonctionne toujours pas (j'ai ajouté une variable état "e1RS" pour le bouton 1 mais ici cela ne fonctionne pas car il y a une boucle qui active puis désactive juste après le relai.

#include <Keypad.h>
#include <Keyboard.h>

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', 'Z', 'D'},
};

byte rowPins[ROWS] = {2, 3, 4, 5}; 
byte colPins[COLS] = {6, 7, 8, 9 };

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

int e1R = 10;
int e2R = 16;
int lR = 14;

boolean e1RS = 0;

void setup() {
  Serial.begin(9600);
  Keyboard.begin();

  pinMode(e1R, OUTPUT);
  pinMode(e2R, OUTPUT);
  pinMode(lR, OUTPUT);
}

void sendMacroCommand(uint8_t key) {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(key);
}

void loop() {
  
  Serial.println(e1RS);
  delay(100);
  char key = keypad.getKey();

  if (key) {
    //Serial.println(key);
    switch (key) {
      case '1':
        if (e1RS == 0){
          e1RS = 1;
          digitalWrite(e1R, HIGH);
          delay(100);
        }
        if (e1RS == 1){
          e1RS = 0;
          digitalWrite(e1R, LOW);
          delay(100);
        }
        break;
      case '2':

        break;
      case '3':

        break;
      case 'A':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F5);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '4':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F1);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '5':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F2);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '6':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F3);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'B':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F4);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '7':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F6);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '8':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F7);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '9':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F8);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'C':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F9);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '*':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F10);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case '0':
        Keyboard.press(KEY_LEFT_SHIFT);
        Keyboard.write(KEY_F11);
        Keyboard.release(KEY_LEFT_SHIFT);
        break;
      case 'Z':
        Keyboard.press(KEY_LEFT_CTRL);
        Keyboard.write(KEY_F1);
        Keyboard.release(KEY_LEFT_CTRL);
        break;
      case 'D':
        Keyboard.press(KEY_LEFT_CTRL);
        Keyboard.write(KEY_F2);
        Keyboard.release(KEY_LEFT_CTRL);
        break;
    }

    delay(100);
    Keyboard.releaseAll();
  }
}

Il suffit d'inverser l'état de la sortie.
Par exemple pour la première sortie

     case '1':
        digitalWrite(e1R, !digitalRead(e1R));
        break;

TimiMaker:
mais ici cela ne fonctionne pas car il y a une boucle qui active puis désactive juste après le relai.

Comprends rien...

Dans ton programme il faut un else après le premier if

      case '1':
        if (e1RS == 0){
          e1RS = 1;
          digitalWrite(e1R, HIGH);
        }
        else if (e1RS == 1){
          e1RS = 0;
          digitalWrite(e1R, LOW);
        }
        break;

Mais utilises plutôt la méthode que je t'ai suggérée en #9 qui ne nécessite pas de variable supplémentaire.

kamill:
Dans ton programme il faut un else après le premier if

C'est mieux (ça évite 1 test) mais ça ne change rien.

La méthode présentée en #9 est correcte, mais un peu compacte pour un débutant.
Elle ne permet pas de comprendre la nécessité, dans une machine à états, de garder en mémoire l'état précédent (qui dans ce cas est conservé implicitement par l'état de la broche). C'est un cas particulier.
Le débutant gagnera en compréhension à utiliser explicitement une variable conservant l'état.

biggil:
C'est mieux (ça évite 1 test) mais ça ne change rien.

Ça évite accessoirement un test, mais surtout ça permet le fonctionnement correct du programme.
Si on ne met pas le else, on arrive dans le premier test avec e1RS à 0, on active la sortie et on met e1RS à 1, ensuite on tombe dans le 2eme test et on remet la sortie à 0.

Autant pour moi !
Au temps pour moi !
(comme ça tout le monde est content :slight_smile: )

biggil:
C'est mieux (ça évite 1 test) mais ça ne change rien.

La méthode présentée en #9 est correcte, mais un peu compacte pour un débutant.
Elle ne permet pas de comprendre la nécessité, dans une machine à états, de garder en mémoire l'état précédent (qui dans ce cas est conservé implicitement par l'état de la broche). C'est un cas particulier.
Le débutant gagnera en compréhension à utiliser explicitement une variable conservant l'état.

Programmant sur Arduino depuis plusieurs années déjà (mais n'ayant pas vraiment travaillé avec des keypad), je n'apprécie pas du tout être qualifié de "débutant". Je suis tout à fait en capacité de comprendre ce bout de code en #9. Seulement, j'ai toujours travaillé avec une variable d'état.

Sur ce, merci à tous pour votre aide et bonne continuation !

Ben en général les personnes qui reproduisent les termes en anglais qu'ils rencontrent parce qu'il ne comprennent pas la signification en français : je pense à "keypad" au lieu ce clavier et "switch" au lieu d’interrupteur, On/off pour marche arret sont des débutants complets d'où ton classement dans les débutants :grin:.

Si la présentation du sujet avait été mieux maîtrisée nous aurions reconnu le professionnel.

TimiMaker:
je n'apprécie pas du tout être qualifié de "débutant"

Alors évite de poser des questions de débutant :slight_smile: