1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! # rotary-encoder
//! A rotary encoder library built for embedded applications

#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]

use embedded_hal::digital::v2::InputPin;

/// Velocity type, the value is between 0.0 and 1.0
pub type Velocity = f32;

/// Direction of Rotary Encoder rotation
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Direction {
    /// No Direction is specified,
    None,
    /// Clockwise direction
    Clockwise,
    /// Anti-clockwise direction
    Anticlockwise,
}
/// The Sensitivity of the Rotary Encoder
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Sensitivity {
    /// Default sensitivity
    Default = 2,
    /// Low sensitivity
    Low = 4,
}

/// State table for recognizing valid rotary encoder values
const STATES: [i8; 16] = [0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0];

#[cfg(feature = "angular-velocity")]
/// Default angular velocity increasing factor
const DEFAULT_VELOCITY_INC_FACTOR: f32 = 0.2;
#[cfg(feature = "angular-velocity")]
/// Default angular velocity decreasing factor
const DEFAULT_VELOCITY_DEC_FACTOR: f32 = 0.01;
#[cfg(feature = "angular-velocity")]
/// Angular velocity action window duration in milliseconds
const DEFAULT_VELOCITY_ACTION_MS: u64 = 25;

/// Rotary Encoder
pub struct RotaryEncoder<DT, CLK> {
    pin_dt: DT,
    pin_clk: CLK,
    pos_calc: i8,
    sensitivity: Sensitivity,
    transition: u8,
    direction: Direction,
}

#[cfg(feature = "angular-velocity")]
/// Rotary Encoder with velocity
pub struct RotaryEncoderWithVelocity<DT, CLK> {
    inner: RotaryEncoder<DT, CLK>,
    velocity: Velocity,
    velocity_inc_factor: f32,
    velocity_dec_factor: f32,
    velocity_action_ms: u64,
    previous_time: u64,
}

impl<DT, CLK> RotaryEncoder<DT, CLK>
where
    DT: InputPin,
    CLK: InputPin,
{
    /// Initiates a new Rotary Encoder, taking two InputPins [`InputPin`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/digital/v2/trait.InputPin.html).
    pub fn new(pin_dt: DT, pin_clk: CLK) -> Self {
        RotaryEncoder {
            pin_dt,
            pin_clk,
            pos_calc: 0,
            transition: 0,
            sensitivity: Sensitivity::Default,
            direction: Direction::None,
        }
    }

    /// Set the sensitivity of the rotary encoder
    pub fn set_sensitivity(&mut self, sensitivity: Sensitivity) {
        self.sensitivity = sensitivity;
    }

    /// Borrow a mutable reference to the underlying InputPins. This is useful for clearing hardware interrupts.
    pub fn borrow_pins(&mut self) -> (&mut DT, &mut CLK) {
        (&mut self.pin_dt, &mut self.pin_clk)
    }

    /// Release the underying resources such as the InputPins back to the initiator
    pub fn release(self) -> (DT, CLK) {
        (self.pin_dt, self.pin_clk)
    }

    /// Update the state machine of the RotaryEncoder. This should be called ideally from an interrupt vector
    /// when either the DT or CLK pins state changes. This function will update the RotaryEncoder's Direction
    pub fn update(&mut self) {
        let dt_state = self.pin_dt.is_high().unwrap_or_default() as u8;
        let clk_state = self.pin_clk.is_high().unwrap_or_default() as u8;

        let current = (dt_state << 1) | clk_state;
        self.transition = (self.transition << 2) | current;
        let index = (self.transition & 0x0F) as usize;
        self.pos_calc += STATES[index];

        let sensitivity = self.sensitivity as i8;
        if self.pos_calc == sensitivity || self.pos_calc == -sensitivity {
            self.direction = if self.pos_calc == sensitivity {
                Direction::Clockwise
            } else {
                Direction::Anticlockwise
            };

            self.pos_calc = 0;
            return;
        }

        self.direction = Direction::None;
    }

    /// Returns the current Direction of the RotaryEncoder
    pub fn direction(&self) -> Direction {
        self.direction
    }
}

#[cfg(feature = "angular-velocity")]
impl<DT, CLK> RotaryEncoderWithVelocity<DT, CLK>
where
    DT: InputPin,
    CLK: InputPin,
{
    /// Initiates a new Rotary Encoder with velocity, taking two InputPins [`InputPin`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/digital/v2/trait.InputPin.html).
    /// Optionally the behaviour of the angular velocity can be modified:
    pub fn new(pin_dt: DT, pin_clk: CLK) -> Self {
        RotaryEncoderWithVelocity {
            inner: RotaryEncoder::new(pin_dt, pin_clk),
            velocity: 0.0,
            velocity_inc_factor: DEFAULT_VELOCITY_INC_FACTOR,
            velocity_dec_factor: DEFAULT_VELOCITY_DEC_FACTOR,
            velocity_action_ms: DEFAULT_VELOCITY_ACTION_MS,
            previous_time: 0,
        }
    }

    /// Set the velocity_inc_factor. How quickly the velocity increases to 1.0.
    pub fn set_velocity_inc_factor(&mut self, inc_factor: f32) {
        self.velocity_inc_factor = inc_factor;
    }

    /// Set the velocity_dec_factor. How quickly the velocity decreases or cools-down
    pub fn set_velocity_dec_factor(&mut self, dec_factor: f32) {
        self.velocity_dec_factor = dec_factor;
    }

    /// Set the velocity_action_ms. The window of duration (milliseconds) that the velocity will increase
    pub fn set_velocity_action_ms(&mut self, action_ms: u64) {
        self.velocity_action_ms = action_ms;
    }

    /// This function should be called periodically, either via a timer or the main loop.
    /// This function will reduce the angular velocity over time, the amount is configurable via the constructor
    pub fn decay_velocity(&mut self) {
        self.velocity -= self.velocity_dec_factor;
        if self.velocity < 0.0 {
            self.velocity = 0.0;
        }
    }

    /// Borrow a mutable reference to the underlying InputPins. This is useful for clearing hardware interrupts.
    pub fn borrow_pins(&mut self) -> (&mut DT, &mut CLK) {
        self.inner.borrow_pins()
    }

    /// Set the sensitivity of the rotary encoder
    pub fn set_sensitivity(&mut self, sensitivity: Sensitivity) {
        self.inner.sensitivity = sensitivity;
    }

    /// Borrow a reference to the underlying RotaryEncoder. Useful for configuring the RotaryEncoder
    pub fn borrow_inner(&mut self) -> &mut RotaryEncoder<DT, CLK> {
        &mut self.inner
    }

    /// Release the underying resources such as the InputPins back to the initiator
    pub fn release(self) -> (DT, CLK) {
        self.inner.release()
    }

    /// Update the state machine of the RotaryEncoder. This should be called ideally from an interrupt vector
    /// when either the DT or CLK pins state changes. This function will update the RotaryEncoder's
    /// Direction and current Angular Velocity.
    /// * `current_time` - Current timestamp in ms (strictly monotonously increasing)
    pub fn update(&mut self, current_time: u64) {
        self.inner.update();

        if self.inner.direction() != Direction::None {
            if current_time - self.previous_time < self.velocity_action_ms && self.velocity < 1.0 {
                self.velocity += self.velocity_inc_factor;
                if self.velocity > 1.0 {
                    self.velocity = 1.0;
                }
            }
            return;
        }

        self.previous_time = current_time;
    }

    /// Returns the current Direction of the RotaryEncoder
    pub fn direction(&self) -> Direction {
        self.inner.direction
    }

    /// Returns the current angular velocity of the RotaryEncoder
    /// The Angular Velocity is a value between 0.0 and 1.0
    /// This is useful for incrementing/decrementing a value in an exponential fashion
    pub fn velocity(&self) -> Velocity {
        self.velocity
    }
}