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.

  1. Comprendre
  2. Voir
  3. Manipuler
  4. Programmer
  5. Résoudre
  6. Construire

En bref

Principeémet 8 salves d’ultrasons à 40 kHz et mesure le temps que met l’écho à revenir
Portée2 cm à 400 cm annoncés, environ 2 à 3 m en pratique sur une surface bien orientée
Précisionenviron 3 mm dans de bonnes conditions
Angle de mesureun cône d’une quinzaine de degrés
Alimentation5 V, une quinzaine de milliampères
SignauxTRIG (entrée, impulsion de 10 µs) et ECHO (sortie, impulsion dont la durée donne la distance)
Niveau de ECHO5 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

HC-SR04 100 cm
Durée de l’impulsion ECHO (échelle : 0 à 25 ms)
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

ÉtapeCe qui se passeEffet sur les broches
1TRIG à l’état haut pendant 10 µsdéclenche une mesure
2Le capteur émet 8 salves à 40 kHzECHO passe à l’état haut
3L’écho revientECHO repasse à l’état bas
4Attendre 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-SR04ESP32Arduino Uno / Nano
VCC5V (VIN)5V
TRIGGPIO 25D9
ECHOGPIO 26, à travers un diviseur de tensionD10
GNDGNDGND
  • Sur ESP32, alimente le capteur avec la broche VIN ou 5V de 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.

hcsr04_basic.inoTélécharger
/*
 * 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.
hcsr04_filter.inoTélécharger
/*
 * 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

ECHO R1 R2 GND GPIO
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ômeCause probableSolution
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 petiteECHO 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 sautentSurface en biais, mou ou trop petiteVise une surface plane et perpendiculaire, applique la médiane et la moyenne exponentielle.
L’ESP32 plante ou redémarre à la mesureECHO à 5 V sur le GPIOAjoute le diviseur de tension, vérifie la sortie au multimètre : 3,3 V au plus.
Erreur de quelques centimètresVitesse du son à 20 °C alors que l’air est plus froid ou plus chaudCompense avec la température de l’air, comme dans le simulateur.
Mesure impossible en dessous de 2 cmZone morte du capteurRecule le capteur : il ne mesure pas les obstacles trop proches.
Résultat faux près d’un mur latéralLe 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_oled.inoTélécharger
/*
 * 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.

Recevoir les prochaines sorties

Un email quand un projet, un guide, un PCB ou un kit est publié. Pas de spam, désinscription à tout moment.

Être prévenu

Recevez un email quand une nouveauté est publiée.