1#![no_std]
12
13#[cfg(any(test, feature = "std"))]
14extern crate std;
15
16#[cfg(any(test, feature = "alloc"))]
17extern crate alloc;
18
19use core::{
20 fmt::{self, Debug, Formatter},
21 future::Future,
22};
23use embedded_hal_async as hal;
24pub use fans::{FanControl, FanDutyCycle, FanRpm, FanSelect};
25use hal::i2c::I2c;
26
27pub use error::Error;
28use registers::*;
29
30mod error;
31mod registers;
32
33pub const EMC2301_I2C_ADDR: u8 = 0b0010_1111;
35
36pub const EMC230X_I2C_ADDR_0: u8 = 0x2C;
38pub const EMC230X_I2C_ADDR_1: u8 = 0x2D;
39pub const EMC230X_I2C_ADDR_2: u8 = 0x2E;
40pub const EMC230X_I2C_ADDR_3: u8 = 0x2F;
41pub const EMC230X_I2C_ADDR_4: u8 = 0x4C;
42pub const EMC230X_I2C_ADDR_5: u8 = 0x4D;
43
44const EMC230X_ADDRESSES: [u8; 6] = [
45 EMC230X_I2C_ADDR_0,
46 EMC230X_I2C_ADDR_1,
47 EMC230X_I2C_ADDR_2,
48 EMC230X_I2C_ADDR_3,
49 EMC230X_I2C_ADDR_4,
50 EMC230X_I2C_ADDR_5,
51];
52
53const _SIMPLIFIED_RPM_FACTOR: f64 = 3_932_160.0;
57
58macro_rules! register_ro {
60 ($get:ident, $return_type:ty) => {
61 pub async fn $get(&mut self) -> Result<$return_type, Error> {
62 self.read_register::<$return_type>(<$return_type>::ADDRESS)
63 .await
64 }
65 };
66}
67
68macro_rules! register {
70 ($get:ident, $set:ident, $return_type:ty) => {
71 pub async fn $get(&mut self) -> Result<$return_type, Error> {
72 self.read_register::<$return_type>(<$return_type>::ADDRESS)
73 .await
74 }
75
76 pub async fn $set(&mut self, value: $return_type) -> Result<(), Error> {
77 self.write_register(<$return_type>::ADDRESS, value.into())
78 .await?;
79 Ok(())
80 }
81 };
82}
83
84macro_rules! fan_register {
86 ($get:ident, $set:ident, $reg_type:ty) => {
87 pub async fn $get(&mut self, sel: FanSelect) -> Result<$reg_type, Error> {
88 self.valid_fan(sel)?;
89 let reg = fan_register_address(sel, <$reg_type>::OFFSET)?;
90 let value = self.read_register(reg).await?;
91 Ok(value)
92 }
93
94 pub async fn $set(&mut self, sel: FanSelect, value: $reg_type) -> Result<(), Error> {
95 self.valid_fan(sel)?;
96 let reg = fan_register_address(sel, <$reg_type>::OFFSET)?;
97 self.write_register(reg, value.into()).await?;
98 Ok(())
99 }
100 };
101}
102
103pub(crate) fn hacky_round(value: f64) -> u8 {
107 let raw = value as u8;
109
110 if value - raw as f64 >= 0.5 {
112 raw + 1
113 } else {
114 raw
115 }
116}
117
118pub(crate) fn hacky_round_u16(value: f64) -> u16 {
122 let raw = value as u16;
124
125 if value - raw as f64 >= 0.5 {
127 raw + 1
128 } else {
129 raw
130 }
131}
132
133#[derive(Copy, Clone, Debug, Default)]
137pub struct ProbeResult([Option<u8>; 6]);
138
139impl ProbeResult {
140 pub fn iter(&self) -> impl Iterator<Item = u8> + '_ {
142 self.0.iter().filter_map(|x| *x)
143 }
144
145 pub fn is_empty(&self) -> bool {
147 self.0.iter().all(|x| x.is_none())
148 }
149
150 pub fn len(&self) -> usize {
152 self.0.iter().filter(|x| x.is_some()).count()
153 }
154}
155
156impl IntoIterator for ProbeResult {
157 type Item = u8;
158 type IntoIter = core::iter::Flatten<core::array::IntoIter<Option<u8>, 6>>;
159
160 fn into_iter(self) -> Self::IntoIter {
161 self.0.into_iter().flatten()
162 }
163}
164
165pub struct Emc230x<I2C> {
166 i2c: I2C,
168
169 address: u8,
171
172 pid: ProductId,
176
177 poles: [u8; 5],
179}
180
181impl<I2C> Debug for Emc230x<I2C> {
182 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
183 f.debug_struct("Emc230x")
184 .field("address", &self.address)
185 .field("pid", &self.pid)
186 .field("poles", &self.poles)
187 .finish()
188 }
189}
190
191impl<I2C: I2c> Emc230x<I2C> {
192 const MANUFACTURER_ID: u8 = 0x5D;
194
195 const TACH_FREQUENCY_HZ: f64 = 32_768.0;
197
198 async fn is_emc230x(i2c: &mut I2C, address: u8) -> Result<ProductId, Error> {
200 let mfg_id: ManufacturerId = Self::raw_read(i2c, address, ManufacturerId::ADDRESS).await?;
201 if mfg_id.mfg_id() == Self::MANUFACTURER_ID {
202 let pid: ProductId = Self::raw_read(i2c, address, ProductId::ADDRESS).await?;
203 Ok(pid)
204 } else {
205 Err(Error::InvalidManufacturerId)
206 }
207 }
208
209 pub async fn probe(i2c: &mut I2C) -> ProbeResult {
226 let mut result = ProbeResult::default();
227 for (slot, &address) in EMC230X_ADDRESSES.iter().enumerate() {
228 if Self::is_emc230x(i2c, address).await.is_ok() {
229 result.0[slot] = Some(address);
230 }
231 }
232 result
233 }
234
235 pub async fn new(i2c: I2C, address: u8) -> Result<Self, Error> {
237 let mut i2c = i2c;
238 let pid = Self::is_emc230x(&mut i2c, address).await?;
239
240 let poles = [2; 5];
242
243 let mut dev = Self {
245 i2c,
246 address,
247 pid,
248 poles,
249 };
250
251 let mut output_cfg = pwm_output_config::PwmOutputConfig::default();
253 let count = dev.count();
254 for fan in 1..=count {
255 output_cfg.push_pull(fan);
256 }
257 dev.set_pwm_output_config(output_cfg).await?;
258
259 for fan in 1..=count {
261 let mut cfg = dev.fan_configuration1(FanSelect(fan)).await?;
262 cfg.set_rngx(fan_configuration1::Range::Rpm500);
263 dev.set_fan_configuration1(FanSelect(fan), cfg).await?;
264 }
265
266 Ok(dev)
268 }
269
270 fn address(&self) -> u8 {
272 self.address
273 }
274
275 pub fn count(&self) -> u16 {
277 self.pid.num_fans()
278 }
279
280 pub fn fan_poles(&self, sel: FanSelect) -> Result<u8, Error> {
282 self.valid_fan(sel)?;
283 Ok(self.poles[sel.0 as usize - 1])
284 }
285
286 pub fn set_fan_poles(&mut self, sel: FanSelect, poles: u8) -> Result<(), Error> {
292 self.valid_fan(sel)?;
293 self.poles[sel.0 as usize - 1] = poles;
294 Ok(())
295 }
296
297 fn tach_freq(&self) -> f64 {
299 Self::TACH_FREQUENCY_HZ
300 }
301
302 fn _mode(&mut self, _sel: FanSelect) -> impl Future<Output = Result<FanControl, Error>> {
303 async { todo!() }
304 }
305
306 pub async fn set_mode(&mut self, sel: FanSelect, mode: FanControl) -> Result<(), Error> {
308 self.valid_fan(sel)?;
309 let mut config = self.fan_configuration1(sel).await?;
310
311 match mode {
312 FanControl::DutyCycle(duty) => {
313 config.set_enagx(false);
319 self.set_fan_configuration1(sel, config).await?;
320
321 self.set_duty_cycle(sel, duty).await?;
322 }
323 FanControl::Rpm(rpm) => {
324 self.set_rpm(sel, rpm).await?;
325
326 config.set_enagx(true);
327 self.set_fan_configuration1(sel, config).await?;
328 }
329 }
330
331 Ok(())
332 }
333
334 pub async fn duty_cycle(&mut self, sel: FanSelect) -> Result<FanDutyCycle, Error> {
336 self.valid_fan(sel)?;
337 let drive = self.fan_setting(sel).await?;
338 let duty = drive.duty_cycle();
339 Ok(duty)
340 }
341
342 pub async fn set_duty_cycle(
344 &mut self,
345 sel: FanSelect,
346 duty: FanDutyCycle,
347 ) -> Result<(), Error> {
348 self.valid_fan(sel)?;
349 let drive = FanDriveSetting::from_duty_cycle(duty);
350 self.set_fan_setting(sel, drive).await?;
351 Ok(())
352 }
353
354 pub async fn rpm(&mut self, sel: FanSelect) -> Result<FanRpm, Error> {
356 self.valid_fan(sel)?;
357 let raw_low = self.tach_reading_low_byte(sel).await?;
358 let raw_high = self.tach_reading_high_byte(sel).await?;
359
360 let raw = u16::from_le_bytes([raw_low.into(), raw_high.into()]) >> 3;
361 let rpm = self.calc_raw_rpm(sel, raw).await?;
362
363 Ok(rpm)
364 }
365
366 pub async fn set_rpm(&mut self, sel: FanSelect, rpm: FanRpm) -> Result<(), Error> {
368 self.valid_fan(sel)?;
369
370 let raw = self.calc_raw_rpm(sel, rpm).await?;
371 let count = (raw << 3).to_le_bytes();
372
373 self.set_tach_target_low_byte(sel, count[0].into()).await?;
374 self.set_tach_target_high_byte(sel, count[1].into()).await?;
375 Ok(())
376 }
377
378 pub async fn report(&mut self, sel: FanSelect) -> Result<(FanDutyCycle, FanRpm), Error> {
380 self.valid_fan(sel)?;
381 let duty = self.duty_cycle(sel).await?;
382 let rpm = self.rpm(sel).await?;
383 Ok((duty, rpm))
384 }
385
386 pub async fn min_duty(&mut self, sel: FanSelect) -> Result<FanDutyCycle, Error> {
388 self.valid_fan(sel)?;
389 let drive = self.minimum_drive(sel).await?;
390 Ok(drive.duty_cycle())
391 }
392
393 pub async fn set_min_duty(&mut self, sel: FanSelect, duty: FanDutyCycle) -> Result<(), Error> {
395 self.valid_fan(sel)?;
396 let drive = FanMinimumDrive::from_duty_cycle(duty);
397 self.set_minimum_drive(sel, drive).await?;
398 Ok(())
399 }
400
401 pub async fn fan_detected(&mut self, sel: FanSelect) -> Result<bool, Error> {
411 self.valid_fan(sel)?;
412 let status = self.stall_status().await?;
413 let stalled = (u8::from(status) >> (sel.0 as u8 - 1)) & 1 != 0;
414 Ok(!stalled)
415 }
416
417 async fn calc_raw_rpm(&mut self, sel: FanSelect, value: u16) -> Result<u16, Error> {
419 let cfg = self.fan_configuration1(sel).await?;
420
421 let poles = self.fan_poles(sel)? as f64;
422 let n = cfg.edgx().num_edges() as f64;
423 let m = cfg.rngx().tach_count_multiplier() as f64;
424 let f_tach = self.tach_freq();
425
426 let value = ((1.0 / poles) * (n - 1.0)) / (value as f64 * (1.0 / m)) * f_tach * 60.0;
427 Ok(hacky_round_u16(value))
428 }
429
430 async fn write_register(&mut self, reg: u8, data: u8) -> Result<(), Error> {
432 let addr = self.address();
433 let data = [reg, data];
434 self.i2c.write(addr, &data).await.map_err(|_| Error::I2c)
435 }
436
437 async fn raw_read<T: TryFrom<u8>>(i2c: &mut I2C, address: u8, reg: u8) -> Result<T, Error> {
439 let mut data = [0];
440 i2c.write_read(address, &[reg], data.as_mut_slice())
441 .await
442 .map_err(|_| Error::I2c)?;
443
444 let data: T = data[0]
445 .try_into()
446 .map_err(|_| Error::RegisterTypeConversion)?;
447
448 Ok(data)
449 }
450
451 async fn read_register<T: TryFrom<u8>>(&mut self, reg: u8) -> Result<T, Error> {
453 let addr = self.address();
454 let data = Self::raw_read(&mut self.i2c, addr, reg).await?;
455 Ok(data)
456 }
457
458 fn valid_fan(&self, select: FanSelect) -> Result<(), Error> {
460 if select.0 <= self.count() && select.0 != 0 {
461 Ok(())
462 } else {
463 Err(Error::InvalidFan)
464 }
465 }
466
467 pub fn release(self) -> I2C {
469 self.i2c
470 }
471
472 register!(config, set_config, Configuration);
474 register_ro!(status, FanStatus);
475 register_ro!(stall_status, FanStallStatus);
476 register_ro!(spin_status, FanSpinStatus);
477 register_ro!(drive_fail_status, FanDriveFailStatus);
478 register!(interrupt_enable, set_interrupt_enable, FanInterruptEnable);
479 register!(pwm_polarity_config, set_pwm_polarity_config, PwmPolarityConfig);
480 register!(pwm_output_config, set_pwm_output_config, PwmOutputConfig);
481 register!(pwm_base_f45, set_pwm_base_f45, PwmBase45);
482 register!(pwm_base_f123, set_pwm_base_f123, PwmBase123);
483
484 fan_register!(fan_setting, set_fan_setting, FanDriveSetting);
486 fan_register!(pwm_divide, set_pwm_divide, PwmDivide);
487 fan_register!(fan_configuration1, set_fan_configuration1, FanConfiguration1);
488 fan_register!(fan_configuration2, set_fan_configuration2, FanConfiguration2);
489 fan_register!(gain, set_gain, PidGain);
490 fan_register!(spin_up_configuration, set_spin_up_configuration, FanSpinUpConfig);
491 fan_register!(max_step, set_max_step, MaxStepSize);
492 fan_register!(minimum_drive, set_minimum_drive, FanMinimumDrive);
493 fan_register!(valid_tach_count, set_valid_tach_count, ValidTachCount);
494 fan_register!(drive_fail_band_low_byte, set_drive_fail_band_low_byte, DriveFailBandLow);
495 fan_register!(drive_fail_band_high_byte, set_drive_fail_band_high_byte, DriveFailBandHigh);
496 fan_register!(tach_target_low_byte, set_tach_target_low_byte, TachTargetLow);
497 fan_register!(tach_target_high_byte, set_tach_target_high_byte, TachTargetHigh);
498 fan_register!(tach_reading_high_byte, set_tach_reading_high_byte, TachReadingHigh);
499 fan_register!(tach_reading_low_byte, set_tach_reading_low_byte, TachReadingLow);
500
501 register_ro!(software_lock, SoftwareLock);
503 register_ro!(product_features, ProductFeatures);
504 register_ro!(product_id, ProductId);
505
506 pub async fn dump_info(&mut self) -> Result<(), Error> {
508 macro_rules! defmt_info_register {
509 ($dev:expr, $reg:tt) => {
510 let value = $dev.$reg().await?;
511 defmt::info!("{}: {:#04x}", stringify!($reg), u8::from(value));
512 };
513 }
514
515 macro_rules! defmt_info_fan_register {
516 ($dev:expr, $reg:tt, $fan:expr) => {
517 let value = $dev.$reg(FanSelect($fan)).await?;
518 defmt::info!("{}: {:#04x}", stringify!($reg), u8::from(value));
519 };
520 }
521
522 let count = self.count();
523
524 defmt::info!("Address: {:#04x}", self.address());
525 defmt::info!("Fan Count: {}", count);
526
527 defmt_info_register!(self, software_lock);
528 defmt_info_register!(self, product_features);
529 defmt_info_register!(self, product_id);
530
531 defmt_info_register!(self, config);
532 defmt_info_register!(self, status);
533 defmt_info_register!(self, stall_status);
534 defmt_info_register!(self, spin_status);
535 defmt_info_register!(self, drive_fail_status);
536 defmt_info_register!(self, interrupt_enable);
537 defmt_info_register!(self, pwm_polarity_config);
538 defmt_info_register!(self, pwm_output_config);
539 defmt_info_register!(self, pwm_base_f45);
540 defmt_info_register!(self, pwm_base_f123);
541
542 for fan in 1..=count {
543 defmt::info!("Fan: {} ----------------------", fan);
544 defmt_info_fan_register!(self, fan_setting, fan);
545 defmt_info_fan_register!(self, pwm_divide, fan);
546 defmt_info_fan_register!(self, fan_configuration1, fan);
547 defmt_info_fan_register!(self, fan_configuration2, fan);
548 defmt_info_fan_register!(self, gain, fan);
549 defmt_info_fan_register!(self, spin_up_configuration, fan);
550 defmt_info_fan_register!(self, max_step, fan);
551 defmt_info_fan_register!(self, minimum_drive, fan);
552 defmt_info_fan_register!(self, valid_tach_count, fan);
553 defmt_info_fan_register!(self, drive_fail_band_low_byte, fan);
554 defmt_info_fan_register!(self, drive_fail_band_high_byte, fan);
555 defmt_info_fan_register!(self, tach_target_low_byte, fan);
556 defmt_info_fan_register!(self, tach_target_high_byte, fan);
557 defmt_info_fan_register!(self, tach_reading_high_byte, fan);
558 defmt_info_fan_register!(self, tach_reading_low_byte, fan);
559 }
560
561 Ok(())
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
569 use std::{vec, vec::Vec};
570
571 use crate::registers::tach_reading::TachReading;
572
573 #[derive(Clone, Debug)]
575 struct Emc230xExpectationBuilder {
576 address: u8,
577 _product_id: ProductId,
578 transactions: Vec<I2cTransaction>,
579 }
580
581 impl Emc230xExpectationBuilder {
582 fn new(address: u8, pid: ProductId) -> Self {
584 let mut transactions = vec![
585 I2cTransaction::write_read(address, vec![ManufacturerId::ADDRESS], vec![0x5D]),
586 I2cTransaction::write_read(address, vec![ProductId::ADDRESS], vec![pid.into()]),
587 ];
588
589 let mut output_cfg = 0x00_u8;
591 for fan in 1..=pid.num_fans() {
592 output_cfg |= 1 << (fan - 1);
593 }
594 transactions
595 .push(I2cTransaction::write(address, vec![PwmOutputConfig::ADDRESS, output_cfg]));
596
597 for fan in 1..=pid.num_fans() {
598 transactions.push(I2cTransaction::write_read(
599 address,
600 vec![FanConfiguration1::fan_address(FanSelect(fan))
601 .expect("Could not set fan address")],
602 vec![FanConfiguration1::default().into()],
603 ));
604
605 transactions.push(I2cTransaction::write(
606 address,
607 vec![
608 FanConfiguration1::fan_address(FanSelect(fan))
609 .expect("Could not set fan address"),
610 0x0B,
611 ],
612 ));
613 }
614
615 Self {
616 address,
617 _product_id: pid,
618 transactions,
619 }
620 }
621
622 fn duty_cycle(&mut self, select: FanSelect, duty_cycle: u8) {
624 let raw = FanDriveSetting::from_duty_cycle(duty_cycle);
625
626 self.transactions.push(I2cTransaction::write_read(
627 self.address,
628 vec![FanDriveSetting::fan_address(select).expect("Could not set fan address")],
629 vec![raw.into()],
630 ));
631 }
632
633 fn rpm(&mut self, select: FanSelect, rpm: u16) {
635 let mut default_cfg = FanConfiguration1::default();
636 default_cfg.set_rngx(fan_configuration1::Range::Rpm500);
637
638 let raw = (_SIMPLIFIED_RPM_FACTOR * default_cfg.rngx().tach_count_multiplier() as f64
639 / rpm as f64) as u16;
640 let count: TachReading = TachReading::from(raw);
641
642 self.transactions.push(I2cTransaction::write_read(
643 self.address,
644 vec![TachReadingLow::fan_address(select).expect("Could not set fan address")],
645 vec![count.raw_low()],
646 ));
647
648 self.transactions.push(I2cTransaction::write_read(
649 self.address,
650 vec![TachReadingHigh::fan_address(select).expect("Could not set fan address")],
651 vec![count.raw_high()],
652 ));
653
654 self.transactions.push(I2cTransaction::write_read(
655 self.address,
656 vec![FanConfiguration1::fan_address(select).expect("Could not set fan address")],
657 vec![default_cfg.into()],
658 ));
659 }
660
661 fn build(self) -> Vec<I2cTransaction> {
662 self.transactions
663 }
664 }
665
666 #[tokio::test]
667 async fn probe_no_devices() {
668 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
669
670 let expectations: Vec<I2cTransaction> = EMC230X_ADDRESSES
671 .iter()
672 .map(|&addr| {
673 I2cTransaction::write_read(addr, vec![ManufacturerId::ADDRESS], vec![0])
674 .with_error(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address))
675 })
676 .collect();
677
678 let mut i2c = I2cMock::new(&expectations);
679 let result = Emc230x::probe(&mut i2c).await;
680 assert!(result.is_empty());
681 i2c.done();
682 }
683
684 #[tokio::test]
685 async fn probe_one_device() {
686 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
687
688 let mut expectations: Vec<I2cTransaction> = Vec::new();
689 for &addr in EMC230X_ADDRESSES.iter() {
690 if addr == EMC230X_I2C_ADDR_3 {
691 expectations.push(I2cTransaction::write_read(
692 addr,
693 vec![ManufacturerId::ADDRESS],
694 vec![0x5D],
695 ));
696 expectations.push(I2cTransaction::write_read(
697 addr,
698 vec![ProductId::ADDRESS],
699 vec![ProductId::Emc2301.into()],
700 ));
701 } else {
702 expectations.push(
703 I2cTransaction::write_read(addr, vec![ManufacturerId::ADDRESS], vec![0])
704 .with_error(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address)),
705 );
706 }
707 }
708
709 let mut i2c = I2cMock::new(&expectations);
710 let result = Emc230x::probe(&mut i2c).await;
711 assert_eq!(result.len(), 1);
712 let addrs: Vec<u8> = result.iter().collect();
713 assert_eq!(addrs, vec![EMC230X_I2C_ADDR_3]);
714 i2c.done();
715 }
716
717 #[tokio::test]
718 async fn probe_wrong_manufacturer_id() {
719 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
720
721 let mut expectations: Vec<I2cTransaction> = Vec::new();
722 for &addr in EMC230X_ADDRESSES.iter() {
723 if addr == EMC230X_I2C_ADDR_0 {
724 expectations.push(I2cTransaction::write_read(
726 addr,
727 vec![ManufacturerId::ADDRESS],
728 vec![0x00],
729 ));
730 } else {
731 expectations.push(
732 I2cTransaction::write_read(addr, vec![ManufacturerId::ADDRESS], vec![0])
733 .with_error(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address)),
734 );
735 }
736 }
737
738 let mut i2c = I2cMock::new(&expectations);
739 let result = Emc230x::probe(&mut i2c).await;
740 assert!(result.is_empty());
741 i2c.done();
742 }
743
744 #[tokio::test]
745 async fn probe_multiple_devices() {
746 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
747
748 let mut expectations: Vec<I2cTransaction> = Vec::new();
749 for &addr in EMC230X_ADDRESSES.iter() {
750 if addr == EMC230X_I2C_ADDR_0 {
751 expectations.push(I2cTransaction::write_read(
752 addr,
753 vec![ManufacturerId::ADDRESS],
754 vec![0x5D],
755 ));
756 expectations.push(I2cTransaction::write_read(
757 addr,
758 vec![ProductId::ADDRESS],
759 vec![ProductId::Emc2305.into()],
760 ));
761 } else if addr == EMC230X_I2C_ADDR_5 {
762 expectations.push(I2cTransaction::write_read(
763 addr,
764 vec![ManufacturerId::ADDRESS],
765 vec![0x5D],
766 ));
767 expectations.push(I2cTransaction::write_read(
768 addr,
769 vec![ProductId::ADDRESS],
770 vec![ProductId::Emc2303.into()],
771 ));
772 } else {
773 expectations.push(
774 I2cTransaction::write_read(addr, vec![ManufacturerId::ADDRESS], vec![0])
775 .with_error(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address)),
776 );
777 }
778 }
779
780 let mut i2c = I2cMock::new(&expectations);
781 let result = Emc230x::probe(&mut i2c).await;
782 assert_eq!(result.len(), 2);
783 let addrs: Vec<u8> = result.iter().collect();
784 assert_eq!(addrs, vec![EMC230X_I2C_ADDR_0, EMC230X_I2C_ADDR_5]);
785 i2c.done();
786 }
787
788 #[tokio::test]
789 async fn new() {
790 let expectations =
791 Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301).build();
792
793 let i2c = I2cMock::new(&expectations);
794 let dev = crate::Emc230x::new(i2c, EMC2301_I2C_ADDR)
795 .await
796 .expect("Could not create device");
797
798 let mut i2c = dev.release();
799 i2c.done();
800 }
801
802 #[tokio::test]
803 async fn get_duty_cycle() {
804 let expected_duty_cycle = [100, 75, 50, 25, 0];
805
806 let mut expectations = Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301);
807
808 for value in expected_duty_cycle {
809 expectations.duty_cycle(FanSelect(1), value);
810 }
811
812 let expectations = expectations.build();
813
814 let i2c = I2cMock::new(&expectations);
815 let mut dev = crate::Emc230x::new(i2c, EMC2301_I2C_ADDR)
816 .await
817 .expect("Could not create device");
818
819 for expected in expected_duty_cycle {
820 let result = dev
821 .duty_cycle(FanSelect(1))
822 .await
823 .expect("Could not get duty cycle");
824 assert_eq!(expected, result);
825 }
826
827 let mut i2c = dev.release();
828 i2c.done();
829 }
830
831 #[tokio::test]
832 async fn get_rpm() {
833 let expected_rpm: [u16; 15_500] = core::array::from_fn(|i| (i + 500) as u16);
834 let mut expectations = Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301);
835
836 for value in expected_rpm {
837 expectations.rpm(FanSelect(1), value);
838 }
839
840 let expectations = expectations.build();
841
842 let i2c = I2cMock::new(&expectations);
843 let mut dev = crate::Emc230x::new(i2c, EMC2301_I2C_ADDR)
844 .await
845 .expect("Could not create device");
846
847 for expected in expected_rpm {
848 let result = dev.rpm(FanSelect(1)).await.expect("Could not get RPM");
849
850 let range = std::ops::Range {
852 start: expected as f64 * 0.99,
853 end: expected as f64 * 1.01,
854 };
855 assert!(
856 range.contains(&(result as f64)),
857 "RPM was out of expected range; Expected: {} in Range: {:?} Got: {}",
858 expected,
859 range,
860 result
861 );
862 }
863
864 let mut i2c = dev.release();
865 i2c.done();
866 }
867
868 #[tokio::test]
869 async fn emc2305_duty_cycle_fan5() {
870 let mut expectations =
871 Emc230xExpectationBuilder::new(EMC230X_I2C_ADDR_0, ProductId::Emc2305);
872 expectations.duty_cycle(FanSelect(5), 75);
873 let expectations = expectations.build();
874
875 let i2c = I2cMock::new(&expectations);
876 let mut dev = Emc230x::new(i2c, EMC230X_I2C_ADDR_0)
877 .await
878 .expect("Could not create device");
879
880 let result = dev
881 .duty_cycle(FanSelect(5))
882 .await
883 .expect("Could not get duty cycle for fan 5");
884 assert_eq!(75, result);
885
886 let mut i2c = dev.release();
887 i2c.done();
888 }
889
890 #[tokio::test]
891 async fn emc2305_rpm_fan5() {
892 let expected_rpm: u16 = 1000;
893 let mut expectations =
894 Emc230xExpectationBuilder::new(EMC230X_I2C_ADDR_0, ProductId::Emc2305);
895 expectations.rpm(FanSelect(5), expected_rpm);
896 let expectations = expectations.build();
897
898 let i2c = I2cMock::new(&expectations);
899 let mut dev = Emc230x::new(i2c, EMC230X_I2C_ADDR_0)
900 .await
901 .expect("Could not create device");
902
903 let result = dev
904 .rpm(FanSelect(5))
905 .await
906 .expect("Could not get RPM for fan 5");
907
908 let range = std::ops::Range {
909 start: expected_rpm as f64 * 0.99,
910 end: expected_rpm as f64 * 1.01,
911 };
912 assert!(
913 range.contains(&(result as f64)),
914 "RPM was out of expected range; Expected: {} in Range: {:?} Got: {}",
915 expected_rpm,
916 range,
917 result
918 );
919
920 let mut i2c = dev.release();
921 i2c.done();
922 }
923
924 #[tokio::test]
925 async fn fan_detected_spinning() {
926 let mut expectations = Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301);
927 expectations.transactions.push(I2cTransaction::write_read(
929 EMC2301_I2C_ADDR,
930 vec![FanStallStatus::ADDRESS],
931 vec![0x00],
932 ));
933 let expectations = expectations.build();
934
935 let i2c = I2cMock::new(&expectations);
936 let mut dev = Emc230x::new(i2c, EMC2301_I2C_ADDR)
937 .await
938 .expect("Could not create device");
939
940 let result = dev
941 .fan_detected(FanSelect(1))
942 .await
943 .expect("fan_detected failed");
944 assert!(result);
945
946 let mut i2c = dev.release();
947 i2c.done();
948 }
949
950 #[tokio::test]
951 async fn fan_detected_stalled() {
952 let mut expectations = Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301);
953 expectations.transactions.push(I2cTransaction::write_read(
955 EMC2301_I2C_ADDR,
956 vec![FanStallStatus::ADDRESS],
957 vec![0x01],
958 ));
959 let expectations = expectations.build();
960
961 let i2c = I2cMock::new(&expectations);
962 let mut dev = Emc230x::new(i2c, EMC2301_I2C_ADDR)
963 .await
964 .expect("Could not create device");
965
966 let result = dev
967 .fan_detected(FanSelect(1))
968 .await
969 .expect("fan_detected failed");
970 assert!(!result);
971
972 let mut i2c = dev.release();
973 i2c.done();
974 }
975
976 #[tokio::test]
977 async fn fan_detected_emc2305_fan5_stalled() {
978 let mut expectations =
979 Emc230xExpectationBuilder::new(EMC230X_I2C_ADDR_0, ProductId::Emc2305);
980 expectations.transactions.push(I2cTransaction::write_read(
982 EMC230X_I2C_ADDR_0,
983 vec![FanStallStatus::ADDRESS],
984 vec![0x10],
985 ));
986 let expectations = expectations.build();
987
988 let i2c = I2cMock::new(&expectations);
989 let mut dev = Emc230x::new(i2c, EMC230X_I2C_ADDR_0)
990 .await
991 .expect("Could not create device");
992
993 let result = dev
994 .fan_detected(FanSelect(5))
995 .await
996 .expect("fan_detected failed");
997 assert!(!result);
998
999 let mut i2c = dev.release();
1000 i2c.done();
1001 }
1002
1003 #[tokio::test]
1004 async fn valid_fan() {
1005 let expectations = Emc230xExpectationBuilder::new(EMC2301_I2C_ADDR, ProductId::Emc2301);
1006 let expectations = expectations.build();
1007 let i2c = I2cMock::new(&expectations);
1008 let dev = Emc230x::new(i2c, EMC2301_I2C_ADDR)
1009 .await
1010 .expect("Could not create device");
1011
1012 let result = dev.valid_fan(FanSelect(1));
1013 assert!(result.is_ok());
1014
1015 let result = dev.valid_fan(FanSelect(0));
1016 assert!(result.is_err());
1017
1018 let result = dev.valid_fan(FanSelect(6));
1019 assert!(result.is_err());
1020
1021 let mut i2c = dev.release();
1022 i2c.done();
1023 }
1024}