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}