Skip to main content

embassy_hcsr04/
lib.rs

1// Copyright (C) 2026 Jorge Andre Castro
2// GPL-2.0-or-later
3
4//! # embassy-hcsr04
5//!
6//! Driver asynchrone `no_std` pour le télémètre ultrasonique HC-SR04 / HC-SR04P.
7//!
8//! ## Exemple minimal
9//! ```rust,ignore
10//! let mut sonar = HcSr04::new(p.PIN_TRIG, p.PIN_ECHO);
11//! let dist = sonar.measure_mm().await?; 
12//! ```
13
14#![no_std]
15#![forbid(unsafe_code)]
16
17pub mod error;
18pub use error::HcSr04Error;
19
20use embassy_time::{Duration, Instant, Timer};
21use embedded_hal::digital::{InputPin, OutputPin};
22
23/// Driver pour le HC-SR04P.
24pub struct HcSr04<TRIG, ECHO> {
25    trig: TRIG,
26    echo: ECHO,
27}
28
29impl<TRIG, ECHO, E> HcSr04<TRIG, ECHO>
30where
31    TRIG: OutputPin<Error = E>,
32    ECHO: InputPin<Error = E>,
33{
34    /// Crée une nouvelle instance du driver.
35    pub fn new(mut trig: TRIG, echo: ECHO) -> Self {
36        let _ = trig.set_low();
37        Self { trig, echo }
38    }
39
40    /// Mesure la distance en millimètres (vitesse du son à 20°C).
41    pub async fn measure_mm(&mut self) -> Result<u32, HcSr04Error<E>> {
42        self.measure_mm_compensated(2000).await // 20.00 °C par défaut
43    }
44
45    /// Mesure la distance en mm avec compensation de température.
46    ///
47    /// `temp_cdeg` : température en centidegrés (ex: `bmp.read().await?.temperature_cdeg`).
48    pub async fn measure_mm_compensated(&mut self, temp_cdeg: i32) -> Result<u32, HcSr04Error<E>> {
49        // 1. Impulsion Trigger de 10µs
50        self.trig.set_high().map_err(HcSr04Error::Gpio)?;
51        Timer::after_micros(10).await;
52        self.trig.set_low().map_err(HcSr04Error::Gpio)?;
53
54        // 2. Attente du front montant de l'Echo (Timeout 5ms)
55        let mut retry = 0;
56        while self.echo.is_low().map_err(HcSr04Error::Gpio)? {
57            Timer::after_micros(10).await;
58            retry += 1;
59            if retry > 500 {
60                return Err(HcSr04Error::Timeout);
61            }
62        }
63
64        let start = Instant::now();
65
66        // 3. Attente du front descendant de l'Echo (Max 30ms pour ~5m)
67        while self.echo.is_high().map_err(HcSr04Error::Gpio)? {
68            if start.elapsed() > Duration::from_millis(30) {
69                return Err(HcSr04Error::EchoTooLong);
70            }
71            // On cède la main pour laisser respirer l'exécuteur
72            Timer::after_micros(20).await;
73        }
74
75        let duration_us = start.elapsed().as_micros() as u32;
76
77        // 4. Calcul de la vitesse du son (v ≈ 331.3 + 0.606 * T)
78        // On calcule la vitesse en (mm/s) * 1024 pour garder de la précision en entier
79        // 331300 mm/s à 0°C. 606 mm/s par degré.
80        let speed_mm_s = 331300 + (606 * temp_cdeg) / 100;
81        
82        // Distance = (Temps_s * Vitesse) / 2
83        // distance_mm = (duration_us / 1_000_000 * speed_mm_s) / 2
84        let distance_mm = ((duration_us as u64 * speed_mm_s as u64) / 2_000_000) as u32;
85
86        Ok(distance_mm)
87    }
88}