Skip to Content

Analyseur de spectre avec ESP32 et MAX4466

Analyseur de spectre avec ESP32 et MAX4466

Dans ce tutoriel, vous apprendrez à construire un analyseur de spectre de fréquence simple avec un ESP32. Nous allons utiliser le microphone MAX4466 et la bibliothèque arduinoFFT pour mesurer les fréquences du signal audio et afficher le spectre de fréquence sur un écran OLED.

Commençons par les composants dont vous aurez besoin pour ce projet.

Composants requis

Vous aurez besoin d’un ESP32, d’un écran OLED et du module microphone MAX4466. J’utilise l’ESP32 lite comme microprocesseur, car il dispose d’une interface de charge de batterie, ce qui permet de faire fonctionner facilement l’analyseur de spectre sur batterie pour le rendre portable. Mais tout autre ESP32 fonctionnera également.

ESP32 lite Lolin32

ESP32 lite

USB data cable

Câble USB de données

Microphone MAX4466

OLED display

Écran OLED

Dupont wire set

Jeu de fils Dupont

Half_breadboard56a

Plaque d’essai (breadboard)

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Analyseur de spectre de fréquence

Un analyseur de spectre de fréquence est un appareil qui visualise et analyse le contenu spectral des signaux et affiche l’amplitude ou la puissance d’un signal sur sa plage de fréquences. Il indique essentiellement comment la puissance du signal est répartie sur des fréquences ou bandes de fréquences spécifiques.

L’image suivante montre un tel spectre de fréquence. La plage de fréquences est sur l’axe des x et l’amplitude du signal est mesurée en Decibel comme indiqué sur l’axe des y :

Exemple de spectre de fréquence (source)

Selon la précision et la plage de fréquences, les analyseurs de spectre peuvent être très coûteux. Ci-dessous une photo de l’analyseur de spectre Siglent Technologies SSA3021X capable d’analyser des fréquences radio de 9 kHz à 2,1 GHz.

Siglent Technologies SSA3021X Spectrum Analyzer
Analyseur de spectre Siglent Technologies SSA3021X (source)

Spectre de fréquence audio

Une application courante des analyseurs de spectre est de visualiser la distribution des fréquences dans unsignal audio. La plage de fréquences audibles par l’homme va de 20 à 20 000 Hz (=20 kHz). Cependant, ce sont les extrêmes et la plage auditive diminue avec l’âge. Pour les adultes, la limite supérieure est généralement autour de 15 000 Hz. Si vous voulez connaître votre limite supérieure, vous pouvez essayer ce petit test auditif.

L’image suivante montre l’affichage d’un analyseur de spectre audio typique. Vous pouvez voir les différentes bandes de fréquences (barres vertes) allant de 20 Hz à 20 kHz.

BDS PP-131P Digital Audio Spectrum Analyzer
Analyseur de spectre audio numérique BDS PP-131P (source)

Dans ce tutoriel, nous allons construire un tel analyseur de spectre audio nous-mêmes. Cependant, ne vous attendez pas à trop de précision. Puisque nous utilisons un ESP32 bon marché, un petit écran OLED et un simple microphone MAX4466, notre analyseur de spectre ne sera pas très précis.

Sa plage de fréquences sera seulement de 125 Hz à 8000 Hz et nous n’aurons que 7 bandes de fréquences, mais ce sera suffisant pour visualiser et animer le contenu fréquentiel de la musique.

Fonctionnement d’un analyseur de spectre

Comment fonctionne notre petit analyseur de spectre audio ? Le microphone convertit les ondes sonores en impulsions électriques. Ces faibles impulsions sont amplifiées par le MAX4466 et envoyées dans le convertisseur analogique-numérique (ADC) de l’ESP32. Si vous lisez la sortie de l’ADC et la tracez, le signal audio détecté pourrait ressembler à ceci :

Time domain signal composed of cosine waves
Signal dans le domaine temporel composé d’ondes cosinus (source)

Sur l’axe des x se trouve le temps et sur l’axe des y l’amplitude du signal. Ce signal est appelé signal dans le domaine temporel, car il est rapporté dans le temps mais ne montre pas les fréquences contenues dans le signal.

Le signal ci-dessus est en fait composé de signaux cosinus aux fréquences 10, 20, 30, 40 et 50 Hz avec des amplitudes croissantes. Si nous appliquons une méthode mathématique appelée Fast Fourier Transformation (FFT), nous pouvons extraire ces fréquences du signal. Le graphique ci-dessous montre le même signal après application de la FFT :

Frequency domain signal composed of cosine waves
Signal dans le domaine fréquentiel composé d’ondes cosinus (source)

Le signal est maintenant dans le domaine fréquentiel avec la fréquence sur l’axe des x (au lieu du temps). Vous pouvez clairement voir les pics à 10, 20, 30, 40 et 50 Hz, qui révèlent les fréquences composant le signal d’entrée. Pour effectuer la FFT, nous laissons l’ESP32 échantillonner le signal audio récupéré de l’ADC puis appelons la arduinoFFT library pour extraire les fréquences. Le spectre de fréquence résultant est ensuite affiché sur l’OLED.

Cependant, nous n’afficherons pas les fréquences individuelles dans le spectre mais les regrouperons en bandes de fréquences plus faciles à lire et couramment utilisées dans les analyseurs de spectre audio. Les bandes de fréquences seront autour de 125, 250, 500, 1000, 2000, 4000 et 8000 Hz, comme montré ci-dessous :

Frequency Spectrum on OLED
Spectre de fréquence sur OLED

Module microphone MAX4466

Le module microphone MAX4466 est une carte d’extension avec un microphone électret 20-20 kHz et le MAX4466 circuit intégré préamplificateur. Au dos de la carte se trouve un petit potentiomètre qui permet d’ajuster le gain de 25x à 125x. L’image ci-dessous montre le devant et le dos de la carte :

Devant et dos du module microphone MAX4466

Le module fonctionne sous 2,4…5,5 V pour le VCC avec un courant de repos très faible inférieur à 24 μA. La sortie aura un biais de VCC/2. Donc, lorsqu’il fait parfaitement silence, la tension de sortie sera à VCC/2 V.

Si vous souhaitez en savoir plus sur le module microphone MAX4466, consultez le Detect sound with MAX4466 and Arduino tutoriel.

Connexion du MAX4466 et de l’OLED à l’ESP32

Dans cette section, nous connectons d’abord le module microphone MAX4466 à l’ESP32 et testons son fonctionnement. Ensuite, nous ajoutons l’OLED au circuit et écrivons le code pour l’analyseur de spectre :

Connexion du MAX4466 à l’ESP32

Pour connecter le MAX4466 à un ESP32, commencez par relier la masse (GND) de l’ESP32 et du module MAX4466 (fil bleu). Puis connectez le VCC du MAX4466 à la sortie 3,3 V de l’ESP32 (fil rouge). Enfin, connectez le GPIO 4 de l’ESP32 à la broche OUT du MAX4466 (fil orange) :

Connecting MAX4466 to ESP32
Connexion du MAX4466 à l’ESP32

Notez qu’au lieu du GPIO 4, vous pouvez utiliser n’importe quelle autre broche tant qu’elle peut lire un signal analogique.

Code de test pour MAX4466

Après avoir connecté le microphone MAX4466 à l’ESP32, lançons un petit test pour vérifier que tout fonctionne comme prévu. Le code suivant lit le signal du microphone sur la broche 4 et l’affiche dans le moniteur série :

const byte micPin = 4;

void setup() {
  Serial.begin(115200);
  pinMode(micPin, INPUT);
}

void loop() {
  int value = analogRead(micPin);
  Serial.println(value);
  delay(100);  
}

Si vous téléversez ce code, ouvrez le traceur série et faites du bruit, vous devriez voir des fluctuations dans le signal audio liées au volume du bruit :

Audio signal on Serial Plotter
Signal audio sur le traceur série

Si vous ne voyez aucun changement dans le signal audio, soit votre câblage est incorrect, soit la broche GPIO pour le microphone dans le code est erronée.

Ajout de l’OLED à l’ESP32

À l’étape suivante, nous ajoutons l’OLED à l’ESP32. Connectez d’abord GND et VCC de l’OLED aux lignes d’alimentation existantes (fils bleu et rouge). Puis connectez SCL à la broche 23 et SDA à la broche 18 de l’ESP32, comme montré ci-dessous :

Connecting MAX4466 and OLED with ESP32
Connexion du MAX4466 et de l’OLED avec l’ESP32

Les GPIO 23 et 18 sont les broches I2C matérielles pour l’ESP32 lite. Si vous utilisez une autre carte ESP32, vos broches I2C matérielles peuvent être différentes. Si vous n’êtes pas sûr des broches à utiliser, consultez le Find I2C and SPI default pins tutoriel. L’image ci-dessous montre le circuit complet sur une breadboard :

Audio Spectrum Analyzer circuit on breadboard
Circuit de l’analyseur de spectre audio sur breadboard

Code pour afficher le spectrogramme de fréquence

Avant de pouvoir écrire le code pour calculer et afficher le spectrogramme de fréquence, nous devons installer la arduinoFFT library. Ouvrez le LIBRARY MANAGER, tapez « arduinoFFT » dans la barre de recherche et appuyez sur leINSTALL bouton. Après une installation réussie, vous devriez voir ceci :

arduinoFFT library installed in Library Manager
Bibliothèque arduinoFFT installée dans le Library Manager

De plus, nous aurons besoin d’installer la Adafruit_SSD1306 Library pour contrôler l’OLED. Tapez « Adafruit_SSD1306 » dans la barre de recherche et, comme précédemment, appuyez surINSTALL pour installer la bibliothèque :

Adafruit_SSD1306 library installed in Library Manager
Bibliothèque Adafruit_SSD1306 installée dans le Library Manager

Maintenant que c’est fait, nous pouvons commencer à écrire le code. Le code suivant pour un visualiseur de spectre audio lit l’entrée audio d’un microphone, effectue une transformation de Fourier rapide (FFT) pour analyser le spectre de fréquences, et affiche les résultats sur un OLED. L’image suivante montre la sortie créée et la signification des éléments visuels :

Visual Elements of the Spectrum Analyzer
Éléments visuels de l’analyseur de spectre

Dans les sections suivantes, nous examinerons ce code fonction par fonction. Le code est basé sur l’exemple ESP32_FFT_VU mais a été découpé en fonctions plus petites et légèrement modifié pour une meilleure lisibilité.

#include "arduinoFFT.h"
#include "Adafruit_SSD1306.h"

const int micPin = 4;
const int volume = 300;        // Adjust depending on audio volume 
const int noise = 2000;        // Noise filter, smaller signal is ignored  
const int nSamples = 256;      // Must be a power of 2
const int sampleFreq = 40000;  // Hz, 40000 or less 

double vReal[nSamples];
double vImag[nSamples];
byte peaks[] = { 0, 0, 0, 0, 0, 0, 0 };

ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, nSamples, sampleFreq);
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

void displayScale() {
  oled.clearDisplay();
  oled.setCursor(0, 0);
  oled.print(".1 .2 .5 1K 2K 4K 8K");
}

void displayBands() {
  for (int i = 2; i < (nSamples / 2); i++) {    
    if (vReal[i] > noise) {                               
      if (i <= 2) displayBand(0, vReal[i]);              // 125Hz
      if (i > 3 && i <= 5) displayBand(1, vReal[i]);     // 250Hz
      if (i > 5 && i <= 7) displayBand(2, vReal[i]);     // 500Hz
      if (i > 7 && i <= 15) displayBand(3, vReal[i]);    // 1000Hz
      if (i > 15 && i <= 30) displayBand(4, vReal[i]);   // 2000Hz
      if (i > 30 && i <= 53) displayBand(5, vReal[i]);   // 4000Hz
      if (i > 53 && i <= 200) displayBand(6, vReal[i]);  // 8000Hz
      if (i > 200) displayBand(7, vReal[i]);             // 16000Hz
    }
  }
}

void calcFFT() {
  FFT.windowing(vReal, nSamples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.compute(vReal, vImag, nSamples, FFT_FORWARD);
  FFT.complexToMagnitude(vReal, vImag, nSamples);
}

void sampleAudio() {
  static unsigned long newTime, oldTime;
  long period_us = round(1000000.0 / sampleFreq);

  for (int i = 0; i < nSamples; i++) {
    newTime = micros() - oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(micPin);
    vImag[i] = 0;
    while (micros() < (newTime + period_us))
      ;
  }
}

void displayPeaks() {
  for (byte band = 0; band <= 6; band++) {
    oled.drawFastHLine(18 * band, 64 - peaks[band], 14, WHITE);
  }
}

void decayPeaks() {
  if (millis() % 4 == 0) {
    for (byte band = 0; band <= 6; band++) {
      peaks[band] = max(0, peaks[band] - 1);
    }
  }
}

void displayBand(int band, double magnitude) {
  int dmax = 50;
  int dsize = min((int)(magnitude / volume), dmax);
  if (band == 7) {
    oled.drawFastHLine(18 * 6, 0, 14, WHITE);
  }  
  for (int s = 0; s <= dsize; s += 2) {
    oled.drawFastHLine(18 * band, 64 - s, 14, WHITE);
  }
  if (dsize > peaks[band]) { 
    peaks[band] = dsize; 
  }
}

void setup() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.setTextSize(1);
  oled.setTextColor(WHITE);

  pinMode(micPin, INPUT);
}

void loop() {
  displayScale();
  sampleAudio();
  calcFFT();
  displayBands();
  displayPeaks();
  decayPeaks();
  oled.display();
  delay(1);
}

Bibliothèques

Le code commence par inclure les bibliothèques nécessaires. Il y a la bibliothèquearduinoFFT pour calculer la transformation de Fourier rapide (FFT) et la bibliothèqueAdafruit_SSD1306, nécessaire pour contrôler l’OLED :

#include "arduinoFFT.h"
#include "Adafruit_SSD1306.h"

Constantes

Nous définissons ensuite quelques constantes. micPin définit la broche GPIO à laquelle la sortie du MAX4466 est connectée. La constantevolume vous permet d’ajuster l’amplitude du spectre de fréquence affiché en fonction du volume audio. Si vous ne voyez aucune barre, baissez cette valeur, et si les barres sont très grandes, même pour un son faible, augmentez cette valeur.

De même, la constante de bruit vous permet de filtrer le bruit de faible niveau. Si vous voyez trop de bruit dans le spectre de fréquence (fluctuations aléatoires même en l’absence de son), augmentez cette valeur.

nSamples définit le nombre d’échantillons audio pris etsampleFreq est la fréquence d’échantillonnage. Pour des microcontrôleurs plus lents que l’ESP32, vous devrez peut-être réduire ces valeurs.

const int micPin = 4;
const int volume = 300;        // Adjust depending on audio volume 
const int noise = 2000;        // Noise filter, smaller signal is ignored  
const int nSamples = 256;      // Must be a power of 2
const int sampleFreq = 40000;  // Hz, 40000 or less 

Variables

Il y a trois variables. vReal et vImag sont des tableaux qui stockent la partie réelle et imaginaire du signal, et le tableau peaks garde la trace des magnitudes maximales pour chaque barre de fréquence.

double vReal[nSamples];
double vImag[nSamples];
byte peaks[] = { 0, 0, 0, 0, 0, 0, 0 };

Objets

Enfin, nous avons les objets. L’objetFFT contient les fonctions nécessaires pour calculer la transformation de Fourier rapide, et l’objetoled contient les fonctions pour contrôler l’affichage OLED.

ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, nSamples, sampleFreq);
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

displayScale

La fonction displayScale() prépare l’affichage OLED en effaçant tout contenu précédent et en imprimant les étiquettes des bandes de fréquences. Cela aide l’utilisateur à comprendre quelle bande correspond à quelle fréquence. La fonction utilise ce code :

void displayScale() {
  oled.clearDisplay();
  oled.setCursor(0, 0);
  oled.print(".1 .2 .5 1K 2K 4K 8K");
}

La fonction displayBands() traite le résultat FFT stocké dans le tableau vReal et décide dans quelle bande de fréquence chaque bin FFT se trouve. Elle filtre les signaux en dessous d’un certain seuil de bruit puis appelle displayBand() avec l’indice de la bande et la magnitude du signal :

void displayBands() {
  for (int i = 2; i < (nSamples / 2); i++) {
    if (vReal[i] > noise) {
      if (i <= 2) displayBand(0, vReal[i]);
      if (i > 3 && i <= 5) displayBand(1, vReal[i]);
      if (i > 5 && i <= 7) displayBand(2, vReal[i]);
      if (i > 7 && i <= 15) displayBand(3, vReal[i]);
      if (i > 15 && i <= 30) displayBand(4, vReal[i]);
      if (i > 30 && i <= 53) displayBand(5, vReal[i]);
      if (i > 53 && i <= 200) displayBand(6, vReal[i]);
      if (i > 200) displayBand(7, vReal[i]);
    }
  }
}

La fonction calcFFT() effectue l’analyse FFT proprement dite. Elle applique une fenêtre de Hamming pour lisser le signal puis calcule la FFT, transformant les parties réelles et imaginaires en magnitudes stockées dansvReal :

void calcFFT() {
  FFT.windowing(vReal, nSamples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.compute(vReal, vImag, nSamples, FFT_FORWARD);
  FFT.complexToMagnitude(vReal, vImag, nSamples);
}

La fonction sampleAudio() collectenSamples échantillons analogiques du microphone. Elle lit le signal à intervalles réguliers, calculés avec sampleFreq, et le stocke dans le tableau vReal. vImag est mis à zéro car l’entrée est purement réelle :

void sampleAudio() {
  static unsigned long newTime, oldTime;
  long period_us = round(1000000.0 / sampleFreq);

  for (int i = 0; i < nSamples; i++) {
    newTime = micros() - oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(micPin);
    vImag[i] = 0;
    while (micros() < (newTime + period_us))
      ;
  }
}

La fonction displayPeaks() affiche les valeurs de pics persistants de chaque bande de fréquence en dessinant de courtes lignes horizontales là où le pic est apparu pour la dernière fois. Ces lignes aident à visualiser les niveaux maximums récents de volume dans chaque bande :

void displayPeaks() {
  for (byte band = 0; band <= 6; band++) {
    oled.drawFastHLine(18 * band, 64 - peaks[band], 14, WHITE);
  }
}

La fonction decayPeaks() réduit lentement la valeur du pic au fil du temps, simulant un effet de décroissance. Elle s’exécute environ toutes les 4 millisecondes et diminue chaque pic de bande de 1, jusqu’à un minimum de 0 :

void decayPeaks() {
  if (millis() % 4 == 0) {
    for (byte band = 0; band <= 6; band++) {
      peaks[band] = max(0, peaks[band] - 1);
    }
  }
}

La fonction displayBand() dessine des barres verticales pour chaque bande sur l’OLED. Elle ajuste la taille des barres en fonction de la magnitude du signal divisée par le paramètre volume. Si l’indice de la bande est 7, une barre spéciale est dessinée en haut de l’écran. La fonction met aussi à jour le tableau peaks si la barre actuelle dépasse le pic précédent :

void displayBand(int band, double magnitude) {
  int dmax = 50;
  int dsize = min((int)(magnitude / volume), dmax);
  if (band == 7) {
    oled.drawFastHLine(18 * 6, 0, 14, WHITE);
  }
  for (int s = 0; s <= dsize; s += 2) {
    oled.drawFastHLine(18 * band, 64 - s, 14, WHITE);
  }
  if (dsize > peaks[band]) {
    peaks[band] = dsize;
  }
}

La fonction setup() initialise l’écran OLED et configure la broche du microphone en entrée. Cela prépare le système à commencer à échantillonner et afficher les données audio :

void setup() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.setTextSize(1);
  oled.setTextColor(WHITE);
  pinMode(micPin, INPUT);
}

Enfin, la fonction loop() est le cycle d’exécution principal. Elle effectue en boucle toutes les étapes nécessaires pour capturer l’audio, le traiter via FFT, visualiser les données et mettre à jour l’écran OLED :

void loop() {
  displayScale();
  sampleAudio();
  calcFFT();
  displayBands();
  displayPeaks();
  decayPeaks();
  oled.display();
  delay(1);
}

Ensemble, ces fonctions forment une boucle complète qui analyse continuellement l’entrée audio et affiche un spectre visuel sur l’écran OLED.

Tons de test

Vous pouvez tester l’analyseur de spectre en jouant des tons de test. Voici des liens vers des vidéos Youtube qui jouent des tons à des fréquences spécifiques :

L’analyseur de spectre devrait afficher une barre plus grande/croissante pour la bande de fréquence correspondant au ton de test. Par exemple, pour un ton à 2000 Hz, vous devriez voir une barre plus grande dans la bande 2 kHz :

2KHz tone detected by Spectrum Analyzer
Ton 2 kHz détecté par l’analyseur de spectre

La hauteur de la barre dépendra du volume. Vous devrez probablement ajuster la constante volume ou legain control du MAX4466 pour obtenir un bon signal. Vous pouvez trouver le potentiomètre pour ajuster le gain à l’arrière du module MAX4466 :

Gain control of MAX4466 module
Contrôle du gain du module MAX4466

Conclusions

Dans ce tutoriel, vous avez appris à construire un analyseur de spectre de fréquence utilisant le module microphone MAX4466, un OLED et un ESP32.

Si vous souhaitez améliorer la précision et la plage de fréquences de l’analyseur, vous pourriez utiliser un convertisseur analogique-numérique externe pour une meilleure résolution et un microphone I2C pour une meilleure plage.

De même, si vous voulez un écran plus grand et en couleur, vous pourriez remplacer l’OLED par un écran TFT. Consultez les tutoriels Interface TFT ILI9341 Touch Display with ESP32 et peut-être How to configure TFT_eSPI Library for TFT display.

La carte ESP32 lite dispose d’une interface batterie LiPo, ce qui facilite l’alimentation de l’analyseur de spectre avec une batterie LiPo 4,2 V. Cependant, la carte est un peu grande, surtout comparée au petit OLED que nous avons utilisé. Au lieu de l’ESP32 lite, vous pourriez utiliser un ESP32-C SuperMini pour construire un analyseur de spectre très petit, mignon et portable.

Bon bricolage ; )

Liens

Voici quelques liens que j’ai trouvés utiles lors de la rédaction de ce tutoriel :