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}