Au clair de la lune avec Arduino

Dans cet article, afin d’apprendre le pilotage d’un haut-parleur nous allons demander à Arduino de jouer la mélodie « Au clair de la lune ».

Montage Arduino pour "Au clair de la lune"

Le matériel nécessaire

Pour cette réalisation, vous avez besoin:

Carte Arduino UNO Rev 3 Cordon USB de type A/B Haut-parleur 8 Ohm de 50 mm
Carte Arduino UNO Câble USB 2.0 Type A/B Haut-parleur 8 Ohm
Résistance de 100 ohm  
Résistance 100 Ohm Câbles  

Concernant le haut-parleur 8 Ohm, il peut-être récupéré sur un vieil équipement électronique. Celui utilisé pour dans cet article provient d’un vieux PC et a l’avantage d’être déjà équipé de fils et d’un connecteur.

Haut-parleur 8 Ohm récupéré sur une carcasse de vieux PC.

Vous pouvez également vous procurer un kit Haut-parleur déjà câblé et soudé sur la boutique: https://tropratik.fr/produit/kit-haut-parleur

Câblage du haut-parleur avec votre carte Arduino

Le câblage du haut-parleur est très simple, il suffit de:

  • relier un de ses fils à la masse
  • relier le deuxième fil à une résistance de 100Ω elle même reliée la sortie digitale 8 de l’Arduino.

Vous vous demandez sûrement: Pourquoi utilise-t-on une résistance de 100 ohms dans ce montage ? C’est tout simplement pour limiter le courant que devra fournir la sortie digitale de l’Arduino. En effet, si l’on étudie la fiche technique de l’Atmega328 qui est le microprocesseur utilisé par la carte Arduino UNO (datasheet), on peut-lire au paragraphe « 26.1 Absolute Maximum Ratings » qu’une sortie digitale peut fournir au maximum 40 mA. Sans rentrer dans le détail du calcul de l’impédance du haut-parleur,  on constate qu’avec une résistance de 100 ohm on est grosso modo sous le plafond requis.

Petite astuce: si vous n’avez pas de fer à souder et que votre haut-parleur n’a pas de fil soudé, vous pouvez utiliser du « bon vieux ruban adhésif » pour attacher l’extrémité des câbles de connexion à votre haut-parleur.

Branchement d'un Haut parleur sur Arduino

Code logiciel pour le pilotage du haut-parleur

Nous allons passer au programme permettant à Arduino de jouer la mélodie « Au clair de la lune ».

Premier programme

Tout d’abord, partons de l’ossature du code Arduino suivante:

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{

}

// Fonction principale du programme, s'exécute en boucle:
void loop()
{

}

Arduino ne connait pas les notes de musique, il ne connait que les fréquences physiques des sons. Afin d’avoir un code logiciel plus compréhensible, nous commençons par nommer les fréquences des notes de musique de la 4ème octave en utilisant la directive de précompilation (l’instruction) « #define« . Ces fréquences des notes peuvent être trouvée sur https://fr.wikipedia.org.

Je vous recommande d’écrire ces directives de précompilation au tout début du code logiciel, au début du croquis. La première ligne démarrant par « // » est une ligne de commentaires non exécutée par le processeur. Les 7 lignes suivantes sont écrites en arrondissant les valeurs de fréquence.

// Définition des fréquences des notes de musiques de la 4ème octave
#define OCTAVE_4_DO 523
#define OCTAVE_4_RE 587
#define OCTAVE_4_MI 659
#define OCTAVE_4_FA 698
#define OCTAVE_4_SOL 784
#define OCTAVE_4_LA 880
#define OCTAVE_4_SI 988

Dans cette mélodie, certaines notes durent plus longtemps que d’autres. Nous allons définir une base de temps à 300 millisecondes. Les notes de la mélodie seront jouées sur une durée multiple de cette base de temps: 1 pour les noires, 2 pour les blanches et 4 pour les rondes.

// Définition de la durée d'un temps en millisecondes 
#define DUREE_TEMPS 300

La fonction Arduino permettant de générer une note de musique sur un haut-parleur est la fonction « tone« .

Je vous propose de jouer l’introduction de la mélodie « Au clair de la lune » en boucle, et par conséquent d’inclure les fonctions d’appel au haut-parleur dans la fonction « loop« . Cette fonction prend 3 arguments en paramètre:

  • le numéro de la sortie digitale reliée au haut parleur,
  • la fréquence de la note
  • et enfin la durée de cette note.

Attention la fonction « loop » n’est pas bloquante, c’est à dire qu’Arduino n’attend pas que la note soit finie de jouée pour exécuter l’instruction suivante du programme. C’est à nous de demander au microcontrôleur d’attendre avant de jouer la note suivante (en utilisant l’instruction « delay » par exemple).

Pour ceux qui comme moi n’ont pas de grande connaissances musicales, vous trouverez les notes de la mélodie « Au clair de la lune » et leur durées relatives sur le site http://www.letoutpetitconservatoire.com.

A partir de ces informations, vous êtes donc capable de rédiger les lignes de code correspondantes:

    // Jeux des 11 premières notes de "Au clair de la lune"
    tone(8, OCTAVE_4_DO, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_DO, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_DO, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_RE, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_MI, 2 * DUREE_TEMPS);
    delay(2 * DUREE_TEMPS);
    tone(8, OCTAVE_4_RE, 2 * DUREE_TEMPS);
    delay(2 * DUREE_TEMPS);
    tone(8, OCTAVE_4_DO, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_MI, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_RE, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_RE, DUREE_TEMPS);
    delay(DUREE_TEMPS);
    tone(8, OCTAVE_4_DO, 4 * DUREE_TEMPS);
    delay(4 * DUREE_TEMPS);

Cela fait un morceau de programme avec beaucoup de répétition, aussi nous allons factoriser le code logiciel. L’article n’est pas fini, rendez-vous (page 2).

Cet article a 3 commentaires

  1. Pierre Vangel

    Bonjour,
    Merci pour cette explication fort bien documentée et détaillée d’ utilisation d’une mélodie d’attente.
    Par ailleurs, la plus gracieuse à l’oreille parmi d’autres. Chapeau, pour une personne ne pratiquant pas la musique.
    J’aimerais en faire usage sur un répondeur piloté par une carte nano, toutefois delay gênerait le bon déroulement d’autres fonctions simultanées synchronisées en millis.
    Auriez-vous SVP l’amabilité de m’indiquer comment vous procéderiez en remplaçant delay par millis dans l’ensemble de votre code. Merci d’avance et encore bravo pour ce joli travail.
    Pierre.

    1. Nico

      Bonsoir Pierre,
      Pour dérouler d’autres fonctions simultanées synchronisées par l’utilisation de l’instruction « millis() », je remplacerais la fonction « JouerNote » par une fonction non-bloquant qui retournerait le temps à attendre avant de jouer la note suivante :
      long DemarrerNote(unsigned int Octave_P, unsigned int NombreTemps_P)
      {
       // Lance l’émission de la note
       tone(8, Octave_P, NombreTemps_P * DUREE_TEMPS);
       // Retourne le délai à attendre avant de jouer la note suivante
       return(NombreTemps_P * DUREE_TEMPS + PAUSE_FIN_NOTE);
      }

      Également, je stockerais les notes dans un tableau global afin de pouvoir les jouer successivement à l’aide d’un index parcourant ce tableau :
      typedef struct
      {
       unsigned int Octave;
       unsigned int NombreTemps;
      } StrNote;

      // Melodie à jouer
      StrNote Melodie_G[] = {
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_MI, 2},
       {OCTAVE_4_RE, 2},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_MI, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_DO, 4},
       {0, 0}
      };

      Si par exemple on prend comme fonction simultanée l’envoi d’un maximum de caractère « . » sur l’interface série, le programme devient :

      // Définition des fréquences des notes de musiques de la 4ème octave
      #define OCTAVE_4_DO 523
      #define OCTAVE_4_RE 587
      #define OCTAVE_4_MI 659
      #define OCTAVE_4_FA 698
      #define OCTAVE_4_SOL 784
      #define OCTAVE_4_LA 880
      #define OCTAVE_4_SI 988
      // Définition de la durée d’un temps en millisecondes
      #define DUREE_TEMPS 300
      // Définition de la pause de fin de note en millisecondes
      #define PAUSE_FIN_NOTE 90

      typedef struct
      {
       unsigned int Octave;
       unsigned int NombreTemps;
      } StrNote;

      // Melodie à jouer
      StrNote Melodie_G[] = {
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_MI, 2},
       {OCTAVE_4_RE, 2},
       {OCTAVE_4_DO, 1},
       {OCTAVE_4_MI, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_RE, 1},
       {OCTAVE_4_DO, 4},
       {0, 0}
      };

      unsigned int NoteCourante_G = 0;
      unsigned long HeurePrecedente_L = 0;
      unsigned long IntervalEntre2Note_L = 0;

      // Fonction de démarrage, s’exécute une seule fois:
      void setup()
      {
       Serial.begin(57600); // Ouverture du port série à 57600 bauds
      }
      // Fonction principale du programme, s’exécute en boucle:
      void loop()
      {
       unsigned long HeureCourante_L = millis();

       if(HeureCourante_L – HeurePrecedente_L > IntervalEntre2Note_L)
       {
        // Sauvegarde de l’heure du dernier démarrage de jeu de note
        HeurePrecedente_L = HeureCourante_L;
        // C’est le moment de lancer le jeu d’une note
        IntervalEntre2Note_L = DemarrerNote(Melodie_G[NoteCourante_G].Octave,Melodie_G[NoteCourante_G].NombreTemps);
        if(Melodie_G[NoteCourante_G+1].Octave!=0)
        {
         NoteCourante_G++; // On passe à la note suivante
        }
        else
        {
         NoteCourante_G=0; // C’était la dernière note de la mélodie, on recommence depuis le début
        }
       }
       else
       {
        // On a le temps de faire autre chose comme d’envoyer plein de caractères sur le port série.
        Serial.print(« . »);
        delay(10);
       }
      }

      long DemarrerNote(unsigned int Octave_P, unsigned int NombreTemps_P)
      {
       // Lance l’émission de la note
       tone(8, Octave_P, NombreTemps_P * DUREE_TEMPS);
       // Retourne le délai à attendre avant de jouer la note suivante
       return(NombreTemps_P * DUREE_TEMPS + PAUSE_FIN_NOTE);
      }

      Voilà, j’espère que cette solution vous a aidé dans votre projet. 🙂

  2. Vangel

    Merci Nico pour votre réponse si rapide et avec autant de détail.
    j’apprécie énormément votre aide; notamment d’avoir composer un code complet.
    Il me reste à le transcrire sur l’IDE Arduino et le tester , ce dont je ne doute pas.
    Encore mille mercis Nico.

Laisser un commentaire