La boucle for
Jusqu'à présent nous avions profité de loop() qui faisait une boucle (infinie) pour faire le comptage. Le comptage ne s'arrêtait pas vraiment, nous avions placé un délai suffisamment long. Cela fonctionnait parce qu'à la fin de la boucle, nous n'avions plus rien à faire. Ce ne sera évidemment pas toujours le cas, après une boucle, il y aura d'autres choses à faire. Il faut donc que nous sachions faire, par exemple 20 boucles, sans l'aide de loop(). L'instruction qui fait ceci ce peut être la boucle for.
On utilisera la boucle for quand le nombre de boucles est connu et fixe, par exemple "je veux faire 20 boucles". Si le nombre de boucles dépend d'une condition, on emploiera une autre boucle. C'est le cas de "je compte les moutons jusqu'à ce que je m'endorme".
Ce que nous allons faire aujourd'hui c'est écrire un code qui fait 20 boucles, et on va laisser de côté le délai d'une seconde dans un premier temps. Bien entendu si je mets ce code dans loop, Arduino va faire le code qui va afficher les nombres de 1 à 20, puis recommencer sans arrêt. C'est le principe de loop de répéter son code. Comme nous voulons ne faire ce code qu'une fois, nous allons le mettre dans le setup. Il n'y aura rien dans loop (le programme finira par boucler alors sans arrêt sans rien faire).
Syntaxe
La syntaxe est de la boucle for est la suivante:
for (<initialisation>; <"tant que">; <incrément>) <instruction>
On remarquera d'abord la place des points-virgules, c'est souvent la cause d'erreurs. Dans les parenthèses il y en a 2 qui séparent les trois morceaux. En mettre un à la fin du troisième morceau n'est pas très grave, cela fait une erreur de compilation qui est facile à corriger. Les points-virgules sont des séparateurs, et en mettre un à la fin rajoute un morceau vide. Comme il doit y avoir que 3 morceaux, cela fait une erreur.
Mais si jamais vous mettez un point-virgule juste après la parenthèse fermante, c'est la catastrophe, car pour le compilateur il y aurait entre la parenthèse et le point-virgule une instruction vide. Du coup il ferait bien nos 20 boucles, mais il ferait 20 fois RIEN (instruction vide) puis ferait une fois l'instruction qui suit, pour lui, c'est deux instructions indépendantes. Il n'y a pas d'erreur, c'est syntaxiquement correct, et le compilateur dit que tout va bien. Mais ce n'est pas du tout ce que nous voulons.
<initialisation>
C'est l'instruction d'initialisation.
Quand on fait un nombre fixe de boucles, il faut les compter, et donc il nous faut une variable qui contienne cette valeur. On avait pris cacheCache dans les pages précédentes. L'initialisation consiste en général (très très peu d'exceptions) à initialiser la variable qui compte l'indice de la boucle. Si on compte 1, 2, 3, 4, 5, 6,... il y a 20 boucles et l'indice va prendre les valeurs 1 puis 2... Si on comptait 2, 4, 6, 8, 10... l'indice de la boucle pourrait prendre les valeurs 2, 4, 6, 8... On pourrait aussi choisir de prendre un indice qui s'incrémente de 1 et afficher le double.
Pour nous, c'est cacheCache qui est notre indice de boucle et qui va débuter à 1. On écrira donc:
for (cacheCache = 1; ...
Bien entendu, il faut que cacheCache soit connu (il faut que la boucle for soit dans sa portée). On peut par exemple déclarer cacheCache globale, avant la ligne void setup(void) {... car nous allons mettre la boucle dans setup.
Mais on peut remarquer que nous avons besoin de cette variable uniquement dans la boucle for, car une fois sorti de la boucle, on ne fait plus grand chose (juste loop qui ne fait rien). Et donc on peut déclarer l'existence de notre variable dans le for. Cela fait alors:
for (byte cacheCache = 1; ...
Avec cette définition, la porté de cacheCache est uniquement l'instruction for et commence à sa définition, et va jusqu'à la fin de <instruction>.
J'ai mis byte car il me faut un type entier positif qui va au moins jusqu'à 20, et plus il est petit, moins j'utilise de ressources. Il me faut en entier non signé 8 bits.
Si vous déclarez cacheCache comme int, cela va fonctionner exactement pareil, mais la donnée va occuper deux cases au lieu d'une et le code va être un peu plus long. En effet, Arduino est équipé d'un microcontrôleur 8 bits et une incrémentation sur 16 bits doit se faire en deux fois. Les autres opérations aussi d'ailleurs. Aujourd'hui nous avons beaucoup trop de mémoire tant pour les données que pour le code, et on peut se permettre de gaspiller, mais un jour ou l'autre on arrive aux limites de la mémoire. Apprendre à bien écrire dès le début est un atout pour l'avenir. Si le code est plus long, cela va aussi ralentir l'exécution, et ce peut être pénalisant quand les programmes seront plus gros.
<"tant que">
Le deuxième morceau est une condition qui répond à la question Tant que quoi? Si je veux faire 20 boucles, Je vais trouver au choix :
cacheCache != 21 cacheCache <= 20 cacheCache < 21 21 > cacheCache ...
Tant que la condition est vraie, je vais faire l'instruction qui est derrière (pour nous ce sera afficher cacheCache) et dès que la condition devient fausse, je sors de la boucle et je poursuis le programme (dans notre cas ce sera la fin de setup()). Avec la première possibilité, pour 20 on fera la boucle car 20 est encore différent de 21, et pour 21 tout est fini. On sort alors de cette boucle sans rien afficher. Avec la deuxième possibilité, la boucle sera faite jusqu'à 20 inclus (condition vraie) et on sort pour 21. Les troisième et quatrième possibilités sont très semblables.
Si ces trois façons sont strictement identiques quand au résultat et sans doute à la taille du code, c'est la troisième forme qui est la plus usitée. On préfère les tests supérieur ou inférieur aux tests d'égalité ou d'inégalité au cas ou la variable s'incrémenterait de 2. Avec une boucle for, cela n'arrive en principe pas, mais c'est par habitude avec l'ensemble des boucles. La troisième forme est pratique pour faire N boucles en commençant par 0 (de 0 à N-1) car on écrit:
for (int n=0; n<N ...
On l'a vu une fois, cela fait N boucles... Maintenant les autres formes sont toutes aussi valables.
<incrément>
Pratiquement tout le temps le troisième morceau sert à changer l'indice de boucle initialisé dans le premier morceau. La plus part du temps c'est une incrémentation. Pour nous ce sera cacheCache++ qui est la forme le plus courante. Mais ce peut être variable-- si on décrit la boucle dans l'autre sens, variable+=2 si on veut aller de 2 en 2...
<instruction>
Après la parenthèse fermante, on y met une seule instruction. Nous verrons comment en mettre plusieurs dans le chapitre suivant, mais pour cette fois ci, une seule suffit: l'affichage de notre variable.
Exercice
Afficher les nombres de 1 à 20 avec une boucle for dans le setup().
Comme en sortant du for, on bouclera dans loop(), on n'a plus besoin de bloquer le fonctionnement par le delay(-1).
Solution?void setup() {
Serial.begin(115200);
for (byte cacheCache = 1; cacheCache < 21; cacheCache++) Serial.println(cacheCache);
}
void loop() {
}