[Exercice] Une animation “YouTube”

Dans ce petit exercice, je vous propose de faire une animation que vous avez tous vu au moins une fois dans votre vie : le .gif de chargement YouTube ! Pour ceux qui se posent des questions, nous n’allons pas faire de Photoshop ou quoi que ce soit de ce genre. Non, nous (vous en fait ;) ) allons le faire … avec des LED ! Alors place à l’exercice !

Énoncé

Pour clôturer votre apprentissage avec les voies analogiques, nous allons faire un petit exercice pour se détendre. Le but de ce dernier est de réaliser une des animations les plus célèbres de l’internet : le .gif de chargement YouTube (qui est aussi utilisé sur d’autres plateformes et applications).

Nous allons le réaliser avec des LED et faire varier la vitesse de défilement grâce à un potentiomètre. Pour une fois, plutôt qu’une longue explication je vais juste vous donner une liste de composants utiles et une vidéo qui parle d’elle même !

Bon courage !

  • 6 LED + leurs résistances de limitation de courant
  • Un potentiomètre
  • Une Arduino, une breadboard et des fils !

Solution

Le schéma

Voici tout d’abord le schéma, car une bonne base électronique permettra de faire un beau code ensuite. Pour tout les lecteurs qui ne pensent qu’aux circuits et ne regardent jamais la version "photo" du montage, je vous invite pour une fois à y faire attention, surtout pour l’aspect géométrique du placement des LED. En passant, dans l’optique de faire varier la luminosité des LED, il faudra les connecter sur les broches PWM (notées avec un '~’).

Le potentiomètre quant à lui sera bien entendu connecté à une entrée analogique (la 0 dans mon cas). Comme toujours, les LED auront leur anode reliées au +5V et seront pilotées par état bas (important de le rappeler pour le code ensuite).

Animation Youtube - Schéma
Animation Youtube - Schéma
Animation Youtube - Montage
Animation Youtube - Montage

Voici le schéma sans code si vous souhaitez faire ce dernier vous-même :

!(https://www.tinkercad.com/embed/9b8W0uB0skv)

Le code

Alors petit défi avant de regarder la solution… En combien de ligne avez vous réussi à écrire votre code (proprement, sans tout mettre sur une seule ligne, pas de triche !) ? Personnellement je l’ai fait en 23 lignes, en faisant des beaux espaces propres. ;) Bon allez, trêve de plaisanterie, voici la solution, comme à l’accoutumé dans des balises secrètes…

Les variables globales

Comme vous devez vous en douter, nous allons commencer par déclarer les différentes broches que nous allons utiliser. Il nous en faut six pour les LED et une pour le potentiomètre de réglage de la vitesse d’animation. Pour des fins de simplicité dans le code, j’ai mis les six sorties dans un tableau. Pour d’autres fins de facilité, j’ai aussi mis les "niveaux" de luminosité dans un tableau de char que j’appellerai "pwm". Dans la balise suivante vous trouverez l’ensemble de ces données :

 // sortie LEDs
const int LED[6] = {3,5,6,9,10,11};
// niveaux de luminosité utilisé
const char pwm[6] = {255,210,160,200,220,240};
// potentiometre sur la broche 0
const int potar = 0;
Le setup

Personne ne devrais se tromper dans cette fonction, on est dans le domaine du connu, vu et revu ! Il nous suffit juste de mettre en entrée le potentiomètre sur son convertisseur analogique et en sortie mettre les LED (une simple boucle for suffit grace au tableau ;) ).

void setup()
{
    // le potentiomètre en entrée
    pinMode(potar, INPUT);
    // les LEDs en sorties
    for(int i=0; i<6; i++)
    pinMode(LED[i], OUTPUT);
}
La loop

Passons au cœur du programme, la boucle loop() ! Je vais vous la divulguer dès maintenant puis l’expliquer ensuite :

void loop()
{
    // étape de l'animation
    for(int i=0; i<6; i++)
    {
        // mise à jour des LEDs
        for(int n=0; n<6; n++)
        {
            analogWrite(LED[n], pwm[(n+i)%6]);
        }
        // récupère le temps
        int temps = analogRead(potar);
        // tmax = 190ms, tmin = 20ms
        delay(temps/6 + 20);
    }
}

Comme vous pouvez le constater, cette fonction se contente de faire deux boucle. L’une sert à mettre à jour les "phases de mouvements" et l’autre met à jour les PWM sur chacune des LED.

Les étapes de l’animation

Comme expliqué précédemment, la première boucle concerne les différentes phases de l’animation. Comme nous avons six LED nous avons six niveaux de luminosité et donc six étapes à appliquer (chaque LED prenant successivement chaque niveau). Nous verrons la seconde boucle après.

Avant de passer à la phase d’animation suivante, nous faisons une petite pause. La durée de cette pause détermine la vitesse de l’animation. Comme demandé dans le cahier des charges, cette durée sera réglable à l’aide d’un potentiomètre. La ligne 9 nous permet donc de récupérer la valeur lue sur l’entrée analogique. Pour rappel, elle variera de 0 à 1023.

Si l’on applique cette valeur directement au délai, nous aurions une animation pouvant aller de très très très rapide (potar au minimum) à très très très lent (delay de 1023 ms) lorsque le potar est dans l’autre sens.

Afin d’obtenir un réglage plus sympa, on fait une petite opération sur cette valeur. Pour ma part j’ai décidé de la diviser par 6, ce qui donne 0mstemps170ms0 ms \leq temps \leq 170 ms . Estimant que 0 ne permet pas de faire une animation (puisqu’on passerait directement à l’étape suivante sans attendre), j’ajoute 20 à ce résultat. Le temps final sera donc compris dans l’intervalle : 20mstemps190ms20 ms \leq temps \leq 190 ms .

Mise à jour des LED

La deuxième boucle possède une seule ligne qui est la clé de toute l’animation !

Cette boucle sert à mettre à jour les LED pour qu’elles aient toute la bonne luminosité. Pour cela, on utilisera la fonction analogWrite() (car après tout c’est le but du chapitre !). Le premier paramètre sera le numéro de la LED (grâce une fois de plus au tableau) et le second sera la valeur du PWM. C’est pour cette valeur que toute l’astuce survient.

En effet, j’utilise une opération mathématique un peu particulière que l’on appelle modulo. Pour ceux qui ne se rappelle pas de ce dernier, nous l’avons vu il y a très longtemps dans la première partie, deuxième chapitres sur [les variables](https:// zestedesavoir.com/tutoriels/537/arduino-premiers-pas-en-informatique-embarquee/742/decouverte-de-larduino/3418/le-langage-arduino-12/#2-les-variables). Cet opérateur permet de donner le résultat de la division euclidienne (mais je vous laisse aller voir le cours pour plus de détail). Pour obtenir la bonne valeur de luminosité il me faut lire la bonne case du tableau pwm[].

Ayant six niveaux de luminosité, j’ai six case dans mon tableau. Mais comment obtenir le bonne ? Eh bien simplement en additionnant le numéro de la LED en train d’être mise à jour (donné par la seconde boucle) et le numéro de l’étape de l’animation en cours (donné par la première boucle). Seulement imaginons que nous mettions à jour la sixième LED (indice 5) pour la quatrième étape (indice 3). Ça nous donne 8. Hors 8 est plus grand que 6 (L’index 5 est la sixième led). En utilisant le modulo, nous prenons le reste de la division de 8/6 soit 2. Il nous faudra donc utiliser la case numéro 2 du tableau pwm[] pour cette utilisation. Tout simplement :p

Je suis conscient que cette écriture n’est pas simple. Il est tout a fait normal de ne pas l’avoir trouvé et demande une certaine habitude de la programmation et ses astuces pour y penser.

Pour ceux qui se demande encore quel est l’intérêt d’utiliser des tableaux de données, voici deux éléments de réponse.

  • Admettons j’utilise une Arduino Mega qui possède 15 pwm, j’aurais pu allumer 15 LEDs dans mon animation. Mais si j’avais fait mon setup de manière linéaire, il m’aurait fallu rajouter 9 lignes. Grâce au tableau, j’ai juste besoin de les ajouter à ce dernier et de modifier l’indice de fin pour l’initialisation dans la boucle for.
  • La même remarque s’applique à l’animation. En modifiant simplement les tableaux je peux changer rapidement l’animation, ses niveaux de luminosité, le nombre de LEDs, l’ordre d’éclairage etc…
Le programme complet

Et pour tout ceux qui doute du fonctionnement du programme, voici dès maintenant le code complet de la machine ! (Attention lorsque vous faites vos branchement à mettre les LED dans le bon ordre, sous peine d’avoir une séquence anarchique).

// sortie LEDs
const int LED[6] = {3,5,6,9,10,11};
// niveaux de luminosité utilisé
const char pwm[6] = {255,210,160,200,220,240};
// potentiometre sur la broche 0
const int potar = 0;

void setup()
{
    pinMode(potar, INPUT);
    for(int i=0; i<6; i++)
        pinMode(LED[i], OUTPUT);
}

void loop()
{
    // étape de l'animation
    for(int i=0; i<6; i++)
    {
        // mise à jour des LEDs
        for(int n=0; n<6; n++)
        {
            analogWrite(LED[n], pwm[(n+i)%6]);
        }
        // récupère le temps
        int temps = analogRead(potar);
        // tmax = 190ms, tmin = 20ms
        delay(temps/6 + 20);
    }
}

Et voici la solution sur simulateur :

!(https://www.tinkercad.com/embed/6cSEQY3m7mQ)


La mise en bouche des applications possibles avec les entrées/sortie PWM est maintenant terminée. Je vous laisse réfléchir à ce que vous pourriez faire avec. Tenez, d’ailleurs les chapitres de la partie suivante utilisent ces entrées/sorties et ce n’est pas par hasard… ;)