embedded_devices/devices/sensirion/scd4x/commands.rs
1use crate::devices::sensirion::commands::define_sensirion_commands;
2use embedded_interfaces::codegen::interface_objects;
3use uom::si::{
4 f64::{Length, Pressure, Ratio, ThermodynamicTemperature},
5 length::meter,
6 pressure::hectopascal,
7 ratio::{part_per_million, percent},
8 thermodynamic_temperature::degree_celsius,
9};
10
11interface_objects! {
12 /// The variant of the sensor
13 enum SensorVariant: u8{3} {
14 0b000 SCD40,
15 0b001 SCD41,
16 0b101 SCD43,
17 _ Invalid,
18 }
19
20 /// Whether data is ready to be read out
21 enum DataReadyStatus: u8{2} {
22 0 NotReady,
23 _ Ready,
24 }
25
26 /// Whether a malfunction was detected
27 enum MalfunctionStatus: u16 {
28 0 Ok,
29 _ Malfunction,
30 }
31
32 struct Measurement(size=6) {
33 /// CO2 concentration, LSB = 1 ppm
34 raw_co2_concentration: u16 = 0 => {
35 quantity: Ratio,
36 unit: part_per_million,
37 lsb: 1f64 / 1f64,
38 },
39 /// Ambient temperature, T(°C) = -45 + 175 / (2^16 - 1) * value
40 raw_temperature: u16 = 0 => {
41 quantity: ThermodynamicTemperature,
42 unit: degree_celsius,
43 from_raw: |x| -45.0 + (175.0 * x as f64) / ((1 << 16) - 1) as f64,
44 into_raw: |x| (((x + 45.0) * ((1 << 16) - 1) as f64) / 175.0) as u16,
45 },
46 /// Ambient relative humidity, RH(%) = 100 / (2^16 - 1) * value
47 raw_relative_humidity: u16 = 0 => {
48 quantity: Ratio,
49 unit: percent,
50 from_raw: |x| (100.0 * x as f64) / ((1 << 16) - 1) as f64,
51 into_raw: |x| ((x * ((1 << 16) - 1) as f64) / 100.0) as u16,
52 },
53 }
54
55 struct TemperatureOffset(size=2) {
56 /// Offset temperature, value = T_offset[°C] * (2^16 - 1) / 175
57 temperature: u16,
58 }
59
60 struct SensorAltitude(size=2) {
61 /// Sensor altitude, between 0 and 3000m. LSB = 1 meter.
62 raw_altitude: u16 = 0 => {
63 quantity: Length,
64 unit: meter,
65 lsb: 1f64 / 1f64,
66 },
67 }
68
69 struct AmbientPressure(size=2) {
70 /// Ambient pressure in hPa to be used for pressure compensation. LSB = 1 hPa.
71 raw_pressure: u16 = 1013 => {
72 quantity: Pressure,
73 unit: hectopascal,
74 lsb: 1f64 / 1f64,
75 },
76 }
77
78 struct TargetCo2Concentration(size=2) {
79 /// Target co2 concentration of the test setup. LSB = 1 ppm.
80 raw_target_co2_concentration: u16 = 400 => {
81 quantity: Ratio,
82 unit: part_per_million,
83 lsb: 1f64 / 1f64,
84 },
85 }
86
87 struct Co2Correction(size=2) {
88 /// Correction value as received from the SCD in ppm. To get the FRC correction in ppm,
89 /// subtract `0x8000` from this value.
90 ///
91 /// If the recalibration has failed this value is 0xFFFF.
92 raw_correction: u16 = 0 => {
93 quantity: Ratio,
94 unit: part_per_million,
95 from_raw: |x| (x - 0x8000) as f64,
96 into_raw: |x| x as u16 + 0x8000,
97 },
98 }
99
100 struct AutomaticSelfCalibrationConfig(size=2) {
101 _: u16{15},
102 /// Set to true to enable or false to disable the automatic CO2 measurement
103 /// self-calibration feature.
104 enable: bool,
105 }
106
107 struct AutomaticSelfCalibrationTarget(size=2) {
108 /// ASC target co2 concentration. LSB = 1 ppm.
109 raw_target_co2_concentration: u16 = 400 => {
110 quantity: Ratio,
111 unit: part_per_million,
112 lsb: 1f64 / 1f64,
113 },
114 }
115
116 struct DataReady(size=2) {
117 _: u16{14},
118 /// When no measurement is running, [`DataReadyStatus::NotReady`] will be returned.
119 data_ready: DataReadyStatus = DataReadyStatus::NotReady,
120 }
121
122 struct SerialNumber(size=6) {
123 /// 6-byte serial number
124 serial_number: [u8; 6],
125 }
126
127 struct SelfTestResult(size=2) {
128 /// Whether a malfunction was detected.
129 malfunction_status: MalfunctionStatus = MalfunctionStatus::Malfunction,
130 }
131
132 struct SensorVariantResult(size=2) {
133 /// The sensor variant.
134 variant: SensorVariant = SensorVariant::Invalid,
135 _: u16{13},
136 }
137
138 struct AutomaticSelfCalibrationInitialPeriod(size=2) {
139 /// ASC initial period in hours.
140 period_hours: u16,
141 }
142
143 struct AutomaticSelfCalibrationStandardPeriod(size=2) {
144 /// ASC standard period in hours.
145 period_hours: u16,
146 }
147}
148
149define_sensirion_commands! {
150 id_len 2;
151 marker [
152 ("sensirion-scd40", crate::devices::sensirion::scd40::SCD40Command),
153 ("sensirion-scd41", crate::devices::sensirion::scd41::SCD41Command),
154 ("sensirion-scd43", crate::devices::sensirion::scd43::SCD43Command),
155 ];
156
157 /// Starts the periodic measurement mode. The signal update interval is 5 seconds.
158 send 0x21b1 time_ms=0 StartPeriodicMeasurement();
159
160 /// Reads the sensor output. The measurement data can only be read out once per signal update
161 /// interval as the buffer is emptied upon read-out. If no data is available in the buffer, the
162 /// sensor returns a NACK. To avoid a NACK response, [`GetDataReady`] can be issued to check
163 /// data status. The I2C master can abort the read transfer with a NACK followed by a STOP
164 /// condition after any data byte if the user is not interested in subsequent data.
165 ///
166 /// May be used while measuring.
167 read 0xec05 time_ms=1 ReadMeasurement() -> Measurement;
168
169 /// Command returns a sensor running in periodic measurement mode or low power periodic measurement
170 /// mode back to the idle state, e.g. to then allow changing the sensor configuration or to save
171 /// power.
172 ///
173 /// May be used while measuring.
174 send 0x3f86 time_ms=500 StopPeriodicMeasurement();
175
176 /// Setting the temperature offset of the SCD4x inside the customer device allows the user to
177 /// optimize the RH and T output signal. The temperature offset can depend on several factors such
178 /// as the SCD4x measurement mode, self-heating of close components, the ambient temperature and
179 /// air flow. Thus, the SCD4x temperature offset should be determined after integration into the
180 /// final device and under its typical operating conditions (including the operation mode to be
181 /// used in the application) in thermal equilibrium. By default, the temperature offset is set to 4
182 /// °C. To save the setting to the EEPROM, the [`PersistSettings`] command may be issued.
183 write 0x241d time_ms=1 SetTemperatureOffset(TemperatureOffset);
184
185 /// See [`SetTemperatureOffset`] for details.
186 read 0x2318 time_ms=1 GetTemperatureOffset() -> TemperatureOffset;
187
188 /// Reading and writing the sensor altitude must be done while the SCD4x is in idle mode.
189 /// Typically, the sensor altitude is set once after device installation. To save the setting to
190 /// the EEPROM, the [`PersistSettings`] command must be issued. The default sensor altitude value
191 /// is set to 0 meters above sea level. Valid input values are between 0 - 3000 m.
192 write 0x2427 time_ms=1 SetSensorAltitude(SensorAltitude);
193
194 /// See [`SetSensorAltitude`] for details.
195 read 0x2322 time_ms=1 GetSensorAltitude() -> SensorAltitude;
196
197 /// Sets the ambient pressure value. The ambient pressure can be used for pressure
198 /// compensation in the CO2 sensor. Setting an ambient pressure overrides any pressure compensation
199 /// based on a previously set sensor altitude. Use of this command is recommended for applications
200 /// experiencing significant ambient pressure changes to ensure CO2 sensor accuracy. Valid input
201 /// values are between 700 to 1200 hPa. The default value is 1013 hPa.
202 ///
203 /// May be used while measuring.
204 write 0xe000 time_ms=1 SetAmbientPressure(AmbientPressure);
205
206 /// See [`SetAmbientPressure`] for details.
207 ///
208 /// May be used while measuring.
209 read 0xe000 time_ms=1 GetAmbientPressure() -> AmbientPressure;
210
211 /// Perform forced recalibration (FRC) of the CO2 signal. Refer to the datasheet for additional
212 /// information on how this is to be used.
213 write_read 0x362f time_ms=400 PerformForcedRecalibration(TargetCo2Concentration) -> Co2Correction;
214
215 /// Sets the status of the CO2 sensor automatic self-calibration (ASC). The CO2 sensor supports
216 /// automatic self-calibration (ASC) for long-term stability of the CO2 output. This feature can be
217 /// enabled or disabled. By default, it is enabled.
218 write 0x2416 time_ms=1 SetAutomaticSelfCalibrationEnabled(AutomaticSelfCalibrationConfig);
219
220 /// See [`SetAutomaticSelfCalibrationEnabled`] for details.
221 read 0x2313 time_ms=1 GetAutomaticSelfCalibrationEnabled() -> AutomaticSelfCalibrationConfig;
222
223 /// Sets the value of the ASC baseline target, i.e. the CO2 concentration in ppm which the ASC
224 /// algorithm will assume as lower-bound background to which the SCD4x is exposed to regularly
225 /// within one ASC period of operation. To save the setting to the EEPROM, the [`PersistSettings`]
226 /// command must be issued subsequently.
227 write 0x243a time_ms=1 SetAutomaticSelfCalibrationTarget(AutomaticSelfCalibrationTarget);
228
229 /// Gets the value of the ASC baseline target. See [`SetAutomaticSelfCalibrationTarget`] for
230 /// details.
231 read 0x233f time_ms=1 GetAutomaticSelfCalibrationTarget() -> AutomaticSelfCalibrationTarget;
232
233 /// To enable use-cases with a constrained power budget, the SCD4x features a low power periodic
234 /// measurement mode with a signal update interval of approximately 30 seconds.
235 send 0x21ac time_ms=0 StartLowPowerPeriodicMeasurement();
236
237 /// Polls the sensor for whether data from a periodic or single shot measurement is ready to be
238 /// read out.
239 ///
240 /// May be used while measuring.
241 read 0xe4b8 time_ms=1 GetDataReady() -> DataReady;
242
243 /// Configuration settings such as the temperature offset, sensor altitude and the ASC
244 /// enabled/disabled parameters are by default stored in the volatile memory (RAM) only. This
245 /// command stores the current configuration in the EEPROM of the SCD4x, ensuring the current
246 /// settings persist after power-cycling. To avoid unnecessary wear of the EEPROM, this command
247 /// should only be sent following configuration changes whose persistence is required. The EEPROM
248 /// is guaranteed to withstand at least 2000 write cycles. Note that field calibration history
249 /// (i.e. FRC and ASC) is automatically stored in a separate EEPROM dimensioned for the specified
250 /// sensor lifetime when operated continuously in either periodic measurement mode, low power
251 /// periodic measurement mode or single shot mode with 5 minute measurement interval (SCD41 and
252 /// SCD43 only).
253 send 0x3615 time_ms=800 PersistSettings();
254
255 /// Reading out the serial number can be used to identify the chip and to verify the presence of
256 /// the sensor.
257 read 0x3682 time_ms=1 GetSerialNumber() -> SerialNumber;
258
259 /// This command can be used as an end-of-line test to check the sensor functionality.
260 read 0x3615 time_ms=10_000 PerformSelfTest() -> SelfTestResult;
261
262 /// Resets all configuration settings stored in the EEPROM and erases the FRC and ASC algorithm
263 /// history.
264 send 0x3632 time_ms=1_200 FactoryReset();
265
266 /// Reinitializes the sensor by reloading user settings from EEPROM. The sensor must be in the idle
267 /// state before sending the reinit command. If the reinit command does not trigger the desired
268 /// re-initialization, a power-cycle should be applied to the SCD4x.
269 send 0x3646 time_ms=30 Reinit();
270
271 /// Reads out the SCD4x sensor variant (e.g. SCD40, SCD41 or SCD43).
272 read 0x202f time_ms=1 GetSensorVariant() -> SensorVariantResult;
273}
274
275define_sensirion_commands! {
276 id_len 2;
277 marker [
278 ("sensirion-scd41", crate::devices::sensirion::scd41::SCD41Command),
279 ("sensirion-scd43", crate::devices::sensirion::scd43::SCD43Command),
280 ];
281
282 /// On-demand measurement of CO2 concentration, relative humidity and temperature. The sensor
283 /// output is read out by using the [`ReadMeasurement`] command.
284 send 0x219d time_ms=1 MeasureSingleShot(); // NOTE: Adjusted time to 1ms; datasheet specifies 5_000ms but we want to allow polling manually using [`GetDataReady`].
285
286 /// On-demand measurement of relative humidity and temperature only, significantly reduces power
287 /// consumption. The sensor output is read out by using the read_measurement command. CO2 output is
288 /// returned as 0 ppm.
289 send 0x2196 time_ms=1 MeasureSingleShotRhtOnly(); // NOTE: Adjusted time to 1ms; datasheet specifies 50ms but we want to allow polling manually using [`GetDataReady`].
290
291 /// Put the sensor from idle to sleep to reduce current consumption. Can be used to power down when
292 /// operating the sensor in power-cycled single shot mode.
293 send 0x36e0 time_ms=1 PowerDown();
294
295 /// Wake up the sensor from sleep mode into idle mode. Note that the SCD4x does not acknowledge the
296 /// wake_up command. The sensor's idle state after wake up can be verified by reading out the
297 /// serial number.
298 send 0x36f6 time_ms=30 WakeUp();
299
300 /// Sets the duration of the initial period for ASC correction (in hours). By default, the initial
301 /// period for ASC correction is 44 hours. Allowed values are integer multiples of 4 hours. A value
302 /// of 0 results in an immediate correction. To save the setting to the EEPROM, the
303 /// [`PersistSettings`] command must be issued.
304 ///
305 /// Note: For single shot operation, this parameter always assumes a measurement interval of 5
306 /// minutes, counting the number of single shots to calculate elapsed time. If single shot
307 /// measurements are taken more / less frequently than once every 5 minutes, this parameter must be
308 /// scaled accordingly to achieve the intended period in hours (e.g. for a 10-minute measurement
309 /// interval, the scaled parameter value is obtained by multiplying the intended period in hours by
310 /// 0.5).
311 write 0x2445 time_ms=1 SetAutomaticSelfCalibrationInitialPeriod(AutomaticSelfCalibrationInitialPeriod);
312
313 /// See [`SetAutomaticSelfCalibrationInitialPeriod`] for details.
314 read 0x2340 time_ms=1 GetAutomaticSelfCalibrationInitialPeriod() -> AutomaticSelfCalibrationInitialPeriod;
315
316 /// Sets the standard period for ASC correction (in hours). By default, the standard period for ASC
317 /// correction is 156 hours. Allowed values are integer multiples of 4 hours. A value of 0 results
318 /// in an immediate correction. To save the setting to the EEPROM, the [`PersistSettings`] command
319 /// must be issued.
320 ///
321 /// Note: For single shot operation, this parameter always assumes a measurement interval of 5
322 /// minutes, counting the number of single shots to calculate elapsed time. If single shot
323 /// measurements are taken more / less frequently than once every 5 minutes, this parameter must be
324 /// scaled accordingly to achieve the intended period in hours (e.g. for a 10-minute measurement
325 /// interval, the scaled parameter value is obtained by multiplying the intended period in hours by
326 /// 0.5).
327 write 0x244e time_ms=1 SetAutomaticSelfCalibrationStandardPeriod(AutomaticSelfCalibrationStandardPeriod);
328
329 /// See [`SetAutomaticSelfCalibrationStandardPeriod`] for details.
330 read 0x234b time_ms=1 GetAutomaticSelfCalibrationStandardPeriod() -> AutomaticSelfCalibrationStandardPeriod;
331}