-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhtu.rs
More file actions
140 lines (114 loc) · 3.87 KB
/
Copy pathhtu.rs
File metadata and controls
140 lines (114 loc) · 3.87 KB
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
//! Driver for temperature and humidity sensors such as:
//! - HTU21D
//! - Si7021
//!
//! There are similiar models that may work as well but are not officially
//! tested nor supported.
//!
//! These sensors work over the I2C protocol.
use super::EnvironmentSensor;
use crate::sysc::{OsError, OsResult};
use esp_idf_svc::hal::i2c::I2cDriver;
use pwmp_client::pwmp_msg::aliases::{AirPressure, Humidity, Temperature};
use std::{thread::sleep, time::Duration};
/// Commands for HTU21D (and similar) sensors.
#[derive(Clone, Copy)]
enum Command {
/// Request temperature reading
ReadTemperature,
/// Request humidity reading
ReadHumidity,
/// Reset the device
Reset,
/// Read the first part of the device serial number
ReadSerial1,
}
/// Driver handle for HTU21D (and similar) sensors.
pub struct Htu<'s>(I2cDriver<'s>);
impl<'s> Htu<'s> {
/// Known default address
pub const DEV_ADDR: u8 = 0x40;
const BUS_TIMEOUT: u32 = 2000;
const CMD_WAIT_TIME: u64 = 50;
/// Initialize the driver with the given I2C driver handle.
pub fn new_with_driver(driver: I2cDriver<'s>) -> Result<Self, OsError> {
log::debug!("Loading driver");
let mut dev = Self(driver);
dev.reset()?;
match dev.model()? {
Some(model) => log::debug!("Detected '{model}'"),
None => log::warn!("Device model is unknown and may not be supported"),
}
Ok(dev)
}
fn model(&mut self) -> OsResult<Option<&'static str>> {
let mut buf = [0u8; 6];
self.write_read(Command::ReadSerial1, &mut buf)?;
let snb3 = buf[0];
match snb3 {
0x15 => Ok(Some("Si7021")),
0x32 => Ok(Some("HTU21D")),
0x14 => Ok(Some("Si7020")),
0x0D => Ok(Some("Si7013")),
_ => Ok(None),
}
}
fn reset(&mut self) -> OsResult<()> {
self.write(Command::Reset)?;
sleep(Duration::from_millis(Self::CMD_WAIT_TIME));
Ok(())
}
fn write(&mut self, command: Command) -> OsResult<()> {
OsError::from_i2c_writeop(
self.0
.write(Self::DEV_ADDR, command.as_bytes(), Self::BUS_TIMEOUT),
Self::DEV_ADDR,
command.as_bytes(),
false,
)
}
fn write_read(&mut self, command: Command, buffer: &mut [u8]) -> OsResult<()> {
let result = self.0.write_read(
Self::DEV_ADDR,
command.as_bytes(),
buffer,
Self::BUS_TIMEOUT,
);
OsError::from_i2c_writeop(result, Self::DEV_ADDR, command.as_bytes(), true)
}
fn write_read_u16(&mut self, command: Command) -> OsResult<u16> {
let mut buffer = [0; 2];
self.write_read(command, &mut buffer)?;
let raw = u16::from_be_bytes(buffer);
Ok(raw)
}
}
impl EnvironmentSensor for Htu<'_> {
fn read_temperature(&mut self) -> OsResult<Temperature> {
let raw = self.write_read_u16(Command::ReadTemperature)?;
let temp = ((175.72 * f32::from(raw)) / 65536.0) - 46.85;
Ok(temp)
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
fn read_humidity(&mut self) -> OsResult<Humidity> {
let raw = self.write_read_u16(Command::ReadHumidity)?;
let hum = ((125.0 * f32::from(raw)) / 65536.0) - 6.0;
let percentage = hum.floor().clamp(0., 100.);
Ok(percentage as u8)
}
fn read_air_pressure(&mut self) -> OsResult<Option<AirPressure>> {
log::warn!("Air pressure is not supported");
Ok(None)
}
}
impl Command {
/// Get the command as a byte array for I2C transmission.
const fn as_bytes(self) -> &'static [u8] {
match self {
Self::ReadTemperature => &[0xE3],
Self::ReadHumidity => &[0xE5],
Self::Reset => &[0xFE],
Self::ReadSerial1 => &[0xFC, 0xC9],
}
}
}