embedded_devices/devices/sensirion/sen60/
commands.rs

1use crate::devices::sensirion::commands::define_sensirion_commands;
2use embedded_interfaces::codegen::interface_objects;
3use uom::si::{
4    f64::{MassConcentration, VolumetricNumberDensity},
5    mass_concentration::microgram_per_cubic_meter,
6    volumetric_number_density::per_cubic_centimeter,
7};
8
9interface_objects! {
10    /// Whether data is ready to be read out
11    enum DataReadyStatus: u16{11} {
12        0 NotReady,
13        _ Ready,
14    }
15
16    struct DataReady(size=2) {
17        _: u8{5},
18        /// When no measurement is running, [`DataReadyStatus::NotReady`] will be returned.
19        data_ready: DataReadyStatus = DataReadyStatus::NotReady,
20    }
21
22    struct MeasuredValuesMassConcentrationOnly(size=8) {
23        /// PM1 mass concentration, LSB = 0.1 µg/m³
24        raw_mass_concentration_pm1: u16 = u16::MAX => {
25            quantity: MassConcentration,
26            unit: microgram_per_cubic_meter,
27            lsb: 1f64 / 10_000_000f64,
28        },
29        /// PM2.5 mass concentration, LSB = 0.1 µg/m³
30        raw_mass_concentration_pm2_5: u16 = u16::MAX => {
31            quantity: MassConcentration,
32            unit: microgram_per_cubic_meter,
33            lsb: 1f64 / 10_000_000f64,
34        },
35        /// PM4 mass concentration, LSB = 0.1 µg/m³
36        raw_mass_concentration_pm4: u16 = u16::MAX => {
37            quantity: MassConcentration,
38            unit: microgram_per_cubic_meter,
39            lsb: 1f64 / 10_000_000f64,
40        },
41        /// PM10 mass concentration, LSB = 0.1 µg/m³
42        raw_mass_concentration_pm10: u16 = u16::MAX => {
43            quantity: MassConcentration,
44            unit: microgram_per_cubic_meter,
45            lsb: 1f64 / 10_000_000f64,
46        },
47    }
48
49    struct MeasuredValues(size=18) {
50        /// PM1 mass concentration, LSB = 0.1 µg/m³
51        raw_mass_concentration_pm1: u16 = u16::MAX => {
52            quantity: MassConcentration,
53            unit: microgram_per_cubic_meter,
54            lsb: 1f64 / 10_000_000f64,
55        },
56        /// PM2.5 mass concentration, LSB = 0.1 µg/m³
57        raw_mass_concentration_pm2_5: u16 = u16::MAX => {
58            quantity: MassConcentration,
59            unit: microgram_per_cubic_meter,
60            lsb: 1f64 / 10_000_000f64,
61        },
62        /// PM4 mass concentration, LSB = 0.1 µg/m³
63        raw_mass_concentration_pm4: u16 = u16::MAX => {
64            quantity: MassConcentration,
65            unit: microgram_per_cubic_meter,
66            lsb: 1f64 / 10_000_000f64,
67        },
68        /// PM10 mass concentration, LSB = 0.1 µg/m³
69        raw_mass_concentration_pm10: u16 = u16::MAX => {
70            quantity: MassConcentration,
71            unit: microgram_per_cubic_meter,
72            lsb: 1f64 / 10_000_000f64,
73        },
74        /// PM0.5 volumetric number concentration, LSB = 0.1 particles/cm³
75        raw_number_density_pm0_5: u16 = u16::MAX => {
76            quantity: VolumetricNumberDensity,
77            unit: per_cubic_centimeter,
78            lsb: 1f64 / 10f64,
79        },
80        /// PM1 volumetric number density, LSB = 0.1 particles/cm³
81        raw_number_density_pm1: u16 = u16::MAX => {
82            quantity: VolumetricNumberDensity,
83            unit: per_cubic_centimeter,
84            lsb: 1f64 / 10f64,
85        },
86        /// PM2.5 volumetric number density, LSB = 0.1 particles/cm³
87        raw_number_density_pm2_5: u16 = u16::MAX => {
88            quantity: VolumetricNumberDensity,
89            unit: per_cubic_centimeter,
90            lsb: 1f64 / 10f64,
91        },
92        /// PM4 volumetric number density, LSB = 0.1 particles/cm³
93        raw_number_density_pm4: u16 = u16::MAX => {
94            quantity: VolumetricNumberDensity,
95            unit: per_cubic_centimeter,
96            lsb: 1f64 / 10f64,
97        },
98        /// PM10 volumetric number density, LSB = 0.1 particles/cm³
99        raw_number_density_pm10: u16 = u16::MAX => {
100            quantity: VolumetricNumberDensity,
101            unit: per_cubic_centimeter,
102            lsb: 1f64 / 10f64,
103        },
104    }
105
106    struct SerialNumber(size=6) {
107        /// 6-byte serial number
108        serial_number: [u8; 6],
109    }
110
111    struct DeviceStatus(size=2) {
112        _: u16{11},
113        /// Fan is switched on, but 0 RPM is measured for multiple consecutive measurement intervals.
114        /// This can occur if the fan is mechanically blocked or broken. Note that the measured values
115        /// are most likely wrong if this error is reported.
116        ///
117        /// Can occur only in measurement mode.
118        fan_error: bool = false,
119        _: u8{2},
120        /// Fan is switched on, but its speed is more than 10% off the target speed for multiple
121        /// consecutive measurement intervals. During the first 10 seconds after starting the
122        /// measurement, the fan speed is not checked (settling time). Very low or very high ambient
123        /// temperature could trigger this warning during startup. If this flag is set constantly, it
124        /// might indicate a problem with the power supply or with the fan, and the measured PM values
125        /// might be wrong. This flag is automatically cleared as soon as the measured speed is within
126        /// 10% of the target speed or when leaving the measure mode.
127        ///
128        /// Can occur only in measurement mode.
129        fan_speed_warning: bool = false,
130        _: bool,
131    }
132}
133
134define_sensirion_commands! {
135    id_len 2;
136    marker [
137        ("sensirion-sen60", crate::devices::sensirion::sen60::SEN60Command),
138    ];
139
140    /// Starts a continuous measurement. After starting the measurement, it takes some time (~1.1s)
141    /// until the first measurement results are available. You could poll with the command
142    /// [`GetDataReady`] to check when the results are ready to be read.
143    ///
144    /// Cannot be executed during measurement.
145    send 0x2152 time_ms=1 StartContinuousMeasurement();
146
147    /// Stops the measurement and returns to idle mode.
148    ///
149    /// May be executed during measurement.
150    send 0x3f86 time_ms=1000 StopMeasurement();
151
152    /// This command can be used to check if new measurement results are ready to read. The data ready
153    /// flag is automatically reset after reading the measurement values
154    ///
155    /// May be executed during measurement.
156    read 0xe4b8 time_ms=1 GetDataReady() -> DataReady;
157
158    /// Returns the measured values (mass concentration only). The command [`GetDataReady`] can be used to
159    /// check if new data is available since the last read operation. The measurement data can only be read
160    /// out once per signal update interval, as the buffer is emptied upon read-out. If no data is available
161    /// in the buffer, the sensor returns a NACK. To avoid a NACK response, the Get Data Ready SEN60 can be
162    /// issued to check data status. The I2C controller can abort the read transfer with a NACK followed by
163    /// a STOP condition after any data byte if the user is not interested in the subsequent data
164    ///
165    /// May be executed during measurement.
166    read 0xec05 time_ms=1 ReadMeasuredValuesMassConcentrationOnly() -> MeasuredValuesMassConcentrationOnly;
167
168    /// Returns the measured values (full measurement data). The command [`GetDataReady`] can be used to
169    /// check if new data is available since the last read operation. The measurement data can only be read
170    /// out once per signal update interval, as the buffer is emptied upon read-out. If no data is available
171    /// in the buffer, the sensor returns a NACK. To avoid a NACK response, the Get Data Ready SEN60 can be
172    /// issued to check data status. The I2C controller can abort the read transfer with a NACK followed by
173    /// a STOP condition after any data byte if the user is not interested in the subsequent data
174    ///
175    /// May be executed during measurement.
176    read 0xec05 time_ms=1 ReadMeasuredValues() -> MeasuredValues;
177
178    /// Gets the serial number from the device.
179    ///
180    /// May be executed during measurement.
181    read 0x3682 time_ms=1 GetSerialNumber() -> SerialNumber;
182
183    /// Reads the current device status.
184    ///
185    /// Note: The status flags of type `Error` are sticky, i.e. they are not cleared automatically even
186    /// if the error condition no longer exists. So, they can only be cleared manually through a reset,
187    /// either by calling [`DeviceReset`] or through a power cycle. All other flags are not sticky,
188    /// i.e. they are cleared automatically if the trigger condition disappears.
189    ///
190    /// May be executed during measurement.
191    read 0xe00b time_ms=1 ReadDeviceStatus() -> DeviceStatus;
192
193    /// Executes a reset on the device. This has the same effect as a power cycle.
194    ///
195    /// Cannot be executed during measurement.
196    send 0x3f8d time_ms=1 DeviceReset();
197
198    /// This command triggers fan cleaning. The fan is set to the maximum speed for 10 seconds and then
199    /// automatically stopped.
200    ///
201    /// Note: Wait at least 10s after this command before starting a measurement.
202    ///
203    /// Cannot be executed during measurement.
204    send 0x3730 time_ms=1 StartFanCleaning();
205}