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    /// Starts the fan at its minimum RPM needed to begin from a dead stop.
172    #[inline]
173    fn start(&mut self) -> Result<(), Self::Error> {
174        self.set_speed_rpm(self.min_start_rpm())?;
175        Ok(())
176    }
177
178    /// Stops the fan completely.
179    #[inline]
180    fn stop(&mut self) -> Result<(), Self::Error> {
181        self.set_speed_rpm(0)?;
182        Ok(())
183    }
184}
185
186impl<T: Fan + ?Sized> Fan for &mut T {
187    #[inline]
188    fn max_rpm(&self) -> u16 {
189        T::max_rpm(self)
190    }
191
192    #[inline]
193    fn min_rpm(&self) -> u16 {
194        T::min_rpm(self)
195    }
196
197    #[inline]
198    fn min_start_rpm(&self) -> u16 {
199        T::min_start_rpm(self)
200    }
201
202    #[inline]
203    fn set_speed_rpm(&mut self, rpm: u16) -> Result<u16, Self::Error> {
204        T::set_speed_rpm(self, rpm)
205    }
206
207    #[inline]
208    fn set_speed_percent(&mut self, percent: u8) -> Result<u16, Self::Error> {
209        T::set_speed_percent(self, percent)
210    }
211
212    #[inline]
213    fn set_speed_max(&mut self) -> Result<(), Self::Error> {
214        T::set_speed_max(self)
215    }
216
217    #[inline]
218    fn stop(&mut self) -> Result<(), Self::Error> {
219        T::stop(self)
220    }
221}
222
223/// Blocking RPM sensing (tachometer) methods
224pub trait RpmSense: ErrorType {
225    /// Returns the fan's currently measured RPM.
226    fn rpm(&mut self) -> Result<u16, Self::Error>;
227}
228
229impl<T: RpmSense + ?Sized> RpmSense for &mut T {
230    #[inline]
231    fn rpm(&mut self) -> Result<u16, Self::Error> {
232        T::rpm(self)
233    }
234}