co2mon/
co2mon.rs

1/****************************************************************************
2    Copyright (c) 2015 Artyom Pavlov All Rights Reserved.
3
4    This file is part of hidapi-rs, based on hidapi_rust by Roland Ruckerbauer.
5    It's also based on the Oleg Bulatov's work (https://github.com/dmage/co2mon)
6****************************************************************************/
7
8//! Opens a KIT MT 8057 CO2 detector and reads data from it. This
9//! example will not work unless such an HID is plugged in to your system.
10
11#[cfg(all(feature = "linux-static-rusb", not(target_os = "macos")))]
12extern crate rusb;
13
14extern crate hidapi_rusb;
15use hidapi_rusb::{HidApi, HidDevice};
16use std::thread::sleep;
17use std::time::Duration;
18
19const CODE_TEMPERATURE: u8 = 0x42;
20const CODE_CONCENTRATION: u8 = 0x50;
21const HID_TIMEOUT: i32 = 5000;
22const RETRY_SEC: u64 = 1;
23const DEV_VID: u16 = 0x04d9;
24const DEV_PID: u16 = 0xa052;
25const PACKET_SIZE: usize = 8;
26
27enum CO2Result {
28    Temperature(f32),
29    Concentration(u16),
30    Unknown(u8, u16),
31    Error(&'static str),
32}
33
34fn decode_temperature(value: u16) -> f32 {
35    (value as f32) * 0.0625 - 273.15
36}
37
38fn decode_buf(buf: [u8; PACKET_SIZE]) -> CO2Result {
39    let mut res: [u8; PACKET_SIZE] = [
40        (buf[3] << 5) | (buf[2] >> 3),
41        (buf[2] << 5) | (buf[4] >> 3),
42        (buf[4] << 5) | (buf[0] >> 3),
43        (buf[0] << 5) | (buf[7] >> 3),
44        (buf[7] << 5) | (buf[1] >> 3),
45        (buf[1] << 5) | (buf[6] >> 3),
46        (buf[6] << 5) | (buf[5] >> 3),
47        (buf[5] << 5) | (buf[3] >> 3),
48    ];
49
50    let magic_word = b"Htemp99e";
51    for i in 0..PACKET_SIZE {
52        let sub_val: u8 = (magic_word[i] << 4) | (magic_word[i] >> 4);
53        res[i] = u8::overflowing_sub(res[i], sub_val).0;
54    }
55
56    if res[4] != 0x0d {
57        return CO2Result::Error("Unexpected data (data[4] != 0x0d)");
58    }
59    let checksum = u8::overflowing_add(u8::overflowing_add(res[0], res[1]).0, res[2]).0;
60    if checksum != res[3] {
61        return CO2Result::Error("Checksum error");
62    }
63
64    let val: u16 = ((res[1] as u16) << 8) + res[2] as u16;
65    match res[0] {
66        CODE_TEMPERATURE => CO2Result::Temperature(decode_temperature(val)),
67        CODE_CONCENTRATION => {
68            if val > 3000 {
69                CO2Result::Error("Concentration bigger than 3000 (uninitialized device?)")
70            } else {
71                CO2Result::Concentration(val)
72            }
73        }
74        _ => CO2Result::Unknown(res[0], val),
75    }
76}
77
78fn open_device(api: &HidApi) -> HidDevice {
79    loop {
80        match api.open(DEV_VID, DEV_PID) {
81            Ok(dev) => return dev,
82            Err(err) => {
83                println!("{}", err);
84                sleep(Duration::from_secs(RETRY_SEC));
85            }
86        }
87    }
88}
89
90fn main() {
91    let api = HidApi::new().expect("HID API object creation failed");
92
93    let dev = open_device(&api);
94
95    dev.send_feature_report(&[0; PACKET_SIZE])
96        .expect("Feature report failed");
97
98    println!(
99        "Manufacurer:\t{:?}",
100        dev.get_manufacturer_string()
101            .expect("Failed to read manufacurer string")
102    );
103    println!(
104        "Product:\t{:?}",
105        dev.get_product_string()
106            .expect("Failed to read product string")
107    );
108    println!(
109        "Serial number:\t{:?}",
110        dev.get_serial_number_string()
111            .expect("Failed to read serial number")
112    );
113
114    loop {
115        let mut buf = [0; PACKET_SIZE];
116        match dev.read_timeout(&mut buf[..], HID_TIMEOUT) {
117            Ok(PACKET_SIZE) => (),
118            Ok(res) => {
119                println!("Error: unexpected length of data: {}/{}", res, PACKET_SIZE);
120                continue;
121            }
122            Err(err) => {
123                println!("Error: {:}", err);
124                sleep(Duration::from_secs(RETRY_SEC));
125                continue;
126            }
127        }
128        match decode_buf(buf) {
129            CO2Result::Temperature(val) => println!("Temp:\t{:?}", val),
130            CO2Result::Concentration(val) => println!("Conc:\t{:?}", val),
131            CO2Result::Unknown(..) => (),
132            CO2Result::Error(val) => {
133                println!("Error:\t{}", val);
134                sleep(Duration::from_secs(RETRY_SEC));
135            }
136        }
137    }
138}