1use crate::io::{FromBytes, ValueWrapper};
2use crate::{Error, io};
3use bitrs::layout;
4use fixed_str::FixedStr;
5use sen6x_macros::SenRead;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9pub(crate) type Milliseconds = u16;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct MicrogramsPerCubicMeter {
17 value: u16,
18}
19impl From<MicrogramsPerCubicMeter> for f32 {
21 fn from(value: MicrogramsPerCubicMeter) -> f32 {
22 value.value as f32 / 10f32
23 }
24}
25
26impl ValueWrapper for MicrogramsPerCubicMeter {
27 type Inner = u16;
28 fn wrap(value: u16) -> Self {
29 MicrogramsPerCubicMeter { value }
30 }
31 fn unwrap(&self) -> Self::Inner {
32 self.value
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41pub struct Percent {
42 value: i16,
43}
44
45impl From<Percent> for f32 {
47 fn from(value: Percent) -> f32 {
48 value.value as f32 / 100f32
49 }
50}
51
52impl ValueWrapper for Percent {
53 type Inner = i16;
54 fn wrap(value: i16) -> Self {
55 Percent { value }
56 }
57 fn unwrap(&self) -> Self::Inner {
58 self.value
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67pub struct DegCelsius {
68 value: i16,
69}
70impl From<DegCelsius> for f32 {
72 fn from(value: DegCelsius) -> f32 {
73 value.value as f32 / 200f32
74 }
75}
76
77impl ValueWrapper for DegCelsius {
78 type Inner = i16;
79 fn wrap(value: i16) -> Self {
80 DegCelsius { value }
81 }
82 fn unwrap(&self) -> Self::Inner {
83 self.value
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92pub struct Index {
93 value: i16,
94}
95
96impl From<Index> for f32 {
98 fn from(value: Index) -> f32 {
99 value.value as f32 / 10f32
100 }
101}
102
103impl ValueWrapper for Index {
104 type Inner = i16;
105 fn wrap(value: i16) -> Self {
106 Index { value }
107 }
108 fn unwrap(&self) -> Self::Inner {
109 self.value
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
118pub struct Ppm {
119 value: i16,
120}
121
122impl Ppm {
123 pub fn new(value: i16) -> Self {
130 Ppm { value }
131 }
132}
133
134impl From<Ppm> for f32 {
136 fn from(value: Ppm) -> f32 {
137 value.value as f32
138 }
139}
140
141impl ValueWrapper for Ppm {
142 type Inner = i16;
143 fn wrap(value: i16) -> Self {
144 Ppm { value }
145 }
146 fn unwrap(&self) -> Self::Inner {
147 self.value
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
157pub struct PpmU16 {
158 value: u16,
159}
160
161impl PpmU16 {
162 pub fn new(value: u16) -> Self {
169 PpmU16 { value }
170 }
171}
172
173impl From<PpmU16> for f32 {
175 fn from(value: PpmU16) -> f32 {
176 value.value as f32
177 }
178}
179
180impl ValueWrapper for PpmU16 {
181 type Inner = u16;
182 fn wrap(value: u16) -> Self {
183 PpmU16 { value }
184 }
185
186 fn unwrap(&self) -> Self::Inner {
187 self.value
188 }
189}
190
191#[derive(Debug, Copy, Clone, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
196pub struct Ppb {
197 value: i16,
198}
199
200impl From<Ppb> for f32 {
202 fn from(value: Ppb) -> f32 {
203 value.value as f32 / 10f32
204 }
205}
206
207impl ValueWrapper for Ppb {
208 type Inner = i16;
209 fn wrap(value: i16) -> Self {
210 Ppb { value }
211 }
212 fn unwrap(&self) -> Self::Inner {
213 self.value
214 }
215}
216
217#[derive(Debug, Copy, Clone, PartialEq, Eq)]
221#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
222pub struct ParticlesPerCm3 {
223 value: u16,
224}
225
226impl From<ParticlesPerCm3> for f32 {
228 fn from(value: ParticlesPerCm3) -> f32 {
229 value.value as f32 / 10f32
230 }
231}
232
233impl ValueWrapper for ParticlesPerCm3 {
234 type Inner = u16;
235 fn wrap(value: u16) -> Self {
236 ParticlesPerCm3 { value }
237 }
238 fn unwrap(&self) -> Self::Inner {
239 self.value
240 }
241}
242
243#[derive(Debug, Copy, Clone, PartialEq, Eq)]
248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
249pub struct Hpa {
250 value: u16,
251}
252
253impl Hpa {
254 pub fn new(value: u16) -> Self {
261 Hpa { value }
262 }
263}
264
265impl From<Hpa> for f32 {
267 fn from(value: Hpa) -> f32 {
268 value.value as f32
269 }
270}
271
272impl ValueWrapper for Hpa {
273 type Inner = u16;
274 fn wrap(value: u16) -> Self {
275 Hpa { value }
276 }
277 fn unwrap(&self) -> Self::Inner {
278 self.value
279 }
280}
281
282#[derive(Debug, Copy, Clone, PartialEq, Eq)]
287#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
288pub struct Meters {
289 value: u16,
290}
291
292impl Meters {
293 pub fn new(value: u16) -> Self {
300 Meters { value }
301 }
302}
303
304impl From<Meters> for f32 {
306 fn from(value: Meters) -> f32 {
307 value.value as f32
308 }
309}
310
311impl ValueWrapper for Meters {
312 type Inner = u16;
313 fn wrap(value: u16) -> Self {
314 Meters { value }
315 }
316 fn unwrap(&self) -> Self::Inner {
317 self.value
318 }
319}
320
321pub type ProductName = FixedStr<32>;
323pub type SerialNumber = FixedStr<32>;
325
326impl FromBytes<48, FixedStr<32>> for FixedStr<32> {
327 fn from_bytes_with_crc<E>(bytes: &[u8; 48]) -> Result<FixedStr<32>, Error<E>> {
328 io::check_crc::<32, E>(bytes).map(|v| FixedStr::<32>::from_slice(&v))
329 }
330}
331
332#[derive(Debug, SenRead, Clone, Copy, PartialEq, Eq)]
334#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
335pub struct DataReady {
336 pub data_ready: bool,
338}
339
340#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
345#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
346pub struct MeasuredValuesSen62 {
347 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
349 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
351 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
353 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
355 pub ambient_humidity: Option<Percent>,
357 pub ambient_temperature: Option<DegCelsius>,
359}
360
361#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
366#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
367pub struct MeasuredValuesSen63c {
368 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
370 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
372 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
374 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
376 pub ambient_humidity: Option<Percent>,
378 pub ambient_temperature: Option<DegCelsius>,
380 pub co2: Option<Ppm>,
382}
383#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
388#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
389pub struct MeasuredValuesSen65 {
390 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
392 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
394 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
396 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
398 pub ambient_humidity: Option<Percent>,
400 pub ambient_temperature: Option<DegCelsius>,
402 pub voc_index: Option<Index>,
404 pub nox_index: Option<Index>,
406}
407#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
412#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
413pub struct MeasuredValuesSen66 {
414 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
416 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
418 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
420 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
422 pub ambient_humidity: Option<Percent>,
424 pub ambient_temperature: Option<DegCelsius>,
426 pub voc_index: Option<Index>,
428 pub nox_index: Option<Index>,
430 pub co2: Option<Ppm>,
432}
433
434#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
439#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
440pub struct MeasuredValuesSen68 {
441 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
443 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
445 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
447 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
449 pub ambient_humidity: Option<Percent>,
451 pub ambient_temperature: Option<DegCelsius>,
453 pub voc_index: Option<Index>,
455 pub nox_index: Option<Index>,
457 pub hcho: Option<Ppb>,
459}
460
461#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
466#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
467pub struct MeasuredValuesSen69c {
468 pub pm_1_0: Option<MicrogramsPerCubicMeter>,
470 pub pm_2_5: Option<MicrogramsPerCubicMeter>,
472 pub pm_4_0: Option<MicrogramsPerCubicMeter>,
474 pub pm_10_0: Option<MicrogramsPerCubicMeter>,
476 pub ambient_humidity: Option<Percent>,
478 pub ambient_temperature: Option<DegCelsius>,
480 pub voc_index: Option<Index>,
482 pub nox_index: Option<Index>,
484 pub hcho: Option<Ppb>,
486 pub co2: Option<Ppm>,
488}
489
490#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
494#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
495pub struct RawValuesSen62Sen63c {
496 pub ambient_humidity: Option<Percent>,
498 pub ambient_temperature: Option<DegCelsius>,
500}
501
502#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
506#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
507pub struct RawValuesSen65Sen68Sen69c {
508 pub ambient_humidity: Option<Percent>,
510 pub ambient_temperature: Option<DegCelsius>,
512 pub voc_index: Option<Index>,
514 pub nox_index: Option<Index>,
516}
517
518#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
522#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
523pub struct RawValuesSen66 {
524 pub ambient_humidity: Option<Percent>,
526 pub ambient_temperature: Option<DegCelsius>,
528 pub voc_index: Option<Index>,
530 pub nox_index: Option<Index>,
532 pub co2: Option<Ppm>,
534}
535
536#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
541#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
542pub struct NumberConcentrationValues {
543 pub pm_0_5: Option<ParticlesPerCm3>,
545 pub pm_1_0: Option<ParticlesPerCm3>,
547 pub pm_2_5: Option<ParticlesPerCm3>,
549 pub pm_4_0: Option<ParticlesPerCm3>,
551 pub pm_10: Option<ParticlesPerCm3>,
553}
554
555#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
558#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
559pub struct TemperatureOffsetParameters {
560 pub offset: DegCelsius,
562 pub slope: i16,
564 pub time_constant: u16,
566 pub slot: u16,
568}
569
570#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
573#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
574pub struct TemperatureAccelerationParameters {
575 pub k: u16,
577 pub p: u16,
579 pub t1: u16,
581 pub t2: u16,
583}
584
585layout! {
586 pub struct DeviceStatus(u32);
600 {
601 let __ @ 31..22;
602 let speed_warning @ 21;
603 let __ @ 20..13;
604 let co2_1_error @ 12;
605 let pm_error @ 11;
606 let hcho_error @ 10;
607 let co2_2_error @ 9;
608 let __ @ 8;
609 let gas_error @ 7;
610 let rh_t_error @ 6;
611 let __ @ 5;
612 let fan_error @ 4;
613 let __ @ 3..0;
614 }
615}
616
617impl ValueWrapper for DeviceStatus {
618 type Inner = u32;
619 fn wrap(value: u32) -> Self {
620 DeviceStatus::from(value)
621 }
622 fn unwrap(&self) -> Self::Inner {
623 self.0
624 }
625}
626
627#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
629#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
630pub struct Version {
631 pub major: u8,
633 pub minor: u8,
635}
636
637#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
639#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
640pub struct ShtHeaterMeasurements {
641 pub sht_relative_humidity: Option<Percent>,
643 pub sht_temperature: Option<DegCelsius>,
645}
646
647#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
653#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
654pub struct VocAlgorithmTuningParameters {
655 pub index_offset: i16,
657 pub learning_time_offset_hours: i16,
659 pub learning_time_gain_hours: i16,
661 pub gating_max_duration_minutes: i16,
663 pub std_initial: i16,
665 pub gain_factor: i16,
667}
668
669#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
674#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
675pub struct VocAlgorithmState {
676 pub state: [u8; 8],
678}
679
680impl FromBytes<12, [u8; 8]> for [u8; 8] {
681 fn from_bytes_with_crc<E>(bytes: &[u8; 12]) -> Result<[u8; 8], Error<E>> {
682 io::check_crc::<8, E>(bytes)
683 }
684}
685
686#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
692#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
693pub struct NoxAlgorithmTuningParameters {
694 pub index_offset: i16,
696 pub learning_time_offset_hours: i16,
698 pub learning_time_gain_hours: i16,
700 pub gating_max_duration_minutes: i16,
702 pub std_initial: i16,
704 pub gain_factor: i16,
706}
707
708#[derive(SenRead, Debug, Clone, PartialEq, Eq)]
710#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
711pub struct Co2Correction {
712 pub result: Option<u16>,
716}
717
718impl Co2Correction {
719 pub fn value(&self) -> Option<Ppm> {
730 self.result
731 .map(|v| <Ppm as ValueWrapper>::wrap(((v as i32) - 0x8000i32) as i16))
732 }
733}