1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Sensor;
6
7use super::{
8 register, AcquisitionMode, Configuration, HumidityResolution, Measurement,
9 TemperatureResolution, DEVICE_ID, I2C_ADDRESS, MANUFACTURER_ID,
10};
11use crate::driver::{read_word, write_word};
12use crate::error::{DriverError, SensorError};
13
14#[derive(Debug)]
44pub struct Hdc1080<I2C, D> {
45 bus: I2C,
46 delay: D,
47 configuration: Configuration,
48 initialized: bool,
49}
50
51impl<I2C, D> Hdc1080<I2C, D> {
52 pub fn new(bus: I2C, delay: D) -> Self {
63 Hdc1080 {
64 bus,
65 delay,
66 configuration: Configuration {
67 mode: AcquisitionMode::TemperatureThenHumidity,
68 ..Configuration::default()
69 },
70 initialized: false,
71 }
72 }
73
74 pub fn with_resolutions(
85 mut self,
86 temperature: TemperatureResolution,
87 humidity: HumidityResolution,
88 ) -> Self {
89 self.configuration.temperature_resolution = temperature;
90 self.configuration.humidity_resolution = humidity;
91 self.initialized = false;
92 self
93 }
94
95 pub fn configuration(&self) -> Configuration {
97 self.configuration
98 }
99
100 pub fn release(self) -> (I2C, D) {
106 (self.bus, self.delay)
107 }
108}
109
110impl<I2C: I2c, D: DelayNs> Hdc1080<I2C, D> {
111 pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
118 if self.read(register::MANUFACTURER_ID)? != MANUFACTURER_ID
119 || self.read(register::DEVICE_ID)? != DEVICE_ID
120 {
121 return Err(SensorError::Identity.into());
122 }
123 self.write_configuration()?;
124 self.initialized = true;
125 Ok(())
126 }
127
128 pub fn measure(&mut self) -> Result<Measurement, DriverError<I2C::Error>> {
142 if !self.initialized {
143 self.init()?;
144 }
145 self.bus
146 .write(I2C_ADDRESS, &[register::TEMPERATURE])
147 .map_err(DriverError::Bus)?;
148 self.delay
149 .delay_us(self.configuration.conversion_time_micros());
150 let mut bytes = [0u8; 4];
151 self.bus
152 .read(I2C_ADDRESS, &mut bytes)
153 .map_err(DriverError::Bus)?;
154 Ok(Measurement::parse(&bytes))
155 }
156
157 pub fn heater(&mut self, on: bool) -> Result<(), DriverError<I2C::Error>> {
167 self.configuration.heater = on;
168 self.write_configuration()
169 }
170
171 fn write_configuration(&mut self) -> Result<(), DriverError<I2C::Error>> {
172 write_word(
173 &mut self.bus,
174 I2C_ADDRESS,
175 register::CONFIGURATION,
176 self.configuration.to_register(),
177 )
178 .map_err(DriverError::Bus)
179 }
180
181 fn read(&mut self, register: u8) -> Result<u16, DriverError<I2C::Error>> {
182 read_word(&mut self.bus, I2C_ADDRESS, register).map_err(DriverError::Bus)
183 }
184}
185
186impl<I2C, D> Sensor for Hdc1080<I2C, D>
187where
188 I2C: I2c + Send,
189 I2C::Error: core::fmt::Debug,
190 D: DelayNs + Send,
191{
192 type Reading = Measurement;
193
194 async fn read(&mut self) -> pamoja_core::Result<Measurement> {
195 self.measure().map_err(pamoja_core::Error::from)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use alloc::vec;
203 use alloc::vec::Vec;
204 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
205 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
206
207 fn init_steps(configuration: u16) -> Vec<I2cStep> {
208 let [high, low] = configuration.to_be_bytes();
209 vec![
210 I2cStep::write_read(
211 I2C_ADDRESS,
212 [register::MANUFACTURER_ID],
213 MANUFACTURER_ID.to_be_bytes(),
214 ),
215 I2cStep::write_read(I2C_ADDRESS, [register::DEVICE_ID], DEVICE_ID.to_be_bytes()),
216 I2cStep::write(I2C_ADDRESS, [register::CONFIGURATION, high, low]),
217 ]
218 }
219
220 #[test]
221 fn a_measurement_is_triggered_by_the_pointer_write_and_read_after_the_conversion() {
222 let mut steps = init_steps(0x1000);
223 steps.push(I2cStep::write(I2C_ADDRESS, [register::TEMPERATURE]));
224 steps.push(I2cStep::read(
225 I2C_ADDRESS,
226 Measurement::from_physical(21_500, 45_000).to_bytes(),
227 ));
228 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
229 let measurement = sensor.measure().unwrap();
230 assert_eq!(measurement, Measurement::from_physical(21_500, 45_000));
231 assert!((measurement.milli_celsius() - 21_500).abs() < 10);
232 assert!(measurement.milli_percent().abs_diff(45_000) < 10);
233 let (bus, delay) = sensor.release();
234 assert!(bus.done());
235 assert_eq!(
236 delay.waits_ns(),
237 [12_850_000],
238 "14-bit temperature plus 14-bit humidity"
239 );
240 }
241
242 #[test]
243 fn lower_resolutions_shorten_the_wait_and_reach_the_configuration() {
244 let configuration = Configuration {
245 mode: AcquisitionMode::TemperatureThenHumidity,
246 temperature_resolution: TemperatureResolution::Bits11,
247 humidity_resolution: HumidityResolution::Bits8,
248 ..Configuration::default()
249 };
250 let mut steps = init_steps(configuration.to_register());
251 steps.push(I2cStep::write(I2C_ADDRESS, [register::TEMPERATURE]));
252 steps.push(I2cStep::read(
253 I2C_ADDRESS,
254 Measurement::from_physical(0, 0).to_bytes(),
255 ));
256 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new())
257 .with_resolutions(TemperatureResolution::Bits11, HumidityResolution::Bits8);
258 assert_eq!(sensor.configuration(), configuration);
259 sensor.measure().unwrap();
260 let (bus, delay) = sensor.release();
261 assert!(bus.done());
262 assert_eq!(delay.waits_ns(), [(3_650 + 2_500) * 1_000]);
263 }
264
265 #[test]
266 fn a_part_with_the_wrong_id_is_refused_at_either_register() {
267 let steps = [
268 I2cStep::write_read(I2C_ADDRESS, [register::MANUFACTURER_ID], [0x54, 0x49]),
269 I2cStep::write_read(I2C_ADDRESS, [register::DEVICE_ID], [0x10, 0x00]),
270 ];
271 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
272 assert_eq!(
273 sensor.init(),
274 Err(DriverError::Sensor(SensorError::Identity))
275 );
276 let steps = [I2cStep::write_read(
277 I2C_ADDRESS,
278 [register::MANUFACTURER_ID],
279 [0x00, 0x00],
280 )];
281 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
282 assert_eq!(
283 sensor.init(),
284 Err(DriverError::Sensor(SensorError::Identity))
285 );
286 }
287
288 #[test]
289 fn a_read_the_part_does_not_acknowledge_is_a_bus_error() {
290 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
291 let mut steps = init_steps(0x1000);
292 steps.push(I2cStep::write(I2C_ADDRESS, [register::TEMPERATURE]));
293 steps.push(I2cStep::fault(I2C_ADDRESS, kind));
294 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
295 assert!(matches!(sensor.measure(), Err(DriverError::Bus(_))));
296 assert!(sensor.release().0.done());
297 }
298
299 #[test]
300 fn the_heater_bit_is_written_through_the_configuration() {
301 let mut steps = init_steps(0x1000);
302 steps.push(I2cStep::write(
303 I2C_ADDRESS,
304 [register::CONFIGURATION, 0x30, 0x00],
305 ));
306 let mut sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
307 sensor.init().unwrap();
308 sensor.heater(true).unwrap();
309 assert!(sensor.configuration().heater);
310 assert!(sensor.release().0.done());
311 }
312
313 #[test]
314 fn as_a_sensor_it_reads_and_maps_to_humidity() {
315 let mut steps = init_steps(0x1000);
316 steps.push(I2cStep::write(I2C_ADDRESS, [register::TEMPERATURE]));
317 steps.push(I2cStep::read(
318 I2C_ADDRESS,
319 Measurement::from_physical(21_500, 45_000).to_bytes(),
320 ));
321 let sensor = Hdc1080::new(I2cScript::new(steps), DelayLog::new());
322 let mut humidity = sensor.map(|measurement| measurement.relative_humidity());
323 assert!((block_on(humidity.read()).unwrap() - 45.0).abs() < 0.01);
324 }
325
326 #[test]
327 fn init_and_one_measurement_make_the_transfers_the_datasheet_gives() {
328 use crate::hdc1080::{Measurement, I2C_ADDRESS};
329
330 const PART: u8 = I2C_ADDRESS;
331 let bus = I2cScript::new([
332 I2cStep::write_read(PART, [0xFE], [0x54, 0x49]),
333 I2cStep::write_read(PART, [0xFF], [0x10, 0x50]),
334 I2cStep::write(PART, [0x02, 0x10, 0x00]),
335 I2cStep::write(PART, [0x00]),
336 I2cStep::read(PART, Measurement::from_physical(21_500, 45_000).to_bytes()),
337 ]);
338
339 let mut sensor = Hdc1080::new(bus, DelayLog::new());
340 let measurement = block_on(Sensor::read(&mut sensor)).expect("the scripted part answers");
341 assert_eq!(measurement, Measurement::from_physical(21_500, 45_000));
342 assert!((measurement.milli_celsius() - 21_500).abs() < 10);
343
344 let (bus, delay) = sensor.release();
345 assert!(bus.done());
346 assert_eq!(delay.total_micros(), 6_350 + 6_500);
347 }
348}