Nombre de visites :
9
 
Copie de bandeau2.jpg
Nombre de visites :
Compteur de visites
ORAGE
  • 14 séquences sonores de coups de tonnerre ;
  • éclairs aléatoires en durée et en intensité ;
  • volume sonore avec intensité et retard modulés en fonction de l'intensité des salves d'éclairs ;
  • intervalle aléatoire entre salves d'éclairs ; 
  • télécommande infrarouge.

Date de création : 01/02/2022

Dernière modification : 09/02/2022


Cette animation lumineuse et sonore est la première que j'ai réalisée.
Elle fait suite à l'animation uniquement lumineuse (et sans télécommande IR), disponible ICI, et destinée à servir d'exercice de programmation en ARDUBLOCKLY pour mes camarades de club.

Si cette animation a un sens pour agrémenter votre réseau, alors n'hésitez surtout pas : le résultat est IMPRESSIONNANT !

Bon. OK. Faut pas habiter en immeuble !
 
PRÉSENTATION DU PROJET
Plus la salve d'éclairs est lumineuse, plus elle est proche, donc plus le son arrive rapidement et plus il est intense.

C'est ce que j'ai cherché à réaliser, et je dois dire que le résultat est à hauteur de mes attentes !
  • le nombre d'éclats par salve est aléatoire.
  • l'intensité de chaque éclat est aléatoire.
  • l'intensité lumineuse globale d'une salve d'éclairs est aléatoire.
  • la séquence sonore associée à une salve est aléatoire.

Le départ et l'arrêt de l'orage est télécommandé par infrarouge.
Suivant le code de démarrage saisi, on opte pour un orage à 4, 8 ou 16 coups de tonnerre (mais on peut paramétrer différemment).

Un son de coup de tonnerre contient pas mal de graves.
Il est donc important d'utiliser une enceinte de qualité. Pas forcément volumineuse.
Celle que j'utilise est une enceinte Sony de récupération (chez Émaüs. Pub gratuite) et qui mesure environ
30 x 20 x 18 cm.

En réalité j'ai choisi d'utiliser 2 HP :
  • une enceinte planquée sous la montagne ;
  • un petit HP de diamètre 60 mm caché derrière un bâtiment, éloigné d'environ 1 mètre de l'enceinte, et qui diffuse un peu plus d'aigus tout en augmentant la spatialisation du son.

Attention toutefois à l'impédance résultante des 2 HP en parallèle.
Le petit HP fait office de tweeter : il faut l'alimenter à travers un condensateur non polarisé d'environ 2,2 µF.


 
SCHÉMA
Orage_schema_2.png

Deux particularités :

VS1838
Deux condensateurs sont raccordés sur l'entrée «3».
Leur rôle est d'empêcher que l'entrée «2» de l'ARDUINO reçoive des signaux parasites captés par les fils de liaison du VS1838 et interprétés comme de faux codes IR.

Si la liaison en question mesure moins de 2 ou 3 cm, on peut se passer de ces condensateurs.

MOSFET
L'étage de puissance qui alimente les LED est connecté à la sortie «9» de l'ARDUINO.
Cet étage sera alimenté par une source capable de délivrer 2 A sous 5 V DC, et indépendante de celle qui alimente l'ARDUINO et le DFPlayer.
En effet, même avec une alimentation commune fortement dimensionnée, les signaux PWM qui pilotent les LED avec un fort courant perturberaient – perturbent... expérience vécue – le fonctionnement de l'ARDUINO.

.
MATÉRIEL
  • x1 DFPlayer Mini ;
  • x1 carte «TF» (micro SD) ;
  • x1 ARDUINO Nano ;
  • X1 récepteur infrarouge VS1838B ;
  • x1 télécommande SONY TV compatible ;
  • x5 résistances 1/4 watt ;
  • x2 bornier à vis, pour la sortie HP et pour les LED ;
  • connecteurs supports pour le DFPlayer et l'ARDUINO Nano ;
  • x1 HP ou mini-enceinte ;
  • x1 bloc d'alimentation 4,5 ou 5 V DC ;
  • x1 bloc d'alimentation indépendant 5 V DC / 1 ou 2 A, pour l'alimentation des LED ;
 
Concernant les LED
J'écris «les» LED alors que le schéma n'en représente qu'une seule.
 
C'est que pour mon prototype j'ai utilisé un «panel» avec 9 LED directement alimenté en 5V DC comme celui représenté ci-contre.
La résistance de limitation est d'origine montée sur le panel.

Le fonctionnement est très satisfaisant mais...
... mais ce «panel» d'environ 35 x 39 mm n'est pas facile à loger dans le décor.
Il doit éclairer une bonne partie de l'arrière plan. Il doit donc être assez reculé, sans être directement visible par le spectateur.
D'autre part je voulais ajouter une composante bleue à la lumière des éclairs.

Au final j'ai opté pour une solution bien plus compacte : 2 LED de 3 watts.
Une blanche et une bleue.
Elle ne sont pas nécessairement installées côte à côte.

La LED blanche consomme environ 1 A pour 3 watts.
Elle sera alimentée à travers une résistance de 2 à 3,3 ohms.

En régime continu cette résistance R8 de 2 ohms dissiperait 2 watts.
Compte-tenu que les éclairs n'allument la LED que par salves courtes, très espacées, et à un niveau moyen qui ne dépasse pas 50% du maximum, o
n peut en fait se contenter d'un modèle dissipant 1 watt (ou même 1/2 watt) :

De même, la LED n'a pas besoin d'être montée sur un refroidisseur, pour exactement les mêmes raisons.

La LED bleue, si on l'utilise, sera alimentée à travers une résistance 1/2 watt d'une valeur de 10 à 56 ohms (ou plus) suivant l'effet désiré.
.
l5.jpg
LED 3 W.png
Einstein-2.jpg
As-tu réfléchi à la possibilité de rendre relativement indépendants les éclairs blancs et les éclairs bleus ?
informatik.gif
Ah ! Toi et ta relativité !
Jamais content...
Bon. OK... J'y réfléchirai... plus tard...


Concernant la platine de prototypage

J'ai utilisé une platine comme celle représentée ci-contre.

À l'une des extrémités elle comporte des emplacements pour connecter les alimentations.


Blocs de connexion

Pour les sorties vers les LED de puissance et le HP, j'ai utilisé des blocs à vis au pas de 5,08 mm.
 
PCB proto_2.png
Bloc de connexion 2P.png
PROTOTYPE sur BREADBOARD

L'implantation ci-dessous correspond au montage avec 2 étages de sortie à MOSFET – comme le suggère Albert – et dont le schéma et les modifications du programme sont donnés en additif à la fin de cet article.

Personnellement j'ai utilisé 2 platines de prototypage comme dans les animations précédentes, ce qui explique l'implantation sur 2 breadboards de 420 points, dessinée dans Tinkercad.

Désolé : Tinkercad ne propose pas le composant «ARDUINO Nano», ni le module DFPlayer.

 
Orage_implantation_2.png
Orage_implantation_1.png
 
(Clic sur les images pour les agrandir).

Remarques :
  • les deux alimentations de labo figurent les deux blocs d'alimentations 5 V DC.
  • les LED de puissance ne sont pas représentées. Seuls leurs connecteurs sont figurés sur le braedboard.
    Pour monter les blocs de connexion à vis il est préférable d'inverser la position du connecteur et du MOSFET, de façon à donner accès aux vis de serrage.
  • en JI-J2 et J7-J8 j'ai monté deux LED auxiliaires. Elles sont destinées à vérifier le fonctionnement du montage sans que les LED de puissance soient raccordées.
  • les MOSFET sont montés sans radiateur. Le modèle IRF540 est capable de délivrer 3 à 5 A sans nécessiter de radiateur ;
  • le connecteur du VS1838 est taillé dans une barrette wrapping au pas de 2,54 mm ;
  • (image de droite) les condensateurs C1 = 100nF et C2 = 10µF sont représentés par 2 petits traits blancs.
    Attention à la polarité du condensateur de 10µF.
  • les résistances en D2-F2, D4-F4, D8-F8 et D10-F10 seront réellement montées plus courtes en E2-F2, E4-F4, E-F8 et E10-F10 pour laisser la place de souder les petits ponts de couleur verte.

Attention 
Image de droite : entourée en rouge, l'extrémité de la résistance G?-G4 est en l'air. (Tinkercad ne permet qu'un seul empattement pour l'implantation des résistances).
Cette résistance sera en réalité montée à l'emplacement G1-G4.
Encore une précaution
Sur les dessins d'implantation, on voit que le connecteur de téléversement de l'ARDUINO Nano est tourné vers la droite, et que la carte «T» sur le DFPlayer est tourné vers la gauche.
Il faut veiller à éloigner suffisamment les deux modules pour permettre d'insérer le cordon de téléversement sans être gêné par le DFPlayer et sa carte mémoire.
LE CODE C++

Au lieu d'une seule séquence sonore, il y en a 15.

Pour chaque coup de tonnerre le programme choisit aléatoirement une séquence sonore de coup de tonnerre et détermine aléatoirement un facteur de proximité.

En fonction de cette valeur de la proximité, il calcule :
  • le volume sonore ;
  • le retard entre la salve d'éclairs et le son ;
  • l'intensité moyenne des éclairs d'une salve ;

Le son n° 015 est un peu plus long. Il «joue» le bruit de la pluie.
Il est utilisé soit directement (on n'entend pas l'orage, mais seulement la pluie qui tombe), soit à la fin de l'orage : ne sorte de retour au calme après l'orage.
 
L'application utilise les codes 10 à 14 pour démarrer.
Elle utilise les codes 19 et 1 + POWER pour stopper.

#include et déclarations de variables
 
#include <IRremote.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

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



// Seules les touches utilisées pour cette application sont «libérées» :

        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 ;
//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 ;

const int Numero_du_son_de_la_pluie = 15 ;

//--- Variables globales ---

int Numero_du_son;
int Volume;
int Retard;
int Facteur_d_intensite;
int Proximite;

int Nombre_de_sons_enregistres ;

int Duree_de_l_orage ;                  
// Détermine le nombre de salves
                                        // (et coups de tonnerre) de l'orage complet.
                                         
const int Liaison_BUSY = 10 ;         
 // Pour connaître la fin de diffusion des sons
const int MOSFET = 9 ;

SoftwareSerial mySoftwareSerial(2,3);
DFRobotDFPlayerMini myDFPlayer;

 

void loop()
 
void loop() 
{
  irrecv.resume() ;
  Attente_du_code_de_demarrage() ;

  for (int count2 = 1; count2 <= Duree_de_l_orage; count2++) 
    {
    Retard_du_son_Volume_et_Facteur_d_intensite();
    Salve();
    Coup_de_tonnerre();
    if (Attente_fin_du_son() == 99) {break ; }
// pour écourter la durée de l'orage.
                                               // Le code d'arrêt doit être saisi
                                               // pendant la diffusion du son.
    }
  Fin_de_l_orage();                          
  // Diffusion du son n°15 (pluie).

}

La variable Duree_de_l_orage contient le nombre de coups de tonnerre de l'orage complet.

La fonction void Attente_du_code_de_demarrage() appelle la fonction void Code_a_2_chiffres().

void Code_a_2_chiffres() intercepte les 2 codes infrarouges et détermine la valeur de Duree_de_l_orage :
  • si le code est 10 ⇒ Duree_de_l_orage = 1 (1 seul coup de tonnerre) ;
  • ...
  • si le code est 13⇒ Duree_de_l_orage = 16 (16 coup de tonnerre) ;
  • si le code est 14 ⇒ bruit de la pluie uniquement et retour au début du programme.

void setup()
 

Rien de très particulier.
 
void setup() 
{
  irrecv.enableIRIn();
 
  Serial.begin(9600);
  pinMode(MOSFET, OUTPUT);          
  // Sortie vers le MOSFET
 
  mySoftwareSerial.begin(9600);
  pinMode(Liaison_BUSY, INPUT);       
 // Indispensable pour tester la fin
                                       // de la diffusion du son en cours.       
 
  myDFPlayer.begin(mySoftwareSerial);
  myDFPlayer.volume(20);

  Nombre_de_sons_enregistres = myDFPlayer.readFileCounts();

 
//  ----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);
}

 



Fonction personnelle
«int Interception_du_premier_code_IR_SONY()»

 

Cette fonction attend indéfiniment la réception d'un premier code infrarouge.

Cette fonction est appelée par la fonction void Code_a_2_chiffres().

Elle prend la valeur Code_touche_obtenu du premier code IR reçu qu'elle transmet à la fonction appelante.
 
int Interception_du_premier_code_IR_SONY()   // Appelée par la fonction
                                             // «Code_a_2_chiffres».
                                             // Cette fonction attend indéfiniment
                                             // la saisie du premier code.
{

int Code_touche_obtenu = 999 ;
 // On impose a priori un code incorrect
                                // hors du champs des codes possibles.
                                // Le code «999» n'existe pas dans la liste
                                // de codes de la télécommande SONY TV.
irrecv.resume() ;
    
            while (!(irrecv.decode(&results)))
       
    // Attend indéfiniment la réception du 1er code.
                  {
                 
// boucle d'attente
                  delay(100) ;
                  }
             Code_touche_obtenu = results.value ;  
                 
//Serial.print("    Code touche obtenu pour le 1er chiffre = ") ;
                  //Serial.println(Code_touche_obtenu) ;
                                        
             delay(600) ;           
// Delai avant la saisie du code suivant
             irrecv.resume() ;
                                       
             return Code_touche_obtenu ;            
}  



Fonction personnelle
«int Interception_du_premier_code_IR_SONY_avec_delay()»

 

Cette fonction n'attend pas indéfiniment le premier code IR.
La variable locale int Limite_du_delai_d_attente limite cette attente à 2 secondes (modifiable).

Cette fonction est appelée par la fonction int Code_d_arret_a_2_chiffres().

Elle prend la valeur Code_touche_obtenu du premier code IR reçu qu'elle transmet à la fonction appelante.
.
// Appelée par la fonction «Code_a_2_chiffres».
// Pour le 2e code, le délai de saisie en secondes est limité par la valeur de
// «Limite_du_delai_d_attente».
// Passé ce délai, on revient au début de void loop pour attendre un nouveau
// premier code.

int Interception_du_premier_code_IR_SONY_avec_delay()       
{
int Code_touche_obtenu = 999 ;
int Limite_du_delai_d_attente = 2 ;
irrecv.resume() ;
    
            int Temps_IN = millis() / 1000 ;
            int Temps_OUT ;
            while (!(irrecv.decode(&results)))        
// Attend la réception du
            // 2e code pendant «Limite_du_delai_d_attente», en secondes.
                  {
                  // boucle d'attente
                  delay(100) ;
                  int Temps_OUT = millis() / 1000 ;
                  if ((Temps_OUT - Temps_IN) > Limite_du_delai_d_attente) 
                     {
                   
 //Serial.println("        Dépassement du temps 1er chiffre") ; 
                     return 999 ; 
                     }
                  }
             Code_touche_obtenu = results.value ;  
                 
//Serial.print("    Code touche obtenu pour le 1er chiffre = ") ;
                  //Serial.println(Code_touche_obtenu) ;
                                        
             delay(600) ;    
// Delai avant la saisie du code suivant
             irrecv.resume() ;
                                       
             return Code_touche_obtenu ;            
}  



Fonction personnelle
«int Interception_du_deuxieme_code_IR_SONY()»

 

Cette fonction n'attend pas indéfiniment le deuxième code IR.
La variable locale int Limite_du_delai_d_attente limite cette attente à 2 secondes (modifiable).

Cette fonction est appelée par la fonction int Code_a_2_chiffres().

Elle prend la valeur Code_touche_obtenu du premier code IR reçu qu'elle transmet à la fonction appelante.
 
// Appelée par la fonction «Code_a_2_chiffres».
// Pour le 2e code, le délai de saisie en secondes est limité par la valeur de
// «Limite_du_delai_d_attente».
// Passé ce délai, on revient au début de void loop pour attendre un nouveau
// premier code.

int Interception_du_deuxieme_code_IR_SONY()
{
int Code_touche_obtenu = 999 ;
int Limite_du_delai_d_attente = 2 ;
irrecv.resume() ;
 
            int Temps_IN = millis() / 1000 ;
            while (!(irrecv.decode(&results)))        
// Attend la réception
            // du 2e code pendant «Limite_du_delai_d_attente», en secondes.
               {
               // boucle d'attente à durée limitée.
               delay(100) ;
               int Temps_OUT = millis() / 1000 ;
               if ((Temps_OUT - Temps_IN) > Limite_du_delai_d_attente) 
                  {
                 
//Serial.println("        Dépassement du temps 2e chiffre") ; 
                  return 999 ; 
                  }
               }
             Code_touche_obtenu = results.value ;  
               
  //Serial.print("    Code touche obtenu pour le 2e chiffre = ") ;
                  //Serial.println(Code_touche_obtenu) ;
                                                  
             irrecv.resume() ;
                                        
             delay(1000) ;  
 // Delai avant la saisie du code suivant
             results.value = 0 ;
             irrecv.resume() ;
                                        
             return Code_touche_obtenu ;            



Fonction personnelle
«int Code_a_2_chiffres()»

 


Cette fonction est appelée par la fonction Attente_du_code_de_demarrage() ;

Elle traite les codes de démarrage :
  • si le code à 2 chiffres reçu est 10, la variable Duree_de_l_orage vaut 1 ⇒ un seul coup de tonnerre ;
  • si le code à 2 chiffres reçu est 11, la variable Duree_de_l_orage vaut 4 ⇒ 4 coups de tonnerre ;
  • si le code à 2 chiffres reçu est 12, la variable Duree_de_l_orage vaut 8 ⇒ 8 coups de tonnerre ;
  • si le code à 2 chiffres reçu est 13, la variable Duree_de_l_orage vaut 16 ⇒ 16 coups de tonnerre ;
 
  • si un de ces 4 codes est reçu, cette fonction prend la valeur true qu'elle transmet à la fonction appelante ;
 
  • si le code à 2 chiffres reçu est 14, la fonction appelle la fonction Fin_de_l_orage ⇒ l'orage sera ainsi écourté ;

À la fin de l'orage (son de la pluie), l'instruction loop() ; force le redémarrage du programme.
 
int Code_a_2_chiffres()
   {
   bool Code = false ;
   
   int Premier_chiffre = Interception_du_premier_code_IR_SONY() ;
   int Deuxieme_chiffre = Interception_du_deuxieme_code_IR_SONY() ;
    
   if (Premier_chiffre == Touche_1) 
      {
      if (Deuxieme_chiffre == Touche_0)   {Code = true ; Duree_de_l_orage = 1 ; }
        // 1 salve ;

      if (Deuxieme_chiffre == Touche_1)   {Code = true ; Duree_de_l_orage = 4 ; }
   
    // 4 salves ;
      if (Deuxieme_chiffre == Touche_2)   {Code = true ; Duree_de_l_orage = 8 ; }
        // ...
      if (Deuxieme_chiffre == Touche_3)   {Code = true ; Duree_de_l_orage = 16 ; } 
      if (Deuxieme_chiffre == Touche_4)   {Code = true ; Fin_de_l_orage() ; loop();}
      }           
   return Code ;
   }

Note :
Les codes 15 à 19 sont éventuellement disponibles pour paramétrer le démarrage avec d'autres avleurs de durée de l'orage, ou même pour diffuser d'autres sons.

 


Fonction personnelle
«int Code_d_arret_a_2_chiffres()»

 


Cette fonction est appelée par la fonction int Attente_fin_du_son().

Elle permet d'interrompre prématurément le déroulement de l'orage.

Pour que le code d'arrêt puisse être pris en compte, il doit donc être saisi pendant le déroulement d'un son.

En effet, lorsqu'un son se déroule, le programme ne fait qu'attendre que le DFPlayer indique que la diffusion du son est terminée (liaison BUSY).
Or le DFPlayer ne mobilise pas l'ARDUINO pendant la diffusion du son ⇒ pendant cette phase, l'ARDUINO a tout le temps pour intercepter les codes IR d'arrêt.

Les 2 codes d'arrêt valides sont :
  • le code 19 ;
  • le code 1 + POWER.

(
Ce double code d'arrêt est une fantaisie dont on peut se passer).
.
int Code_d_arret_a_2_chiffres()
   {
   bool Code = false ;
    
   int Premier_chiffre = Interception_du_premier_code_IR_SONY_avec_delay() ;
   int Deuxieme_chiffre = Interception_du_deuxieme_code_IR_SONY() ;

   
// Traitement du code d'arrêt
    
   if (Premier_chiffre == Touche_1) 
       {

        //if (Deuxieme_chiffre == Touche_0)          {Code = true ; }     
        //if (Deuxieme_chiffre == Touche_1)          {Code = true ; }  
        //if (Deuxieme_chiffre == Touche_2)          {Code = true ; }  
        //if (Deuxieme_chiffre == Touche_3)          {Code = true ; }       
        //if (Deuxieme_chiffre == Touche_4)          {Code = true ; }       
        //if (Deuxieme_chiffre == Touche_5)          {Code = true ; }             
        //if (Deuxieme_chiffre == Touche_6)          {Code = true ; }
        //if (Deuxieme_chiffre == Touche_7)          {Code = true ; }
        //if (Deuxieme_chiffre == Touche_8)          {Code = true ; }
        if (Deuxieme_chiffre == Touche_9)            {Code = true ; }
        if (Deuxieme_chiffre == Touche_POWER)        {Code = true ; }
        }  
   
   return Code ;
   }



Fonction personnelle
«int mathRandomInt(int min, int max)»

 

On sait que la fonction random() – ou rand() – ne génère pas une vraie série aléatoire, mais toujours la même suite de nombre à chaque redémarrage du programme.

Très vite on se rend compte que chaque orage qu'on déclenche déroule la même séquence de sons et d'éclairs... et la surprise n'existe plus.

Pour améliorer le côté aléatoire, on ajoute l'instruction randomSeed(...) qui choisit la «graine», c'est-à-dire la valeur sur laquelle s'appuie la fonction rand() pour débuter sa série.

Pour que cette «graine» soit vraiment différente, quoi de plus simple que de lui affecter la valeur millis() du temps courant – compté en millisecondes à partir de l'allumage de l'ARDUINO – au moment aléatoire où on appelle la fonction 
int mathRandomInt(int min, int max) ?

Cette une fonction avec «passage d'arguments» : on l'appelle en lui fournissant les 2 valeurs extrêmes entre lesquelles on souhaite que se situe la valeur aléatoire.

La fonction rand(x, y) renvoie un nombre compris entre x et (y-1).
La fonction int mathRandomInt(int min, int max) renvoie un nombre compris entre min et max inclu.
Sympa !
.
int mathRandomInt(int min, int max) 
  {
  randomSeed(millis()) ;      
// Ajouté le 23/01/2022.
                              // Pour se rapprocher d'un véritable tirage aléatoire.  
  if (min > max) 
    { 
    int temp = min;          
 // Swap min and max to ensure min is smaller.
    min = max;
    max = temp;
    }
  return min + (rand() % (max - min + 1));
  }


Fonction personnelle
«void Salve() »

 

Cette fonction est appelée par void loop().

La sortie 9, qui commande la LED de puissance par l'intermédiaire du MOSFET, est pilotée en analogique.
C'est ce qui permet de moduler le niveau lumineux des éclairs.

.
void Salve() 
  {
  int repeat = mathRandomInt(2, 4) * 5;   
// Nombre d'éclairs par salve.
  for (int count = 0; count < repeat ; count++)                      
    {
    analogWrite(9, (mathRandomInt(1, 4) * (Facteur_d_intensite * 8)));
   
// Intensité «haute» de l'éclair.

    delay((mathRandomInt(1, 4) * 25));
   
 // Durée de l'intensité «haute».

    analogWrite(9, (mathRandomInt(0, 4) * (Facteur_d_intensite * 5)));
 
 // Intensité «basse» de l'éclair.

    delay((mathRandomInt(1, 4) * 15)); 
 
  // Durée de l'intensité «basse».
    }
  analogWrite(9, 0);
  }


À la fin d'une salve, on force la sortie 9 au niveau 0 pour éteindre la LED à coup sûr, ce que ne fait pas la fonction analogWrite(9, mathRandom(0, 4)... ou très exceptionnellement.
 


Fonction personnelle
«void Coup_de_tonnerre() »

 
Cette fonction est appelée par void loop().

Elle choisit au hasard un son parmi les 15 sons enregistrés sur la carte «T».

En fait, Nombre_de_sons_enregistres-1 évite de choisir le son n°15, qui est le son de la pluie seule.

Le son débute avec un retard et une intensité déterminés dans la fonction :
void Retard_du_son_Volume_et_Facteur_d_intensite() 
.
void Coup_de_tonnerre() 
  {
  Numero_du_son = mathRandomInt(1, Nombre_de_sons_enregistres-1);
 
// «... -1» pour exclure le son final de la pluie

  delay(Retard);
  myDFPlayer.volume(Volume);
  myDFPlayer.play(Numero_du_son);
  }


Fonction personnelle
«int Attente_fin_du_son()»

 
... et interception d'un éventuel code d'arrêt.

Cette fonction est appelée par la fonction Coup_de_tonnerre() dans void loop().

Tant que le son est en cours de diffusion, la broche BUSY du DFPlayer reste à l'état LOW.

Pendant cette phase, le programme est disponible pour tester l'arrivée d'un code IR en appelant la fonction 
Code_d_arret_a_2_chiffres().

À la fin, la variable Duree_du_son – comme son nom l'indique – permet de connaître la durée du son qui vient d'être diffusé.
J'ai utilisé cette variable pendant la phase de mise au point du programme.
Les instructions qui permettent l'impression des différents paramètres d'un coup de tonnerre sont passée en commentaires pour ne pas surcharger inutilement le microcontrôleur.


.
int Attente_fin_du_son()// ... et interception du code d'arrêt.

  delay(1000);          
// Pour être sûr que la pin 10 (BUSY) est bien passée à «LOW».
  long Initialisation_timer = millis();
  long Duree_du_son = 0 ;
 
  while (digitalRead(Liaison_BUSY) == LOW) 
     { 
     if (Code_d_arret_a_2_chiffres() == true)
// Permet de saisir le code «19»
                                              // pour écourter la durée de l'orage.
       {       
       irrecv.resume(); 
       delay(100) ;
       return 99 ;
       }     
     }     
  Duree_du_son = (millis() - Initialisation_timer) + 1000;

/*
  Serial.print("Fin du son n° ") ;
  Serial.println(Numero_du_son) ;
  Serial.print("Durée du son = ") ;
  Serial.print(Duree_du_son) ;
  Serial.println(" millisecondes");  
  Serial.print("- Current elapsed time (ms) = ");
  Serial.println(millis());
  Serial.println("");
  */
}


Fonction personnelle
«void Retard_du_son_Volume_et_Facteur_d_intensite()»

 
 
Cette fonction est appelée par void loop().
C'est un peu le cœur du programme : Pour chaque coup de tonnerre, un facteur de proximité est déterminé aléatoirement.

En fonction de ce facteur, le programme détermine :
  • le volume sonore du coup tonnerre ;
  • le retard entre la salve d'éclairs et le son ;
  • le facteur d'intensité moyenne des éclairs.

Note :
Les paramètres d'impression sont mis en commentaires pour alléger le code téléversé.

.
void Retard_du_son_Volume_et_Facteur_d_intensite() 
{
  Proximite = mathRandomInt(1, 8) * 5;   
// Aléatoire de 8 à 40 :
                                         // Proximité = 5 ⇒ lointain ;
                                         // Proximité = 40 ⇒ proche.
  Volume = Proximite;
  Retard = (40 - Proximite) * 100;      
 // Ce coefficient varie de «0» à 3200 ms.
  Facteur_d_intensite = Proximite / 5;   // Ce coefficient varie de 1 à 8.
/*
  Serial.print("- Volume = ");
  Serial.println(Volume);
  Serial.print("- Retard = ");
  Serial.print(Retard);
  Serial.println(" millisecondes");
  Serial.print("- Facteur d\'intensité = ");
  Serial.println(Facteur_d_intensite);
*/
}


Fonction personnelle
«void Attente_du_code_de_demarrage()»

 

Cette fonction est appelée par void loop().

Elle appelle la fonction Code_a_2_chiffres().

.
void Attente_du_code_de_demarrage() 
{

  //Serial.println("Attente code") ;
  while (!(Code_a_2_chiffres() == true)) 
  {

  //Serial.println("code non reçu ou incorrect. Nouvel essai...") ;
  irrecv.resume(); 
  delay(50) ;  
  }
}


Fonction personnelle
«void Fin_de_l_orage() »

 

Cette fonction est appelée par la fonction int Code_a_2_chiffres()​ et par void loop().

Dans void loop(), elle diffuse le son de la pluie à la fin du déroulement de l'orage.

Dans 
int Code_a_2_chiffres(), elle diffuse uniquement le son de la pluie, à la place de l'orage.


.
void Fin_de_l_orage() 
{
  myDFPlayer.volume(20);
  myDFPlayer.play(Numero_du_son_de_la_pluie);
  Attente_fin_du_son() ;
}
PARAMÈTRAGES

Dans void setup()

myDFPlayer.EQ(DFPLAYER_EQ_JAZZ); ⇒ six paramètres disponibles pour ajuster la tonalité du son en fonction du HP utilisé, mais aussi de la qualité des sons enregistrés.


Dans void Fin_de_l_orage() 

myDFPlayer.volume(20); ⇒ pour régler le volume du son de la pluie.


Dans void Retard_du_son_Volume_et_Facteur_d_intensite()

Les 4 paramètres sont réglables, mais attention :
  • dans Proximite = mathRandomInt(1, 8) * 5;
  • et dans Facteur_d_intensite = Proximite / 5;
... les deux valeurs en gras doivent être identiques.


Dans void Salve() 

Tous les paramètres sont réglables... mais allez-y avec prudence.


Dans int Code_a_2_chiffres()

Duree_de_l_orage = 4 ; }     // 4 salves ; ⇒ les 4 occurrences pour 1 à 16 salves peuvent être ajustées à convenance.

Les codes 15 à 19 sont disponibles pour d'autres modes de démarrage du programme : plus de durées d'orages disponibles, autres son de pluie, etc.


Dans les déclarations 

const int Numero_du_son_de_la_pluie = 15 ;

Si d'autres séquences sonores sont prévues (comme par exemple celle son de la pluie en n°15), elles seront déclarés à la suite et paramétrées dans la fonction int Code_a_2_chiffres().

.
Looney_tunes.png

That's all Folks !
 
SCHÉMA POUR 2 LED, BLANCHE ET BLEUE,
INDÉPENDANTES
(ajouté le 09/02/2022)
Orage_schema_3.png

Un 2e étage de puissance est connecté sur la sortie 10 de l'ARDUINO, pour piloter la LED bleue.


 

Fonction personnelle modifiée
«void Salve()»

 

Cette fonction est appelée par void loop().

.
void Salve() 
  {
  int repeat = mathRandomInt(2, 4) * 5; 
   // Nombre d'éclairs par salve.
  for (int count = 0; count < repeat ; count++)                      
    {
    analogWrite(9, (mathRandomInt(1, 4) * (Facteur_d_intensite * 8)));
    
// Intensité «haute» de l'éclair blanc.
    delay((mathRandomInt(1, 4) * 12)); 
    analogWrite(10, (mathRandomInt(1, 4) * (Facteur_d_intensite * 4)));
   
// Intensité «haute» de l'éclair bleu.
    delay((mathRandomInt(1, 4) * 12));      // Durée de l'intensité «haute».   
    analogWrite(9, (mathRandomInt(0, 4) * (Facteur_d_intensite * 5)));

    // Intensité «basse» de l'éclair blanc.
    delay((mathRandomInt(1, 4) * 7));
    analogWrite(10, (mathRandomInt(0, 4) * (Facteur_d_intensite * 2)));

   
 // Intensité «basse» de l'éclair bleu.
    delay((mathRandomInt(1, 4) * 7));       // Durée de l'intensité «basse».
    }
  analogWrite(9, 0);
  }

Réalisations avec DFPlayer Mini