Les interrupteurs

Ou comment basculer d’un avis à l’autre...

. Par : Thierry. URL : https://www.locoduino.org/spip.php?article176

Un interrupteur (’switch’ dans la langue du brexit) est un dispositif matériel destiné à changer un état permanent parmi deux choix possibles.

Des interrupteurs, vous en utilisez tous les jours : allumer la lumière, mettre son clignotant (enfin ceux qui les utilisent...), allumer son aspirateur (si, si, le truc bruyant qui vous réveille le samedi matin !). C’est un élément extrêmement courant de notre vie quotidienne.
Il ne faut pas confondre ces bascules matérielles avec des dispositifs logiciels qui remplissent la même fonction mais basés sur de simples poussoirs, comme les touches de votre télévision, de votre téléphone, de votre clim... Vous allez me dire, ’Oui, mais quand j’appuie sur le bouton On/Off de la télé, ça s’allume et ça s’éteint comme la lumière de la salle à manger !’. C’est vrai, mais c’est juste que le poussoir en question se sert d’une impulsion temporaire pour changer l’état courant et passer à l’autre état. Si la télé était éteinte, on va l’allumer, et inversement. Contrairement au bouton de la salle à manger qui va maintenir une connexion électrique dans une position, et la couper dans l’autre.
Les interrupteurs sont incroyablement variés dans leurs formes, leurs modes de fonctionnement, leurs prix !

PNG - 38.3 kio
En haut, des boutons ’bruts’ pour des montages électroniques. En bas, plutôt du joli pour un montage définitif.

La forme et le prix, c’est vous qui voyez, mais le fonctionnement doit être conforme à ce que le programme, et donc vous, en attendez. Les grandes caractéristiques à prendre en compte sont : la puissance électrique transmissible, la capacité à rester sur l’une de ses positions, le nombre de connexions commutées... Une terminologie a été adoptée comme un standard de fait pour le monde l’électronique et décrit les différents modèles :

Nom En clair Description Symbole
SPST Single pole, Single throw Dans une position le contact est établi entre une entrée et une sortie, dans l’autre position, le contact est rompu.
SPDT Single pole, Double throw Le contact est établi alternativement entre une entrée et deux sorties.
DPST Double pole, Single throw On a là deux entrées, connectées à deux sorties ou non connectées.
DPDT Double pole, Double throw Deux entrées sont connectées alternativement à deux sorties chacune. Chaque entrée et ses sorties sont électriquement indépendantes de l’autre entrée et ses sorties.
nPmT Multi pole, Multi throw ’n’ Entrées sont connectées à ’m’ sorties. Ici un 2P6T

D’autres configurations existent avec par exemple un point central stable qui permet de ne rien connecter dit ’Center Off’, ou plus de deux positions stables comme le gros bouton d’une machine à laver le linge ou de la sélection de source d’un vieil ampli HI-FI.

PNG - 30.6 kio
En haut, un ’Central Off’. En bas deux sélecteurs à multiples positions

Réagir à ces interrupteurs consiste à identifier le moment où un contact est activé ou relâché. Noter que tout comme un bouton poussoir, et comme tout dispositif mécanique de commutation, des rebonds sont présents au moment du changement d’état. Et il convient de les gérer dans le croquis Arduino.

Prenons un interrupteur SPST connecté à la broche 3 :

#define INTER 3

void setup()
{
  pinMode(INTER, INPUT);
}

void loop()
{
  if (digitalRead(INTER) == LOW) // Si la broche est LOW, faire :
    {instructions off}
  else // sinon, faire :
    {instructions on}
}

C’est le codage minimal. Il va nous exposer à plusieurs problèmes : les rebonds ne sont pas gérés, et la mémorisation de l’état courant n’est pas globale, ce qui fait qu’à chaque loop on va refaire les instructions on ou off... Ce qui peut être sans importance sur l’allumage d’une DEL par exemple, ou au contraire problématique dans un autre contexte. Voyons la version correcte, utilisant la bibliothèque Bounce2 recommandée par... LOCODUINO (et Arduino.cc, mais c’est accessoire...) !

#include <Bounce2.h>

#define INTER 3

// Créer (instancier) un objet Bounce
Bounce inter; 

void setup() 
{
  // Configure la broche avec la résistance de tirage
  pinMode(INTER, INPUT_PULLUP);
  // Attache l'objet 'inter' à la même broche
  inter.attach(INTER);
  inter.interval(5); // temps de latence entre deux mesures d'état
}

// Variable qui va contenir l'état courant du bouton
// On commence avec -1 qui est un état inconnu...
int courant = -1;

void loop() 
{
  // Mise à jour de l'état de inter : bouton pressé ou non ?
  inter.update();

  // Récupération de l'état du bouton
  int valeur = inter.read();

  // Si la valeur n'a pas changé depuis la dernière fois, ne rien faire.
  if (valeur == courant)
    return;

  // Agir en fonction de l'état du bouton
  if ( valeur == LOW)
    { Instructions On }
  else
    { Instructions Off }

  courant = valeur;
}

Dans cette version, grâce à Bounce on va bien éliminer les rebonds, et grâce à une petite gestion de valeur courante, on ne fera les choses qu’une seule fois... La connexion d’une broche de sortie d’un interrupteur répond exactement aux mêmes exigences que pour un bouton poussoir, en terme de présence ou non de résistance et de OUTPUT_PULLUP (plus d’explications ici).
Pour finir voyons le code pour un interrupteur à une entrée et deux sorties (SPDT) :

#include <Bounce2.h>

// Une broche par position
#define INTER_A 3
#define INTER_B 4

// Liste des états possibles de l'interrupteur. Les valeurs utilisées doivent juste être différentes entre elles.
// J'aurais d'ailleurs pu ré-utiliser INTER_A et INTER_B...
#define INTER_ETAT_INDEFINI  -1
#define INTER_ETAT_A  10
#define INTER_ETAT_B  20

// Créer (instancier) un objet Bounce pour chaque broche
Bounce interA; 
Bounce interB; 

void setup() 
{
  // Configure les broches avec la résistance de tirage
  pinMode(INTER_A, INPUT_PULLUP);
  pinMode(INTER_B, INPUT_PULLUP);

  // Attache l'objet 'interA' à sa broche
  interA.attach(INTER_A);
  interA.interval(5); // temps de latence entre deux mesures d'état

  // Attache l'objet 'interB' à sa broche
  interB.attach(INTER_B);
  interB.interval(5); // temps de latence entre deux mesures d'état
}

// Variable qui va contenir l'état courant du bouton
// On commence avec -1 qui est un état inconnu...
int courant = INTER_ETAT_INDEFINI;

void loop() 
{
  // Mise à jour de l'état des interA et B : bouton pressé ou non ?
  interA.update();
  interB.update();

  // Récupération de l'état des broches
  int valeurA = interA.read();
  int valeurB = interB.read();

  // Si la valeur n'a pas changé depuis la dernière fois, ne rien faire.
  if (valeurA == true && courant == INTER_ETAT_A)
    return;
  if (valeurB == true && courant == INTER_ETAT_B)
    return;

  if (valeurA == true)
    courant = INTER_ETAT_A;
  if (valeurB == true)
    courant = INTER_ETAT_B;

  // Agir en fonction de l'état du bouton
  if ( courant == INTER_ETAT_A)
    { Instructions etat A }
  else
    { Instructions etat B }
}

Il y a moyen de faire plus concis avec des tableaux, surtout si on veut augmenter le nombre de positions, mais au moins on comprend le principe. En fait chaque broche est gérée comme un bouton simple indépendant, alors que la variable ’courant’, elle, contient l’état général du switch.

Faites bien attention lorsque vous achetez ces interrupteurs, leurs caractéristiques électriques doivent être respectées (pas de 220V sur un switch pour montage électronique !), et il est très facile de se tromper en passant une commande sur le mode de fonctionnement : point milieu ou pas, nombre de positions stables, nombre de connexions commutées sur chaque position...