embedded_fans/
lib.rs

1//! Blocking Fan API
2//!
3//! This API provides generic methods for interfacing with fans.
4//!
5//! # For HAL authors
6//!
7//! Here is an example of an embedded-fans implementation of the Fan and RpmSense traits.
8//!
9//! ```
10//! use embedded_fans::{self, Fan, RpmSense};
11//!
12//! // A struct representing a fan device.
13//! pub struct MyFan {
14//!     // ...
15//! }
16//!
17//! #[derive(Clone, Copy, Debug)]
18//! pub enum Error {
19//!     // ...
20//! }
21//!
22//! impl embedded_fans::Error for Error {
23//!     fn kind(&self) -> embedded_fans::ErrorKind {
24//!         match *self {
25//!             // ...
26//!         }
27//!     }
28//! }
29//!
30//! impl embedded_fans::ErrorType for MyFan {
31//!     type Error = Error;
32//! }
33//!
34//! impl Fan for MyFan {
35//!     fn max_rpm(&self) -> u16 {
36//!         3150
37//!     }
38//!
39//!     fn min_rpm(&self) -> u16 {
40//!         0
41//!     }
42//!
43//!     fn min_start_rpm(&self) -> u16 {
44//!         1120
45//!     }
46//!
47//!     fn set_speed_rpm(&mut self, rpm: u16) -> Result<u16, Self::Error> {
48//!         // ...
49//!         Ok(rpm)
50//!     }
51//! }
52//!
53//! impl RpmSense for MyFan {
54//!     fn rpm(&mut self) -> Result<u16, Self::Error> {
55//!         // ...
56//!         Ok(42)
57//!     }
58//! }
59//! ```
60
61#![doc = include_str!("../README.md")]
62#![forbid(missing_docs)]
63#![forbid(unsafe_code)]
64#![no_std]
65
66/// Fan error.
67pub trait Error: core::fmt::Debug {
68    /// Convert error to a generic Fan error kind.
69    ///
70    /// By using this method, Fan errors freely defined by HAL implementations
71    /// can be converted to a set of generic Fan errors upon which generic
72    /// code can act.
73    fn kind(&self) -> ErrorKind;
74}
75
76/// Fan error kind.
77///
78/// This represents a common set of Fan operation errors. HAL implementations are
79/// free to define more specific or additional error types. However, by providing
80/// a mapping to these common Fan errors, generic code can still react to them.
81#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83#[non_exhaustive]
84pub enum ErrorKind {
85    /// An error occurred on the underlying peripheral supporting the fan.
86    /// e.g. A PWM error occured for a PWM-controlled fan or a DAC error occured for a voltage-controlled fan.
87    Peripheral,
88    /// The fan is not capable of operating at the requested speed.
89    InvalidSpeed,
90    /// A different error occurred. The original error may contain more information.
91    Other,
92}
93
94impl Error for ErrorKind {
95    #[inline]
96    fn kind(&self) -> ErrorKind {
97        *self
98    }
99}
100
101impl core::fmt::Display for ErrorKind {
102    #[inline]
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        match self {
105            Self::Peripheral => {
106                write!(f, "An error occured on the underlying peripheral")
107            }
108            Self::InvalidSpeed => {
109                write!(f, "Fan is not capable of operating at the requested speed")
110            }
111            Self::Other => write!(
112                f,
113                "A different error occurred. The original error may contain more information"
114            ),
115        }
116    }
117}
118
119/// Fan error type trait.
120///
121/// This just defines the error type, to be used by the other traits.
122pub trait ErrorType {
123    /// Error type
124    type Error: Error;
125}
126
127impl<T: ErrorType + ?Sized> ErrorType for &mut T {
128    type Error = T::Error;
129}
130
131impl Error for core::convert::Infallible {
132    #[inline]
133    fn kind(&self) -> ErrorKind {
134        match *self {}
135    }
136}
137
138/// Blocking fan methods
139pub trait Fan: ErrorType {
140    /// Returns the maximum RPM the fan is capable of running at.
141    fn max_rpm(&self) -> u16;
142
143    /// Returns the minimum RPM the fan is capable of running at.
144    fn min_rpm(&self) -> u16;
145
146    /// Returns the minimum RPM needed for the fan to begin running from a dead stop
147    /// (which may be the same as the minimum running speed).
148    fn min_start_rpm(&self) -> u16;
149
150    /// Sets the fan's speed in terms of absolute RPM.
151    /// Returns the actual RPM set on success.
152    fn set_speed_rpm(&mut self, rpm: u16) -> Result<u16, Self::Error>;
153
154    /// Sets the fan's speed in terms of percent of maximum RPM.
155    /// Returns the actual RPM set on success.
156    #[inline]
157    fn set_speed_percent(&mut self, percent: u8) -> Result<u16, Self::Error> {
158        debug_assert!((0..=100).contains(&percent));
159
160        // Cast operands to u32 to prevent overflow during multiplication
161        self.set_speed_rpm(((u32::from(self.max_rpm()) * u32::from(percent)) / 100) as u16)
162    }
163
164    /// Sets the fan's speed to the maximum RPM it's capable of running at.
165    #[inline]
166    fn set_speed_max(&mut self) -> Result<(), Self::Error> {
167        self.set_speed_rpm(self.max_rpm())?;
168        Ok(())
169    }
170
171    /// Stops the fan completely.
172    #[inline]
173    fn stop(&mut self) -> Result<(), Self::Error> {
174        self.set_speed_rpm(0)?;
175        Ok(())
176    }
177}
178
179impl<T: Fan + ?Sized> Fan for &mut T {
180    #[inline]
181    fn max_rpm(&self) -> u16 {
182        T::max_rpm(self)
183    }
184
185    #[inline]
186    fn min_rpm(&self) -> u16 {
187        T::min_rpm(self)
188    }
189
190    #[inline]
191    fn min_start_rpm(&self) -> u16 {
192        T::min_start_rpm(self)
193    }
194
195    #[inline]
196    fn set_speed_rpm(&mut self, rpm: u16) -> Result<u16, Self::Error> {
197        T::set_speed_rpm(self, rpm)
198    }
199
200    #[inline]
201    fn set_speed_percent(&mut self, percent: u8) -> Result<u16, Self::Error> {
202        T::set_speed_percent(self, percent)
203    }
204
205    #[inline]
206    fn set_speed_max(&mut self) -> Result<(), Self::Error> {
207        T::set_speed_max(self)
208    }
209
210    #[inline]
211    fn stop(&mut self) -> Result<(), Self::Error> {
212        T::stop(self)
213    }
214}
215
216/// Blocking RPM sensing (tachometer) methods
217pub trait RpmSense: ErrorType {
218    /// Returns the fan's currently measured RPM.
219    fn rpm(&mut self) -> Result<u16, Self::Error>;
220}
221
222impl<T: RpmSense + ?Sized> RpmSense for &mut T {
223    #[inline]
224    fn rpm(&mut self) -> Result<u16, Self::Error> {
225        T::rpm(self)
226    }
227}