Distance LabCapteurs
Capteur de distance HC-SR04
Mesurez une distance avec des ultrasons, comprenez d’où vient le chiffre, et branchez-le sur un ESP32 sans griller la carte. Le simulateur vous montre la durée de l’écho en direct.
En bref
| Principe | émet 8 salves d’ultrasons à 40 kHz et mesure le temps que met l’écho à revenir |
|---|---|
| Portée | 2 cm à 400 cm annoncés, environ 2 à 3 m en pratique sur une surface bien orientée |
| Précision | environ 3 mm dans de bonnes conditions |
| Angle de mesure | un cône d’une quinzaine de degrés |
| Alimentation | 5 V, une quinzaine de milliampères |
| Signaux | TRIG (entrée, impulsion de 10 µs) et ECHO (sortie, impulsion dont la durée donne la distance) |
| Niveau de ECHO | 5 V : il faut le réduire pour un ESP32 |
Il existe des variantes 3,3 V (HC-SR04P, RCWL-1601) qui se branchent directement sur l’ESP32. Vérifie la référence gravée sur votre module.
Ultrasons
Le capteur envoie un train d’ondes inaudibles à 40 kHz. L’onde touche un obstacle, revient, et la broche ECHO reste à l’état haut pendant tout l’aller-retour. La distance se déduit de cette durée et de la vitesse du son, qui dépend de la température : environ 331,3 + 0,606 × T m/s (343 m/s à 20 °C).
Simulateur de distance
- Distance
- –
- Durée ECHO
- –
- Vitesse du son
- –
- Règle « µs / 58 »
- –
La règle « µs divisé par 58 » suppose 20 °C. À 0 °C ou à 35 °C, l’erreur atteint 3 à 4 %, soit 3 à 4 cm à 1 m.
TRIG / ECHO
| Étape | Ce qui se passe | Effet sur les broches |
|---|---|---|
| 1 | TRIG à l’état haut pendant 10 µs | déclenche une mesure |
| 2 | Le capteur émet 8 salves à 40 kHz | ECHO passe à l’état haut |
| 3 | L’écho revient | ECHO repasse à l’état bas |
| 4 | Attendre 60 ms au moins entre deux mesures | évite de capter l’écho précédent |
La durée de l’état haut de ECHO est mesurée avec pulseIn(PIN_ECHO, HIGH, timeout). Le paramètre de délai évite de rester bloqué quand aucun écho ne revient : 30 000 µs (30 ms) correspondent à environ 5 m.
Câblage
| Broche HC-SR04 | ESP32 | Arduino Uno / Nano |
|---|---|---|
| VCC | 5V (VIN) | 5V |
| TRIG | GPIO 25 | D9 |
| ECHO | GPIO 26, à travers un diviseur de tension | D10 |
| GND | GND | GND |
- Sur ESP32, alimente le capteur avec la broche
VINou5Vde la carte (5 V venant du port USB), pas avec 3V3. - Les GPIO 34 à 39 sont des entrées seules : ils conviennent pour ECHO, pas pour TRIG.
- Oriente le capteur vers une surface plane et perpendiculaire. Un obstacle en biais, mou (tissu) ou très fin renvoie mal l’onde.
Premier test
Ce sketch envoie l’impulsion, mesure l’écho et affiche la distance toutes les 100 ms dans le moniteur série.
/*
* Premier test HC-SR04 — Kaihatsu Lab
* Mesure la distance et l'affiche dans le moniteur série (115 200 bauds).
*
* ESP32 : ECHO sort en 5 V. Passe-le par un diviseur de tension (par exemple 1 kΩ et 2 kΩ)
* avant de le brancher sur PIN_ECHO. TRIG accepte le 3,3 V.
*/
const uint8_t PIN_TRIG = 25;
const uint8_t PIN_ECHO = 26;
const float TEMPERATURE_C = 20.0; // température de l'air : elle change la vitesse du son
const float SOUND_CM_PER_US = (331.3 + 0.606 * TEMPERATURE_C) / 10000.0; // m/s -> cm/µs
void setup() {
Serial.begin(115200);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
digitalWrite(PIN_TRIG, LOW);
}
// Retourne la distance en cm, ou -1 si aucun écho n'est revenu.
float readDistanceCm() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH); // impulsion de 10 µs : le capteur envoie 8 salves à 40 kHz
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
unsigned long us = pulseIn(PIN_ECHO, HIGH, 30000UL); // 30 ms maximum, soit environ 5 m
if (us == 0) return -1;
return us * SOUND_CM_PER_US / 2.0; // l'onde fait l'aller-retour : on divise par 2
}
void loop() {
float d = readDistanceCm();
if (d < 0) {
Serial.println(F("Pas d'echo"));
} else {
Serial.print(d, 1);
Serial.println(F(" cm"));
}
delay(100); // laisse s'éteindre les échos de la mesure précédente
}
Résultat attendu : une distance qui change quand vous approchez la main, et « Pas d’echo » quand rien ne renvoie l’onde.
Calcul de distance
Distance = durée de l’écho × vitesse du son ÷ 2, parce que l’onde fait l’aller et le retour. Avec la vitesse du son à 20 °C (0,0343 cm/µs) : distance (cm) = durée (µs) × 0,0343 ÷ 2, soit environ durée ÷ 58,3.
Calculateur distance ↔ durée ECHO
Filtrage
Une mesure brute saute : écho parasite, surface irrégulière, capteur mal aligné. Deux réflexes simples :
- Médiane de 3 à 5 mesures : élimine les valeurs aberrantes sans décaler le résultat.
- Moyenne exponentielle :
lissé = α × mesure + (1 − α) × lissé. Un α proche de 1 réagit vite, un α proche de 0 est stable mais lent. - Bornes : ignore ce qui sort de 2 à 400 cm.
/*
* HC-SR04 filtré — Kaihatsu Lab
* Médiane de 5 mesures (supprime les valeurs aberrantes), puis moyenne exponentielle (lisse le résultat).
* Les mesures hors de la plage 2 à 400 cm sont ignorées.
*/
const uint8_t PIN_TRIG = 25;
const uint8_t PIN_ECHO = 26; // derrière un diviseur de tension sur ESP32
const float MIN_CM = 2.0;
const float MAX_CM = 400.0;
const float ALPHA = 0.3; // 0 à 1 : plus c'est petit, plus c'est lisse mais lent
float smooth = -1;
float rawCm() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
unsigned long us = pulseIn(PIN_ECHO, HIGH, 30000UL);
if (us == 0) return -1;
return us / 58.3; // environ 20 °C
}
float medianOf5() {
float v[5];
uint8_t n = 0;
for (uint8_t i = 0; i < 5; i++) {
float d = rawCm();
if (d >= MIN_CM && d <= MAX_CM) v[n++] = d;
delay(30);
}
if (n == 0) return -1;
for (uint8_t i = 1; i < n; i++) { // tri par insertion
float x = v[i];
int8_t j = i - 1;
while (j >= 0 && v[j] > x) { v[j + 1] = v[j]; j--; }
v[j + 1] = x;
}
return v[n / 2];
}
void setup() {
Serial.begin(115200);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
}
void loop() {
float m = medianOf5();
if (m > 0) {
smooth = (smooth < 0) ? m : ALPHA * m + (1.0 - ALPHA) * smooth;
Serial.print(F("brut "));
Serial.print(m, 1);
Serial.print(F(" cm | filtre "));
Serial.print(smooth, 1);
Serial.println(F(" cm"));
} else {
Serial.println(F("Pas de mesure valide"));
}
}
ESP32 / 5 V
La broche ECHO sort à 5 V, alors que les GPIO de l’ESP32 ne supportent officiellement que le 3,3 V. Un diviseur de tension à deux résistances ramène le niveau dans la bonne plage. La sortie doit rester autour de 3,3 V (3,6 V est le maximum toléré) et dépasser 2,5 V environ pour être lue comme un état haut.
Diviseur de tension
- Sortie
- –
- Courant
- –
- R2 idéal (série E12)
- –
Le couple 1 kΩ et 2 kΩ donne 3,33 V : c’est le montage classique. Des valeurs plus fortes (par exemple 10 kΩ et 20 kΩ) consomment moins mais rendent le signal plus lent à monter.
Dépannage
| Symptôme | Cause probable | Solution |
|---|---|---|
| Toujours « pas d’écho » | VCC en 3,3 V, TRIG et ECHO inversés ou fil mal enfoncé | Alimente en 5 V, vérifie TRIG et ECHO, teste avec un obstacle plan à 30 cm. |
| Distance toujours 0 ou très petite | ECHO lu sans délai ou parasité | Ajoute le délai de pulseIn, attends 60 ms entre les mesures, éloigne les fils du servo ou du Wi-Fi. |
| Valeurs qui sautent | Surface en biais, mou ou trop petite | Vise une surface plane et perpendiculaire, applique la médiane et la moyenne exponentielle. |
| L’ESP32 plante ou redémarre à la mesure | ECHO à 5 V sur le GPIO | Ajoute le diviseur de tension, vérifie la sortie au multimètre : 3,3 V au plus. |
| Erreur de quelques centimètres | Vitesse du son à 20 °C alors que l’air est plus froid ou plus chaud | Compense avec la température de l’air, comme dans le simulateur. |
| Mesure impossible en dessous de 2 cm | Zone morte du capteur | Recule le capteur : il ne mesure pas les obstacles trop proches. |
| Résultat faux près d’un mur latéral | Le cône de mesure touche le mur | Éloigne le capteur du mur ou oriente-le autrement. |
Projets
Projets Kaihatsu Lab qui utilisent ce composant :
Radar HC-SR04, servo et SSD1306
Le capteur, fixé sur un servomoteur, balaie de 15° à 165°. Chaque mesure est tracée sur l’écran OLED : c’est un petit radar de proximité. Le sketch suivant réutilise la lecture de ce guide et l’affichage du guide SSD1306.
/*
* Radar — Kaihatsu Lab
* HC-SR04 monté sur un servomoteur qui balaie de 15° à 165°, résultat tracé sur un écran OLED SSD1306.
* Bibliothèques : Adafruit GFX, Adafruit SSD1306, ESP32Servo.
*
* ECHO passe par un diviseur de tension (5 V vers 3,3 V). Le servo est alimenté en 5 V à part,
* les masses sont reliées entre elles.
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>
const uint8_t PIN_TRIG = 25;
const uint8_t PIN_ECHO = 26;
const uint8_t PIN_SERVO = 13;
#define SCREEN_W 128
#define SCREEN_H 64
#define OLED_ADDR 0x3C
const int ANGLE_MIN = 15;
const int ANGLE_MAX = 165;
const int ANGLE_STEP = 5;
const int STEPS = (ANGLE_MAX - ANGLE_MIN) / ANGLE_STEP + 1;
const float RANGE_CM = 100.0; // distance affichée au bord de l'écran
const int CX = 64, CY = 63, RADIUS = 60;
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, -1);
Servo servo;
float dist[STEPS]; // dernière distance mesurée à chaque angle (-1 = rien)
float readCm() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
unsigned long us = pulseIn(PIN_ECHO, HIGH, 25000UL);
if (us == 0) return -1;
return us / 58.3;
}
void drawRadar(int current) {
display.clearDisplay();
display.drawCircle(CX, CY, RADIUS, SSD1306_WHITE);
display.drawCircle(CX, CY, RADIUS / 2, SSD1306_WHITE);
display.drawLine(0, CY, SCREEN_W - 1, CY, SSD1306_WHITE);
for (int i = 0; i < STEPS; i++) { // points détectés
if (dist[i] < 0 || dist[i] > RANGE_CM) continue;
float a = (ANGLE_MIN + i * ANGLE_STEP) * DEG_TO_RAD;
float r = dist[i] / RANGE_CM * RADIUS;
display.fillCircle(CX + r * cos(a), CY - r * sin(a), 2, SSD1306_WHITE);
}
float a = (ANGLE_MIN + current * ANGLE_STEP) * DEG_TO_RAD; // ligne de balayage
display.drawLine(CX, CY, CX + RADIUS * cos(a), CY - RADIUS * sin(a), SSD1306_WHITE);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
if (dist[current] >= 0) { display.print((int)dist[current]); display.print(F(" cm")); }
else display.print(F("--"));
display.display();
}
void setup() {
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
Wire.begin(21, 22);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
servo.setPeriodHertz(50);
servo.attach(PIN_SERVO, 500, 2400);
for (int i = 0; i < STEPS; i++) dist[i] = -1;
}
void loop() {
for (int i = 0; i < STEPS; i++) { // aller
servo.write(ANGLE_MIN + i * ANGLE_STEP);
delay(70); // temps pour que le servo se stabilise
dist[i] = readCm();
drawRadar(i);
}
for (int i = STEPS - 1; i >= 0; i--) { // retour
servo.write(ANGLE_MIN + i * ANGLE_STEP);
delay(70);
dist[i] = readCm();
drawRadar(i);
}
}
Idées à construire
- Aide au stationnement : la distance affichée sur l’écran, un buzzer qui accélère en approchant.
- Niveau de cuve : mesure la distance à la surface du liquide.
- Distributeur sans contact : un servo s’active quand une main s’approche.
- Compteur de passages avec un seuil de distance.
Le PCB et les composants pour reproduire les projets arriveront avec la boutique.
