Analyseur de spectre
Objectif : réaliser un analyseur de spectre (son, lumière, …) et afficher le résultat sur un écran LCD
Présentation
Décomposition de Fourier Discrète
Tout signal périodique peut être décomposé en une somme de signaux sinusoïdaux (série de Fourier) appelées harmoniques :
\(u_m(t)=U_0+\displaystyle \sum_{k=1}^\infty U_k\sqrt{2}\sin\left(2\pi f_k\cdot t+\varphi_k\right)\)
avec :
- \(U_0\) : valeur moyenne de \(u_m(t)\)
- \(U_k\) : valeur efficace de l’harmonique de rang \(k\)
- \(f_k\) : fréquence de l’harmonique de rang \(k\)
- \(\varphi_k\) : phase de l’harmonique de rang \(k\)
Cette décomposition permet de représenter un signal sous forme fréquentielle :

Un analyseur de spectre est un logiciel qui réaliser une transformation de Fourier discrète (TFD) afin d’afficher le spectre d’amplitude d’un signal physique.
Matériel nécessaire
- Un Arduino UNO
- Un capteur pour le signal analogique (microphone, photodiode, …)
- Un afficheur LCD 16×2

Logiciels nécessaires
- Arduino IDE
- Bibliothèques :
Travail demandé
Affichage des barres sur le LCD
Câblage du module d’affichage
Suivre les instructions de câblage sur la page dédiée à cet afficheur :
Pour utiliser un afficheur LCD avec la bibliothèque LiquidCrystal, il faut commencer par importer la bibliothèque, puis créer un objet de type LiquidCrystal :
#include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
Puis, dans le fonction setup, initialiser l’objet lcd avec les dimensions de l’afficheur :
void setup() {
lcd.begin(16, 2);
}
Création des caractères « barre »
Il n’existe pas de caractères permettant d’afficher des barres verticales, mais la bibliothèque LiquidCrystal permet de créer jusqu’à 8 caractères personnalisés.
Ça tombe bien puisque les caractères de ce type d’afficheur font 8 lignes de pixels, ce qui va nous permettre de définir les 8 caractères suivants, pour les niveaux 1 à 8 :

La barre de niveau « zéro » sera réalisée avec le caractère » » (espace).
Pour créer un caractère personnalisé, il faut utiliser la méthode createChar.
Par exemple pour créer un caractère « smiley »
, on commence par créer un tableau (array) de 8 nombres (type byte) de 5 bits, correspondants aux 8 lignes de 5 pixels du caractère :
byte smiley[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b10001,
0b01110,
0b00000,
0b00000
};
Écrire ces 8 nombres les uns sous les autres, et sous forme binaire (
0b...), permet de directement visualiser l’emplacement des pixels à 1.
Ensuite, on utilise la méthode createChar (dans le setup par exemple afin d’utiliser les caractères personnalisés dans tous le reste du programme) :
lcd.createChar(0, smiley);
Le premier argument correspond au code du caractère personnalisé, un entier de 0 à 7.
Et pour l’afficher :
lcd.write(byte(0));

Pour les barres, nous allons stocker les données des caractères dans un tableau barres. On définit alors un tableau de 8 caractères (tableaux de 8 lignes de 5 pixels) :
byte barres[8][8] = {
{
...,
...,
...,
...,
...,
...,
...,
...
},
{
...,
...,
...,
...,
...,
...,
...,
...
},
...
};
Afin de simplifier la création puis l’utilisation des 8 caractères, on décide d’utiliser une boucle for.
barres[8][8] puis définir une fonction print_barre (voir signature ci-dessous) qui permet d’afficher une barre de niveau niv (0 à 16) à la colonne col (0 à 15) :void print_barre(byte col, byte niv) {
...
}
Analyse du signal analogique
Installation d’un capteur analogique
N’importe quel capteur analogique convient.
Il faut cependant que le signal soit périodique (même si la période change régulièrement).
Exemple : signal sonore avec un microphone (comme ce modèle)
signal lumineux PWM (tel qu’émit par les lampes LED avec variateur) avec une photodiode ou simplement une LED.
Utilisation de la bibliothèque ArduinoFFT
Voici un programme simple illustrant l’utilisation de la bibliothèque ArduinoFFT :
#include "arduinoFFT.h" // import de la bibliothèque
// Nombre d'échantillons (puissance de 2)
const uint16_t samples = 32; // = nombre de fréquences analysées
// Fréquence d'échantillonage [Hz] (doit être <10000)
const double samplingFrequency = 2000; // = 2xfréquence maximale analysée
unsigned long microseconds;
// Période d'échantillonage (à calculer)
unsigned int sampling_period_us;
// Vecteurs d'entrée et de sortie pour stocker les données
double vReal[samples];
double vImag[samples];
// Creation de l'objet FFT
ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, samples, samplingFrequency);
// Port numérique du signal à analyser
#define CHANNEL A0
/******************************************************************************************/
void setup() {
Serial.begin(115200);
while(!Serial);
// Calcul de la période d'échantillonage
sampling_period_us = round(1000000*(1.0/samplingFrequency));
//...
}
/******************************************************************************************/
void loop() {
// Échantillonage ********************************
microseconds = micros();
for(int i=0; i<samples; i++) {
vReal[i] = analogRead(CHANNEL);
vImag[i] = 0;
while(micros() - microseconds < sampling_period_us) {
// attente ...
}
microseconds += sampling_period_us;
}
// Analyse FFT ***********************************
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
FFT.compute(FFTDirection::Forward);
FFT.complexToMagnitude();
// Affichage des résultats (magnitudes) **********
Serial.println("Magnitudes :");
for (uint16_t i = 0; i < samples/2; i++) {
double freq = ((i * 1.0 * samplingFrequency) / samples);
Serial.print(freq , 1);
Serial.print("Hz");
Serial.print("\t");
Serial.println(vReal[i], 2);
}
Serial.println("Fréquence à magnitude maximale :");
double x = FFT.majorPeak();
Serial.println(x, 6);
Serial.println();
delay(200);
}
log, ainsi qu’un gain pour ajuster la hauteur.

