Nombre de visites :
9
 
PRÉAMBULE
Dans les applications «Cattle Dock» et «Sifflets et klaxons», il n'y a pas d'animation lumineuse couplée avec la ou les séquences sonores. Uniquement du son.


Les trois applications développées ci-dessous allient le son et la lumière :

«Bruits de la ferme» :
 
  • une seule séquence sonore d'environ 9 minutes ;
  • animation lumineuse du four à pain et de la maison.
«Feu de camp» :
  • une seule séquence sonore d'environ 30 minutes ;
  • animation lumineuse du feu de camp avec croissance et décroissance de l'intensité du feu.
«Orage» :
  • 14 sons de tonnerre différents ;
  • éclairs et coups de tonnerre synchronisés ;
  • effets de proximité ou d'éloignement.
Les programmes sont juste un peu plus complexes.
PRÉSENTATION du DFPlayer Mini
(... au cas où vous n'avez pas déjà visité la page de 3 premières applications..)
DFPlayer Mini de DFROBOT est un lecteur «.MP3» ou «.WAV».

Il comporte un amplificateur stéréo intégré pouvant délivrer directement jusqu'à 3 watts sur une enceinte d'impédence 4 à 8 ohms.

Une sortie est prévue pour attaquer un amplificateur externe.


Utilisé seul, le module DFPlayer Mini offre déjà de très nombreuses possibilités.

Liens utiles :
https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
https://www.wikidebrouillard.org/wiki/Item:DFPlayer_Lecteur_MP3​
https://lesiteferroviaire.pagesperso-orange.fr/DF%20Player.htm​


 
Basse-cour.jpg
Nombre de visites :
Compteur de visites

Sons de la ferme

 
  • une seule séquence sonore d'une durée de 9 minutes ;
  • télécommande infrarouge ;
  • possibilité de modifier le volume sonore en cours de déroulement ;
  • possibilité de stopper le déroulement de l'application ;
  • animation lumineuse du four à pain et éclairage de la maison.

Date de création : 02/02/2022

Dernière modification : 04/02/2022

Emoticone_chantier.jpg
PROJET
Dans un coin de désert j'ai installé une toute petite ferme en adobe, avec un four à pain et un enclos pour chevaux.

Jusqu'à présent l'allumage du four à pain (fixe) et l'éclairage de la maison étaient pilotés ensemble par commande vocale grâce à un module «Alexa».

Sympa, mais... peu satisfaisant.


L'utilisation d'un module DFPlayer commandé par un ARDUINO permet de faire beaucoup mieux :
 
  • diffusion d'une longue séquence sonore (environ 9 minutes dans mon appli) avec des bruits d'animaux, de chant, de coyotes dans le lointain... ;
  • allumage du four à pain avec rougeoiement, puis extinction progressive ;
  • allumage de l'intérieur de la maison en «fin de journée» ;
  • possibilité de modifier le volume sonore en cours de déroulement de la séquence ;
  • possibilité d'écourter l'animation ;
 
Petit bonus : le programme est écrit de façon à pouvoir diffuser au hasard plusieurs séquences sonores différentes, et pas seulement une. Les jours se suivent, mais ne se ressemblent pas...

La (petite) difficulté pour cette application consiste à capter les codes IR – émis par la télécommande – à tout moment du déroulement des animations lumineuses.

Rien d'insurmontable.

 
MATÉRIEL
MP3_Player.jpg
Platine_proto_edited.jpg
 
  • un module «DFPlayer Mini» 
 
 

 
  • une plaque de prototypage.
Celle qui est représentée ci-contre est la réplique d'un Breadboard de 420 points.

J'ai choisi cette solution parce qu'il est très facile de transposer sur ce type de platine le montage d'essai élaboré avec le breadboard.
 
  • ​un ARDUINO Nano ;
  • un HP 4 ou 8 ohms de petite taille (un HP de diamètre 50 à 60 mm est suffisant) ;
  • une alimentation de préférence 4,5V DC (ou à la rigueur 5 V DC) ;
  • 1 carte «TF» (micro SD) ;
  • 1 bornier à vis au pas de 5,08 mm pour la sortie HP ;
  • 1 capteur infrarouge VS 1838 B ;
  • quelques résistances 1/4 watt ;
Le DFPlayer n'est pas directement soudé sur la platine de prototypage.
Il est monté sur 2 connecteurs 8 pôles du type de ceux-ci...
Connecteur 8 poles.png
SCHÉMA
Schema_2.png


Même schéma que pour les animations «Cattle Dock» et «Sifflets et Klaxons», avec seulement deux différences :
  • la liaison «Busy > ARDUINO» n'est pas utilisée : on ne teste pas la fin de la séquence sonore ;
  • deux LED sont ajoutées, pour l'animation lumineuse du four à pain et de la maison.
CABLAGE SUR LA PLATINE DE PROTOTYPAGE

Pour aider au câblage de la platine de prototypage, voici l'implantation éditée dans TINKERCAD :
 
Schema_3.png

En noir : les ponts pour GND (masse).
En orange (entourés en vert) : les ponts pour les liaisons vers les LED du four et de la maison ;
En rouge : les ponts pour «+5 V DC» ;

Entourées en vert, à droite : les deux résistances de limitation de courant pour les LED.

En gris, positions «h14» à «h17» : le connecteur pour le capteur infrarouge ;
En gris, positions «c10» à «c13» : le connecteur pour les LED du four et de la maison.

Note 1 :
Les valeurs des résistances pour les LED ont été fixées à 1k.
On peut éventuellement choisir des valeurs plus faibles, de l'ordre de 470 ohms.
À l'usage, si on s'aperçoit que les LED sont trop lumineuses, on peut ajuster leur brillance dans le programme.



Ci-dessous, vue du câblage de la platine de prototypage :
 
cattle-dock-01_edited.jpg
Le câblage est exactement le même que celui des applications «Cattle Dock» et «Sifflets et klaxons».

(Il suffit de changer l'étiquette).

Cette fois, les deux résistances en positions «+11 – a11» et «-12 – b12» sont utilisés, ainsi que le connecteur wrapping blanc (pour la connexion vers les 2 LED.




 
SÉQUENCE SONORE

Une fois mise au point – dans un logiciel comme AUDACITY par exemple – la séquence sera enregistrée sur la carte «TF».
 
Peu importe son nom : elle sera forcément considérée comme étant le son n°1.

... mais attention aux libellés trop longs.



 
LE CODE C++
Petit préambule :

La présente application «Sons de la ferme» utilise le code 2 + POWER pour le de démarrage et d'arrêt.
Les codes 21, 22 et 23 sont utilisés pour modifier le volume sonore en cours de fonctionnement.

 

#include et déclarations de variables
 
#include <IRremote.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h> 
// Bibliothèque à télécharger.

SoftwareSerial mySoftwareSerial(11,12); // la broche «11» de l'ARDUINO est utilisée
             //comme «Rx» pour communiquer avec la broche «Tx» du DFPlayer
             // la broche «12» de l'ARDUINO est utilisée comme «Tx» pour
             // communiquer avec la broche «Rx» du DFPlayer.
             // Note : la liaison «Rx ARDUINO» --> «Tx DFPlayer» n'est pas
             // utilisée dans ce programme.
                                        
DFRobotDFPlayerMini myDFPlayer;      
  // Déclare une instance pour le DFPlayer.

int IRpin = 2;                          // Entrée du capteur IR
IRrecv irrecv(IRpin);
decode_results results;


int LED_interieure = 9 ;                // LED jaune
int LED_four_a_pain = 6 ;               // LED rouge

int Numero_du_son ;                     // au cas où on dispose de plusieurs
int Nombre_de_sons ;                    // séquences sonores différentes
                                        // si on veut pouvoir varier.
int Volume ;

//const int  Touche_9 = 272 ; 
//const int  Touche_8 = 3600 ;
//const int  Touche_7 = 1552 ;
//const int  Touche_6 = 2576 ;
//const int  Touche_5 = 528 ;
//const int  Touche_4 = 3088 ;
const int  Touche_3 = 1040 ;
const int  Touche_2 = 2064 ;
const int  Touche_1 = 16 ;

//const int  Touche_0 = 2320 ;
//const int  Touche_Up = 1904 ;  Ces touches ne sont pas utilisées dans ce programme.
//const int  Touche_Down = 3952 ;
//const int  Touche_Left = 2800 ;
//const int  Touche_Right = 752 ;
//const int  Touche_VolUp = 1168 ;
//const int  Touche_VolDown = 3216 ;
//const int  Touche_CH_Up = 144 ;
//const int  Touche_CH_Down = 2192 ;
const int  Touche_POWER = 2704 ;
//const int  Touche_Menu = 112 ;

int Code_a_2_chiffres ;
int OK ;
                  // Utilisé comme quittance quand le code à 2 chiffres reçu
                           // est celui qui est attendu.


int numero ;               // variable pour choisir un son différent à chaque
                           // redémarrage du programme.
                           // Ce n'est pas un nombre aléatoire.
                           // Juste différent à chaque fois.
                           // Ce nombre dépend aussi du nombre de faux codes de
                           // démarrage (volontaires ou non).

bool flag ;                // flag = true  ⇒ les séquences s'enchaînent normalement.
                           // flag = false ⇒ les séquences sont ignorées et le
                           // programme se repositionne à son début.

int ON ;
int OFF ;
 
long Temps_IN ;         
long Temps_OUT ;
long Temps_initial ;
long Temps_final ;
long Duree_allumage_du_four_a_pain ;

 

La bibliothèque «DFRobotDFPlayerMini» doit être téléchargée est installée.
(Elle figure dans le fichier .zip).

J'ai déclaré toutes les touches du modèle de télécommande SONY TV que j'utilise.
Les touches non utilisées dans le programme sont passées en commentaires.



 
void setup()
 
void setup() 
  {
  irrecv.enableIRIn();

  pinMode(6, OUTPUT);      
// LED rouge ...
  pinMode(9, OUTPUT);      // et LED jaune du foyer

  ON = 255 ;               // pour régler le niveau d'éclairement de la LED intérieure.
  OFF = 0 ;

  mySoftwareSerial.begin(9600);         
// pour communiquer avec le DFPlayer
  myDFPlayer.begin(mySoftwareSerial);
  myDFPlayer.volume(0);
 
  Serial.begin(9600);                  
 // Pour communiquer avec le moniteur
  pinMode(13, OUTPUT);                  // LED de contrôle de la réception d'un code.

  Nombre_de_sons = myDFPlayer.readFileCounts() ; // C'est le nombre de séquences
                                        // sonores enregistrées sur la carte «T»
                                        // si on veut une diversité de scenarii.
 
  //----Set different EQ----
  //  myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
  //  myDFPlayer.EQ(DFPLAYER_EQ_POP);
  //  myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
             myDFPlayer.EQ(DFPLAYER_EQ_JAZZ); //Pour renforcer un peu les aigus
                                              // quand le HP est masqué dans le décor.
  //  myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
  //  myDFPlayer.EQ(DFPLAYER_EQ_BASS);
  }

Remarques :
 
L'instruction myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);  :

DFPlayer possède un equalizer avec des réglages de tonalités sonores pré-établies.

«EQ_JAZZ» permet de renforcer les aigus, par rapport au mode «EQ_NORMAL» ⇒ utile quand le HP ou l'enceinte est planquée sous la table du réseau ou dans une montagne en polystyrène extrudé !

L'instruction ON = 255 ;

Utilisée dans la fonction Eclairage_interieur(ON); pour régler le niveau de luminosité de l'éclairage intérieur de la maison.
Si on a choisi une résistance de limitation pour la LED intérieure (sortie 9), il est probable que la luminosité de la LED soit trop élevée.
Il suffit alors, dans le setup(), de régler «ON» à une valeur inférieure.
 
ordi.gif
Pas mal ton idée...
C'est plus facile que de changer une résistance...

L'instruction Nombre_de_sons = myDFPlayer.readFileCounts() ;
Permet de connaître le nombre d'enregistrements sonores enregistrés sur la carte «T».
De cette façon, ce n'est pas toujours le même son qui est diffusé.

 

Fonction personnelle «int delay_seconds(long nb_seconds)»
 
Le paramètre de la fonction delay([xxx]) est exprimé en millisecondes.
Pas très pratique quand on a besoin de temporisation longues.
Il est plus facile de raisonner en secondes.

La toute petite fonction ci-dessous va donc faciliter le réglage des temporisations.
C'est une fonction avec passage d'argument.

Elle présente une particularité : elle peut être «court-circuitée» lorsque flag == false.
flag devient false lorsque l'ordre d'arrêt est envoyé en cours de déroulement du programme.

 
int delay_seconds(long nb_seconds)
   {
   for(int i = 1 ; i <= nb_seconds ; i++)
      {
      Interception_du_code_IR_pour_interrompre_le_programme() ;
      if (flag == false) {Eclairage_interieur(OFF) ; loop() ; }
      delay (1000) ;     
      }  
   }



void loop()
 
Cette fois, parce qu'il y a une animation lumineuse, void loop() est un peu plus fournie que dans les précédentes animations.
 
void loop() 
  {
  results.value = 0 ;        
    // Mise à zero du code reçu.
  flag = true ;                   // flag = false : lorsque le code d'arrêt est reçu
                                  // ⇒ le déroulement est écourté.
  OK = true ;                     // ... si le code à 2 chiffres reçu est le
                                  // code attendu.
  Volume = 23 ;                  
// volume sonore par défaut
                                  // (modifier en fonction du HP utilisé).
  myDFPlayer.volume(Volume);   
   
 
        Serial.println("-------------------------") ;
        Serial.println("Attente du code démarrage") ; 
   
  Interception_du_code_IR_SONY_pour_le_demarrage() ;
 

  if (Code_a_2_chiffres == OK) 
    { 
    int MAP_vs_REEL = 10 ;          
 // Ce coefficient vaut «1» en phase de mise au
                                      // point (MAP) et 10 à 50 en utilisation réelle.

    myDFPlayer.play(Numero_du_son);   // «Numero_du_son» est calculé dans la fonction
                                 // «Interception_du_code_IR_SONY_pour_le_demarrage()»
    delay_seconds(5 * MAP_vs_REEL) ;  // Le son démarre seul pendant (5 x MAP_vs_REEL)
                                      // secondes.
    Allumage_four_a_pain(MAP_vs_REEL) ;

    Flame_four_a_pain((20 * MAP_vs_REEL)) ;
    Eclairage_interieur(ON) ;    
          // La valeur de «ON» est réglée dans SETUP
                                           // entre 0 et 255 pour régler l'intensité
                                           // de l'éclairage intérieur.
    Flame_four_a_pain(2 * MAP_vs_REEL) ;   // On RE-enclenche le four à pain...
                                           // parce que l'instruction précédente,
                                           // «Eclairage_interieur», stoppe le four.

    Extinction_four_a_pain(MAP_vs_REEL) ;  // Phase d'extinction progressive du four.

    delay_seconds(5 * MAP_vs_REEL) ;       // Le four est arrêté, mais l'éclairage
                                           // intérieur est toujours «ON».
    Eclairage_interieur(OFF) ;

    delay_seconds(20 * MAP_vs_REEL) ;     
 // Cette tempo doit être ajustée pour
                                           // laisser à la séquence sonore de se
                                           // terminer naturellement.
    myDFPlayer.stop() ;  
    }
  }
 
Tout d'abord, «void loop()» initialise 4 variables globales et le volume du son.

Ces initialisations n'auraient-elles pas pu figurer dans «setup()» ?
Non.
Les variables en question doivent impérativement contenir ces valeurs là au départ de la boucle void loop().
Or, en cours d'exécution du programme, ces valeurs ont changé.
À la fin du cycle ou suite à un arrêt imposé par télécommande, la boucle void loop() redémarre.
Si l'initialisation de ces variables se fait dans void setup(), la boucle void loop() redémarre avec des valeurs incorrectes et le programme fonctionne mal ou pas du tout.

int MAP_vs_REEL :

C'est une variable locale qui sert de multiplicateur des paramètres de temporisations dans les différentes fonctions.
MAP_vs_REEL prendra la valeur «1» pendant la phase de mise au point du programme, pour raccourcir les cycles.
MAP_vs_REEL prendra la valeur «10» (ou 8, ou 12...) en exploitation normale.

Exemple : 
La temporisation delay_seconds(5 * MAP_vs_REEL) ;  dure 5 secondes en mode de mise au point, et 50 secondes en fonctionnement normal.

En gros, la séquence complète du programme dure 10 fois moins longtemps en mode de mise au point, ce qui permet de vérifier plus rapidement le réglage d'un nouveau cycle.



Fonction personnelle «int Eclairage_interieur(int state)»
 
Fonction avec passage d'argument pour éclairer ou éteindre l'intérieur de la maison.
 
int Eclairage_interieur(int state)
   {
   Serial.println("Allumage/extinction éclairage intérieur") ;

   analogWrite (LED_interieure, state) ;  
// niveau d'éclairement réglé par la
                                          // valeur de la variable «ON».
   }

 

Cette fonction est appelée ainsi :
  • Eclairage_interieur(ON) ;  pour allumer la LED_interieure

Dans void setup(), «ON» a été réglé à la valeur «255».
Cette valeur est transmise à la fonction, et stockée dans la variable locale int state.

analogWrite(LED_interieure, state) ; allume la LED avec l'intensité 255 (c'est le maximum).
Si on désire une intensité moins élevée, il suffit par exemple de régler ON = 120 dans void setup().
  • Eclairage_interieur(OFF) ;  pour éteindre la LED.
 
Dans void setup(), «OFF» a été réglé à la valeur «0».
Note :
On aurait pus se passer d'une fonction aussi simple et écrire directement dans void loop() :
analogWrite (LED_interieure, ON) ;
analogWrite (LED_interieure, OFF) ;


Fonction personnelle «int Allumage_four_a_pain(int coef)»
 
Fonction avec passage d'argument pour éclairer progressivement le four.

C'est la première fonction de l'animation lumineuse appelée par «void loop()».
 
int  Allumage_four_a_pain(int coef) 
  {
  if (flag == false) {return ; }  
 
  for (int Luminosite = 0 ; Luminosite <= 160 ; Luminosite++)
      {
      analogWrite(LED_four_a_pain, Luminosite);
// LED rouge
      delay(5 * coef) ;
      Interception_du_code_IR_pour_interrompre_le_programme() ;
      if (flag == false)                 
// En cas d'arrêt forcé...
         {
         analogWrite(LED_four_a_pain, 0) ;
// on éteint la LED du four ;
         Eclairage_interieur(OFF) ;       // on éteint la LED intérieure, au cas où...
         loop() ; }
         }
      }
  }

Par le passage d'argument, coef  vaut MAP_vs_REEL dont la valeur a été fixée dans void loop().

coef est utilisé par la temporisation delay(5 * coef) ;
Il s'agit du délai exprimé en millisecondes qui règle la rapidité de croissance de l'intensité du feu.


Interception du code IR :

L'appel à cette fonction surlignée en vert est répété dans chacune des fonctions :
  • allumage de fonctionnement du four ;
  • fonctionnement du four ;
  • extinction du four,

Qu'on saisisse ou pas un code, l'effet de cette fonction est invisible sur le déroulement de l'animation lumineuse.
 
Si le code 2 + POWER est intercepté pendant cette phase, alors flag == false :
⇒ le four est éteint ;
⇒ l'éclairage intérieur est éteint (au cas où...) ;
⇒ la phase d'allumage du four est immédiatement interrompue ;
⇒ le programme revient au début de void loop().

 


Fonction personnelle «int Extinction_four_a_pain(int coef)»
 
Fonction avec passage d'argument pour éteindre progressivement le four.
 
int Extinction_four_a_pain(int coef) 
  {
  for (int Luminosite = 160 ; Luminosite >= 0 ; Luminosite--)
      {
      analogWrite(LED_four_a_pain, Luminosite);
// LED rouge
      delay(30 * coef) ;
      Interception_du_code_IR_pour_interrompre_le_programme() ;
      if (flag == false)
    
         {
         analogWrite(LED_four_a_pain, 0) ;
         Eclairage_interieur(OFF) ; 
         loop() ;
     
    }
      }
  }

Cette fonction est très semblable à la fonction d'allumage, avec tout de même une particularité : le test de la variable booléenne flag.

Au départ du programme, flag == true.
 
Si le code 2 + POWER est intercepté pendant cette phase, alors flag == false :

⇒ le four est éteint ;
⇒ l'éclairage intérieur est éteint (au cas où...) ;
⇒ la phase d'allumage du four est immédiatement interrompue ;
⇒ le programme revient au début de void loop().


Fonction personnelle
«int Flame_four_a_pain(int Duree_allumage_du_four_a_pain)»

 
Fonction avec passage d'argument pour faire rougeoyer la LED du four.
 
int Flame_four_a_pain(int Duree_allumage_du_four_a_pain) 
  {
  Temps_IN = millis() / 1000 ; 
   // Enregistre l'instant d'entrée dans la fonction
 
  while ((Temps_OUT - Temps_IN) <= Duree_allumage_du_four_a_pain)
      {
      if (flag == false) {return ;}
      analogWrite(LED_four_a_pain, (random(10 , 16) * 10));
// LED rouge 
      delay((random(1, 4) * 24));  // Ces deux instructions provoquent le
                                   // rougeoiement aléatoire de la LED du four.
      
      Interception_du_code_IR_pour_interrompre_le_programme() ;
     
      Temps_OUT = millis() / 1000 ;
// À chaque tour dans la boucle «while(...)»
                                   // le temps courant est enregistré dans «Temps_OUT»
      }
  }
Si le code 2 + POWER est intercepté pendant cette phase, alors flag == false :

⇒ le four est éteint ;
⇒ l'éclairage intérieur est éteint (au cas où...) ;
⇒ la phase d'allumage du four est immédiatement interrompue ;
⇒ le programme revient au début de void loop().

Dans void loop(), l'appel de la fonction Flame_four_a_pain(...) ;  passe l'argument 20 * MAP_vs_REEL qui est la durée de fonctionnement souhaitée (exprimée en secondes) pour le rougeoiement de la flame du four.

La fonction int Flame_four_a_pain(...) stocke cette valeur dans int Duree_allumage_du_four_a_pain.

La boucle while(...) estparcourue tant que (Temps_OUT - Temps_IN) <= Duree_allumage_du_four_a_pain
 


Fonction personnelle
«void Interception_du_code_IR_pour_interrompre_le_programme() )»

 
... comme son nom l'indique...

Cette fonction est appelée par les fonctions :
  • Allumage_four_a_pain() ;
  • Flame_four_a_pain() ;
  • Extinction_four_a_pain() ;
  • delay_secons() ;

Ce sont les seules fonctions «bloquantes» du programme.
C'est donc pendant leur déroulement qu'il faut pouvoir intercepter un code d'arrêt.
 
void Interception_du_code_IR_pour_interrompre_le_programme() 
  {
  Code_a_2_chiffres = !OK ;
  flag = true ;
  results.value = 999 ;
  irrecv.resume(); 
  delay(200) ; 
   

    // Saisie et traitement du premier chiffre.
    if (irrecv.decode(&results)) // Y a-t-il un code IR reçu ?
     {
     delay(400) ;  
     irrecv.resume();
  // Receive the next value
     }

    if (results.value == Touche_2)
// Est-ce que ce code est celui de la touche «2» ?
     {        
     results.value = 999 ;
// Si «oui», on prépare results.value pour le code suivant
        
     
// Saisie et traitement du deuxième chiffre.        
     Temps_IN = millis() ;
     while (!(irrecv.decode(&results)))  
 // Attend indéfiniment (sans rien faire)
                                           // la réception du 2e code.
        {
        // boucle d'attente
        delay(10) ;
        Temps_OUT = millis() ;
        if ((Temps_OUT - Temps_IN) >= 1200) 
// Si aucun 2e code détecté avant
                                             // 1200 millisecondes, on quitte
                                             // la boucle «while»...
               {
              
// Serial.println("Temps d'attente dépassé") ;
               results.value = 999 ;         // ... en imposant un code non valide.
               break ;
               } 
        } 
// fin de la boucle d'attente

     // Traitement du 2e code       
      if (results.value == Touche_POWER) // Code d'arrêt
          {
          flag = false ;       
// sera utilisé dans les fonctions concernant le four 
          delay(1000) ;       
// Délai minimal imposé avant un nouveau code de départ
          results.value = 999 ;// On prépare results.value à recevoir une vraie valeur
          return(0) ;
          }                  
      if (results.value == Touche_1)
 // Code pour mettre le volume à 23 ;
          {
          delay(100) ; myDFPlayer.volume(23); return(0) ;
          }
      if (results.value == Touche_2)
 // Code pour mettre le volume à 25 ;
          {
          delay(100) ; myDFPlayer.volume(25); return(0) ;
          } 
      if (results.value == Touche_3)  
// Code pour mettre le volume à 27 ;
          {
          delay(100) ; myDFPlayer.volume(27); return(0) ;
          }               
     }
     return ;       
// Si le premier code saisi n'était pas celui de la touche «2»
                     // alors on sort tout de suite de la fonction.
}

Cette fonction ne traite pas que le code d'arrêt 2 + POWER.
 
Puisque cette fonction intercepte un code IR pendant le déroulement des fonctions listées ci-dessus, pour ne pas se donner la possibilité de transmettre un ordre de modification du volume sonore (par exemple) ?
Ainsi, les codes 21, 22 et 23 établissent le volume sonore à 23, 25 et 27 respectivement.
Lorsque le code d'arrêt 2 + POWER est reçu, flag devient false.
flag == false est utilisé dans les 4 fonctions listées ci-dessus pour stopper immédiatement le programme et revenir au départ de void loop() ;


Fonction personnelle
«void Interception_du_code_IR_pour_le_demarrage() )»

 
... comme son nom l'indique...

Cette fonction est appelée au tout début de void loop().

Elle attend le code 2 + POWER pour démarrer l'animation.
 
void Interception_du_code_IR_SONY_pour_le_demarrage() 
  {

  // Code 20 (Touche_2 ; Touche_0) : code de mise en route ;

  Code_a_2_chiffres = !OK ;          // ... pour être sûr de ne pas transmettre une
                                     // fausse information en cas de code incorrect
  irrecv.resume(); 

  // Saisie et traitement du code IR du premier chiffre
  while (!(irrecv.decode(&results))) // Attend indéfiniment (sans rien faire) la
                                     // réception du premier code.
     { 
     
// boucle d'attente
     }
  numero = numero + 1 ;             
// A chaque réception d'un code, le numero
                                     // de la séquence sonore est incrémenté.
  Numero_du_son = numero % Nombre_de_sons + 1 ;  // Si par ex. la carte «T» contient 4
                                     // séquences sonores, «numero» doit être
                                     // transformé en un nombre
                                     // compris entre 1 et 4 inclus.
      Serial.print("Premier chiffre : ");
      Serial.println(results.value);
// Affiche de contrôle du code reçu (facultatif).

  // Saisie et traitement du code IR du 2e chiffre  
  if (results.value !
= Touche_2)  {loop() ; }
  else                             
  // Si le code est celui de la touche «2»,
                                     // alors on attend le 2e code.
        {
        results.value = 999 ;
        delay(300) ;
        irrecv.resume();                          
// Receive the next value 
               
        Temps_IN = millis() / 1000 ;
        while (!(irrecv.decode(&results)))        
// Attend sans rien faire) la
                                                   // réception du 2e code.
              {
           
  // boucle d'attente
              delay(5) ;
              Temps_OUT = millis() / 1000 ;
              if ((Temps_OUT - Temps_IN) > 2)
// Si aucun code détecté avant 2
                                              // secondes, on quitte la boucle «while»
                  {
                  results.value = 999 ;      
// ... en imposant un code non valide.
                  loop() ;
                  } 
              }
        Serial.print("Deuxième chiffre ⇒ ");
        Serial.println(results.value);       
// Impression de contrôle (facultatif)
           
        if (results.value == Touche_POWER) 
  // Code de mise en route
          {
          Code_a_2_chiffres = OK ;     
          }         
    }
    delay(1000) ;                            
     // Délai avant une 2e tentative.

Si les deux codes sont corrects, le programme quitte la fonction avec Code_a_2_chiffres == OK.

Ce code est testé dans void loop() pour autoriser le démarrage de la séquence d'animation.
 



Pour résumer...
 
void loop() :
  • initialisation de 4 variables globales pour permettre un éventuel redémarrage du programme dans les conditions requises ;
  • appel de la fonction de saisie du code de démarrage ;

Si ce code est OK :
  • démarrage de la séquence sonore. Le DFPlayer la joue de façon autonome.
  • après une temporisation de 50 secondes, le four à pain s'allume progressivement pendant une durée approximative de 8 secondes) ;
    (la temporisation est modifiable dans void loop()  avec MAP_vs_REEL).
  • le four à pain fonctionne à plein régime pendant 200 secondes (modifiable...) ;
  • allumage de l'éclairage intérieur... mais cette action arrête le four à pain ;
  • relance du mode plein régime pour environ 20 secondes (modifiable...) ;
  • extinction progressive du four à pain (durée approximative : 50 secondes) ;
  • après une temporisation de 50 secondes, l'éclairage intérieur est éteint ;
  • après une temporisations de 200 secondes, l'ordre est donné au DFPlayer de couper le son.

Note :
Dans le cas présent, la durée totale du cycle est environ (50+8+200+20+50+50+200) = 578 secondes, soit environ 9 minutes et 40 secondes.
Il faut s'assurer que cette durée est juste supérieure à la durée de la séquence sonore.



Fonctions concernant le four :
(Allumage, flame, extinction)

Ces fonctions, ainsi que les fonctions delay_seconds(xx * MAP_vs_REEL) ; contiennent toutes l'appel à la fonction :
int Interception_du_code_IR_pour_interrompre_le_programme() ;

C'est ce qui permet d'intercepter un code d'arrêt, ou un des codes de modification du volume sonore.
Looney_tunes.png

That's all Folks !
 
 
Feu_de_camp_4.jpg
Nombre de visites :
Compteur de visites

FEU de CAMP

  • 1 séquence sonore d'environ 30 minutes (dans mon application) ;
  • animation lumineuse du feu ;
  • télécommande infrarouge.

Date de création : 04/02/2022

Dernière modification : 04/02/2022

PRÉSENTATION DU PROJET

Comme dans le projet précédent, il n'y a qu'une seule séquence sonore, essentiellement constituée du crépitement du foyer, mais à laquelle j'ai superposé des chants et le cris des coyotes dans le lointain...

Le programme se déroule en 4 phases :

Première phase : le feu démarre progressivement 

⇒ le rougeoiement s'accentue ;
⇒ le volume du crépitement augmente.

Deuxième phase : 

⇒ le rougeoiement garde ses valeurs maximales ;
⇒ le volume du son reste constant ;

Troisième phase : le feu s'éteint progressivement

⇒ le rougeoiement diminue d'intensité ;
⇒ le son des crépitement diminue d'intensité puis s'arrête ;

Quatrième phase : le foyer rougit de façon décroissante, pour simuler les braises qui refroidissent lentement.

Les durées de ces phases sont paramétrables.

Commandes :

  • le code 5 + POWER provoque le démarrage du cycle ;

  • le code 5 + POWER provoque aussi l'arrêt brutal du cycle ;

  • le code 50 interrompt la phase en cours d'exécution et démarre la phase d'extinction.

MATÉRIEL
 
  • x1 platine de prototypage  420 points ;
  • x1 DFPlayer Mini ;
  • x1 carte «T» (micro SD) ;
  • x1 ARDUINO Nano (*) ;
  • X1 récepteur infrarouge VS1838B ;
  • x1 télécommande SONY TV compatible ;
  • x3 résistances 1/4 watt ;
  • x1 bornier à vis pour la sortie HP ;
  • connecteurs supports pour le DFPlayer et l'ARDUINO Nano ;
  • x1 HP ou mini-enceinte ;
  • x1 bloc d'alimentation 5 V DC ;
(*) un ARDUINO Nano et un DFPlayer montés sur une une platine de prototypage donne un montage bien moins encombrant qu'un ARDUINO UNO + un shield + un DFPlayer monté sur le shield.
VS1838B_2-300x300.png
SCHÉMA
Feu_de_camp_1.png
 
VS1838

Il est relié à la platine de prototypage par 3 fils :
  • le + 5 V DC ;
  • la masse (GND) ;
  • le signal.
 
Les 2 résistances et les 2 condensateurs sont montés sur la platine de prototypage.
 
Attention : 
Si la liaison filaire entre le VS1838 et la platine excède 3 à 4 cm, les 2 condensateurs sont indispensables.
En effet, j'ai remarqué que si cette liaison est trop longue, la réception infrarouge reçoit des signaux parasites qui perturbent la détection des vrais codes et donc gênent le fonctionnement correct du programme.

DFPlayer
 
  • il reçoit les commandes en provenance de l'ARDUINO sur son entrée «Rx».
  • cette entrée «Rx» est reliée à la pin «12» de l'ARDUINO par une résistance de 1k (indispensable).
  • la pin «12» de l'ARDUINO joue le rôle de «Tx» vers le DFPlayer.
     
LED 1 et LED 2 sont en fait une seule LED bicolore jaune + rouge, en diamètre 3 mm. Sa petite taille permet de la loger facilement sous les bûches du foyer.​
LED 3 et LED 4

 

Petite particularité :

En cours d déroulement du programme, le code d'arrêt ou le code qui permet d'écourter la séquence n'est pas forcément intercepté au moment où il est émis.

Ceci est dû à la fonction personnelle void Flame().

L'exécution de cette fonction dure entre environ 50 ms et 200 ms ⇒ si l'émission du code intervient pendant ce laps de temps, le code n'est pas reçu et la commande est inopérante. 

On doit réitérer la commande, mais sans avoir plus de certitude qu'elle sera prise en compte.

LED 3 sur la sortie 13 :

  • elle s'allume en rouge si le premier code de démarrage est correct ;

  • elle s'éteint et se rallume si le deuxième code est correct ;

  • elle reste éteinte si un code erroné est envoyé.

LED 3 sur la sortie A0 :

  • elle s'allume en jaune si le premier code de démarrage (correct) a pu être intercepté ;

  • elle s'éteint et se rallume si le deuxième code est correct ;

  • elle s'éteint après le temps d'attent si le 2e code n'a pas été saisi ;

  • elle reste éteinte si un code erroné est envoyé.

PLATINE DE PROTOTYPAGE
On utilise la même platine de prototypage que pour les autres animations.

Les composants et les ponts représentés sur le breadboard ci-dessous seront disposés aux mêmes emplacements sur la platine de prototypage.
 
Feu_de_camp_2.png
 
En gris : les emplacements
  • de l'ARDUINO NANO ;
  • du DFPlayer ;
  • du connecteur pour le VS1838.
  • du HP ;
Entouré en bleu :
(Éventuellement) 2 cosses pour raccorder un jack 5,5 x 2,1 sur cordon, pour alimenter le montage en 5 V DC.
Entouré en vert :
Le condensateur de 100 nF.
Le condensateur chimique de 10µF sera monté à l'emplacement entouré en violet. Pour cela, la résistance de 100 ohms sera montée en J14 au lieu de I14.
En C10-C11-C12-C13
Représenté en gris, c'est le petit connecteur à 4 broches de wrapping pour connecter les 2 LED du foyer.
En H14-15-16-17
Connecteur wrapping à 4 broches pour le capteur VS1838.
À l'extérieur
Les LED 3 et 4 qui permettent de visionner la réception d'un code correct de démarrage ou d'arrêt.
LE CODE C++
Petit préambule :

L'application utilise :
  • le code 5 + POWER pour démarrer ou arrêter le cycle ;
  • le code 50 pour écourter la séquence, en respectant la phase d'extinction.

Le code, disponible en téléchargement, contient des commentaires très complets.
Ils ne sont pas reproduits ci-dessous : inutile de surcharger la présentation du code.
 

#include et déclarations de variables
 
#include <IRremote.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h> 
// Bibliothèque à télécharger.

SoftwareSerial mySoftwareSerial(11,12); // la broche «11» de l'ARDUINO est utilisée
             //comme «Rx» pour communiquer avec la broche «Tx» du DFPlayer
             // la broche «12» de l'ARDUINO est utilisée comme «Tx» pour
             //communiquer avec la broche «Rx» du DFPlayer.
             // Note : la liaison «Rx ARDUINO» --> «Tx DFPlayer» n'est pas
             // utilisée dans ce programme.
                                        
DFRobotDFPlayerMini myDFPlayer;      
  // Déclare une instance pour le DFPlayer.

int IRpin = 2;                          // Entrée du capteur IR
IRrecv irrecv(IRpin);
decode_results results;

const int LED_jaune = 9 ;
const int LED_rouge = 6 ;
const int LED_POWER = 13 ;  // Rouge
const int LED_STOP = A0 ;   // Jaune

int Numero_du_son ;
int Nombre_de_sons ;
int Volume ;

int Duree_phase_plein_feu ; 


//const int  Touche_9 = 272 ; 
//const int  Touche_8 = 3600 ;
//const int  Touche_7 = 1552 ;
//const int  Touche_6 = 2576 ;
                    const int  Touche_5 = 528 ;
//const int  Touche_4 = 3088 ;
//const int  Touche_3 = 1040 ;
//const int  Touche_2 = 2064 ;
//const int  Touche_1 = 16 ;
                    const int  Touche_0 = 2320 ;
//const int  Touche_Up = 1904 ;  Ces touches ne sont pas utilisées dans ce programme.
//const int  Touche_Down = 3952 ;
//const int  Touche_Left = 2800 ;
//const int  Touche_Right = 752 ;
//const int  Touche_VolUp = 1168 ;
//const int  Touche_VolDown = 3216 ;
//const int  Touche_CH_Up = 144 ;
//const int  Touche_CH_Down = 2192 ;
                    const int  Touche_POWER = 2704 ;
//const int  Touche_Menu = 112 ;

int Code_a_2_chiffres ;
int OK ;            // Utilisé comme quittance quand le code à 2 chiffres reçu
                    // est celui qui est attendu.

int Luminosite ;    // concerne le foyer.
int Luminosite_minimale ;  // luminosité avec laquelle débute la phase finale
                           // de rougeoiement.

int numero ;        // variable pour choisir un son différent à chaque
                    // redémarrage du programme.

bool flag ;         // flag = TRUE ⇒ les séquences allumage - plein feu - extinction
                    // s'enchaînent normalement.
long Temps_IN ;     // pour mesurer la durée des phases.
long Temps_OUT ;

La bibliothèque «DFRobotDFPlayerMini» doit être téléchargée est installée.
(Elle figure dans le fichier .zip).




 
void loop()
 
void loop() est réduite à sa plus simple expression :
 
void loop() 
  {
  results.value = 0 ;      
// Mise à zero du code reçu.
 irrecv.enableIRIn();​
  flag = true ;            // flag = false : lorsque le code d'arrêt est reçu
                           // ⇒ le déroulement est écourté.
  OK = true ;              // ... si le code à 2 chiffres reçu est le code attendu.

  digitalWrite(LED_POWER, LOW) ; // Initialise les 2 LED qui servent à visualiser la
  digitalWrite(LED_STOP, LOW) ;  // réception des codes émis.

  Luminosite = 0;
  Luminosite_minimale = 6;
// Niveau de luminosité du début de la phase finale
                           // de rougeoiement du foyer. 

  numero = numero + 1 ;    // A chaque réception d'un code, le numero est incrémenté
  Numero_du_son = numero % Nombre_de_sons + 1 ;    // Si la carte «T» contient
     // 4 sons, «numero» doit être transformé en un nombre compris entre 1 et 4.

  Volume = 0 ; 
  myDFPlayer.reset() ;
  myDFPlayer.volume(Volume);

    Serial.println("-------------------------") ;
    Serial.println("Attente du code démarrage") ; 

   
 irrecv.resume(); delay(200) ;
  Interception_du_code_IR_SONY_pour_le_demarrage() ;

 if (Code_a_2_chiffres == OK) 
    {
    myDFPlayer.play(Numero_du_son);
       Serial.print("Numéro du son = ") ;
       Serial.println(Numero_du_son) ;
    delay(500) ;
    Phase_d_allumage();
    Phase_plein_feu();
    Phase_d_extinction();   
    }
  }

Les lignes «Serial.print...» sont des éléments de contrôle sur le moniteur, bien utiles pendant la mise au point.
 

Que fait cette boucle «void loop()» ?
Tout d'abord 8 paramètres sont initialisés.
Pourquoi ici et pas dans void setup() ?
⇒ pour garantir la valeur de ces paramètres si on relance le programme sans avoir coupé l'alimentation.
Ensuite, Interception_du_code_IR_SONY_pour_le_demarrage() ; teste la validité du code de démarrage.
Si Code_a_2_chiffres == OK, alors la séquence d'animation démarre :
  • le DFPlayer commence à jouer le son Numero_du_son. Cette variable n'est utile que si on envisage d'avoir plusieurs séquences sonores différentes (histoire de ne pas toujours entendre le même chant au même endroit, si on a élaboré une séquence complexe).
  • les 3 phases s'enchaînent.

 
void setup()
 
void setup() 
  {
  irrecv.enableIRIn();                    // initialise la réception infrarouge.

  pinMode(6, OUTPUT);                     // LED rouge ...
  pinMode(9, OUTPUT);                     // et LED jaune du foyer
  pinMode(LED_POWER, OUTPUT) ;            // LED qui indique la bonne réception du
                                          // code de démarrage.
  pinMode(LED_STOP, OUTPUT) ;             // LED qui indique la bonne réception du
                                          // code d'arrêt.

  mySoftwareSerial.begin(9600);           // pour communiquer avec le DFPlayer
  myDFPlayer.begin(mySoftwareSerial);
 
  //  ----EQUALIZER : PARAMETRAGES POSSIBLES
  //  myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
  //  myDFPlayer.EQ(DFPLAYER_EQ_POP);
  //  myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
  myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
  //  myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
  //  myDFPlayer.EQ(DFPLAYER_EQ_BASS);

  Volume = 0;                        // variable pour le contrôle du volume sonore
                                     // au cours des différentes phases.
  numero = -1 ; 

  Serial.begin(9600);                // Pour communiquer avec le moniteur
  pinMode(13, OUTPUT);               // LED de contrôle de la réception d'un code.

  Nombre_de_sons = 8) ;              // dans mon cas.
  // C'est le nombre de séquences sonores enregistrées sur la carte «T».


  Duree_phase_plein_feu = 14000 ;    // = 1000 pour environ 100 secondes ;
                                     // = 7000 pour 700 secondes, càd environ 11 mn ;
                                     // = 100 pour les tests.
                                     // Avec 14000, la séquence «Plein feu» dure
                                     // environ 23 minutes.
  }

La ligne «myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);» est intéressante.
DFPlayer possède un equalizer avec des réglages de tonalité sonore pré-établis.

«EQ_JAZZ» permet de renforcer les aigus, par rapport au mode «EQ_NORMAL» ⇒ utile quand le HP ou l'enceinte est planquée sous la table du réseau ou dans une montagne en polystyrène extrudé !

 

Fonction personnelle
«void Interception_du_code_IR_SONY_pour_le_demarrage()»

 
void Interception_du_code_IR_SONY_pour_le_demarrage() 
  {

  // Pour cette application : code de mise en route : (Touche_5 ; Touche_POWER)  
    
  Code_a_2_chiffres = !OK ;
 

  // Premier chiffre
  while (!(irrecv.decode(&results)))
     { 
     
// Attend indéfiniment (sans rien faire) la réception du premier code.
     }

      Serial.print("Premier chiffre : ");
      Serial.println(results.value);        
 //Affiche le code reçu.
  
if (results.value != Touche_5)                   
        {
        myDFPlayer.volume(10) ;
        myDFPlayer.play(10) ;    
// Joue un son de coyote quand le 1er code est faux
                                  // pour avertir qu'on s'est trompé.
        irrecv.resume() ;
        delay(5000) ;            
// C'est la durée du son n°10
        myDFPlayer.volume(0) ;
        loop() ;
        }
 
  else if (results.value == Touche_5)    
// Si le code est celui de la touche «1»,
                                          // alors on attend le 2e code.
        {
        digitalWrite(LED_POWER, HIGH) ;  
// Indique que le 1er code reçu est correct.
        delay(300) ;
        irrecv.resume();                  
// Receive the next value




        //Deuxième chiffre              
        Temps_IN = millis() ;
        while (!(irrecv.decode(&results)))  
 // Attend indéfiniment (sans rien faire)
                                              // la réception du 2e code.
              {
           
  // boucle d'attente
              delay(5) ;
              Temps_OUT = millis() ;
              if ((Temps_OUT - Temps_IN) > 1200)
 // Si aucun 2e code détecté avant
                                                  // 1200 ms, on quitte la boucle
                  {
                  break ;
                  } 
              }
        Serial.print("Deuxième chiffre de démarrage ⇒ ");
        Serial.println(results.value); 
           
        if (results.value != Touche_POWER)  
// Si le code est celui de la touche «2»,
                                             // alors «Code_a_2_chiffres» = OK
          {
          myDFPlayer.volume(10) ;           
// Joue un son de coyote quand le 2e code
                                             // est faux
          myDFPlayer.play(10) ;              // ou si le temps d'attente est dépassé.
                                             // pour avertir qu'on s'est trompé.
          irrecv.resume() ;                          
          delay(5000) ;                      // C'est la durée du son n°10
          myDFPlayer.volume(0) ;
          loop() ;                           
// retour au début du programme.       
          }
        else  
          {
          analogWrite(LED_POWER, 0) ; delay(200) ; analogWrite(LED_POWER, 255) ;

          // Indique que le 2e code reçu est correct.
          irrecv.resume() ; 
          Code_a_2_chiffres = OK ; 
          }         
    }
    delay(1000) ;                           
 // Delai avant une 2e tentative.
}   


Fonction personnelle
«void Interception_du_code_pour_interrompre_le_programme()()»

 
void Interception_du_code_pour_interrompre_le_programme() 
    {

    // Pour cette application :
    // -- code pour écourter    : 50 (Touche_5 ; Touche_0). 
    // -- code pour stopper     : 59 (Touche_5 ; Touche_POWER)
      
    flag = true ;
 
        if (irrecv.decode(&results))  
  // Réception du premier chiffre.
      {
      Serial.print("Premier chiffre d'arrêt ⇒ ");
      Serial.println(results.value); 
      delay(400) ;                      
//Pour laisser le temps de relâcher la touche.
      }
    else {return ;}

     if (results.value == Touche_5)     
// Traitement du premier chiffre.
        { 
        analogWrite(LED_STOP, 255) ;   
// Indique que le 1er code reçu est correct.
        
        irrecv.resume(); 
       

        // Saisie et traitement du deuxième chiffre. 
        Serial.println(" -- Attente deuxième chiffre d'arrêt") ;
        
        Temps_IN = millis() ;
        while (!(irrecv.decode(&results)))     
              {

              // boucle d'attente
              delay(10) ;
              Temps_OUT = millis() ;
              if ((Temps_OUT - Temps_IN) >= 1200)  
// Si aucun 2e code détecté
                             // avant 1200 millisecondes, on quitte la boucle «while»
                  {
                  Serial.println("Temps d'attente dépassé") ;
                  analogWrite(LED_STOP, 0) ;      
// La LED s'éteint si le temps
                                                   // d'attente est dépassé.
                  return ;
                  } 
              }
              Serial.print("Deuxième chiffre d'arrêt⇒ ");
              Serial.println(results.value);
    
      if (results.value == Touche_0)    
// Pour écourter la séquence
          {
          analogWrite(LED_STOP, 0) ; delay(200) ; analogWrite(LED_STOP, 255) ;

          // Indique que le 2e code reçu est correct.
          
          flag = false ;
          delay(500);
          return ;
          } 

      if (results.value == Touche_POWER)
          {
          digitalWrite(6, LOW) ;        
 // Extinction forcée des deux LED.
          digitalWrite(9, LOW) ; 
          loop() ;                      
 // ... et retour au début du programme.
          
          }                    
     }  
   // Fin de if(results.value == Touche_5)
return ;    
}           //
Fin de la fonction

Rien de particulier.
Tout est expliqué dans les commentaires.

Les deux instructions surlignées en jaune sont facultatives.
Cependant, elles sont TRES utiles pour connaître le code de chacune des touches de la télécommande.

 

Fonction personnelle «void Duree_phase()»
 
void Duree_phase()    // Affichage de contrôle de la durée de la phase qui se termine.
  {
  Serial.print("Durée de la phase = ") ;
  Serial.print(Temps_OUT - Temps_IN) ;
  Serial.println(" secondes") ;  
  }

Ce n'est qu'une fonction d'affichage sur moniteur, utilisée en phase de mise au point du programme.

 

Fonction personnelle «void Phase_d_allumage()»
 

void Phase_d_allumage()           // Ainsi paramétrée, elle dure environ 125 secondes.
    { 
    Temps_IN = millis() / 1000 ;   
    
    Serial.println("Démarrage phase d'allumage") ; 


    myDFPlayer.volume(0) ;       // niveau sonore de départ.
    
    for (Luminosite = 1 ; Luminosite <= 252 ; Luminosite++ ) 
       { 
       Volume = (Luminosite / 12) + 10 ;       
// règle le volume du son en fonction
                                               // de l'intensité du feu.

       if ( (Luminosite % 42) == 0 || (Luminosite % 42) == 6 )
     
 // Le volume sonore est modifié seulement 6 fois pendant la phase d'allumage
       // ... pour contrer la lenteur d'exécution de la fonction myDFPlayer.volume().
          {                                                        
          myDFPlayer.volume(Volume);
          }
                 
       for (int counter = 1 ; counter <= 5 ; counter++)  
// pour rallonger la durée de
                                                         // la phase d'allumage.
                                                         // (Normal = 5).
          {
          Flame() ;              
 // C'est la fonction qui fait rougeoyer le feu.
          }                                                  

       Interception_du_code_pour_interrompre_le_programme(); 
//comme son nom l'indique
         if (flag == false) {return ; }   // Si code reçu = 50 ⇒ permet d'ignorer la
                                          // phase «Plein feu» et passe directement
                                          // à la phase «Extinction».
       }           
    Temps_OUT = millis() / 1000 ;
    Serial.println("Fin de la phase d'allumage") ; 

    Duree_phase() ;       // Petite fonction d'affichage (utilisée dans les 3 phases).
}

Un petit mot sur l'usage de flag :

Dans Interception_du_code_pour_interrompre_le_programme(), 2 codes sont traités :
  • le code 50 qui met flag = false, pour écourter l'animation ;
  • le code 5 + POWER qui renvoie directement à void loop() pour arrêter brutalement l'animation, quelle que soit la phase pendant laquelle il est saisi (y compris la phase d'extinction).

Si le code 50 est envoyé pendant la phase d'allumage, alors :
  • la phase d'allumage est écourtée ;
  • la phase plein feu démarre et teste tout de suite l'état du flag ;
⇒ comme flag = false, la fonction n'est pas exécutée ;
  • la phase d'extinction démarre ;
⇒ la décroissance de l'intensité du feu a lieu normalement mais en partant du niveau de luminosité qui était celui en quittant la phase d'allumage, ce qui évite de voir un sursaut anormal d'intensité ;
 
Si le code 50 est envoyé pendant la phase plein feu, alors :
  • la phase plein feu est écourtée ;
  • la phase d'extinction démarre ;
⇒ la décroissance de l'intensité du feu a lieu normalement mais en partant du niveau de luminosité qui était celui en quittant la phase plein feu, ce qui évite de voir un sursaut anormal d'intensité ;

Si le code 50 est envoyé pendant la phase d'extinction, il est sans effet.

 

Fonction personnelle «Flame()»
 
void Flame() 
  {
 
analogWrite(LED_rouge, (random(Luminosite_minimale, Luminosite + Luminosite_minimale) * 1)); // LED rouge
  delay((random(1, 4) * 24));
  analogWrite(LED_jaune, (random(Luminosite_minimale, Luminosite + Luminosite_minimale) * 1));
 // LED jaune
  delay((random(1, 4) * 24)); 
  }



Fonction personnelle «void Phase_plein_feu()»
 
void Phase_plein_feu() 
    { 
    Temps_IN = millis() / 1000 ;  
    
    Serial.println("Démarrage phase «plein feu»") ;
// Peut être supprimée.

    results.value = 0 ;
    
    if (flag == false) {return ; }
    
    for (int count = 0; count <= Duree_phase_plein_feu ; count++) 
       {
       Interception_du_code_pour_interrompre_le_programme();
       if (flag == false) {return ; }     
// Si code reçu = 50 ⇒ passe directement
                                          // à la phase «Extinction»
         
       Flame();
       } 
     Temps_OUT = millis() / 1000 ;
     Serial.println("Fin de la phase «plein feu»")
     Duree_phase() ;       
// Petite fonction d'affichage (utilisée dans les 3 phases).
}



Fonction personnelle «void Phase_d_extinction()»
 
void Phase_d_extinction() 
  {
  Serial.println("Démarrage phase d'extinction") ;
 
  Temps_IN = millis() / 1000 ;
  for (Luminosite = Luminosite ; Luminosite >= Luminosite_minimale ; Luminosite-- ) 
     {
     Volume = (Luminosite / 12) + 10 ;
     if ( (Luminosite % 42) == 0 || (Luminosite % 42) == 6 ) 
        {
        myDFPlayer.volume(Volume);
        }
        
     for (int count = 0; count <= 4 ; count++) 
// En modifiant «count <= 4», permet de
                                               // rallonger la phase d'extinction.
       {
       Interception_du_code_pour_interrompre_le_programme();
       
       Flame(); 
       }  
// Fin de la boucle «for(int count...»
     }    // Fin de la boucle «for(Luminosite...»
     
     analogWrite(LED_jaune, 0);
// La LED jaune est forcée à l'état «éteint».
                                // Seule la LED rouge participe au rougeoiement final.

     Serial.println("Début du rougeoiement final") ;
     for (Luminosite = Luminosite_minimale ; Luminosite >= 1 ; Luminosite-- )
     
// Démarrage de la phase «rougeoiement décroissant» pour l'extinction du foyer.
        { 
        analogWrite(LED_rouge, Luminosite);
        delay(6000);
        }

     Temps_OUT = millis() / 1000 ;
     Serial.println("Fin de la phase d'extinction") ;
     Duree_phase() ;
     loop ;                                   // retour au début du programme.
}

Cette fonction, est un peu particulière.

Elle se déroule en 2 temps.

Premier temps :
  • décroissance de l'intensité du feu jusqu'à une Luminosite_minimale dont la valeur est fixée à 6 dans void loop() (mais on peut en changer) ;
  • décroissance synchronisée du volume sonore ;
  • la fonction Interception_du_code_pour_interrompre_le_programme(); teste l'éventuelle réception d'un code d'arrêt ;
  • si c'est le code 5 + POWER, alors on force les 2 LED à l'état LOW et on quitte la fonction ⇒ arrêt brutal.
⇒ retour au début de void loop().

Sinon, deuxième temps :
  • la LED jaune est forcée à l'état éteint pour ne conserver qu'une lueur rouge avec la LED rouge.
  • décroissance de la luminosité du foyer, de Luminosite_minimale jusqu'à 1.


 
COMMENT PARAMÉTRER LE PROGRAMME
Dans void loop()
Luminosite_minimale = 6 ; ⇒ régle le niveau lumineux à partir duquel le rougeoiement du foyer commence.

Dans void setup()
myDFPlayer.EQ(DFPLAYER_EQ_JAZZ) ; ⇒ 7 option différentes pour changer la tonalité de la séquence sonore. Utile en fonction du type de son et de la qualité de l'enceinte ou du HP.

Nombre_de_sons = 8 ; ⇒ à modifier en fonction du nombre de séquences sonores enregistrées.

Duree_phase_plein_feu = 14000 ; ⇒ pour allonger ou raccourcir la durée de la phase plein feu.

Dans void Phase_d_extinction()
for (int count = 0; count <= 4 ; count++) ⇒ pour allonger ou raccourcir la durée de la phase d'extinction.

Dans void Phase_d_allumage()
for (int counter = 1 ; counter <= 5 ; counter++) ⇒ pour allonger ou raccourcir la durée de la phase d'allumage.
 
Looney_tunes.png

That's all Folks !
 

Réalisations avec DFPlayer Mini