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
+ hidapi-rs is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ hidapi-rs is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with hidapi-rs. If not, see <http://www.gnu.org/licenses/>.
19
+ ****************************************************************************/
20
+
21
+
22
+ //! Opens a KIT MT 8057 CO2 detector and reads data from it. This
23
+ //! example will not work unless such an HID is plugged in to your system.
24
+
25
+ extern crate hidapi;
26
+
27
+ use hidapi:: { HidApi , HidDevice } ;
28
+ use std:: thread:: sleep;
29
+ use std:: time:: Duration ;
30
+
31
+ const CODE_TEMPERATURE : u8 = 0x42 ;
32
+ const CODE_CONCENTRATION : u8 = 0x50 ;
33
+ const HID_TIMEOUT : i32 = 5000 ;
34
+ const RETRY_SEC : u64 = 1 ;
35
+ const DEV_VID : u16 = 0x04d9 ;
36
+ const DEV_PID : u16 = 0xa052 ;
37
+ const PACKET_SIZE : usize = 8 ;
38
+
39
+ enum CO2Result {
40
+ Temperature ( f32 ) ,
41
+ Concentration ( u16 ) ,
42
+ Unknown ( u8 , u16 ) ,
43
+ Error ( & ' static str ) ,
44
+ }
45
+
46
+ fn decode_temperature ( value : u16 ) -> f32 {
47
+ ( value as f32 ) * 0.0625 - 273.15
48
+ }
49
+
50
+ fn decode_buf ( buf : [ u8 ; PACKET_SIZE ] ) -> CO2Result {
51
+ let mut res: [ u8 ; PACKET_SIZE ] = [
52
+ ( buf[ 3 ] << 5 ) | ( buf[ 2 ] >> 3 ) ,
53
+ ( buf[ 2 ] << 5 ) | ( buf[ 4 ] >> 3 ) ,
54
+ ( buf[ 4 ] << 5 ) | ( buf[ 0 ] >> 3 ) ,
55
+ ( buf[ 0 ] << 5 ) | ( buf[ 7 ] >> 3 ) ,
56
+ ( buf[ 7 ] << 5 ) | ( buf[ 1 ] >> 3 ) ,
57
+ ( buf[ 1 ] << 5 ) | ( buf[ 6 ] >> 3 ) ,
58
+ ( buf[ 6 ] << 5 ) | ( buf[ 5 ] >> 3 ) ,
59
+ ( buf[ 5 ] << 5 ) | ( buf[ 3 ] >> 3 )
60
+ ] ;
61
+
62
+ let magic_word = b"Htemp99e" ;
63
+ for i in 0 ..PACKET_SIZE {
64
+ let sub_val: u8 = ( magic_word[ i] << 4 ) | ( magic_word[ i] >> 4 ) ;
65
+ res[ i] = u8:: overflowing_sub ( res[ i] , sub_val) . 0 ;
66
+ }
67
+
68
+ if res[ 4 ] != 0x0d {
69
+ return CO2Result :: Error ( "Unexpected data (data[4] != 0x0d)" ) ;
70
+ }
71
+ let checksum = u8:: overflowing_add (
72
+ u8:: overflowing_add ( res[ 0 ] , res[ 1 ] ) . 0 , res[ 2 ] ) . 0 ;
73
+ if checksum != res[ 3 ] {
74
+ return CO2Result :: Error ( "Checksum error" ) ;
75
+ }
76
+
77
+ let val: u16 = ( ( res[ 1 ] as u16 ) << 8 ) + res[ 2 ] as u16 ;
78
+ match res[ 0 ] {
79
+ CODE_TEMPERATURE => CO2Result :: Temperature ( decode_temperature ( val) ) ,
80
+ CODE_CONCENTRATION => {
81
+ if val > 3000 {
82
+ CO2Result :: Error (
83
+ "Concentration bigger than 3000 (uninitialized device?)" )
84
+ } else {
85
+ CO2Result :: Concentration ( val)
86
+ }
87
+ } ,
88
+ _ => CO2Result :: Unknown ( res[ 0 ] , val) ,
89
+ }
90
+ }
91
+
92
+ fn open_device ( api : & HidApi ) -> HidDevice {
93
+ loop {
94
+ match api. open ( DEV_VID , DEV_PID ) {
95
+ Ok ( dev) => return dev,
96
+ Err ( err) => {
97
+ println ! ( "{}" , err) ;
98
+ sleep ( Duration :: from_secs ( RETRY_SEC ) ) ;
99
+ } ,
100
+ }
101
+ }
102
+ }
103
+
104
+ fn main ( ) {
105
+ let api = HidApi :: new ( ) . expect ( "HID API object creation failed" ) ;
106
+
107
+ let dev = open_device ( & api) ;
108
+
109
+ dev. send_feature_report ( & [ 0 ; PACKET_SIZE ] ) . expect ( "Feature report failed" ) ;
110
+
111
+ println ! ( "Manufacurer:\t {}" , dev. get_manufacturer_string( )
112
+ . expect( "Failed to read manufacurer string" ) ) ;
113
+ println ! ( "Product:\t {}" , dev. get_product_string( )
114
+ . expect( "Failed to read product string" ) ) ;
115
+ println ! ( "Serial number:\t {}" , dev. get_serial_number_string( )
116
+ . expect( "Failed to read serial number" ) ) ;
117
+
118
+ loop {
119
+ let mut buf = [ 0 ; PACKET_SIZE ] ;
120
+ match dev. read_timeout ( & mut buf[ ..] , HID_TIMEOUT ) {
121
+ Ok ( PACKET_SIZE ) => ( ) ,
122
+ Ok ( res) => {
123
+ println ! ( "Error: unexpected length of data: {}/{}" ,
124
+ res, PACKET_SIZE ) ;
125
+ continue ;
126
+ } ,
127
+ Err ( err) => {
128
+ println ! ( "Error: {:}" , err) ;
129
+ sleep ( Duration :: from_secs ( RETRY_SEC ) ) ;
130
+ continue ;
131
+ }
132
+ }
133
+ match decode_buf ( buf) {
134
+ CO2Result :: Temperature ( val) => println ! ( "Temp:\t {:?}" , val) ,
135
+ CO2Result :: Concentration ( val) => println ! ( "Conc:\t {:?}" , val) ,
136
+ CO2Result :: Unknown ( ..) => ( ) ,
137
+ CO2Result :: Error ( val) => {
138
+ println ! ( "Error:\t {}" , val) ;
139
+ sleep ( Duration :: from_secs ( RETRY_SEC ) ) ;
140
+ } ,
141
+ }
142
+ }
143
+ }
0 commit comments