Bonsoir,
Edit du 29.10.2021 v02 pour prendre compte remarques de @Thias et @sdel95, pour un code plus lisible et compréhensible pour un débutant.
Ce tutoriel est un prolongement des épisodes Une débutante dans le décor - Ep6 - Quelques automatismes (Arduino Nano)), Une débutante dans le décor - Ep8 - Un servo pour l'Arduino et Une débutante dans le décor - Ep9 - Paparazzi, comment réaliser des automatismes en mode “lego” basé sur la plateforme Arduino Nano.
Objectif : pas de fer à souder, pas de câblage compliqué, pas de résistance/condo/… à rajouter et bien sur un prix mini mini. Si tu sais brancher un M84, tu sais utiliser un Arduino à la sauce Julaye !
Pour agrémenter le bâtiment administratif d’Obourg, j’ai décidé d’embarquer un Arduino Nano pour proposer des automatismes sur l’éclairage des différents bureaux et couloirs du bâtiment.
Cet épisode sera en deux parties principales.
La première partie va mettre en place une séquence d’allumage d’une lampe avec un tableau de configuration. Dans l’exercice proposé, nous allons allumer cette led en simulant l’allumage d’un néon, que nous compléterons avec des glitchs aléatoires, un néon en pas très bon état donc.
La deuxième partie va mettre en place un automate permettant de gérer jusqu’à 6 éclairages différents en proposant des séquences d’allumage et extinction pour chaque pièce, comme en domotique lorsque l’on veut simuler une présence dans une maison inoccupée …
Pour aujourd’hui, je vous propose la première partie. C’est parti !
Première partie : un néon sinon rien !
L’Arduino Nano possède 6 sorties digitales que nous pouvons utiliser en mode PWM pour commander autant de lampes, une lampe pouvant être constituée de plusieurs leds. Attention néanmoins à ne pas dépasser 40 mA par sortie, le total ne devant excéder 200 mA au risque de détruire le Nano.
Les sorties utilisables sont D3, D5, D6, D9, D10 et D11. Le mode PWM est utile pour commander des servos moteurs mais permet aussi de faire varier l’intensité lumineuse de la led . Nous avions abordé le mode PWM dans Une débutante dans le décor - Ep8 - Un servo pour l'Arduino.
Le schéma de branchement de l’Arduino Nano est minimalistique :
- alimentation par le connecteur USB ou par GND / +12V
- led branchée entre GND et D3 (+ de la led)
Je n’ai pas mis de résistance avec la led pour les essais, la sortie est limité à 5V et le PWM va envoyer un signal moyen très inférieur à ce que peut supporter la led.
Reste à écrire le programme que nous allons dérouler pas à pas
On charge la bibliothèque standard de l’Arduino et on définit une constante debug
que l’on met à 0 en temps normal mais que l’on pourra mettre à 1 pour obtenir des traces sur la console connectée sur le port série.
#include <Arduino.h>
// mettre à 1 pour un debug dans la console série, 2 pour full debug
const int debug = 1;
Une constante stocke le port utilisé, ici D3 d’autres constantes pour jouer sur l’intensité de l’éclairage avec des valeurs différentes sur le PWM
// configuration sortie D3 pour connecter la led
int ledPin = 3;
// l'intensité maximum de chaque sortie PWM
const int PWM_FOR_LED = 12;
// ON ou OFF ou autre valeur (fading)
const int LIGHT_ON = PWM_FOR_LED;
const int LIGHT_FAD2 = PWM_FOR_LED/2;
const int LIGHT_FAD4 = PWM_FOR_LED/4;
const int LIGHT_OFF = 0;
On crée un tableau formé par des couples dont la première valeur contient un délai en millisecondes et la deuxième valeur indique l’intensité de l’allumage pendant le délai spécifié.
A noter que vous pouvez ainsi vous créer tout un tas d’animation lumineuses, le tableau ci-après simule un néon qui s’allume.
// blink
const struct blink {
int duration;
int intensity;
} blinkOn[] = {
10, LIGHT_ON, 20, LIGHT_OFF,
20, LIGHT_ON, 240, LIGHT_OFF,
20, LIGHT_ON, 40, LIGHT_OFF,
20, LIGHT_ON, 100, LIGHT_OFF,
20, LIGHT_ON, 20, LIGHT_OFF,
20, LIGHT_ON, 260, LIGHT_OFF,
80, LIGHT_ON, 20, LIGHT_OFF,
240, LIGHT_ON, 60, LIGHT_OFF,
160, LIGHT_ON, 20, LIGHT_OFF,
240, LIGHT_ON, 20, LIGHT_OFF,
1000, LIGHT_FAD2, 20, LIGHT_OFF,
20, LIGHT_ON, 40, LIGHT_OFF,
100, LIGHT_ON, 20, LIGHT_OFF,
2740, LIGHT_ON, 340, LIGHT_FAD4,
860, LIGHT_FAD2, 20, LIGHT_OFF,
1400, LIGHT_ON, 20, LIGHT_OFF,
60, LIGHT_ON, 20, LIGHT_OFF
};
Les deux variables qui suivent permettent de se déplacer dans le tableau précédent et de connaitre le nombre d’éléments du tableau.
// etat de clignotement (position dans le tableau précédent)
int gStateBlink = 0;
// nombre de transitions
const int maxStateBlink = sizeof(blinkOn)/sizeof(blink);
Comme à mon habitude, nous allons mettre en place un petit automate à états finis (Finite State Machine) avec trois états : lumière éteinte, lumière allumée et lumière en allumage.
La variable gStateRunning contient l’état courant de l’automate.
// stateLight
const int state_OFF = 0;
const int state_ON = 1;
const int state_PWRUP = 2;
int gStateRunning = state_OFF;
L’automate est simple. Lorsque l’Arduino démarre, il est dans l’état state_OFF puis il passe dans l’état state_PWRUP qui va simuler l’allumage du néon et enfin il sera dans l’état State_ON d’allumage permanent.
Pour agrémenter un peu cette première partie, j’ai rajouté un tirage aléatoire qui génère un glitch toutes les 10 secondes environ. Le glitch consiste à remettre l’automate dans l’état state_PWRUP en commençant à une séquence précise dans le tableau blinkOn …
Enfin la variable gDelay est utilisée pour prendre en compte le délai d’allumage ou d’extinction de la led, conformément aux informations de la séquence extraite du tableau blinkOn.
// delay avant une transition de la machine à état
int gDelay = 0;
Commence ensuite le programme proprement dit, avec la fonction de démarrage de l’Arduino, executée une seule fois à la mise sous tension de la carte.
void setup() {
// code de démarrage executé une seule fois à la mise sous tension de la carte
// ouvre le port série (console de l'outil) avec la vitesse 57600 bauds
// attention que le paramètre sur la console soit bien 57600 !
Serial.begin(57600);
// Ports digitaux programmables (PWM) : D3, D5, D6, D9, D10 et D11
pinMode(ledPin,OUTPUT);
// Annonce la version
Serial.println("Neon v02 20211029 - (c) Julie Dumortier - Licence GPL");
}
Séquence assez classique qui consiste à paramétrer le port série à la vitesse de 57600 bauds pour une éventuelle sortie si debug est défini à une valeur différente de 0.
Ensuite la fonction paramètre la broche D3 en sortie et imprime sur la console la version du programme. A noter que le code que je vous propose est comme à mon habitude sous licence GPL, vous en faites ce que vous voulez, sauf du business .
Le reste du programme est constitué de trois fonctions : lightOn(), lightOff() et powerUp() qui correspondent aux trois états state_ON, state_OFF et state_PWRUP de notre machine à état fini.
Le programme principal est une boucle sans fin qui ne s’arrête que lorsque vous coupez l’alimentation de l’Arduino. Ce programme consiste à appeler une des trois fonctions précédentes en fonction de l’état de l’automate, qui je vous le rappelle se trouve stocké dans la variable stateRunning.
void loop() {
switch (gStateRunning) {
case state_OFF : lightOff(); break;
case state_ON : lightOn(); break;
case state_PWRUP : powerUp(); break;
} // end switch
delay(1);
}
Vous remarquerez le délai d’une milliseconde à chaque itération dans la boucle, c’est ce qui est aligné avec la variable gDelay dont l’unité est justement la milliseconde !.
Nous reste à étudier chacune des trois fonctions lightOn(), lightOff() et powerUp() .
lightOff() est appelée au démarrage de la carte, elle permet d’initialiser l’automate et de s’assurer que la led est bien éteinte. Puis l’état de l’automate passe en state_PWRUP.
void lightOff()
{
if (debug) Serial.println("state_OFF");
// eteint la led (au cas où)
analogWrite(ledPin,0);
// passe en mode démarrage
gStateRunning = state_PWRUP;
if (debug) Serial.println("stateRunning <-- PWRUP");
// prépare la séquence
gStateBlink = 0;
gDelay = 0;
}
La fonction powerUp() est appelée tant que l’automate est dans l’état state_PWRUP qui correspond à la séquence d’allumage de notre lampe.
A chaque fois que l’on entre dans cette fonction, on va regarder si le délai est écoulé en testant la variable gDelay. Si le délai est écoulé, on avance dans le tableau de séquence blinkOn en incrémentant l’état de clignotement gSateBlink, c’est à dire que l’on se déplace dans ce tableau blinkOn.
Lorsque l’on arrive à la fin du tableau, c’est que la séquence de démarrage est terminée et on passe l’automate dans l’état state_ON.
C’est la fonction analogWrite() proposé par l’Arduino qui active la sortie D3.
void powerUp()
{
int lightOn;
// si le délai est échu, démarre une nouvelle séquence
if (gDelay<=0)
{
if (debug) {
Serial.print("state_PWRUP - blink sequence ");
Serial.print(gStateBlink);
}
lightOn = blinkOn[gStateBlink].intensity;
analogWrite(ledPin,lightOn);
if (debug) {
Serial.print(" intensity: ");
Serial.print(lightOn);
Serial.print(" delay:");
}
gDelay = blinkOn[gStateBlink].duration;
if (debug) Serial.println(gDelay);
gStateBlink += 1;
if (gStateBlink>=maxStateBlink) {
gStateBlink = 0;
gStateRunning = state_ON;
if (debug) Serial.println("state_ON");
}
}
if (debug>1) Serial.print(".");
gDelay = gDelay - 1;
}
Dernière fonction, lightOn() allume la lampe de façon définitive avec la commande analogWrite().
Mais j’ai ajouté un tirage aléatoire pour relancer l’automate sur la séquence de démarrage en position 22 du tableau, ce qui permet de faire un petit glitch de temps en temps .
void lightOn()
{
int alea;
analogWrite(ledPin,PWM_FOR_LED);
// fait un tirage aléatoire et refait un glitch à partir de la séquence 22
// ajuster la valeur random en fonction de la fréquence d'apparition (en ms)
alea = random(0,10000);
if (alea < 1) {
if (debug) Serial.print("Glitch alea:");
if (debug) Serial.println(alea);
gStateRunning = state_PWRUP;
gStateBlink = 22;
gDelay = 0;
}
}
Ceci met fin à cette première partie que je referme avec une petite vidéo qui montre la séquence de démarrage suivi de quelques glitchs. Désolée pour le bruit de fond, j’ai mon ex qui est bruyant quand il se mouche
Et le programme complet ci-après :
// Tutoriel Neon
// (c) Julie Dumortier, 2021
// licence GPL
//
// Evolutions
// v0.1 première version sur plateforme de démonstration
// v0.2 polish pour un code plus lisible et prendre en compte quelques remarques (Thias, Stéphane)
//
// branchez une LED entre GND et D3
// et appréciez l'allumage du néon à la mise sous tension de l'Arduino
//
// pensez à protéger votre led avec une résistance
// utilisez le calculateur de résistance svp https://www.digikey.fr/fr/resources/conversion-calculators/conversion-calculator-led-series-resistor
// pas plus de 40 mA par sortie, pas plus de 200 mA au total, au risque de griller une sortie et/ou l'Arduino Nano
//
// l'intensité est réglable sur les sorties D3, D5, D6, D9, D10 et D11 (cf pwmPin)
//
// Retrouvez ce tutoriel sur le lien :https://forum.3rails.fr/t/une-debutante-dans-le-decor-ep13-que-la-lumiere-jaillisse/20889
#include <Arduino.h>
// mettre à 1 pour un debug dans la console série, 2 pour full debug
const int debug = 1;
// configuration sortie D3 pour connecter la led
int ledPin = 3;
// l'intensité maximum de chaque sortie PWM
const int PWM_FOR_LED = 12;
// ON ou OFF ou autre valeur (fading)
const int LIGHT_ON = PWM_FOR_LED;
const int LIGHT_FAD2 = PWM_FOR_LED/2;
const int LIGHT_FAD4 = PWM_FOR_LED/4;
const int LIGHT_OFF = 0;
// blink
const struct blink {
int duration;
int intensity;
} blinkOn[] = {
10, LIGHT_ON, 20, LIGHT_OFF,
20, LIGHT_ON, 240, LIGHT_OFF,
20, LIGHT_ON, 40, LIGHT_OFF,
20, LIGHT_ON, 100, LIGHT_OFF,
20, LIGHT_ON, 20, LIGHT_OFF,
20, LIGHT_ON, 260, LIGHT_OFF,
80, LIGHT_ON, 20, LIGHT_OFF,
240, LIGHT_ON, 60, LIGHT_OFF,
160, LIGHT_ON, 20, LIGHT_OFF,
240, LIGHT_ON, 20, LIGHT_OFF,
1000, LIGHT_FAD2, 20, LIGHT_OFF,
20, LIGHT_ON, 40, LIGHT_OFF,
100, LIGHT_ON, 20, LIGHT_OFF,
2740, LIGHT_ON, 340, LIGHT_FAD4,
860, LIGHT_FAD2, 20, LIGHT_OFF,
1400, LIGHT_ON, 20, LIGHT_OFF,
60, LIGHT_ON, 20, LIGHT_OFF
};
// nombre de transitions
const int maxStateBlink = sizeof(blinkOn)/sizeof(blink);
// etat de clignotement (position dans le tableau précédent)
int gStateBlink = 0;
// stateLight
const int state_OFF = 0;
const int state_ON = 1;
const int state_PWRUP = 2;
int gStateRunning = state_OFF;
// delay avant une transition de la machine à état
int gDelay = 0;
// ---------------------------------------------------------------------
// --- setup()
// ---------------------------------------------------------------------
void setup() {
// code de démarrage executé une seule fois à la mise sous tension de la carte
// ouvre le port série (console de l'outil) avec la vitesse 57600 bauds
// attention que le paramètre sur la console soit bien 57600 !
Serial.begin(57600);
// Ports digitaux programmables (PWM) : D3, D5, D6, D9, D10 et D11
pinMode(ledPin,OUTPUT);
// Annonce la version
Serial.println("Neon v02 20211029 - (c) Julie Dumortier - Licence GPL");
}
// ---------------------------------------------------------------------
// ligthOff()
//
// éteint le néon
// ---------------------------------------------------------------------
void lightOff()
{
if (debug) Serial.println("state_OFF");
// eteint la led (au cas où)
analogWrite(ledPin,0);
// passe en mode démarrage
gStateRunning = state_PWRUP;
if (debug) Serial.println("stateRunning <-- PWRUP");
// prépare la séquence
gStateBlink = 0;
gDelay = 0;
}
// ---------------------------------------------------------------------
// ligthOn()
//
// allume le néon en permanent
// ---------------------------------------------------------------------
void lightOn()
{
int alea;
analogWrite(ledPin,PWM_FOR_LED);
// fait un tirage aléatoire et refait un glitch à partir de la séquence 22
// ajuster la valeur random en fonction de la fréquence d'apparition (en ms)
alea = random(0,10000);
if (alea < 1) {
if (debug) Serial.print("Glitch alea:");
if (debug) Serial.println(alea);
gStateRunning = state_PWRUP;
gStateBlink = 22;
gDelay = 0;
}
}
// ---------------------------------------------------------------------
// powerUp()
//
// gère une séquence d'allumage du néon
// ---------------------------------------------------------------------
void powerUp()
{
int lightOn;
// si le délai est échu, démarre une nouvelle séquence
if (gDelay<=0)
{
if (debug) {
Serial.print("state_PWRUP - blink sequence ");
Serial.print(gStateBlink);
}
lightOn = blinkOn[gStateBlink].intensity;
analogWrite(ledPin,lightOn);
if (debug) {
Serial.print(" intensity: ");
Serial.print(lightOn);
Serial.print(" delay:");
}
gDelay = blinkOn[gStateBlink].duration;
if (debug) Serial.println(gDelay);
gStateBlink += 1;
if (gStateBlink>=maxStateBlink) {
gStateBlink = 0;
gStateRunning = state_ON;
if (debug) Serial.println("state_ON");
}
}
if (debug>1) Serial.print(".");
gDelay = gDelay - 1;
}
// ---------------------------------------------------------------------
// loop()
//
// L'automate néon
// ---------------------------------------------------------------------
void loop() {
switch (gStateRunning) {
case state_OFF : lightOff(); break;
case state_ON : lightOn(); break;
case state_PWRUP : powerUp(); break;
} // end switch
delay(1);
}
Seconde partie : la gestion d’une scène
Objectif du projet : Le programme Lumières est une tentative pour proposer aux adeptes du modélisme un automate de gestion des éclairages d’un bâtiment, d’une scène, ou plus généralement d’un ensemble d’éclairages, qui soit facile d’accès pour un non initié à la programmation d’un Arduino, et fortement paramétrable pour couvrir les besoins les plus courants.
L’utilisateur adapte le fichier ConfigLumieres.h selon ses besoins et il obtient un automate opérationnel, sans écrire une seule ligne de code, seulement des données dans le fichier susmentionné. C’est une approche NO CODE - LOW CODE pour surfer sur le buzz des outils de programmation actuels.
Les types d’éclairages actuellement supportés sont :
- lampe standard (allumé ou éteint)
- néon neuf avec sa séquence d’allumage,
- néon fatigué avec des glitchs aléatoires,
- flash de photographe avec pre-flash,
- poste de soudure à l’arc,
- servo moteur 0-90° pour l’ouverture/fermeture d’une porte
- gyrophare
- bougie / brasero
- feu clignotant qui ne clignote que s’il n’est pas permanent.
Sur les sorties PWM (D3,D5,D6,D9,D10 et D11), l’automate gère la variation d’intensité lumineuse pour plus de réalisme.
Licence : Le logiciel est sous licence GPL et peut être librement utilisé et modifié dans le cadre de notre passion de modélisme, vos éventuelles modifications doivent être reversées à la communauté.
Documentation : La débutante dans le décor - Ep13 - Que la lumière jaillisse ! - Google Docs
La documentation proposée présente aussi dans son chapitre 9 de nombreux exemples d’automatismes qui sont faciles à réaliser avec cet automate :
- Photographe paparazzi dont le flash se déclenche au passage d’un train
- Gestion de la séquence d’éclairage d’une fosse d’inspection au stationnement d’une locomotive
- Animation d’un poste de soudure en présence d’une locomotive dans l’atelier
- Animation de feux tricolores à un croisement routier, inclu les feux en panne (orange clignotant)
- L’ouverture et la fermeture d’une porte de remise avec un gyrophare de sécurité lorsqu’une locomotive se présente devant la porte
- Passage à niveau avec feu clignotant et barrière commandée par un servo
- …
La suite est ici : GitHub - Julaye/Lumieres: Gestion d'une scène ou d'un ensemble d'éclairages avec un Arduino Nano (échelle HO 1/87 ou supérieure)
Retrouvez tous les épisodes de la débutante en clickant ici !