• 15 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 !

Utilisez les fonctions et les nombres aléatoires

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

Vous êtes maintenant en mesure de réaliser un projet plus complexe. Nous sommes tous un peu joueurs dans l'âme, et depuis tout petit, nous connaissons le dé, nous avons même appris à compter en partie grâce à lui ! Je vous propose dans ce chapitre de lui rendre hommage et de l'électroniser : nous allons donc réaliser un dé avec 5 LED.

Vous allez pouvoir, grâce à ce projet, convoquer l'ensemble de vos connaissances, ce qui va être un très bon exercice.

Vous y apprendrez également à utiliser :

  • Les nombres aléatoires, qui permettent justement de réaliser des projets comme un dé, mais qui peuvent servir dans bien d'autres cas.

  • Les fonctions, qui deviennent vite incontournables quand le code se complexifie.

Les schémas et montages vont commencer à devenir plus complexes et vous aurez sûrement à passer un peu de temps à vérifier vos montages si tout ne se passe pas comme prévu...

À l’issue de ce chapitre, vous aurez créé un dé fonctionnant de la façon suivante  :

Lors que vous appuyez sur un bouton poussoir, les LED s'allument s’allument à des intervalles de plus en plus lents en affichant un nombre aléatoire (entre 0 et 5)  pour s'arrêter sur un nombre final entre 0 et 5. Les LEDs seront disposées comme le 5 d'un dé.

Avant d'arriver au programme final de ce dé, nous passerons par des programmes intermédiaires qui nous permettrons de tester étape par étape notre montage.

Finalement, on peut dire que nous allons passer un cap, que dis-je un cap, une péninsule ! 

 

Alea jacta est

("Les dés sont jetés", Jules César)

Nous avons vu précédemment qu'un ordinateur est très bon en calcul mais qu'il ne peut pas réfléchir par lui-même. Et bien il a un autre défaut, il est complètement prévisible. Les explications qui suivent risquent de vous dégoûter de jouer contre un ordinateur à un jeu de hasard (oui, oui, même dans les casinos...) car finalement, de hasard il n'y a pas !

Voyons tout d'abord comment créer un petit programme qui affiche sur la console un nombre aléatoire (non Lukas, il ne s'agit pas d'aller à Thoires...). Pour cela, on utilise le mot clé  random  du langage Arduino :

random(max); // retourne un entier entre 0 et et la valeur max précisée (non comprise)

On peut écrire aussi :

random(min,max); //retourne un entier entre les valeurs min et max précisées (max non compris)

Quand je dis  max (non compris) c'est que l'Arduino ne renverra jamais le nombre  max. Par exemple, si vous écrivez :

random(5,20);

L'Arduino vous renverra un nombre parmi : 5,6,7,8,9,10,11,12,13,14,15,16,17,18 et 19.

Voici donc un petit programme à tester avec votre Arduino :

int nbAlea;
void setup() {
  Serial.begin(9600);
}

void loop() {
  nbAlea=random(100); //construit le nombre aléatoire
  Serial.println(nbAlea);//affichage du nombre
  delay(500);//attente
}

Téléversez-le et ouvrez la console. Si tout se passe bien vous voyez des nombres qui s'affichent les uns en dessous des autres, de façon aléatoire, entre 0 et 99 (puisque 100 n'est pas compris dedans).

Très bien, appuyez maintenant sur le bouton 'reset' situé sur votre Arduino. Observez la série de nombres...

Appuyez encore sur le reset, observez, appuyez, observez...

Ben c'est tout le temps la même série de nombres, c'est pas aléatoire ça !!!

En effet, comme je vous l'ai indiqué, un ordinateur c'est prévisible. Créer un nombre aléatoire avec c'est impossible. C'est la raison pour laquelle on parle de nombres pseudo-aléatoires. Heureusement les ingénieurs ingénieux ont pensé détourner le problème pour tenter de produire une série de nombres différents et presque imprévisibles. Il suffit de glisser dans votre programme (souvent dans le  setup ) le code suivant :

randomSeed(unNombre);

La variable unNombre étant un int pris au hasard.

Mais comment prendre un  int au hasard s'il n'y a pas de hasard ?

Bien vu ! Il faut trouver un moyen pour que cette graine (seed en anglais) soit un nombre qu'on ne peut pas deviner ou connaître. Nous n'avons pas encore vu les entrées analogiques, mais sur l'Arduino, c'est la seule méthode pour être à peu près tranquille sur le choix de la variable  unNombre . On va donc assigner à la variable  unNombre une entrée analogique en appelant la méthode  analogRead comme ceci :

randomSeed(analogRead(0));

Et pour que ça marche, il faut que le pin A0 de votre Arduino ne soit pas connecté.

Voici donc le programme de test de nombres aléatoires modifié :

int nbAlea;
void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));//initialise la séquence aléatoire
}

void loop() {
  nbAlea=random(100); //construit le nombre aléatoire
  Serial.println(nbAlea);//affichage du nombre
  delay(500);//attente
}

Si vous appuyez sur 'reset', vous obtenez des séquences différentes. C'est déjà bien mieux !

Notre projet concerne un dé à 5 LEDs, il nous faudra donc un nombre aléatoire entre 0 et 5. Je vous laisse modifier le code pour qu'il corresponde à nos attentes. Testez-le ensuite vérifier qu'on obtient bien tous ces nombres.

Bien, nous savons maintenant produire une série de nombres aléatoires entre 0 et 5, passons donc au montage ! (Lukas, vous pouvez cesser d'appuyer sur le reset...)

Préparer le montage

Pour que notre dé ressemble à un dé, et bien il faut lui en donner l'apparence. Voici les constellations (c'est le terme technique) pour un dé commun à six faces :

Les constellations d'un dé à 6 faces
Les constellations d'un dé à 6 faces

Notre projet concerne un dé à 5 LEDs donc nous allons essayer de positionner les LEDs pour qu'elles ressemblent à la constellation du 5 sur un dé normal. En utilisant cette forme, on peut afficher chaque nombre entre 1 et 5.

Alors ça veut dire qu'on n'aura jamais un 6 ?

En effet, le 6 n'existe pas dans notre projet, car il nous aurait fallu ajouter deux LED pour obtenir la bonne constellation, ça nous aurait fait trop de travail juste pour un nombre.  Mais rassurez-vous, nous aurons encore 6 positions : 0,1,2,3,4, et 5. Finalement, le 0 remplace le 6...

Alors essayons de placer les diodes dans la position de la constellation du 5. Les diodes seront pilotées par l'Arduino avec les pins 2,3,4,5,6.

Pour rappel, chaque diode doit être connectée au pin de l’Arduino avec sa patte longue, et au ground avec sa patte plus courte, mais elles doivent aussi être en série avec une résistance (prenons 220Ω). Voici le schéma pour une diode :

Schéma de montage pour une diode
Schéma de montage pour une diode

Je vous propose de vous lancer un peu tout seul comme d'habitude. Rappelez-vous que l'objectif pour vous est de devenir autonome...

Voici le schéma avec fritzing :

Schéma de montage des LEDs en forme de dé
Schéma de montage des LED en forme de dé

Prenez votre temps pour observer et comprendre. J'ai choisi des fils oranges pour les connexions vers l'Arduino, rouge pour le +5V (non utilisé ici) et bleu pour la connexion vers le ground. Chaque patte + des diodes est bien reliée à un pin de l'Arduino. Chaque patte - est reliée vers le ground en passant par une résistance de 220Ω.

Ça fait pas mal de fils, mais ça on ne peut pas l'éviter... Voyons maintenant comment programmer l'allumage des LED.

Un programme pour tester nos LEDs

Commençons par le nombre 5. Pourquoi ? Tout simplement car il va nous permettre de vérifier que toutes les diodes s'allument.  

Je vous laisse essayer. Il faut que votre programme contienne :

  • une variable pour chaque pin de LED ;

  • une initialisation de tous les pins de LED en mode OUTPUT (envoi de courant) ;

  • un positionnement de chaque pin de LED en HIGH (valeur haute).

Voici une proposition de correction simple et lisible :

//définition des variables de pin pour chaque LED
int ledHautGauche=2;
int ledBasGauche=3;
int ledCentre=4;
int ledHautDroite=6;
int ledBasDroite=5;

void setup() {
    
  //positionnement des pin en OUTPUT
  pinMode(ledHautGauche,OUTPUT);
  pinMode(ledBasGauche,OUTPUT);
  pinMode(ledCentre,OUTPUT);
  pinMode(ledHautDroite,OUTPUT);
  pinMode(ledBasDroite,OUTPUT);
  
  //allumage de tous les pins
  digitalWrite(ledHautGauche,HIGH);
  digitalWrite(ledBasGauche,HIGH);
  digitalWrite(ledCentre,HIGH);
  digitalWrite(ledHautDroite,HIGH);
  digitalWrite(ledBasDroite,HIGH);
}

void loop() {
  //inutile pour ce test
}

Vous remarquerez que j'ai nommé les LED en fonction de leur position.  Rien ne vous oblige à faire pareil, c'est juste beaucoup plus facile ensuite...

Voici maintenant un code plus compact qui fait la même chose. Je vous laisse le soin de le comprendre :

void setup() {
  for (int l=2;l<7;l++){// boucle de 2 à 6
    pinMode(l,OUTPUT);// mode OUTPUT
    digitalWrite(l,HIGH);// allumage
  }
}
void loop() {
  //inutile pour ce test
}

Dans les deux cas, vous devez avoir les 5 LED allumées. Si ce n'est pas le cas, vérifiez votre programme, vos connexions, le sens des LED, les résistances...

Nous pouvons facilement maintenant construire un programme qui teste le 0. En effet, il suffit de tout mettre en position LOW.

Voici un tableau qui montre l'état des Pins (HIGH ou LOW) en fonction du nombre :

Nombre

ledHautGauche

ledBasGauche

ledCentre

ledHautDroite

ledBasDroite

0

LOW

LOW

LOW

LOW

LOW

1

LOW

LOW

HIGH

LOW

LOW

2

HIGH

LOW

LOW

LOW

HIGH

3

LOW

HIGH

HIGH

HIGH

LOW

4

HIGH

HIGH

LOW

HIGH

HIGH

5

HIGH

HIGH

HIGH

HIGH

HIGH

Ce tableau d'état permet de bien repérer ce que nous voulons réaliser.

Maintenant, faisons compter notre Arduino avec ce dé ! Je m’explique : nous allons programmer les LED connectées à l’Arduino pour qu’elles affichent la constellation du dé correspondant à 0, puis à 1, puis à 2... jusqu’à 5, puis à nouveau 0, 1, etc. (on prendra une seconde d’intervalle entre l’affichage de chaque nombre par exemple).

On imagine assez bien une boucle du genre :

for (int l=0;l<6;l++){
    if (l==0){
        //affichage des diodes en forme de 0
    }
    if (l==1){
        //affichage des diodes en forme de 1
    }
    if (l==2){
        //affichage des diodes en forme de 2
    }
    if (l==3){
        //affichage des diodes en forme de 3
    }
    if (l==4){
        //affichage des diodes en forme de 4
    }
    if (l==5){
        //affichage des diodes en forme de 5
    }
}
// à répéter à l'infini

Avec pour chaque forme un affichage précis des diodes en fonction du tableau précédent.

Je vous laisse construire ce programme et le tester (la boucle  for est à placer dans la  loop()). Je vais en profiter pour prendre l'air...

...

Alors ? Votre essai a été concluant ? Super !

Sinon, pas de panique, vérifiez la correction

Et là, je suis sûr que vous vous dites : quand même, c’était un peu répétitif mon code, c’est toujours aussi fastidieux d’écrire des programmes ? N’ayez crainte, je m’en vais de ce pas vous initier à un concept qui va vous permettre de rendre votre code plus simple, lisible et agréable : les fonctions !

Les fonctions

La boucle que nous venons de voir risque d'être appelée plusieurs fois dans le programme. En effet, je vous rappelle que le but est d'appuyer sur un bouton et que les nombres affichés défilent de plus en plus lentement jusqu'à l'arrêt sur un nombre final. Donc à chaque affichage, la boucle va être utilisée pour gérer l'affichage des diodes.

Si l'on écrit tout dans laloop (test du bouton, tirage du nombre, affichage des diodes...), nous allons avoir un programme surchargé, et pas très lisible. C'est là que les fonctions vont nous servir.

Le principe est simple : 

Le programme se déroule, puis à un moment on va lui demander d’exécuter une fonction, et lorsqu’il aura fini de réaliser cette fonction, il reviendra dans le programme principal, là où il s’était arrêté.

Voici un schéma qui explique un appel à une seule fonction :

Appel à la fonction1 depuis le programme principal
Appel à la fonction1 depuis le programme principal

Il y a donc une interruption du programme principal pour passer dans la fonction, puis retour.

Pour programmer une fonction il faut écrire deux choses :

  • La définition (ou déclaration) de la fonction, c'est-à-dire le code à exécuter par la fonction. Le programme de la fonction en fait.

  • L'appel à la fonction dans le programme principal.

Déclaration de la fonction

La déclaration est de la même forme que le  setup ou la  loop (qui sont en fait des fonctions !) mais on est libre d'indiquer plusieurs choses :

type nomDeLaFonction(){
//code du programme de la fonction
}
  • type : c'est le type de variable que la fonction peut renvoyer. Pour le moment on indiquera “void” qui veut dire “vide”, c’est-à-dire que la fonction comprend l’exécution d’un certain nombre d’instructions mais ne renvoie aucune valeur de sortie.

  • nomDeLaFonction : c'est le nom que l'on choisit pour la fonction. Dans l'exemple d'avant j'ai choisi "fonction1" et "fonction2", mais j'aurais pu mettre "patate" ou "affichageDuNombre".

  • () :  ces deux parenthèse sont obligatoires. Nous allons voir plus loin à quoi elles servent. Si vous les oubliez, l'IDE vous retournera une erreur.

  • {} : le code du programme de la fonction doit être mis entre accolades. Vous noterez qu'il n'y a pas de point-virgule après l'accolade de fermeture.

Je place en général mes fonctions après la fonction  loop(), et je vous conseille de faire de même. Mais elles peuvent être positionnées partout ailleurs dans le programme tant qu'elles sont déclarées de façon globale, c'est-à-dire en dehors de la  loop, du  setup ou de toute autre fonction.

Appel de la fonction

Pour appeler une fonction ensuite, il suffit de mettre ce code au bon endroit dans votre programme :

nomDeLaFonction();

Lors du déroulement du programme, lorsque l’Arduino lira cet appel, il ira directement retrouver le contenu de la fonction ! Vous verrez vite que c'est bien pratique.

Voici un programme qui affiche dans la console le déroulement du code :

void setup() {
  Serial.begin(9600);
}
//boucle de programme principal
void loop() {
  Serial.println("Je suis dans le programme principal, en début de boucle");
  delay(1000);
  fonction1();
  fonction2();
}

//code de la fonction1
void fonction1(){
  Serial.println("Je suis dans la fonction1");
  delay(1000);
}

//code de la fonction2
void fonction2(){
  Serial.println("Je suis dans la fonction2");
  delay(1000);
}

Testez-le (n'oubliez pas d'activer la console ;) ).

Ce programme montre bien le passage dans chaque fonction.

 

Passer une information à la fonction

Il faut savoir qu'une fonction peut recevoir une ou plusieurs informations. C'est-à-dire une ou plusieurs variables ou constantes qu'on appelle des paramètres. Il faut pour cela modifier la déclaration de la fonction et son appel.

Déclaration :

type nomDeLaFonction(type unParametre){
    //code de la fonction qui peut utiliser la variable 'unParametre'
}

Appel :

nomDeLaFonction(parametre);

Il est possible de passer  plusieurs paramètres à une fonction, il suffit simplement de les déclarer à la suite les uns des autres en les séparant d'une virgule :

Déclaration :

type nomDeLaFonction(type param1,type param2,type param3...){
    code de la fonction
}

Appel :

nomDeLaFonction(param,autreParam, encoreAutreParam,...);

 

Voici un exemple de programme utilisant des fonctions à plusieurs paramètres :

void setup(){
    Serial.begin(9600);
}
void loop(){
    //appel de fonction avec variables pré-déclarées
    int a=5;
    int b=3;
    int s=somme(a,b);
    Serial.println(s);
    //appel de fonction en paramètres directs
    Serial.println(produit(5,2));
}
//déclaration des fonctions
int somme(int nb1,int nb2){
    //utilisation de variables dans la fonction
    int res=nb1+nb2;
    return res;
}
int produit(int nb1,int nb2){
    //retour direct d'un résultat sans variable intermédiaire
    return nb1*nb2;
}

Je vous laisse observer les différentes façons d'envoyer les paramètres et d'utiliser les variables ou les valeurs de retour.

Utilisez des fonctions pour le dé à 5 LED

Nous venons de voir comment déclarer et utiliser une fonction. Voyons comment l'inclure dans notre programme de comptage. L'idée est la suivante :

  • La boucle principale fait défiler un nombre de 0 à 5 (grâce à une boucle for).

  • Une fois le nombre défini, on fait appel à la fonction qui va allumer les LED représentant la bonne constellation sur le dé.

  • Une fois l'affichage réalisé, on revient à notre programme principal.

Voici en pseudo-code ce que ça peut donner :

//définition des variables
 
//fonctions principales setup et loop
void setup(){
//on place les pins en mode OUTPUT
}
void loop(){
for (t=0;t<6;t++){
//on teste le nombre t et on envoie le programme à la bonne fonction
}
}
 
//définition des fonctions
void afficheZero(){
//code pour tout éteindre
}
void afficheUn(){
//code pour le 1
}
void afficheDeux(){
//code pour le 2
}
void afficheTrois(){
//code pour le 3
}
void afficheQuatre(){
//code pour le 4
}
void afficheCinq(){
//code pour le 5
}

Je vous laisse essayer de construire ce programme. C'est important de passer par cette phase d'essai/erreur pour pouvoir se débrouiller seul par la suite.

Allumez vos diodes pour chaque face de dé

Afin de mieux appréhender les fonctions et leurs possibilités je vais vous fournir le code que je propose pour l'allumage des diodes en fonction d'un nombre, afin de vous l'expliquer pas à pas.

//déclaration des variables
int ledHautGauche=2;
int ledBasGauche=3;
int ledCentre=4;
int ledHautDroite=6;
int ledBasDroite=5;

//fonction d'initialisation
void setup() {
  //mise en mode OUTPUT des pins 2 à 6 et positionnement en LOW
  for (int t=2;t<7;t++){
    pinMode(t,OUTPUT);
    digitalWrite(t,LOW);
  }
}

//boucle principale
void loop() {
  // boucle pour faire varier le nombre
  for (int nb=0;nb<6;nb++){
    affichage(nb);//appel de la fonction d'allumage des LEDs
    delay(500);
  }
}

// déclaration des fonctions

//cette fonction sert à éteindre toutes les diodes
void setZero(){
  for (int t=2;t<7;t++){
    digitalWrite(t,LOW);
  }
}

//cette fonction récupère un nombre et allume les LED en conséquence
void affichage(int nombre){
  setZero();//appel de la fonction qui éteint toutes les LED
    
  //il suffit maintenant d'allumer les bonnes diodes
  //en testant la valeur de 'nombre'
  if (nombre==1){
    digitalWrite(ledCentre,HIGH); //on allume la diode du centre
    return;//sortie de la fonction
  }
  if (nombre==2){
    //on allume les diodes haut/droite et bas/gauche
    digitalWrite(ledHautDroite,HIGH);
    digitalWrite(ledBasGauche,HIGH);
    return;//sortie de la fonction
  }
  if (nombre==3){
    //on allume les diodes centre, haut/gauche, bas/droite
    digitalWrite(ledHautDroite,HIGH);
    digitalWrite(ledCentre,HIGH);
    digitalWrite(ledBasGauche,HIGH);
    return;//sortie de la fonction
  }
  if (nombre==4){
    //on allume toutes les diodes sauf celle du centre
    digitalWrite(ledHautGauche,HIGH);
    digitalWrite(ledHautDroite,HIGH);
    digitalWrite(ledBasGauche,HIGH);
    digitalWrite(ledBasDroite,HIGH);
    return;//sortie de la fonction
  }
  if (nombre==5){
    //on allume toutes les diodes
    digitalWrite(ledHautGauche,HIGH);
    digitalWrite(ledBasGauche,HIGH);
    digitalWrite(ledHautDroite,HIGH);
    digitalWrite(ledBasDroite,HIGH);
    digitalWrite(ledCentre,HIGH);
    return;//sortie de la fonction
  }
  //inutile de tester le 0 car on a commencé par tout éteindre
  return;//sortie de la fonction
}

Dans la fonction  affichage qui prend un nombre en paramètre, vous voyez qu'on teste de quel nombre il s'agit et on allume les LEDs en fonction de la constellation à obtenir. Vous remarquerez le mot-clé return  qui permet de quitter une fonction pour revenir au programme précédent. Le tout dernier return  de cette fonction n'est pas obligatoire dans cette fonction. En effet en arrivant au bout, le programme de fonction sera quitté pour revenir au programme principal.

Le programme final pour le dé à 5 LEDs

Alors j'ai deux nouvelles pour vous :

une bonne ,

et une mauvaise  

Vous voulez commencer par la mauvaise ? Ça me va !

Je ne vais pas vous donner le montage et le programme final ici, c’est vous qui allez devoir les réaliser dans l’activité notée de cette partie. (Lukas, refermez la bouche, ce n'est pas si terrible !)

La bonne nouvelle est que je vais vous aider en vous donnant la structure générale du code en pseudo-code. (Mais si Lukas, c'est une bonne nouvelle !)

Voici donc votre guide de programmation :

//déclaration des variables
  //LEDs sur les pins 2 à 6
  //bouton sur le pin 7
  
//initialisation
void setup(){
    //appel de la fonction randomSeed() pour la séquence aléatoire
    //pin 2 à 6 en mode OUTPUT
    //pin 7 en mode INPUT_PULLUP
    //appel de la fonction setZero() pour tout éteindre
}

//boucle principale
void loop(){
    //test de l'état du bouton avec boolean etatBouton
    if (le bouton est cliqué){
            for...  //boucle for qui temporise l'affichage de plus en plus lentement
            {
                //exctinction brève des LEDs (100ms)
                //tirage d'un nombre aléatoire avec la fonction random()
                //appel de la fonction d'allumage des LEDs
                //temporisation
            }//sortie de la boucle de temporisation
    }//sortie du test bouton
}

//déclaration de la fonction setZero()
void setZero(){
    //code pour tout éteindre
}
//déclaration de la fonction d'allugmage
void allumage(int nombre){
   //code pour l'affichage en fonction du nombre 
}

Vous avez tout ce qu'il vous faut dans les chapitres précédents et dans celui-ci pour y arriver. Je vous conseille tout de même réellement d'essayer sans copier-coller des bouts de code que vous ne comprenez pas.

Rendez-vous dans l'activité de cette partie pour compléter ce programme de dé à 5 LED ! Vous la trouverez dans le sommaire du cours, intitulée "Complétez un dé numérique à 5 LED".

Vous avez terminé l’activité, ou bien vous voulez vous plonger dedans plus tard ? Alors rejoignez-moi dans le chapitre suivant pour découvrir tout le potentiel du potentiomètre !

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