Ardu? No!Initiation ≫ switch case

switch case

Dans la page précédente, nous avons fait clignoter la led de la carte avec un temps d'allumage décroissant. Ce que nous allons faire maintenant c'est de pouvoir modifier par le clavier (via l'affichage de la console) le temps pendant lequel la led est allumée et le temps de l'extinction. Les touches (sur l'ordinateur branché comme console) qui permettront les changements sont:

Ces touches ont été choisies pour leur situation sur le pavé numérique.

Base précédente

On va reprendre la base précédente en utilisant une variable pour le temps d'allumage et une variable pour le temps d'extinction. On va utiliser les réels, ce sera plus simple pour les opérations. Ici, on a plein de place et d'espace, on ne le ferait peut être pas dans un programme conséquent:

void setup() {
  Serial.begin(115200); // Pour indiquer les temps
  pinMode(LED_BUILTIN, OUTPUT);
}

float tempsAllume = 500.0;
float tempsEteint = 500.0;

void loop() {
  // Affichage des durées
  Serial.print(F("La led est allumée pendant "));
  Serial.print(tempsAllume / 1000.0);
  Serial.print(F("s et éteinte pendant "));
  Serial.print(tempsEteint / 1000.0);
  Serial.println(F("s"));
    
  // Un clignotement de la led
  digitalWrite(LED_BUILTIN, HIGH);
  delay(tempsAllume);
  digitalWrite(LED_BUILTIN, LOW);
  delay(tempsEteint);
}

Ici il y a peu de commentaires, principalement parce que les noms de variables sont explicites. Au tout début vous pourrez avoir envie de mettre par exemple:

  digitalWrite(LED_BUILTIN, HIGH); // Allume la led

Mais dès qu'on sait lire le code Arduino, le commentaire n'apporte rien du tout, c'est un pléonasme. Et il vaut mieux ne rien mettre que de mettre des choses inutiles.

Lecture de la console

Nous avons vu dans la page Le type char comment recevoir des caractères venant de la console. Il faut donc tester si un caractère est présent et si c'est le cas, on va agir en fonction du caractère. On profitera pour ne faire l'affichage que si on a changé quelque chose. Cela va ressembler à:

void loop() {
  if (Serial.available()) {
    // Modification des delay()
    ...
    // Affichage des durées
    ...
  }
  // Un clignotement de la led
  ...
}

Commençons par remarquer que si pendant un clignotement, on envoie plusieurs touches, une seule sera prise en compte par cycle complet allumage/extinction. Pour prendre en compte toutes les modifications avant de faire le clignotement, il suffit de remplacer le if par un while. Ainsi tant qu'un caractère est présent on modifiera les temps. Si aucun caractère n'est arrivé, tout ce qu'il y a derrière le while est ignoré. On avait vu qu'il y a deux boucles qui se ressemblent, le while (...) {...}; et le do {...} while (...); . Dans le deuxième type de boucle, on fait au moins une fois le traitement. Ici ce sera un peu gênant car si aucun caractère n'est arrivé, on fera systématiquement un affichage. Mais comme on ne trouvera pas ni le 7, ni le 1 ni le 9, ni le 3, et il n'y aura pas de changements.

Pour les modifications des delay, avec ce que nous avons déjà appris, nous pouvons écrire:

caractereArrive = Serial.read();
if (caractereArrive == '7') 
  ... // Augmenter tempsAllume de 10%
else if (caractereArrive == '9') 
  ... // Augmenter tempsEteint de 10%
else if (caractereArrive == '1') 
  ... // Diminuer tempsAllume de 10%
else if (caractereArrive == '3') 
  ... // Diminuer tempsEteint de 10%

Notez tout d'abord que l'on est obligé d'utiliser une variable supplémentaire car si on faisait:

if (Serial.read() == '7') 
  ... // Augmenter tempsAllume de 10%
else if (Serial.read() == '9') 
  ... // Augmenter tempsEteint de 10%
else if (Serial.read() == '1') 
  ... // Diminuer tempsAllume de 10%
else if (Serial.read() == '3') 
  ... // Diminuer tempsEteint de 10%

en envoyant les caractères un par un, la première ligne lirait le caractère et tous les tests suivants retourneraient faux et les clauses if ne seraient jamais exécutées. En effet le caractère serait lu par le premier Serial.read et les suivants ne liraient plus rien.

En envoyant plusieurs caractères en une fois, il y aurait des modifications si le premier caractère est un 7, le deuxième un 9...

Il y a en C une structure switch...case qui permet de simplifier un peu l'écriture de ce que l'on a au dessus, et surtout qui facilite la lecture. La forme complète usuelle est, pour notre application:

switch (Serial.read()) {
  case '7': ... // Augmenter tempsAllume de 10%
    break;
  case '9': ... // Augmenter tempsEteint de 10%
    break;
  case '1': ... // Diminuer tempsAllume de 10%
    break;
  case '3': ... // Diminuer tempsEteint de 10%
    break;
  default: ... // Si c'est une autre touche
    break;
}

La clause default

Elle est facultative. Elle va être exécutée si aucune clause précédente n'est exécutée. Dans notre cas, si on veut afficher un message d'erreur plutôt que de ne rien faire en cas de caractère reçu différent de 7, 9, 1 ou 3, on peut mettre la clause default. Mais ici on ne va pas la mettre.

Les break

Quant on tombe sur l'instruction break, on sort du bloc, en l’occurrence ici, on sort du switch (par exemple case '7' est terminé). En général, on terminera toutes les clauses case et default par break;. Si on ne met pas ce break; on exécute aussi la clause suivante. Sans aucun break;, si on lit un 7, on exécuterait les 4 clauses et on diminuerait ce que l'on a augmenté. Un appui sur 7 ne ferait pas de changements. Personnellement, je ne mets jamais de break; en dernière position, vu qu'il ne sert à rien.

Certaines personnes mettent systématiquement des accolades derrière les différents tests:

switch (Serial.read()) {
  case '7': {
    ... // Augmenter tempsAllume de 10%
    break;
  }
  case '9': {
    ... // Augmenter tempsEteint de 10%
    break;
  }
  ...

ou:

switch (Serial.read()) {
  case '7': {
    ... // Augmenter tempsAllume de 10%
  }
  break;
  case '9': {
    ... // Augmenter tempsEteint de 10%
  }
  break;
  ...

J'ai tendance à mettre des accolades quand cela est nécessaire seulement.

Il peut arriver que l'on ne mette pas les break; par exemple si je voulais que 7 ou 8 augmentent tempsAllume de 10% j'écrirais:

switch (Serial.read())
{
  case '7':
  case '8':  ... // Augmenter tempsAllume de 10%
    break;
  case '9': ... // Augmenter tempsEteint de 10%
    break;

Vous avez maintenant tout ce qu'il faut pour mener à bien le programme. Aide?void setup() { Serial.begin(115200); // Pour indiquer les temps pinMode(LED_BUILTIN, OUTPUT); } float tempsAllume = 500.0; float tempsEteint = 500.0; void loop() { // Affichage des durées Serial.print(F("La led est allumée pendant ")); Serial.print(tempsAllume / 1000); Serial.print(F("s et éteinte pendant ")); Serial.print(tempsEteint / 1000.0); Serial.println(F("s")); // Un clignotement de la led digitalWrite(LED_BUILTIN, HIGH); delay(tempsAllume); digitalWrite(LED_BUILTIN, LOW); delay(tempsEteint); // Changements éventuels des durées if (Serial.available()) { switch (Serial.read()) { case '7': tempsAllume *= 1.1; // Augmenter tempsAllume de 10% break; case '9': tempsEteint *= 1.1; // Augmenter tempsEteint de 10% break; case '1': tempsAllume *= 0.9; // Diminuer tempsAllume de 10% break; case '3': tempsEteint *= 0.9; // Diminuer tempsEteint de 10% } } } Notez quand même que les ordres ne sont pris en compte que juste avant que la led ne s'allume. C'est à cause des delay qui sont bloquants (on ne peut rien faire pendant delay). Si on voulait prendre les envois immédiatement, il faudra utiliser millis comme on l'a fait dans la page correspondante.

.