1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
extern crate byteorder;
extern crate i2cdev;
extern crate measurements;
pub use measurements::Temperature;
pub use measurements::Pressure;
use i2cdev::core::I2CDevice;
use i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};
use byteorder::{ByteOrder, LittleEndian};
use std::fmt;
pub struct RelativeHumidity {
value: f64,
}
pub struct SenseHat {
pressure_dev: LinuxI2CDevice,
humidity_dev: LinuxI2CDevice,
temp_m: f64,
temp_c: f64,
hum_m: f64,
hum_c: f64,
}
#[derive(Debug)]
pub enum SenseHatError {
NotReady,
GenericError,
I2CError(LinuxI2CError),
}
pub type SenseHatResult<T> = Result<T, SenseHatError>;
const HTS221_AV_CONF: u8 = 0x10;
const HTS221_CTRL1: u8 = 0x20;
const HTS221_STATUS: u8 = 0x27;
const HTS221_HUMIDITY_OUT_L: u8 = 0x28;
const HTS221_HUMIDITY_OUT_H: u8 = 0x29;
const HTS221_TEMP_OUT_L: u8 = 0x2a;
const HTS221_TEMP_OUT_H: u8 = 0x2b;
const HTS221_H0_H_2: u8 = 0x30;
const HTS221_H1_H_2: u8 = 0x31;
const HTS221_T0_C_8: u8 = 0x32;
const HTS221_T1_C_8: u8 = 0x33;
const HTS221_T1_T0: u8 = 0x35;
const HTS221_H0_T0_OUT: u8 = 0x36;
const HTS221_H1_T0_OUT: u8 = 0x3a;
const HTS221_T0_OUT: u8 = 0x3c;
const HTS221_T1_OUT: u8 = 0x3e;
const LPS25H_RES_CONF: u8 = 0x10;
const LPS25H_CTRL_REG_1: u8 = 0x20;
const LPS25H_CTRL_REG_2: u8 = 0x21;
const LPS25H_STATUS_REG: u8 = 0x27;
const LPS25H_PRESS_OUT_XL: u8 = 0x28;
const LPS25H_PRESS_OUT_L: u8 = 0x29;
const LPS25H_PRESS_OUT_H: u8 = 0x2a;
const LPS25H_TEMP_OUT_L: u8 = 0x2b;
const LPS25H_TEMP_OUT_H: u8 = 0x2c;
const LPS25H_FIFO_CTRL: u8 = 0x2e;
impl SenseHat {
pub fn new() -> SenseHatResult<SenseHat> {
let mut hat = SenseHat {
pressure_dev: LinuxI2CDevice::new("/dev/i2c-1", 0x5c)?,
humidity_dev: LinuxI2CDevice::new("/dev/i2c-1", 0x5f)?,
temp_m: 0.0,
temp_c: 0.0,
hum_m: 0.0,
hum_c: 0.0,
};
hat.init_pressure()?;
hat.init_humidity()?;
Ok(hat)
}
fn init_pressure(&mut self) -> SenseHatResult<()> {
self.pressure_dev.smbus_write_byte_data(LPS25H_CTRL_REG_1, 0xc4)?;
self.pressure_dev.smbus_write_byte_data(LPS25H_RES_CONF, 0x05)?;
self.pressure_dev.smbus_write_byte_data(LPS25H_FIFO_CTRL, 0xc0)?;
self.pressure_dev.smbus_write_byte_data(LPS25H_CTRL_REG_2, 0x40)?;
Ok(())
}
fn init_humidity(&mut self) -> SenseHatResult<()> {
self.humidity_dev.smbus_write_byte_data(HTS221_CTRL1, 0x87)?;
self.humidity_dev.smbus_write_byte_data(HTS221_AV_CONF, 0x1b)?;
let mut buf = [0u8; 2];
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_T0_C_8)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_T1_T0)? & 0x03;
let t0 = (LittleEndian::read_i16(&buf) as f64) / 8.0;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_T1_C_8)?;
buf[1] = (self.humidity_dev.smbus_read_byte_data(HTS221_T1_T0)? & 0x0C) >> 2;
let t1 = (LittleEndian::read_i16(&buf) as f64) / 8.0;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_T0_OUT)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_T0_OUT + 1)?;
let t0_out = LittleEndian::read_i16(&buf) as f64;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_T1_OUT)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_T1_OUT + 1)?;
let t1_out = LittleEndian::read_i16(&buf) as f64;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_H0_H_2)?;
let h0 = (buf[0] as f64) / 2.0;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_H1_H_2)?;
let h1 = (buf[0] as f64) / 2.0;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_H0_T0_OUT)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_H0_T0_OUT + 1)?;
let h0_t0_out = LittleEndian::read_i16(&buf) as f64;
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_H1_T0_OUT)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_H1_T0_OUT + 1)?;
let h1_t0_out = LittleEndian::read_i16(&buf) as f64;
self.temp_m = (t1 - t0) / (t1_out - t0_out);
self.temp_c = t0 - (self.temp_m * t0_out);
self.hum_m = (h1 - h0) / (h1_t0_out - h0_t0_out);
self.hum_c = h0 - (self.hum_m * h0_t0_out);
Ok(())
}
pub fn get_temperature_from_pressure(&mut self) -> SenseHatResult<Temperature> {
let status = self.pressure_dev.smbus_read_byte_data(LPS25H_STATUS_REG)?;
if (status & 1) != 0 {
let mut buf = [0u8; 2];
buf[0] = self.pressure_dev.smbus_read_byte_data(LPS25H_TEMP_OUT_L)?;
buf[1] = self.pressure_dev.smbus_read_byte_data(LPS25H_TEMP_OUT_H)?;
let celcius = ((LittleEndian::read_i16(&buf) as f64) / 480.0) + 42.5;
Ok(Temperature::from_celsius(celcius))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_pressure(&mut self) -> SenseHatResult<Pressure> {
let status = self.pressure_dev.smbus_read_byte_data(LPS25H_STATUS_REG)?;
if (status & 2) != 0 {
let mut buf = [0u8; 4];
buf[0] = self.pressure_dev.smbus_read_byte_data(LPS25H_PRESS_OUT_XL)?;
buf[1] = self.pressure_dev.smbus_read_byte_data(LPS25H_PRESS_OUT_L)?;
buf[2] = self.pressure_dev.smbus_read_byte_data(LPS25H_PRESS_OUT_H)?;
let hectopascals = (LittleEndian::read_u32(&buf) as f64) / 4096.0;
Ok(Pressure::from_hectopascals(hectopascals))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_temperature_from_humidity(&mut self) -> SenseHatResult<Temperature> {
let status = self.humidity_dev.smbus_read_byte_data(HTS221_STATUS)?;
if (status & 1) != 0 {
let mut buf = [0u8; 2];
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_TEMP_OUT_L)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_TEMP_OUT_H)?;
let celcius = ((LittleEndian::read_i16(&buf) as f64) * self.temp_m) + self.temp_c;
Ok(Temperature::from_celsius(celcius))
} else {
Err(SenseHatError::NotReady)
}
}
pub fn get_humidity(&mut self) -> SenseHatResult<RelativeHumidity> {
let status = self.humidity_dev.smbus_read_byte_data(HTS221_STATUS)?;
if (status & 2) != 0 {
let mut buf = [0u8; 2];
buf[0] = self.humidity_dev.smbus_read_byte_data(HTS221_HUMIDITY_OUT_L)?;
buf[1] = self.humidity_dev.smbus_read_byte_data(HTS221_HUMIDITY_OUT_H)?;
let percent = ((LittleEndian::read_i16(&buf) as f64) * self.hum_m) + self.hum_c;
Ok(RelativeHumidity::from_percent(percent))
} else {
Err(SenseHatError::NotReady)
}
}
}
impl From<LinuxI2CError> for SenseHatError {
fn from(err: LinuxI2CError) -> SenseHatError {
SenseHatError::I2CError(err)
}
}
impl RelativeHumidity {
pub fn from_percent(pc: f64) -> RelativeHumidity {
RelativeHumidity { value: pc }
}
pub fn as_percent(&self) -> f64 {
self.value
}
}
impl fmt::Display for RelativeHumidity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:.1}%", self.as_percent())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn pressure_test() {
let p = Pressure::from_hectopascals(1000.0);
assert_eq!(p.as_bar(), 1.0);
assert_eq!(p.as_psi(), 14.5038);
}
}