Ce projet a pour but de tester le pilotage par WiFi de la carte Wemos D1 mini, celle-ci étant reliée à un bipeur piézoélectrique. On lui fera jouer différents airs de musique célèbres.
L'image ci-dessous représente le schéma de câblage, dessiné avec le logiciel Fritzing :
La mise en place des composants et quelques soudures produisent l'assemblage suivant :
La carte se programme avec l'EDI Arduino en y ajoutant le module adéquat.
Le code est conçu pour que le boîtier musical se comporte en point d'accès Wifi.
L'utilisateur se connecte à ce point d'accès, nommé BOITIER_MUSICAL
sans mot de passe.
Puis il entre 192.168.0.222
dans la barre d'adresse d'un navigateur.
Il obtient alors cette interface Web :
Les notes se nomment selon la désignation latine, suivie du numéro de l'octave. Conformément à la norme internationale ISO 16, le la3 vibre à la fréquence de 440 hertz. La durée de la note se définit par un nombre, 2 pour un demi-temps, 4 pour un quart de temps, etc. Un nombre négatif symbolise une note pointée, c'est à dire allongée de moitié.
#include <ESP8266WiFi.h> const char* ssid = "BOITIER_MUSICAL"; //const char* password = "987654321"; IPAddress local_IP(192,168,0,222); IPAddress gateway(192,168,4,9); IPAddress subnet(255,255,255,0); WiFiServer server(80); int bipeur = 2; // connecteur D4 sur la carte // notes de musique et leurs fréquences en Hz #define do2 65 #define doD2 69 #define re2 73 #define reD2 78 #define mi2 82 #define fa2 87 #define faD2 93 #define sol2 98 #define solD2 104 #define la2 110 #define laD2 117 #define si2 123 #define do3 131 #define doD3 139 #define re3 147 #define reD3 156 #define mi3 165 #define fa3 175 #define faD3 185 #define sol3 196 #define solD3 208 #define la3 220 #define laD3 233 #define si3 247 #define do4 262 #define doD4 277 #define re4 294 #define reD4 311 #define mi4 330 #define fa4 349 #define faD4 370 #define sol4 392 #define solD4 415 #define la4 440 #define laD4 466 #define si4 494 #define do5 523 #define doD5 554 #define re5 587 #define reD5 622 #define mi5 659 #define fa5 698 #define faD5 740 #define sol5 784 #define solD5 831 #define la5 880 #define laD5 932 #define si5 988 #define do6 1047 #define doD6 1109 #define re6 1175 #define reD6 1245 #define mi6 1319 #define fa6 1397 #define faD6 1480 #define sol6 1568 #define solD6 1661 #define la6 1760 #define laD6 1865 #define si6 1976 #define silence 0 // notes et leurs durées, 2 pour un demi-temps, 4 un quart de temps, etc. // un nombre négatif signifie une note pointée (allongée de moitié) int air1[] = { // Jeux interdits si4,4, si4,4, si4,4, si4,4, la4,4, sol4,4, sol4,4, faD4,4, mi4,4, mi4,4, sol4,4, si4,4, mi5,4, mi5,4, mi5,4, mi5,4, re5,4, do5,4, do5,4, si4,4, la4,4, la4,4, si4,4, do5,4, si4,4, do5,4, si4,4, reD5,4, do5,4, si4,4, si4,4, la4,4, sol4,4, sol4,4, faD4,4, mi4,4, faD4,4, faD4,4, faD4,4, faD4,4, sol4,4, faD4,4, mi4,4, mi4,4, mi4,4, mi4,-2, silence,-2, solD4,4, solD4,4, solD4,4, solD4,4, faD4,4, mi4,4, mi4,4, reD4,4, reD4,4, reD4,4, re4,4, reD4,4, doD5,4, doD5,4, doD5,4, doD5,4, reD5,4, doD5,4, doD5,4, si4,4, si4,4, si4,4, doD5,4, reD5,4, mi5,4, mi5,4, mi5,4, mi5,4, reD5,4, re5,4, doD5,4, doD5,4, doD5,4, doD5,4, si4,4, la4,4, solD4,4, solD4,4, solD4,4, solD4,4, la4,4, faD4,4, mi4,4, mi4,4, mi4,4, mi4,-2, silence,-2, }; int air2[] = { // Boléro do5,2, si4,16, do5,16, re5,16, do5,16, si4,16, la4,16, do5,8, do5,16, la4,16, do5,2, si4,16, do5,16, la4,16, sol4,16, mi4,16, fa4,16, sol4,2, fa4,16, mi4,16, re4,16, mi4,16, fa4,16, sol4,16, la4,16, sol4,2, la4,16, si4,16, la4,16, sol4,16, fa4,16, mi4,16, re4,16, mi4,16, re4,16, do4,4, do4,16, re4,16, mi4,8, fa4,8, re4,4, sol4,2, re5,2, do5,16, si4,16, la4,16, si4,16, do5,16, re5,16, do5,16, si4,4, do5,16, si4,16, la4,16, do5,16, si4,16, la4,16, fa4,8, fa4,16, fa4,16, fa4,8, la4,8, do5,16, la4,16, si4,16, sol4,16, fa4,8, fa4,16, fa4,16, fa4,8, la4,8, si4,16, sol4,16, la4,16, fa4,16, re4,8, re4,16, do4,16, re4,2, re4,16, re4,16, re4,8, fa4,8, la4,16, fa4,16, sol4,16, mi4,16, re4,8, re4,16, do4,16, re4,2, re4,16, re4,16, re4,8, mi4,16, fa4,16, sol4,2, fa4,16, mi4,16, re4,16, do4,1 }; int air3[] = { // Carmen - Habanera //re3,4, la3,16, fa3,8, la3,8, re3,4, la3,16, fa3,8, la3,8, re3,4, la3,16, fa3,8, la3,8, re3,4, re5,8, doD5,8, do5,16, do5,8, do5,16, si4,8, laD4,8, la4,4, la4,16, solD4,8, sol4,8, fa4,8, mi4,16, fa4,16, sol4,8, fa4,8, mi4,4, re5,8, doD5,8, do5,16, do5,8, do5,16, si4,8, laD4,8, la4,4, la4,16, sol4,8, fa4,8, mi4,8, re4,16, mi4,16, fa4,8, mi4,8, re4,4, //re5,8, doD5,8, do5,16, do5,8, do5,16, si4,8, laD4,8, la4,4, la4,16, //solD4,8, sol4,8, fa4,8, mi4,16, fa4,16, sol4,8, fa4,8, mi4,4, //re5,8, doD5,8, do5,16, do5,8, do5,16, si4,8, laD4,8, la4,4, la4,16, //sol4,8, fa4,8, mi4,8, re4,16, mi4,16, fa4,8, mi4,8, re4,4, la4,16, re4,8, mi4,8, faD4,4, la4,16, faD4,8, mi4,8, re4,4, mi4,16, faD4,8, sol4,8, la4,16, la4,16, la4,16, la4,16, si4,8, la4,8, sol4,4, si4,16, mi4,8, faD4,8, sol4,4, si4,16, sol4,8, faD4,8, mi4,4, faD4,16, sol4,8, la4,8, si4,16, si4,16, si4,16, si4,16, doD5,8, si4,8, la4,4, la4,16, re4,8, mi4,8, faD4,4, la4,16, faD4,8, mi4,8, re4,4, mi4,16, faD4,8, sol4,8, la4,16, la4,16, la4,16, la4,16, re5,8, doD5,8, sol4,4, si4,16, mi4,8, faD4,8, sol4,4, si4,16, sol4,8, faD4,8, mi4,4, faD4,16, sol4,8, la4,8, doD5,16, si4,16, solD4,16, la4,16, faD5,8, mi5,8, re5,2, la4,8, re5,8, silence,2 }; int air4[] = { // Clair de Lune sol4,8, sol5,2, mi5,2, la4,8, re5,8, mi5,8, re5,-2, sol4,8, do5,8, re5,8, do5,-4, mi5,-4, do5,8, fa4,8, si4,8, do5,8, si4,-2, fa4,8, la4,8, si4,8, la4,8, re5,8, la4,8, sol4,8, la4,8, sol4,8, re4,8, fa4,8, sol4,8, fa4,-4, mi4,-4, do4,8, mi4,8, fa4,8, mi4,8, la4,8, mi4,8, re4,8, mi4,8, re4,8, sol3,8, do4,8, re4,8, do4,-4, si3,-4, sol3,8, do3,8, silence,-8, sol3,8, sol4,-4, mi4,-4, silence,8, re4,8, mi4,8, re4,-2, silence,8, do4,8, re4,8, sol4,-4, mi4,-4, silence,8, re4,8, mi4,8, re4,-4, do4,-4, silence,8, do4,8, re4,8, la4,-4, mi4,-4, silence,8, fa4,8, sol4,8, do5,-4, la4,-4, silence,8, la4,8, si4,8, mi5,-4, sol4,-4, silence,8, do3,16, mi3,16, sol3,16, do4,16, mi4,16, sol4,16, mi3,16, sol3,16, si3,16, mi4,16, sol4,16, si4,16, sol3,16, laD3,16, reD4,16, sol4,16, laD4,16, reD5,16, sol5,-4, do3,-4, silence,-4 }; int air5[] = { // Marche funèbre la3,4, la3,-8,la3,16, la3,4, fa3,4, la3,4, la3,-8,la3,16, la3,4, fa3,4, la3,4, la3,-8,la3,16, la3,4, do4,-8,si3,16, si3,-8,la3,16, la3,-8,la3,16, la3,4, fa3,4, do4,4, do4,-8,do4,16, do4,4, mi4,-8,re4,16, re4,-8,do4,16, do4,-8,do4,16, do4,4, la3,4, la4,-8,sol4,16, fa4,-8,mi4,16, mi4,4, do4,4, la4,-8,sol4,16, fa4,-8,mi4,16, mi4,4, do4,4, la3,4, la3,-8,la3,16, la3,4, do4,-8,si3,16, si3,-8,la3,16, la3,-8,la3,16, la3,4, fa3,4, // la4,-8,sol4,16, fa4,-8,mi4,16, mi4,4, do4,4, la4,-8,sol4,16, fa4,-8,mi4,16, mi4,4, do4,4, la3,4, la3,-8,la3,16, la3,4, do4,-8,si3,16, si3,-8,la3,16, la3,-8,la3,16, la3,4, fa3,4, // mi4,-8,fa4,16, sol4,-8,la4,16, si4,-8,do5,16, mi5,4, mi4,2, re4,-4, silence,16, fa5,16, mi5,4, mi5,-8,faD5,16, solD5,-8,la5,16, si5,-8,do6,16, do6,2, si5,4, silence,4, sol4,4, sol4,4, fa4,4, silence,4, mi4,4, mi4,4, do4,2 }; // une note se code sur 2 entiers, sizeof() retourne le nombre d'octets int nb_notes1 = sizeof(air1)/sizeof(air1[0])/2; int nb_notes2 = sizeof(air2)/sizeof(air2[0])/2; int nb_notes3 = sizeof(air3)/sizeof(air3[0])/2; int nb_notes4 = sizeof(air4)/sizeof(air4[0])/2; int nb_notes5 = sizeof(air5)/sizeof(air5[0])/2; // durée d'une note ronde int temps1 = 2400; int temps2 = 3800; int temps3 = 3600; int temps4 = 3800; int temps5 = 4000; int diviseur = 0; int duree_note = 0; int freq_note = 0;; int mode_air = 0; // 0 pour stop, 1 pour air 1, 2 pour air 2... int note = 0; // numéro de la note jouée void jouer() { tone(bipeur, freq_note, duree_note*0.9); delay(duree_note); // la fonction tone() n'est pas bloquante... note++; } // page au format html String html="<!DOCTYPE html>" "<html>" "<head>" "<meta charset=\"utf-8\">" "<style>" "body { background-color: #003; font-family: sans-serif; }" "a {" "display: block;" "text-align: center;" "background-color: #cfc;" "padding: 10px;" "margin: 20px;" "color: #009;" "font-size: 3em;" "text-decoration: none;" "border: 4px solid #888;" "border-radius: 25px 25px 50px 50px;" "}" "a:hover { opacity: 0.8; }" ".stop { background-color: #fcc; border-radius: 50px 50px 25px 25px; }" "</style>" "</head>" "<body>" "<a href=\"/AIR=1\">Jeux interdits</a>" "<a href=\"/AIR=2\">Boléro</a>" "<a href=\"/AIR=3\">Carmen - Habanera</a>" "<a href=\"/AIR=4\">Clair de lune</a>" "<a href=\"/AIR=5\">Marche funèbre</a>" "<a href=\"/AIR=0\" class=\"stop\">Stop</a>" "</body>" "</html>"; void setup() { pinMode(bipeur, OUTPUT); Serial.begin(9600); delay(500); Serial.println(""); Serial.print("Configuration en point d'accès Wifi ... "); Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Prêt" : "Erreur!"); Serial.print("Démarrage du point d'accès Wifi ... "); //Serial.println(WiFi.softAP(ssid,password) ? "Prêt" : "Erreur!"); Serial.println(WiFi.softAP(ssid) ? "Prêt" : "Erreur!"); Serial.print("Adresse IP du point d'accès Wifi : "); Serial.println(WiFi.softAPIP()); // Démarrer le serveur server.begin(); Serial.println("Serveur Web démarré"); Serial.println(""); } void loop() { // Vérifier si le client est connecté WiFiClient client = server.available(); if (!client) { if (mode_air == 1) { diviseur = air1[2*note+1]; duree_note = temps1/abs(diviseur); if (diviseur < 0) { duree_note *= 1.5; } freq_note = air1[2*note]; jouer(); if (note >= nb_notes1) { note = 0; } } if (mode_air == 2) { diviseur = air2[2*note+1]; duree_note = temps2/abs(diviseur); if (diviseur < 0) { duree_note *= 1.5; } freq_note = air2[2*note]; jouer(); if (note >= nb_notes2) { note = 0; } } if (mode_air == 3) { diviseur = air3[2*note+1]; duree_note = temps3/abs(diviseur); if (diviseur < 0) { duree_note *= 1.5; } freq_note = air3[2*note]; jouer(); if (note >= nb_notes3) { note = 0; } } if (mode_air == 4) { diviseur = air4[2*note+1]; duree_note = temps4/abs(diviseur); if (diviseur < 0) { duree_note *= 1.5; } freq_note = air4[2*note]; jouer(); if (note >= nb_notes4) { note = 0; } } if (mode_air == 5) { diviseur = air5[2*note+1]; duree_note = temps5/abs(diviseur); if (diviseur < 0) { duree_note *= 1.5; } freq_note = air5[2*note]; jouer(); if (note >= nb_notes5) { note = 0; } } return; } // Attendre jusqu'à ce que le client envoie des données Serial.println("Nouveau client"); while (!client.available()) { delay(100); } // Lire la première ligne de la requête String request = client.readStringUntil('\r'); Serial.println(request); client.flush(); // Vérifier la requête pour la musique numéro 1 if (request.indexOf("AIR=0") != -1) { mode_air = 0; note = 0; Serial.println("Musique stoppée"); } if (request.indexOf("AIR=1") != -1) { mode_air = 1; note = 0; Serial.println("Jeux interdits"); } if (request.indexOf("AIR=2") != -1) { mode_air = 2; note = 0; Serial.println("Boléro"); } if (request.indexOf("AIR=3") != -1) { mode_air = 3; note = 0; Serial.println("Carmen - Habanera"); } if (request.indexOf("AIR=4") != -1) { mode_air = 4; note = 0; Serial.println("Clair de Lune"); } if (request.indexOf("AIR=5") != -1) { mode_air = 5; note = 0; Serial.println("Marche funèbre"); } // Retourner la réponse client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(""); // A ne pas oublier! client.println(html); delay(1000); Serial.println("Client déconnecté"); Serial.println(""); }
Pour arrêter la musique à n'importe quel instant, plusieurs solutions existent. Dans ce programme, une note d'un air est jouée, puis, si un client ne se connecte pas, la note suivante est jouée, et ainsi de suite. A la fin du morceau, le programme revient à la première note. Si un client se connecte, le programme joue l'air demandé en commençant par la première note.
Le boîtier comporte deux pièces imprimées en 3D :
Les deux pièces sont modélisées avec le logiciel FreeCAD, version 0.20.2 :
Les deux modèles volumiques sont ensuite exportées au format STL, compatible avec les imprimantes 3D :
Les tests réalisés avec un ordinateur portable ou une tablette donnent des résultats satisfaisants.
Par contre, les essais effectués avec des téléphones portables se concluent diversement :
La démonstration avec le téléphone portable d'un spectateur pris au hasard conduit tantôt à la réussite, tantôt à l'échec. Ces dysfonctionnements restent à éclaircir et à corriger. Au cours de la réalisation de cet objet, des idées d'améliorations apparaissent :