Skip to main content

modulino/
knob.rs

1//! Modulino Knob driver.
2//!
3//! The Modulino Knob module is a rotary encoder with a push button.
4
5use crate::{addresses, Error, I2cDevice, Result};
6use embedded_hal::i2c::I2c;
7
8/// Driver for the Modulino Knob module (rotary encoder).
9///
10/// # Example
11///
12/// ```rust,ignore
13/// use modulino::Knob;
14///
15/// let mut knob = Knob::new(i2c)?;
16///
17/// // Set a range for the encoder value
18/// knob.set_range(-100, 100);
19///
20/// loop {
21///     knob.update()?;
22///     
23///     if knob.pressed() {
24///         println!("Button pressed!");
25///     }
26///     
27///     println!("Value: {}", knob.value());
28/// }
29/// ```
30pub struct Knob<I2C> {
31    device: I2cDevice<I2C>,
32    value: i16,
33    pressed: bool,
34    range: Option<(i16, i16)>,
35}
36
37impl<I2C, E> Knob<I2C>
38where
39    I2C: I2c<Error = E>,
40{
41    /// Create a new Knob instance with the default address.
42    pub fn new(i2c: I2C) -> Result<Self, E> {
43        Self::new_with_address(i2c, addresses::KNOB[0])
44    }
45
46    /// Create a new Knob instance with a custom address.
47    pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, E> {
48        let mut knob = Self {
49            device: I2cDevice::new(i2c, address),
50            value: 0,
51            pressed: false,
52            range: None,
53        };
54
55        // Read initial state
56        knob.update()?;
57
58        Ok(knob)
59    }
60
61    /// Get the I2C address.
62    pub fn address(&self) -> u8 {
63        self.device.address
64    }
65
66    /// Read the current encoder state from the device.
67    fn read_data(&mut self) -> Result<(i16, bool), E> {
68        let mut buf = [0u8; 4]; // 1 pinstrap + 2 encoder + 1 button
69        self.device.read(&mut buf)?;
70
71        // Skip first byte (pinstrap address)
72        let raw_value = i16::from_le_bytes([buf[1], buf[2]]);
73        let pressed = buf[3] != 0;
74
75        Ok((raw_value, pressed))
76    }
77
78    /// Update the encoder state.
79    ///
80    /// This should be called periodically to read the latest values.
81    /// Returns `true` if the state has changed.
82    pub fn update(&mut self) -> Result<bool, E> {
83        let previous_value = self.value;
84        let previous_pressed = self.pressed;
85
86        let (mut new_value, new_pressed) = self.read_data()?;
87
88        // Apply range constraint if set
89        if let Some((min, max)) = self.range {
90            if new_value < min {
91                new_value = min;
92                self.set_value_internal(min)?;
93            } else if new_value > max {
94                new_value = max;
95                self.set_value_internal(max)?;
96            }
97        }
98
99        self.value = new_value;
100        self.pressed = new_pressed;
101
102        Ok(self.value != previous_value || self.pressed != previous_pressed)
103    }
104
105    /// Get the current encoder value.
106    pub fn value(&self) -> i16 {
107        self.value
108    }
109
110    /// Set the encoder value.
111    pub fn set_value(&mut self, value: i16) -> Result<(), E> {
112        // Check range if set
113        if let Some((min, max)) = self.range {
114            if value < min || value > max {
115                return Err(Error::OutOfRange);
116            }
117        }
118
119        self.set_value_internal(value)?;
120        self.value = value;
121        Ok(())
122    }
123
124    /// Internal method to set the encoder value on the device.
125    fn set_value_internal(&mut self, value: i16) -> Result<(), E> {
126        let bytes = value.to_le_bytes();
127        let data = [bytes[0], bytes[1], 0, 0];
128        self.device.write(&data)?;
129        Ok(())
130    }
131
132    /// Reset the encoder value to 0.
133    pub fn reset(&mut self) -> Result<(), E> {
134        self.set_value(0)
135    }
136
137    /// Check if the button is currently pressed.
138    pub fn pressed(&self) -> bool {
139        self.pressed
140    }
141
142    /// Set the value range for the encoder.
143    ///
144    /// The encoder value will be constrained to this range.
145    /// Pass `None` to remove the range constraint.
146    pub fn set_range(&mut self, min: i16, max: i16) {
147        self.range = Some((min, max));
148
149        // Constrain current value to new range
150        if self.value < min {
151            self.value = min;
152        } else if self.value > max {
153            self.value = max;
154        }
155    }
156
157    /// Clear the range constraint.
158    pub fn clear_range(&mut self) {
159        self.range = None;
160    }
161
162    /// Get the current range, if set.
163    pub fn range(&self) -> Option<(i16, i16)> {
164        self.range
165    }
166
167    /// Get the rotation direction since the last update.
168    ///
169    /// Returns:
170    /// - Positive value for clockwise rotation
171    /// - Negative value for counter-clockwise rotation
172    /// - 0 for no rotation
173    pub fn rotation_delta(&self, previous_value: i16) -> i16 {
174        // Handle wraparound
175        let diff = self.value.wrapping_sub(previous_value);
176
177        // Check for wraparound (if diff is too large, it wrapped)
178        if !(-16384..=16384).contains(&diff) {
179            diff.wrapping_add(i16::MIN)
180        } else {
181            diff
182        }
183    }
184
185    /// Release the I2C bus.
186    pub fn release(self) -> I2C {
187        self.device.release()
188    }
189}