|
|
|
|
|||
|
Petit haut-parleur + clavier |
|
Maj : 02/05/2026
|
||
![]()
Dans ce texte, par facilité, je parlerai de « buzzer » mais le terme exact serait « petit haut-parleur », car au sens strict, un buzzer vibre à fréquence fixe quand il est alimenté en continu, alors que dans notre application nous fournirons un signal carré variable pour tenter de produire des sons variés bien que peu mélodieux.
Vous trouverez sur le Net pléthore de montages du buzzer (lire : dispositif sonore), chacun ayant recopié sans le comprendre un montage trouvé n’importe où.
Les plus simplistes attaquent directement le buzzer sur un GPIO ce qui parasite le microcontrôleur, d’autres rajoutent une résistance en protection, certains une diode de roue libre, d’autres un transistor…
Nous allons voir qu’un montage propre est un peu plus compliqué mais donne de meilleurs résultats.
De plus, avec un seul fil commun, il permet aussi de gérer 3 à 9 boutons poussoirs, ce qui économise les rares GPIOs disponibles sur une carte à ESP32.
Attention la fonction <tone> ne fonctionne pas sur les ESP32!
![]()
![]() La loterie ! |
![]() Disques acceptables |
Dans tous les cas un vrai petit haut-parleur de récupération sera bien meilleur que les petits gadgets ! |
![]() |
| Traditionnellement, nous utiliserons les modèles chinois à bas prix, ronds de 14 mm de diamètre sur 8 mm de haut. Il sont constitués de disques piezzoélectriques avec une résistance parallèle interne de moins de 15 Ohms. Mais la sélection est très difficile sur les sites chinois, les descriptions sont totalement farfelues !
|
![]()
Il ne faudra pas espérer reproduire un Stradivarius, mais simplement générer de petits sons caractéristiques pour marquer les étapes d’un programme.
Cette image empruntée à Wikipédia montre l’effet des premiers harmoniques. |
![]() |
Pour commencer, voyons très simplement le comportement d’un petit mosFet N (AO3400A), source à la masse, charge résistive sur le drain à Vcc 3.3V, attaque de la gate par une rampe 0..Vcc (en jaune). La courbe Violette est la tension drain. De Vgate 0 à 0.7V, le mosFet est bloqué, Vdrain= Vcc. Donc attaqué en signal carré : bloqué à OV, conduit à Vcc.
Rgate (1 k Ohms) est une simple protection, le courant étant nul. |
![]() |
Le condensateur Ccont est utile , car si par erreur la sortie GPio (pin BUZ) restait à l’état haut, le haut-parleur recevrait un courant permanent. En négligeant Rgate, le circuit de gate est constitue par Rsec + Ccont dont le Tau = R*C est égal à :
Sur le buzzer, deux composants optionnels : Le réglage du volume se fait basiquement par une résistance mobile (1/4 watt) sur la prise 1-3, c’est moins encombrant qu’un potentiomètre et on ne le règle qu’une seule fois. Le branchement en 1-2 permet de tester divers autres dispositifs. La résistance en parallèle ainsi que la diode de roue libre sont inutiles avec un vrai haut-parleur. |
![]() |
En jaune tension Vgate, en violet Vdrain, sur charge résistive 1 kOhm, attaque en signal carré.
|
![]() |
Deuxième courbe pour une fréquence très haute de 10 kHz. Le condensateur est presque un court-circuit et a peu d’influence, sauf à raccourcir un peu la période active, mais l’oreille ne perçoit pas la différence d’avec un vrai court-circuit qui produirait un créneau à 50%. Remarque : |
![]() |
|
![]() |
![]()
Autres approches, toujours en signal digital
Approche PWM
L’ESP32 possède 16 PWM, mais il y en a peu d’utilisables à cause de limitations.
Les fréquences utilisables sont : 1,5,8 et 10 kHz.
La résolution est entre 1 et 16 bits.
Approche modulation delta
Un seul bit commande, soit la charge (bit = 1), soit la décharge (bit = 0), d’un condensateur.
Il est ainsi possible d’obtenir, derrière un simple passe-bas pour éliminer les dents de scie, toute tension entre 0 et Vcc. Il suffit d’un MosFet et de deux résistances pour réaliser le montage (plus RC du passe-bas)
Le choix de la commande est simple. Si "Vsortie > référence", commande 0, sinon 1.
Le problème est qu’il faut comparer en permanence la sortie au signal de commande, donc faire une lecture analogique ce qui prend du temps.
Pour l’ESP32, il est très intéressant d’utiliser ledc, prévu à l’origine pour moduler les leds.
Le soft est très simple mais est limité pour les basses fréquences.
Attention, l’écriture n’est pas la mème en IDE 2.xx ou IDE 3.xx : Cette page Expressif le précise très clairement.
![]()
Un gros problème apparait quand on doit gérer simultanément des routines qui prennent du temps machine, par exemple : Pour résoudre ce problème, je rajoute sur ma carte de développement, un ATTiny 85 Si vous avez des idées de grandeur, utilisez l’ATTiny 1616, c’est un monstre de puissance, low power et plein de GPIOs... Cet ajout d’un deuxième microcontrôleur peut sembler surabondant, mais à 2€ le composant, il va vite apparaitre comme un sous-traitant incontournable. L'ATTiny 85 est vraiment un composant fascinant. Il dispose de : 8k Bytes de flash pour le programme, 512 bytes EEprom, 512 bytes Sram, ADC 10 bits. Cela permet de faire tourner beaucoup de routines performantes. Si vous ne l’avez pas testé, lisez un descriptif des multiples combinaisons du vieux NE555, et pour vous faire la main, réalisez toutes les temporisations et monostables en ATTiny, c’est encore beaucoup mieux… Vous développez sur une carte Arduino quelconque, et quand le firmware est au point, il suffit de le transférer vers l’ATTiny. Je les utilise aussi pour des décodages de clavier de portier, la conversion de signaux NMEA183 en RS232, des déclencheurs photo, etc.. |
![]() |
![]()
L’approche convertisseur Digital vers Analogique
Dans les exemples précédents, nous attaquions via un mosFet, le haut-parleur en signaux carrés générant plein d’harmoniques désagréables. L’ESP32 Devkit V1 possède deux broches spéciales utilisables en convertisseur Digital vers Analogique, ce n’est que du 8 bits, mais suffisant pour des applications basses fréquences. DAC1 utilise GPIO25, et DAC2 utilise GPIO26. Considérons ce début de programme : Il ne fait que créer un tableau de de 256 bytes représentant une période de sinus, entre 0 et 255. |
#define stepResol 8 |
|||||||||||||||||||||||||||||
Nous savons faire une fréquence unique avec une table unique, mais on peut faire bien mieux !
|
256 points -> 731 Hz 16 points -> 11.7 kHz |
Si dans la boucle « for » nous ajoutons un delayMicrodeconds (tempo), nos rallongeons la boucle donc baissons la fréquence. Pour la table de 256, avec une tempo de 160 µS, la fréquence est de 23.4 Hz, la période est de 42.74 mS, il y a 256 boucles, donc le traitement de la condition for + dacWrite +160 µS prend à peu près 42.74/255 = 167 µS, donc <condition for> + dacWrite = 7 µS En rajoutant notre délai, la nouvelle fréquence sera environ de 1/(256* (7+tempo)), car il y a l’arrondi, le délai ne prenant que des entiers. Par exemple pour s’approcher du La 3 à 440 Hz, l’appel avec shift=1 et tempo= 3µS, nous donnera 421 Hz plus proche du Sol# ou La bémol à 415 Hz. |
|
En passant à cette petite boucle « for » les entiers shift et tempo, nous pouvons obtenir toute la gamme audible. Les fréquences se calculent par de simples tables Excel.
Cela a toutefois un défaut, les fréquences obtenues sont serrées, mais ne tombent pas juste sur les notes de la gamme.
Pour des bruitages, aucun problème, mais pour jouer un son parfait sur un bon haut-parleur, les écarts aux vraies notes seront trop sensibles pour un musicien.
Amélioration de la résolution
| Dans ce chapitre nous prendrons un shift de 2, donc 128 points par période. Le delayMicrosecond est trop long pour faire un réglage fin. Nous remarquons que delayMicrosecond(255), qui nous donnait une fréquence de 14.95 Hz prend exactement le même temps que : |
|
| Donc <la boucle for> + <l’instruction nop> prennent exactement 255µS/20540, soit environ 12.41 nanoSecondes, soit 3 coups d'horloge (2 pour la boucle, 1 pour le nop) à 240 MHz. Cela donne un delai 80 fois plus fin que delayMicrosecond() Nous pouvons maintenant ajuster plus finement les notes de la gamme, par exemple : Octave 1 : La 110 Hz avec 2616 boucles (exactement) Octave 2 : La 220 Hz avec 1207 boucles (exactement) Octave 3 : La 440 Hz avec 501 boucles (exactement) en descendant les octaves le nombre de boucles augmente ainsi que la résolution, mais en montant les octaves la précision diminue de plus en plus : |
Octave 4 : La 880 Hz, avec 149 -> 886 Hz et avec 150 -> 874.4 Hz
Octave 5 : La 1760 Hz, impossible, trop rapide, avec 1 boucle -> 1464 Hz seulement
La synthèse ne sera juste que dans les graves ce qui pose problème…Avec un shift de 2, donc 128 points on obtient un nouveau jeu de fréquences mais le problème se pose toujours.
Si l'on prend un shift de 1, on descend encore d'une octave sur les valeurs précedentes.
Le ESP32 S3 a perdu ces DACs
J’ai abandonné le Devkit V1 pour passer au ESP32 S3.
Un problème est apparu, les deux GPIO utilisables sur le Devkit, de DAC (25 et 26) n’existent plus !
Cette version est bien plus performante mais a supprimé cette possibilité.
Il faudra donc utiliser d’autres artifices comme le PWM ou un convertisseur numérique-analogique externe pour contourner ce problème gênant.
![]()
![]()
La bibliothèque d’utilitaires « sons »
La classe < C_utils_Sons> utilise seulement la sortie numérique et le mosFet comme décrit en début de page.
Elle n’utilise pas la synthèse sinusoïdale du chapitre précèdent.
La classe < C_utils_Sons> contient les outils nécessaires pour produire divers bruitages, sans utiliser le fonction « tone » qui ne fonctionne pas sur ESP332.
Elle est remplacée par la méthode « ton ». Cette méthode accepte plusieurs formats :
void ton (float note, uint8_t octave, int duration, int pause);
void ton (float note, uint8_t octave, int duration);
void ton (float frequency, int duration);
Dans cette classe, les notes (et dièses) sont écrites en toutes lettres, ce qui rend leur interprétation précise et sans ambiguïté. Pour l'ESP32-S3, j'ai utilisé à la place le module ledc, qui fonctionne très bien.
Le terminal série de la démo permet de lancer l'ensemble des sons en séquence, ou d'appeler chaque son individuellement en tapant son numéro au clavier:
Petits sons utilitaires pour ponctuer les actions courantes.
Sirènes réglementaires — SAMU, pompiers, ambulance, police — fidèlement reproduites, le ministère de l'Intérieur publiant des normes très précises à ce sujet.
Quelques thèmes musicaux, modestes mais identifiables en huit mesures.
J'avais envisagé d'intégrer des sons d'explosion, de chocs métalliques ou de chants d'oiseaux, mais cela s'avère impossible sans recourir à de lourdes tables de centaines d'octets — auquel cas, autant passer directement au MP3. J'ai tenu à garder la bibliothèque aussi compacte que possible, et j'ai donc renoncé à tout ce qui ne peut pas se jouer au piano, d'un seul doigt de la main droite.
Cette contrainte limite considérablement les possibilités et explique la pauvreté relative des rendus.
![]()
De nombreux circuits synthétiseurs audio existent, en solution « clefs en main », fournissant une sortie audio parfaite, bien meilleure que ce qui fait l’objet de cette page consacrée à l’approche par le firmware seul.
C'est une excellente approche pour des oreilles sensibles. Une recherche sur Internet <synthétiseur audio arduino> vous donnera de nombreuses pistes. Il y a beaucoup moins de choix en synthétiseurs BF qu’en HF.
Pensez aussi au très riche format Midi très bien documenté.
Autre alternative performante, la carte annexe jouant des MP3 contenus dans sa mémoire SD. Vous pouvez échantillonner toute musique, voix parlées ou chantées, des bruits…
Il existe de multiples applications spécialisées, pour traiter les sons.
L’exemple fourni avec la carte contient un exemple de sortie vocale qui illustre parfaitement les capacités de ce circuit. Le code source permet de mettre rapidement le circuit en œuvre pour jouer des MP3 (fichiers sur SD ou mémoire). L’intérêt du MP3 est que l’on peut enregistrer des paroles et les rejouer avec une très bonne qualité pour faire sa propre sortie vocale.
Ma bibliothèque « son » s’adapte parfaitement au format I2S, seules les méthodes « initSons » et « ton » sont à modifier.
Le volume se règle par un pont résistif, ou un potentiomètre numérique I2C (exemple : DS1841, MCP4551 ...) si l’on veut faire varier le niveau dans le programme. Les 256 positions linéaires peuvent paraitre surabondantes, mais comme l’oreille a une sensibilité logarithmique, il faudra ruser pour afficher quelques paliers à 3dB !
Mais cela nous éloigne évidemment du cahier des charges original pour cette page, qui était de faire du son en utilisant simplement une sortie GPIO et quelques lignes de code…
![]()
Amplification du signal vers le haut-parleur
Si l’on veut plus de puissance (?) , il faut rajouter un petit circuit intégré amplificateur. Voir par exemple le LM4890MX.
Il existe des modules chinois qui comportent l’ampli et le haut-parleur sur la même plaquette comme le SC8002B, mais vous pouvez utiliser un quelconque ampli de récupération qui traîne dans une de vos boîtes.
Avantages et inconvénients de l’amplificateur SC8002B (ou LM4871)
+ Coût dérisoire
+ Fonctionne à 3.3 V (c’est rare)
+ Surpuissant pour notre application, mais le niveau est simple à contrôler avec une résistance
+ Meilleur son possible sur un petit haut-parleur
- Consommation permanente de quelques milliampères incompatible avec des montages « low power », mais une coupure automatique de l’alimentation en rajoutant un MosFet est simple
- S’il n’est pas en stock, délai pour commande en Chine
![]()
N’hésitez pas à m’envoyer vos remarques sur le fond (parties à compléter ou modifier), et sur la forme (fautes de frappe…).
L’expérimentation sur Arduino est très enrichissante, mais se contenter de pomper des pages douteuses sans comprendre ce que l’on fait n’est pas une bonne idée.
Il faut essayer d’aller plus profondément si l’on veut en tirer une satisfaction intellectuelle…
Ne vous attendez pas à faire du son haute-fidélité aussi simplement avec un Arduino, pensez à l’alternative des circuits spécialisés évoqués.
Pour de simples petits sons ponctuant un programme, le montage décrit dans cette page suffit.
De plus, les fréquences sont calculées exactement, mais l’horloge de l’ESP32 n’est pas très stable.
Pour s’en convaincre, il suffit de réaliser une pendule élémentaire qui se base sur millis(), sans autre code parasite et la laisser tourner pour regarder la dérive importante, variable d’un chip à l’autre.
Donc quand vous lisez « le La 3 est callé exactement à 440 Hz », il faut comprendre « pour autant que millis() batte exactement la milliSeconde », ce qui n’est jamais exact.
A titre d’exemple, sur une de cartes ESP32S3, le 440 Hz généré en utilisant ledc, donne 439.882 exact.
Vous trouverez joint un petit programme qui envoie le temps toutes les minutes, calez vous sur un top à l’heure entière et laissez tourner pour voir les dérives, entre autres liés à la température (testez dans le frigo sur batterie).
![]()