1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! # SGP30 Sensor Module
//!
//! Provides an interface to the SGP30 sensor for air quality measurement,
//! including CO2 and VOC levels. This module abstracts over the
//! `embedded_sgp30` crate to provide a simpler interface for initializing
//! the sensor and reading the air quality metrics.

use embedded_hal::blocking::delay::DelayMs;
use embedded_sgp30::{Sgp30 as ExternalSgp30, I2C_ADDRESS as DEFAULT};
use esp_hal::{delay::Delay, i2c::I2C};

use super::{CO2Sensor, I2cPeriph, PeripheralError, UnifiedData, VOCSensor};

/// Represents an SGP30 air quality sensor.
pub struct Sgp30Sensor {
    /// The internal SGP30 sensor instance.
    pub inner: ExternalSgp30<I2C<'static, esp_hal::peripherals::I2C0>, Delay>,
    /// Delay provider for timing-sensitive operations.
    pub delay: Delay,
}

impl I2cPeriph for Sgp30Sensor {
    type Returnable = Self;

    /// Creates and initializes an SGP30 sensor over the I2C bus.
    ///
    /// Initializes the sensor and starts the air quality measurement process.
    ///
    /// # Arguments
    /// * `bus` - The I2C bus instance to communicate with the sensor.
    /// * `delay` - A delay provider for timing-sensitive operations during
    ///   initialization.
    ///
    /// # Returns
    /// A result containing the initialized `Sgp30Sensor` or an error of type
    /// `PeripheralError` if initialization fails.
    fn create_on_i2c(
        bus: I2C<'static, esp_hal::peripherals::I2C0>,
        delay: Delay,
    ) -> Result<Self::Returnable, PeripheralError> {
        let mut sensor = match ExternalSgp30::new(bus, DEFAULT, delay) {
            Ok(sensor) => sensor,
            Err(_) => return Err(PeripheralError::InitializationFailed),
        };
        match sensor.initialize_air_quality_measure() {
            Ok(_) => {}
            Err(_) => return Err(PeripheralError::InitializationFailed),
        }
        Ok(Sgp30Sensor {
            inner: sensor,
            delay: delay,
        })
    }
}

impl CO2Sensor for Sgp30Sensor {
    /// Measures the CO2 concentration in the air.
    ///
    /// # Returns
    /// A result containing the CO2 concentration in ppm (parts per million) as
    /// `Ok(f32)` if successful, or an error of type `PeripheralError` if the
    /// measurement fails.
    fn get_co2(&mut self) -> Result<f32, PeripheralError> {
        //
        self.delay.delay_ms(500u32);
        match self.inner.measure_air_quality() {
            Ok(measurement) => Ok(measurement.co2 as f32),
            Err(_) => Err(PeripheralError::ReadError),
        }
    }
}

impl VOCSensor for Sgp30Sensor {
    /// Measures the VOC in the air.
    ///
    /// # Returns
    /// A result containing the VOC as `Ok(f32)` if successful, or an error of
    /// type `PeripheralError` if the measurement fails.
    fn get_voc(&mut self) -> Result<f32, PeripheralError> {
        self.delay.delay_ms(500u32);
        match self.inner.measure_air_quality() {
            Ok(measurement) => Ok(measurement.tvoc as f32),
            Err(_) => Err(PeripheralError::ReadError),
        }
    }
}

impl UnifiedData for Sgp30Sensor {
    type Output = (f32, f32);
    /// Reads the CO2 concentration in the air and VOC from the
    /// SGP30 sensor.
    ///
    /// # Returns
    /// Returns an `Ok((f32, f32))` representing the relative
    /// CO2 concentration(ppm) and VOC in the air if the
    /// read is successful, or `Err(PeripheralError::ReadError)` if the data
    /// from sensor cannot be read.
    fn read(&mut self, _delay: Delay) -> Result<Self::Output, PeripheralError> {
        self.delay.delay_ms(500u32);
        match self.inner.measure_air_quality() {
            Ok(measurement) => Ok((measurement.co2 as f32, measurement.tvoc as f32)),
            Err(_) => Err(PeripheralError::ReadError),
        }
    }
}