LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

vendredi 22 octobre 2021

25 visiteurs en ce moment

Passage à niveau géré par Arduino (4)

La sonnerie

. Par : Christian

DIFFICULTÉ :

Depuis le premier article, notre passage à niveau a bien évolué et il joue pleinement son rôle en retenant la circulation routière lorsqu’un train passe. Il lui manque pourtant un élément que nous allons rajouter aujourd’hui : la sonnerie. Nous utiliserons un lecteur DFPlayer Mini pour reproduire le son comme nous l’avions fait dans l’article Annonces en gare avec la RFID.

Nous vous renvoyons donc vers cet article pour plus de détails sur le lecteur et la façon de le mettre en œuvre puisque nous nous sommes inspirés du montage de cet article pour réaliser la partie son de notre PN.

La figure 1 montre comment relier le DFPlayer à la carte Arduino ; il est alimenté en 5 V et deux fils seulement sont utilisés pour faire la liaison avec la carte Arduino (les sorties 10 et 11) grâce à la bibliothèque SoftwareSerial qui fait partie de l’IDE d’Arduino. Le son est produit par un petit haut-parleur de 3 W maximum mais il est possible d’utiliser une paire d’enceintes pour ceux qui veulent la stéréo. Pour reproduire la sonnerie du PN, il suffit de lire un fichier son au format MP3 stocké sur une carte micro-SD.

Figure 1
Figure 1
Raccordement du Player à une carte Uno (extraite du site de DFRobot)

Ce lecteur a besoin de la bibliothèque DFRobotDFPlayerMini qui devra donc être installée dans l’IDE : on la trouve grâce au gestionnaire de bibliothèque.

L’idée première était donc de rajouter ce montage sur le montage de notre PN puisque les sorties 10 et 11 ne sont pas utilisées, mais ceci perturbait la fréquence de clignotement des feux de PN. Un remplacement des sorties 10 et 11 par les sorties 7 et 12, non utilisées et ne donnant pas de PWM n’y fit rien ; visiblement nous étions face à une incompatibilité entre les bibliothèques utilisées. Vu que le prix d’une carte Uno est aujourd’hui dérisoire, nous avons adopté comme solution de déporter la fonction son sur une deuxième carte Uno, commandée par la première. Cette solution a aussi l’avantage d’une maintenance plus facile : on peut perdre la fonction son sans pour autant perdre tout le PN. De plus, ce module son, constitué d’une carte Uno (ou Nano) et d’un DFPlayer Mini, peut être utilisé pour jouer d’autres ambiances sonores (un train qui siffle à l’entrée d’un tunnel pour prévenir d’éventuels ouvriers, des annonces en gare, etc.), mais il faut se rappeler qu’il est impossible de jouer plusieurs fichiers son en même temps ! Enfin, un autre avantage est que ce module son peut être remplacé par un module déjà en votre possession comme le module sonore Séries 100 ou 200 d’AVT Products (en vente chez LR-Modélisme).

Modification du programme de PN

Nous allons donc utiliser la sortie 7 pour créer une sortie son qui commande la carte son par son état haut. Pour la mise au point du programme, une simple LED permet de contrôler à quel moment cette sortie est active à l’état haut. En fait, la sonnerie doit retentir depuis le début du clignotement des feux de PN jusqu’à la fin du mouvement fermant les barrières. En dehors de cette phase, la sonnerie ne doit jamais être active.

On introduit une variable booléenne appelée etatSon qui vaut false si le son n’est pas commandé (sortie 7 à l’état bas) et qui vaut true si le son est commandé (sortie 7 à l’état haut). La sortie 7 commandera ultérieurement la carte son par l’intermédiaire d’un coupleur optique. C’est assez facile de mettre etatSon à false lorsqu’ etatZone est également false (zone non occupée). Par contre, lorsque la zone est occupée (etatZone = true), etatSon devient égale à true puis redevient égale à false dès que le mouvement des barrières est terminé. L’état de la sortie 7 est mis à jour à chaque itération de la fonction loop() en fonction de la valeur etatSon.

Voici donc le nouveau programme :

  1. /*******************************************************************************
  2.  * PN_TIB_FeuBarrieresAttente_InitFerme_SortieSon.ino
  3.  * *****************************************************************************
  4.  * Programme developpe pour le projet de PN pour Train In Box.
  5.  * Il prend en compte l arrivee du train, le clignotement des feux en simulant
  6.  * une ampoule a filaments, un delai de 0 a 8 secondes avant mouvement des
  7.  * barrieres, le mouvements des deux barrieres sur 90° d amplitude (depend de
  8.  * la transmission adoptee).
  9.  * Le programme s'initialise barrieres fermees, donc AVEC TRAIN EN GARE SUR TIB.
  10.  * La LED_SON est allumee tant que sonnerie doit jouer ; cette sortie commande
  11.  * le module sonore.
  12.  * *****************************************************************************
  13.  * Christian BEZANGER - 13 Novembre 2018 - 2 Juin 2019 - 29 mai 2020
  14.  ******************************************************************************/
  15.  
  16. #include <LightDimmer.h> // Bibliotheque pour gerer les feux du PN
  17. #include <Servo.h> // Bibliotheque pour gerer les barrieres du PN
  18.  
  19. const byte ILS=2; // entree des capteurs
  20. const byte LED=6; // sortie PWM des feux du PN
  21. const byte LED_SON=7; // sortie pour indiquer sonnerie
  22. const byte S1=4; // sortie pour le servomoteur 1
  23. const byte S2=8; // sortie pour le servomoteur 2
  24. unsigned int compteur = 1; // compteur d evenements (survol ILS)
  25.  
  26. // Variables utilisateur pour reglage du mouvement des barrieres
  27. // -------------------------------------------------------------
  28. unsigned long delaiFermeture = 0; // Regle le delai entre clignotement LED
  29. // et fermeture barrieres - entre 2000 et 8000 mais 0 sur TIB (pas de delai)
  30. int speedServo = 30; // Regle la vitesse de mouvement des barrieres
  31.  
  32. // Variables non utilisateur
  33. // -------------------------
  34. volatile static boolean etatZonePN = true; // true si la zone du PN est occupee
  35. volatile static boolean old_etatZonePN = false; // etat anterieur de la zone
  36. volatile static unsigned long old_top_debutISR; // Date anterieure d appel ISR
  37. unsigned long old_top = 0; // variable pour afficher donnees utiles
  38. int posServo = 90; // position courante a initialiser imperativement FERMEE
  39. int posOuvert = 0; // barriere a la verticale
  40. int posFerme = 90; // barriere a l'horizontale (90° de la verticale)
  41. unsigned long topAttente = 0; // top pris au début franchissement de la zone PN
  42. boolean etatSon = false; // true si sonnerie doit retentir
  43.  
  44. // Instanciations
  45. LightDimmer feuPN;
  46. Servo servo1;
  47. Servo servo2;
  48.  
  49. void changeEtat() { // routine d'interruption (ISR)
  50. unsigned long top_debutISR = millis(); // date appel ISR
  51. if((top_debutISR - old_top_debutISR) > 2000) {
  52. // 2 secondes au moins entre execution ISR
  53. etatZonePN = !etatZonePN; // etat passe a etat oppose
  54. old_top_debutISR = top_debutISR; // initialisation date anterieure d appel ISR
  55. }
  56. } // fin de ISR
  57.  
  58. void setup() {
  59. // put your setup code here, to run once:
  60. Serial.begin(115200); // communication avec le moniteur
  61. pinMode (ILS, INPUT_PULLUP); // entree capteur
  62. pinMode (LED_BUILTIN, OUTPUT); // LED du module allumee si Zone PN occupee
  63. pinMode (LED, OUTPUT); // sortie pour feux du PN
  64. pinMode (LED_SON, OUTPUT);
  65. attachInterrupt (digitalPinToInterrupt(ILS), changeEtat, FALLING);
  66. digitalWrite (LED_BUILTIN, LOW);
  67. feuPN.begin(LED, HIGH); // LED s'allume avec etat haut
  68. servo1.attach(S1);
  69. servo2.attach(S2);
  70. // initialisation des barrieres en position fermee
  71. servo1.write(posFerme); // barriere 1 FERMEE apres initialisation
  72. servo2.write(posFerme); // barriere 2 FERMEE apres initialisation
  73. delay(2000);
  74. digitalWrite (LED_BUILTIN, HIGH); // Indique fin de setup
  75. } // fin de setup
  76.  
  77. void loop() {
  78. // put your main code here, to run repeatedly:
  79. if(etatSon == false) {digitalWrite (LED_SON, LOW);}
  80. if(etatSon == true) {digitalWrite (LED_SON, HIGH);}
  81. if(etatZonePN == false) {
  82. old_etatZonePN = etatZonePN;
  83. digitalWrite (LED_BUILTIN, LOW); // eteint LED de controle de la zone PN
  84. etatSon = false; // pas de sonnerie
  85. feuPN.stopBlink(); // arrete le clignotement
  86. feuPN.off(); // eteint les feux
  87. // ouverture barriere
  88. if(posServo > posOuvert) {
  89. posServo = posServo - 1;
  90. servo1.write(posServo);
  91. servo2.write(posServo);
  92. delay(speedServo);
  93. } // fin du test sur position des servos
  94. } // fin du test sur etat de la Zone du PN -> false
  95. if(etatZonePN == true) {
  96. if(etatZonePN != old_etatZonePN) {
  97. topAttente = millis(); // prend un top d'entree dans zone PN
  98. old_etatZonePN = etatZonePN;
  99. etatSon = true; // sonnerie
  100. } // fin du deuxieme if
  101. digitalWrite (LED_BUILTIN, HIGH); // allume LED de controle de la zone PN
  102. feuPN.startBlink(); // commence le clignotement
  103. // fermeture barriere après attente
  104. if(millis() - topAttente > delaiFermeture) {
  105. if(posServo <= posFerme) {
  106. posServo = posServo + 1;
  107. servo1.write(posServo);
  108. servo2.write(posServo);
  109. if(posServo >= 89) {etatSon = false;}
  110. delay(speedServo);
  111. } // fin du test sur position des servos
  112. } // fin du test sur delai avant ouverture
  113. } // fin du test sur etat de la Zone du PN -> true
  114. LightDimmer::update();
  115. if(old_top_debutISR != old_top) { // Affichage pour chaque nouveau survol ILS
  116. Serial.print(compteur);
  117. Serial.print(" ");
  118. Serial.print(old_top_debutISR);
  119. Serial.print(" ");
  120. Serial.println(old_top_debutISR - old_top);
  121. old_top = old_top_debutISR;
  122. compteur = compteur + 1;
  123. }
  124. } // fin de loop

Télécharger

Dans ce nouveau programme, à la ligne 28, la variable delaiFermeture qui règle le délai entre le début de clignotement des feux et le début de mouvement de fermeture des barrières, est initialisée à 0 car ce programme est livré pour le réseau TIB qui est un petit réseau. Pour un réseau avec de grands cantons, ce délai peut être réglé entre 2 et 8 secondes, ce dernier chiffre correspondant à la réalité. N’oubliez pas de régler cette variable en fonction des caractéristiques de votre réseau.

Montage avec le module son

La figure 2 montre comment raccorder notre module son à la carte de commande du passage à niveau.

Figure 2
Figure 2
Raccordement du module son à la carte de commande du PN via un coupleur optique 4N35

On utilise un coupleur optique 4N35 (ou équivalent) dont la LED est alimentée par la sortie 7 de la carte de commande du PN à travers une résistance de limitation de courant de 1 kΩ. Lorsque cette LED est allumée, le transistor est passant et l’entrée 2 de la carte Uno du module son est reliée à la masse. Ce coupleur optique permet une isolation galvanique des deux cartes Arduino.

Programme du module son

Le programme est inspiré d’un programme de démonstration donné sur le site du constructeur DFRobot, modifié selon nos besoins. Le principe consiste à surveiller les changements d’état de l’entrée 2 sur laquelle est installé le coupleur optique. Tant que le transistor du coupleur optique n’est pas passant, cette entrée est à l’état haut car elle a été initialisée en INPUT_PULLUP. Lorsque la sortie son (sortie 7) de la carte de commande du PN est à l’état haut, la LED du coupleur optique est allumée et le transistor devient passant : l’entrée est alors à l’état LOW. Un changement d’état de cette entrée de HIGH vers LOW commande la lecture du fichier son et un changement d’état de cette entrée de LOW vers HIGH arrête cette lecture. La carte PN provoque un événement (faut-il ou non du son ?) et l’information est envoyée à la carte son, ce qui provoque un changement d’état de l’entrée. On surveille donc les changements d’état de l’entrée et lorsqu’ils ont lieu, on regarde l’état de l’entrée qui indique l’opération à réaliser (produire du son ou l’arrêter).

Le programme commence par instancier un objet appelé myDFPlayer à partir de la bibliothèque DFRobotDFPlayerMini. Pour commander la lecture du fichier son, nous utilisons la fonction myDFPlayer.play(1) qui lit le premier fichier son de la carte micro-SD (cette carte n’a d’ailleurs pas besoin d’avoir plusieurs fichiers pour notre application). Pour arrêter la lecture du fichier son, on utilise la fonction myDFPlayer.stop() (ici pas besoin de préciser le numéro du fichier audio, c’est celui en cours de lecture qui est arrêté).

  1. /*********************************************************
  2.  PNA_CommandeSonnerie
  3.  ---------------------------------------------------------
  4.  Ce programme commande la sonnerie du PN selon les
  5.  changements d'etat de l'entree entreeCommande
  6.  De HIGH -> LOW : joue sonnerie
  7.  De LOW -> HIGH : arrete sonnerie
  8.  ---------------------------------------------------------
  9.  Christian BEZANGER - mai 2020
  10.  Programme dans le domaine public
  11.  ********************************************************/
  12.  
  13. #include "Arduino.h"
  14. #include "SoftwareSerial.h"
  15. #include "DFRobotDFPlayerMini.h"
  16.  
  17. SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
  18. DFRobotDFPlayerMini myDFPlayer;
  19. void printDetail(uint8_t type, int value);
  20.  
  21. const byte entreeCommande = 2; // entree commandee par carte PN
  22. const int volumeSon = 25; // volume du son entre 0 et 30 (a regler)
  23. int etatEntree = HIGH; // etat courant de l entree de commande
  24. int old_etatEntree = HIGH; // etat précedent de l entree de commande
  25.  
  26. void setup()
  27. {
  28. pinMode (entreeCommande, INPUT_PULLUP); // inactive a etat HIGH
  29. mySoftwareSerial.begin(9600);
  30. Serial.begin(115200);
  31.  
  32. Serial.println();
  33. Serial.println(F("DFRobot DFPlayer Mini Demo"));
  34. Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  35.  
  36. if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
  37. Serial.println(F("Unable to begin:"));
  38. Serial.println(F("1.Please recheck the connection!"));
  39. Serial.println(F("2.Please insert the SD card!"));
  40. while(true);
  41. }
  42. Serial.println(F("DFPlayer Mini online."));
  43.  
  44. myDFPlayer.volume(volumeSon); // Regle le volume entre 0 et 30
  45. myDFPlayer.play(1); // Joue 3 secondes de sonnerie pour controle
  46. delay(3000);
  47. myDFPlayer.stop(); // fin initialisation
  48. }
  49.  
  50. void loop()
  51. {
  52. etatEntree = digitalRead(entreeCommande); // lecteure de l entree
  53. if(etatEntree != old_etatEntree) {
  54. // il y a eu changement d etat
  55. if(etatEntree == LOW) {
  56. myDFPlayer.play(1); // on joue la sonnerie
  57. } // fin etatEntree LOW
  58. if(etatEntree == HIGH) {
  59. myDFPlayer.stop(); // on arrete la sonnerie
  60. } // fin etatEntree HIGH
  61. } // fin de comparaison
  62. old_etatEntree = etatEntree;
  63. // fin de traitement
  64. // Imprime eventuellement des messages d erreur ou d etats du DFPlayer
  65. if (myDFPlayer.available()) {
  66. printDetail(myDFPlayer.readType(), myDFPlayer.read());
  67. }
  68. }
  69.  
  70. void printDetail(uint8_t type, int value){
  71. switch (type) {
  72. case TimeOut:
  73. Serial.println(F("Time Out!"));
  74. break;
  75. case WrongStack:
  76. Serial.println(F("Stack Wrong!"));
  77. break;
  78. case DFPlayerCardInserted:
  79. Serial.println(F("Card Inserted!"));
  80. break;
  81. case DFPlayerCardRemoved:
  82. Serial.println(F("Card Removed!"));
  83. break;
  84. case DFPlayerCardOnline:
  85. Serial.println(F("Card Online!"));
  86. break;
  87. case DFPlayerPlayFinished:
  88. Serial.print(F("Number:"));
  89. Serial.print(value);
  90. Serial.println(F(" Play Finished!"));
  91. break;
  92. case DFPlayerError:
  93. Serial.print(F("DFPlayerError:"));
  94. switch (value) {
  95. case Busy:
  96. Serial.println(F("Card not found"));
  97. break;
  98. case Sleeping:
  99. Serial.println(F("Sleeping"));
  100. break;
  101. case SerialWrongStack:
  102. Serial.println(F("Get Wrong Stack"));
  103. break;
  104. case CheckSumNotMatch:
  105. Serial.println(F("Check Sum Not Match"));
  106. break;
  107. case FileIndexOut:
  108. Serial.println(F("File Index Out of Bound"));
  109. break;
  110. case FileMismatch:
  111. Serial.println(F("Cannot Find File"));
  112. break;
  113. case Advertise:
  114. Serial.println(F("In Advertise"));
  115. break;
  116. default:
  117. break;
  118. }
  119. break;
  120. default:
  121. break;
  122. }
  123. }

Télécharger

Ce programme est à téléverser dans la carte Arduino du module son ; au préalable, pensez à modifier la valeur de volumeSon pour augmenter ou baisser le volume sonore.

L’alimentation du module son se fait par le câble USB et une prise de smartphone, comme préconisé pour la carte de commande du PN. Bien évidemment d’autres solutions d’alimentation sont possibles et ont été décrites sur ce site.

Vous trouverez ci-dessous un fichier son au format MP3 d’une durée de 15 secondes, ce qui est bien suffisant pour nos réseaux. Pour le récupérer, cliquez dessus pour le jouer, puis dans l’image qui s’ouvre, faites un clic droit pour faire apparaître l’option "Enregistrer le fichier audio sous…".

Sonnerie du PN

Cette série d’articles pourrait se terminer ici puisque nous avons reproduit le fonctionnement complet d’un passage à niveau automatique SAL 2. Néanmoins, je vous donne rendez-vous pour un cinquième et dernier article où nous verrons quelques améliorations possibles et comment dépanner ce montage s’il ne fonctionne pas du premier coup.

6 Messages

Réagissez à « Passage à niveau géré par Arduino (4) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Comment gérer le temps dans un programme ?

La programmation, qu’est ce que c’est

Types, constantes et variables

Installation de l’IDE Arduino

Répéter des instructions : les boucles

Les interruptions (1)

Instructions conditionnelles : le if … else

Instructions conditionnelles : le switch … case

Comment concevoir rationnellement votre système

Comment gérer l’aléatoire ?

Calculer avec l’Arduino (1)

Calculer avec l’Arduino (2)

La compilation d’un projet Arduino

Les structures

Systèmes de numération

Les fonctions

Trois façons de déclarer des constantes

Transcription d’un programme simple en programmation objet

Ces tableaux qui peuvent nous simplifier le développement Arduino

Les chaînes de caractères

Trucs, astuces et choses à ne pas faire !

Processing pour nos trains

Arduino : toute première fois !

Démarrer en Processing (1)

TCOs en Processing (1)

TCOs en Processing (2)

Comment réussir son projet Arduino

Du sketch à l’exécutable

Programmer vos Arduino avec un fichier hexadécimal

L’assembleur (1)

L’assembleur (2)

L’assembleur (3)

L’assembleur (4)

L’assembleur (5)

L’assembleur (6)

L’assembleur (7)

L’assembleur (8)

L’assembleur (9)

Le monde des objets (1)

Le monde des objets (2)

Le monde des objets (3)

Le monde des objets (4)

Les pointeurs (1)

Les pointeurs (2)

Les Timers (I)

Les Timers (II)

Les Timers (III)

Les Timers (IV)

Les Timers (V)

Bien utiliser l’IDE d’Arduino (1)

Bien utiliser l’IDE d’Arduino (2)

Piloter son Arduino avec son navigateur web et Node.js (1)

Piloter son Arduino avec son navigateur web et Node.js (2)

Piloter son Arduino avec son navigateur web et Node.js (3)

Piloter son Arduino avec son navigateur web et Node.js (4)

Les derniers articles

L’assembleur (9)


Christian

L’assembleur (8)


Christian

L’assembleur (7)


Christian

L’assembleur (6)


Christian

L’assembleur (5)


Christian

L’assembleur (4)


Christian

L’assembleur (3)


Christian

L’assembleur (2)


Christian

L’assembleur (1)


Christian

Programmer vos Arduino avec un fichier hexadécimal


Christian

Les articles les plus lus

Les interruptions (1)

Les Timers (I)

Instructions conditionnelles : le if … else

Ces tableaux qui peuvent nous simplifier le développement Arduino

Comment gérer le temps dans un programme ?

Calculer avec l’Arduino (1)

Répéter des instructions : les boucles

Instructions conditionnelles : le switch … case

Calculer avec l’Arduino (2)

Bien utiliser l’IDE d’Arduino (1)