Dans le chapitre précédent décrivant l'Arduino, je vous ai présenté rapidement ce que l'on pouvait faire avec une sortie digitale "tout-ou-rien". Ce type est équivalent au fonctionnement d'un interrupteur. Dans ce nouvel exemple, je vous présente un autre type de sortie: le PWM pour Pulse Width Modulation, ce qui signifie: "impulsion à largeur variable". On parlera aussi de signal rectangulaire.
Qu'est-ce que cela signifie? Habituellement, lorsqu'on parle de signal continu, on vous informe que la valeur est constante au cours du temps. Par exemple: à la sortie d'une pile électrique, vous mesurerez idéalement une valeur constante de 9V. Dans le cas d'un signal rectangulaire, la valeur mesurée en amplitude est aussi de 9V, mais pas en permanence soit à interval régulier. Par exemple, pendant un intervalle de référence, la valeur du signal est 9V pendant 1/10ème de cet intervalle. La largeur de l'impulsion peut donc varier de 0 à la totalité de l'intervalle de référence. Dans le cas de l'Arduino, celui-ci est capable d'envoyer un signal ou "valeur" sur une sortie. Cette valeur varie de 0 à 255 et correspond à une impulsion de longueur 0/255 = 0 à 255/255 = 1.
Maintenant, essayons de vulgariser cet effet et voir comment il agit. Considèrons un composant comme une led. Si on envoie un signal rectangulaire de largeur 1, alors la led éclairera en permanence. Par contre, si on lui envoie de petites impulsions de largeur 1/10, la led n'a pas le temps de s'allumer ou de "réagir", qu'elle s'éteint déjà à cause de la durée d'impulsion trop petite. Elle s'allume pendant 1/10ème de l'intervalle alors qu'elle est éteinte pendant les 9/10ème de l'intervalle. L'observateur aura l'impression que cette même led éclaire moins fortement, puisqu'elle est plus souvent éteinte qu'allumée... Bien évidement, il faut que cet intervalle de référence soit suffisamment court (ou qu'il soit éxecuté de nombreuses fois par seconde) pour que l'on ne perçoive pas un effet de clignotement rapide...
Voilà pour la "théorie" très simplement résumée. Si vous n'avez pas bien compris mes explications, ce n'est pas grave... ;-) Retenez seulement que: "faire varier la valeur de sortie sur un port PWM est équivalent à faire varier l'intensité lumineuse d'une led" même si ce n'est pas tout à fait ça... Il faut en fait interpréter cela comme une "information digitale" qui est envoyée au port de sortie, mais qui est rendue visible grâce à une led...
Passons maintenant à un premier exemple concret.
Le principe est celui du fader ou "variateur d'intensité lumineuse". Il est très simple. A l'aide d'une sortie PWM, nous allons alimenter une led en faisant varier progressivement la longueur de l'impulsion de 0 à 255 puis à 0 et ainsi de suite... Tout d'abord le montage:
Comme vous pouvez le voir, il y a peu de différence par rapport au montage précédent: la led est verte ;-) et la sortie utilisée est la "3". Mais identifiez bien cette sortie: c'est en fait "~3". Le "~" indique que la sortie est capable de générer un signal PWM. Si on branche la led sur la sortie 2, l'intensité ne variera pas... On prendra aussi un instant pour vérifier qu'il y a 6 sorties PWM sur un Arduino Uno: 3, 5, 6, 9, 10 et 11.
Le code se résume ainsi:
int i = 0;
void setup()
{
pinMode( led, OUTPUT );
}
void loop()
{
for ( i = 0; i < 256; i = i + 1 )
{
analogWrite( led, i );
delay( 10 );
}
}
Examinons ce programme en détails...
Au départ, deux variables (
Puis, deux fonctions sont déclarées. Elles sont requises pour que le programme fonctionne:
La première (
La seconde (
- dans le corps de l'instruction, exécute le code qui s'y trouve pour la valeur courante de la variable
i . - la première valeur de i est 0 (
i = 0 ). C'est l'étape d'initalisation. - ré-exécute ce code pour toutes les valeurs de
i tant que cette valeur est strictement inférieure à 256 (i < 256 ), donc sans jamais atteindre cette dernière valeur. C'est ce qu'on appelle la condition d'arrêt. - à chaque itération ajoute 1 à la valeur courante de i (
i = i + 1 ) après que le corps ait été exécuté. C'est l'itérateur.
Donc, la suite équivalente d'instructions serait:
jusqu'à:
Comme cette instruction
Voyons le résultat:
Comme on le voit, l'intensité lumineuse passe du maximum au minimum entre chaque appel à
{
for ( i = 0; i < 256; i = i + 1 )
{
analogWrite( led, i );
delay( 10 );
}
for ( i = 254; i > 0; i = i - 1 )
{
analogWrite( led, i );
delay( 10 );
}
}
Remarquez:
- la valeur initiale 254 (puisque l'instruction précédente se terminait par 255),
- la condition
i > 0 (permettant de terminer la suite de valeurs par ...3, 2, 1 sans atteindre 0) - et enfin la variation décroissante de la variable
i de 1 en 1 aveci = i - 1 .
- premier
for() : 0, 1, 2... 254, 255 - second
for() : 254, 253... 2, 1 - premier
for() : 0, 1, 2... - etc...
Ce projet est très semblable au fader puisqu'il nécessite aussi de faire varier l'intensité lumineuse de la led. Cependant, il y a une petite subtilité... Un phare côtier est normalement représenté par une lampe munie d'une lentille de Fresnel rotative. Bien évidemment, notre but est de simuler cet effet sans avoir à introduire de mécanisme rotatif.
Dans ce cas, examinons l'effet réel. Lorsqu'on fixe un tel phare, on observe deux caractéristiques:
- l'intensité croit progressivement mais non linéairement (de plus en plus rapidement et de plus en plus intensément)...
- ... jusqu'à aboutir à un simili flash. Ce flash est en fait le point où la lentille de Fresnel est orientée dans la direction de l'observateur.
- (puis l'intensité décroit selon la même courbe)
La première caractéristique de cette simulation est la représentation d'un accroissement (et d'une diminution) de l'intensité lumineuse selon une courbe non-linéaire. Le calcul de la valeur à envoyer à
Introduisons la notion de tableau dans notre programme. Il se définit ainsi:
Bien évidemment, j'ai établi cette suite de valeurs un peu au hasard, mais en essayant de suivre une progression croissante qui s'accélère jusqu'à un point de pleine intensité (255) pour redescendre ensuite jusqu'à 0. En observant attentivement le trait de lumière réel, on a l'impression que ce trait s'éloigne plus rapidement qu'il ne s'approche. J'expliquerais cela par la persistence rétinienne du "flash". En effet, le temps que le flash se dissipe, l'oeil n'a plus le temps de distinguer le reste du trait qui s'éloigne. Il lui semble alors plus court... C'est pourquoi j'ai choisi une courbe décroissante plus rapide que la courbe montante... Mais ce n'est qu'une interprétation personnelle. Libre à chacun de choisir des courbes de valeurs différentes.
La seconde caractéristique est la simulation du flash. Le fait d'utiliser la valeur maximale est importante dans cette suite, mais aussi les valeurs adjacentes (140) doivent être beaucoup plus faibles afin d'accentuer l'effet. Voici le code que nous obtenons:
int i = 0;
int lum[ 32 ] = { 1, 1, 1, 1, 2, 2, 2, 5, 5, 9, 14, 30, 50, 100, 140, 255, 140, 100, 50, 30, 14, 9, 5, 2, 1, 0, 0, 0, 0, 0, 0, 0 };
void setup()
{
pinMode( led, OUTPUT );
}
void loop()
{
for ( i = 0; i < 32; i = i + 1 )
{
analogWrite( led, lum[ i ] );
delay( 20 );
}
}
Remarque: dans l'exemple du fader, nous avions utilisé la valeur de la variable
Mais notre simulation n'est pas tout à fait complète. Un phare possède une signature qui l'identifie et le distingue des autres phares. Par exemple, on observera deux flashs espacés d'une seconde environ, sur un cycle de 5 secondes. Ceci est simulé dans notre programme d'une part par l'exécution d'une boucle permettant deux affichages consécutifs du flash, et d'autre part par l'introduction de pauses supplémentaires. Et tout simplement:
int i = 0;
int j = 0;
int lum[ 32 ] = { 1, 1, 1, 1, 2, 2, 2, 5, 5, 9, 14, 30, 50, 100, 140, 255, 140, 100, 50, 30, 14, 9, 5, 2, 1, 0, 0, 0, 0, 0, 0, 0 };
void setup()
{
pinMode( led, OUTPUT );
}
void loop()
{
for ( j = 0; j < 2; j = j + 1 )
{
for ( i = 0; i < 32; i = i + 1 )
{
analogWrite( led, lum[ i ] );
delay( 20 );
}
delay( 1000 );
}
delay( 5000 );
}
Nous avons ici deux boucles/instructions
Lorsque vous testerez ce programme avec votre Arduino, essayer de modifier le delai de 20ms entre chaque itération de l'instensité et examiner le résultat. Aussi, essayer d'ajouter des valeurs d'intensité dans le tableau. Ces deux modifications auront une incidence sur la vitesse de l'animation...
Dans ce dernier exemple, nous allons complexifier un peu le code en introduisant deux leds qui vont clignoter alternativement. Tout d'abord, les branchements:
La seconde led est ajoutée sur le pin 5, lui aussi PWM. Le programme est très similaire au précédent. Cependant, pour simplifier les choses, nous allons introduire un deuxième tableau de valeurs
int led2 = 5;
int i = 0;
int lum1[ 38 ] = { 0, 0, 0, 2, 5, 8, 11, 15, 20, 25, 30, 35, 40, 50, 70, 100, 130, 180, 220, 255, 220, 180, 130, 100, 70, 50, 40, 35, 30, 25, 20, 15, 11, 8, 5, 2, 0, 0 };
int lum2[ 38 ] = { 255, 220, 180, 130, 100, 70, 50, 40, 35, 30, 25, 20, 15, 11, 8, 5, 2, 0, 0, 0, 0, 0, 2, 5, 8, 11, 15, 20, 25, 30, 35, 40, 50, 70, 100, 130, 180, 220 };
void setup()
{
pinMode( led1, OUTPUT );
pinMode( led2, OUTPUT );
}
void loop()
{
for ( i = 0; i < 38; i = i + 1 )
{
analogWrite( led1, lum1[ i ] );
analogWrite( led2, lum2[ i ] );
delay( 30 );
}
}
Et le résultat obtenu (imaginez-le à l'horizontal):
Bien évidemment, nous aurions pu utiliser des sorties "tout-ou-rien" sans mode PWM, comme dans le premier programme du clignotant, mais l'effet n'aurait pas été aussi réaliste...
Voilà qui conclu ce chapitre sur des utilisations simples d'une ou plusieurs sorties PWM et les exemples des projets associés: le fader, le phare côtier et le signal de passage à niveau style US. Nous avons pu examiner quelques-unes des instructions du langage de programmation, ainsi que les fonctions principales qui permettent de structurer un programme pour l'Arduino. Ce qui est frappant est la facilité avec laquelle il est possible de développer et modifier un programme et de voir les résultats rapidement en ajustant quelques instructions...
Dans les exemples précédents, nous avons pu voir l'utilisation de l'instruction
void setup()
{
pinMode( LED_PWM, OUTPUT );
}
void loop()
{
analogWrite( LED_PWM, 0 );
delay( 100000 );
for ( int i = 0; i < 255; ++i )
{
analogWrite( LED_PWM, i );
delay( 100 );
}
analogWrite( LED_PWM, 255 );
delay( 100000 );
for ( int i = 255; i > 0; --i )
{
analogWrite( LED_PWM, i );
delay( 100 );
}
}
En effet, on peut vouloir utiliser l'Arduino pour d'autres fonctionnalités pendant les longs moments d'attente...
Donc, nous allons définir ce que j'appelle un "séquenceur". C'est une manière de signaler les "évènements" auxquels des changements se produisent (lever du soleil, coucher du soleil, etc...) et de définir des "régimes" pendant lesquels une action est effectuée sans avoir à stopper le cours du programme. Chaque évènement est déterminé en comparant une "horloge" courante à des "temps" pré-déterminés.
Ainsi, nous commençons par définir les temps auxquels les évènements ont lieux. Dans l'exemple suivant:
- la nuit dure de 0 à 100s
- le soleil se lève de 100s à 125s
- le jour dure de 125s à 225s
- le soleil se couche de 225s à 250s
- et l'horloge repart à 0.
unsigned long evenements[ EVENEMENTS ] =
{
100000,
125000,
225000,
250000
};
Ensuite, il suffit de tester si l'horloge courante (
if ( horloge < debut + evenements[ 0 ] )
{
analogWrite( LED_PWM, 0 );
}
La variable
Le programme sera ainsi écrit:
#define EVENEMENTS 4
unsigned long evenements[ EVENEMENTS ] =
{
100000,
125000,
225000,
250000
};
unsigned long debut;
unsigned long horloge;
void setup()
{
pinMode( LED_PWM, OUTPUT );
debut = millis();
}
void loop()
{
horloge = millis();
if ( horloge < debut + evenements[ 0 ] )
{
analogWrite( LED_PWM, 0 );
}
else if ( horloge < debut + evenements[ 1 ] )
{
float lumiere =
(float)( horloge - ( debut + evenements[ 0 ] ) ) /
(float)( evenements[ 1 ] - evenements[ 0 ] );
analogWrite( LED_PWM, (int)( 255 * lumiere ) );
}
else if ( horloge < debut + evenements[ 2 ] )
{
analogWrite( LED_PWM, 255 );
}
else if ( horloge < debut + evenements[ 3 ] )
{
float lumiere =
(float)( horloge - ( debut + evenements[ 2 ] ) ) /
(float)( evenements[ 3 ] - evenements[ 2 ] );
analogWrite( LED_PWM, (int)( 255 * ( 1.0f - lumiere ) ) );
}
else
{
debut = millis();
}
}
Pour simuler le lever et le coucher du soleil, il suffit de faire varier une sortie PWM en fonction du temps qui passe... Ceci est effectué en déterminant la position de l'horloge entre le début et la fin du régime et en calculant un simple ratio (représenté par la variable
Voilà, il n'y a rien de bien sorcier dans ce petit programme. Vous devriez pouvoir ajouter une seconde série de leds (comme dans l'animation que j'ai créée sur mon réseau) de façon à mélanger les tonalités lumineuses. Si vous avez l'intention d'utiliser des rubans de leds pour éclairer votre réseau, il suffit de se reporter au montage incluant le transistor de puissance TIP122 comme décrit dans l'article 8.2.1.
On peut aussi penser à quelques variantes afin de personnaliser le montage:
- ajout de sorties supplémentaires qui permettront de simuler l'allumage de l'éclairage urbain pendant la nuit
- ajout d'un second bandeau de leds comme précisé un peu plus haut
- utilisation d'une variation non linéaire de l'intensité lors du lever et du coucher du soleil
Merci pour cet article.
RépondreEffacerJe suis en train de réfléchir au programme de mon éclairage. j'ai acquis le power-shield Sparkfun. Plus qu'à y mettre des refroidisseurs.
par contre, j'ai regardé le shéma de celui-ci avec le logiciel Eagle. A priori, il utilises les broches 3-5-6-9-10-11, donc les sorties PWM. est-il possible d'utiliser les autres broches 2-4-7-8-12-13 afin de créer un petit séquenceur qui allumerai durant les phases de nuit ou jours d'autres leds?
En gros: le jour se lève, les lumières sont toutes allumées grâce aux broches PWM. La nuit tombe, je veux allumer trois leds de lampadaires via la sortie 2 par exemple. Est-ce possible? Dis moi juste oui ou non et je verrai comment modifier le programme.
Merci beaucoup.
Oui c'est possible. Normalement le shield n'utilise pas les autres sorties (non PWM)
RépondreEffacerPar contre:
1) je ne sais pas exactement le nombre de mA consommés par le shield par sortie. Cela doit être assez peu (quelques mA). Mais il faut toujours garder en tête que la consommation totale supportée par l'Arduino est d'environ 150mA...
2) utiliser les autres sorties de l'Arduino vient augmenter ce total... Je n'utiliserais pas toutes les autres sorties!
3) 3 leds/lampadaires sur une seule sortie est trop, mais cela dépend des leds bien sû... Une led tire environ 20mA sur une sortie. 3 leds dépasseraient alors le max de 40mA par sortie... Utilises plutôt plusieurs sorties...
Voilà,
Patrick
OK. C'est bien ce qu'il me semblait. Il va falloir que je réfléchisse à cela alors. D'après toi, je peux commander un petit circuit qui serait alimenté par l'arduino et qui lui servirai de "séquenceur"?
RépondreEffacerJe ne comprends pas ce que tu désires faire: "D'après toi, je peux commander un petit circuit qui serait alimenté par l'arduino et qui lui servirai de "séquenceur"? "
RépondreEffacerL'arduino est le séquenceur. Pourquoi voudrais-tu un circuit supplémentaire???
Bonjour,
RépondreEffacerSur mon réseau, j'ai installé des rubans de leds blanc chaud et aussi des rubans de ledsRVB pour créer différentes ambiances ( plus rouge pour les couchers de soleil,etc..).
Avec votre séquenceur, est-il possible de commander ces différents rubans: par exemple quand les leds blanches sont en train de décroitre,a mi-parcours, est-ce que je peux faire croitre mes leds rouge?
J'ai bien compris que je peux créer d'autres évènements mais quand je passe au else if suivant, le précédent se bloque. Les ratios ne sont actifs que dans le if concerné.
Bonjour,
RépondreEffacerHmmm... Je ne suis pas sur de bien comprendre le probleme. Pourriez-vous me faire un petit graphique avec le temps qui croit de gauche a droite par exemple, et decrire quel ruban s'allume ou s'eteint?
Je crois voir que vous desirez "superposer" l'allumage et l'exctinction d'un ruban par rapport a l'autre, mais je n'en suis pas sur...
Patrick
En fait... pourriez-vous m'envoyer votre code?
RépondreEffacerje souhaite fermer un passage a niveau dans les deux sens de circulation et ouverture après le passage du train.je débute avec arduino est ce possible de faire avec une arduino uno
RépondreEffacer