Skip to main content

emc230x/
lib.rs

1// Copyright (c) 2024 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8//! The EMC230x device family is a fan controller with up to five independently
9//! controlled PWM fan drivers.
10
11#![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
33/// Default I2C address for the EMC2301 device
34pub const EMC2301_I2C_ADDR: u8 = 0b0010_1111;
35
36/// I2C addresses for the EMC230x family, selected by the ADDR resistor configuration.
37pub 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
53/// Simplified RPM factor for calculating RPM from raw values
54///
55/// See Equation 4-3, page 17 of the datasheet. ((SIMPLIFIED_RPM_FACTOR * m) / COUNT)
56const _SIMPLIFIED_RPM_FACTOR: f64 = 3_932_160.0;
57
58/// Fetch a read-only register from the device
59macro_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
68/// Fetch and set a register from the device which applies to all fans
69macro_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
84/// Fetch and set a register from the device which applies to a specific fan
85macro_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
103/// Manually hack rounding the value because [`core`] doesn't have `round`
104///
105/// This is a terrible practice. Is there a better way to do this?
106pub(crate) fn hacky_round(value: f64) -> u8 {
107    // Interpret the value as a u8 first to get an integer value
108    let raw = value as u8;
109
110    // Reinterpret the integer value as a f64 and compare it to the original value
111    if value - raw as f64 >= 0.5 {
112        raw + 1
113    } else {
114        raw
115    }
116}
117
118/// Manually hack rounding the value because [`core`] doesn't have `round`
119///
120/// This is a terrible practice. Is there a better way to do this?
121pub(crate) fn hacky_round_u16(value: f64) -> u16 {
122    // Interpret the value as a u8 first to get an integer value
123    let raw = value as u16;
124
125    // Reinterpret the integer value as a f64 and compare it to the original value
126    if value - raw as f64 >= 0.5 {
127        raw + 1
128    } else {
129        raw
130    }
131}
132
133/// The set of I2C addresses at which EMC230x devices were discovered during a bus probe.
134///
135/// Returned by [`Emc230x::probe`]. Iterating yields only addresses where a device was found.
136#[derive(Copy, Clone, Debug, Default)]
137pub struct ProbeResult([Option<u8>; 6]);
138
139impl ProbeResult {
140    /// Returns an iterator over found device addresses.
141    pub fn iter(&self) -> impl Iterator<Item = u8> + '_ {
142        self.0.iter().filter_map(|x| *x)
143    }
144
145    /// Returns `true` if no devices were found.
146    pub fn is_empty(&self) -> bool {
147        self.0.iter().all(|x| x.is_none())
148    }
149
150    /// Returns the number of devices found.
151    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 bus
167    i2c: I2C,
168
169    /// I2C address of the device
170    address: u8,
171
172    /// Device Product Identifier
173    ///
174    /// The product identification will determine the number of fans the device supports.
175    pid: ProductId,
176
177    /// Configurable number of poles in a fan. Typically 2.
178    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    /// Manufacturer ID
193    const MANUFACTURER_ID: u8 = 0x5D;
194
195    /// Tachometer measurement frequency (kHz)
196    const TACH_FREQUENCY_HZ: f64 = 32_768.0;
197
198    /// Determine if the device at the specified address is an EMC230x device
199    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    /// Probe the I2C bus for any EMC230x devices.
210    ///
211    /// Checks all six possible EMC230x I2C addresses (0x2C–0x2F, 0x4C–0x4D) and returns a
212    /// [`ProbeResult`] containing each address where an EMC230x was identified.
213    /// Issues up to 12 I2C transactions (2 per address).
214    ///
215    /// The I2C bus is borrowed so the caller can pass it to [`Emc230x::new`]
216    /// using one of the returned addresses.
217    ///
218    /// # Example
219    /// ```ignore
220    /// let found = Emc230x::probe(&mut i2c).await;
221    /// if let Some(addr) = found.iter().next() {
222    ///     let dev = Emc230x::new(i2c, addr).await?;
223    /// }
224    /// ```
225    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    /// Initialize a new EMC230x device at the specified address
236    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        // Assume 2 poles for all fans by default. This is common for most fans and is a safe default.
241        let poles = [2; 5];
242
243        // Form the device so that some defaults can be set
244        let mut dev = Self {
245            i2c,
246            address,
247            pid,
248            poles,
249        };
250
251        // Set all fan outputs to push-pull to avoid waveform distortion
252        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        // Set RPM range to 500 RPM for all drives to capture slower fans
260        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        // Device is configured
267        Ok(dev)
268    }
269
270    /// Get the I2C address of the device
271    fn address(&self) -> u8 {
272        self.address
273    }
274
275    /// Get the number of fans the device supports
276    pub fn count(&self) -> u16 {
277        self.pid.num_fans()
278    }
279
280    /// Get the number of poles for the selected fan (used in RPM calculations)
281    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    /// Set the number of poles for the selected fan (used in RPM calculations)
287    ///
288    /// It is unlikely that this value will need to change unless a non-standard fan is used.
289    /// If it does need to change, there are likely other configuration changes that need to
290    /// happen as well.
291    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    /// Get the tachometer frequency of the device
298    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    /// Set the mode of the fan
307    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                // Disable RPM mode first if it is enabled.
314                //
315                // The device appears to set the fan drive with the duty cycle corresponding to the
316                // last target RPM set. If the mode bit is set after the duty cycle, the desired
317                // duty cycle gets overwritten.
318                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    /// Fetch the current duty cycle of the fan
335    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    /// Set the duty cycle of the fan
343    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    /// Fetch the current RPM of the fan
355    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    /// Set the target RPM of the fan
367    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    /// Fetch the current duty cycle and RPM of the fan
379    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    /// Minimum configured duty cycle the fan will run at.
387    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    /// Set the minimum duty cycle the fan will run at.
394    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    /// Returns `true` if a spinning fan is detected on the selected channel.
402    ///
403    /// Reads the [`FanStallStatus`] register and checks the per-fan stall bit. A clear bit
404    /// means the tachometer is receiving pulses - indicating a connected, spinning fan. A set
405    /// bit means the tach count exceeded the [`ValidTachCount`] threshold, which occurs when
406    /// no fan is connected **or** when a connected fan is not spinning.
407    ///
408    /// This is the closest the hardware can indicate fan presence; it cannot distinguish
409    /// between an absent fan and a stalled one.
410    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    /// Calculate either the RPM or raw value of the RPM based on the input value.
418    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    /// Write a value to a register on the device
431    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    /// Read a value from a register on the device attached to the I2C bus
438    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    /// Read a value from a register on the device
452    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    /// Determine if the fan number is valid by comparing it to the number of fans the device supports.
459    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    /// Release the I2C bus from the device
468    pub fn release(self) -> I2C {
469        self.i2c
470    }
471
472    // General register access
473    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 specific register access
485    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    // Chip registers
502    register_ro!(software_lock, SoftwareLock);
503    register_ro!(product_features, ProductFeatures);
504    register_ro!(product_id, ProductId);
505
506    /// Dump all the info and registers from the EMC230x Device
507    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    /// Transaction expectation builder for a [`Emc230x`] device.
574    #[derive(Clone, Debug)]
575    struct Emc230xExpectationBuilder {
576        address: u8,
577        _product_id: ProductId,
578        transactions: Vec<I2cTransaction>,
579    }
580
581    impl Emc230xExpectationBuilder {
582        /// Create expectations for a mock [`Emc230x`] device that has not been initialized.
583        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            // Set the output configuration to push-pull for all fans
590            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        /// Set expectations to retrieve the duty cycle of a fan.
623        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        /// Set expectations to retrieve the RPM of a fan.
634        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                // Responds but returns wrong manufacturer ID
725                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            // Allow a ±1% margin of error due to count step size varying over different settings
851            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        // stall status = 0x00 -> fan 1 not stalled
928        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        // stall status = 0x01 -> bit 0 set -> fan 1 stalled
954        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        // stall status = 0x10 -> bit 4 set -> fan 5 stalled
981        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}