1515#include " RotaryEncoder.h"
1616#include " Arduino.h"
1717
18- #define LATCH0 0 // input state at position 0
19- #define LATCH3 3 // input state at position 3
18+ #define LATCH0 0 // input state at position 0
19+ #define LATCH3 3 // input state at position 3
2020
2121
2222// The array holds the values �1 for the entries where a position was decremented,
2323// a 1 for the entries where the position was incremented
2424// and 0 in all the other (no change or not valid) cases.
2525
2626const int8_t KNOBDIR[] = {
27- 0 , -1 , 1 , 0 ,
28- 1 , 0 , 0 , -1 ,
29- -1 , 0 , 0 , 1 ,
30- 0 , 1 , -1 , 0 };
27+ 0 , -1 , 1 , 0 ,
28+ 1 , 0 , 0 , -1 ,
29+ -1 , 0 , 0 , 1 ,
30+ 0 , 1 , -1 , 0
31+ };
3132
3233
3334// positions: [3] 1 0 2 [3] 1 0 2 [3]
@@ -38,15 +39,91 @@ const int8_t KNOBDIR[] = {
3839
3940// ----- Initialization and Default Values -----
4041
41- RotaryEncoder::RotaryEncoder (int pin1, int pin2, LatchMode mode)
42- {
42+ /* *
43+ * @brief Default constructor that initializes the RotaryEncoder with non-hardware specific setup.
44+ *
45+ * This constructor creates a RotaryEncoder instance with only software initialization.
46+ * No pins are configured or reserved. This is useful for scenarios where:
47+ * - Pins will be managed externally
48+ * - Hardware-specific tick() will be called with explicit pin values
49+ * - Deferred or dynamic pin configuration is needed
50+ *
51+ * @param mode The latch mode defining the encoder sensitivity.
52+ * See RotaryEncoder.h for details on the available modes.
53+ *
54+ * Initialization sets:
55+ * - Encoder mode
56+ * - Position counter to 0
57+ * - Internal state variables to initial values (no motion detected)
58+ * - No pins are reserved (_pin1 and _pin2 set to NO_PIN)
59+ * - Timestamp tracking for rotation speed calculation
60+ *
61+ * @note To use this constructor effectively, call tick(sig1, sig2) with explicit pin values
62+ * in your main loop, or use the two-parameter constructor if you need automatic pin handling.
63+ *
64+ * @see RotaryEncoder(int pin1, int pin2, LatchMode mode) for hardware-managed pin setup
65+ * @see tick(int sig1, int sig2) for software-managed pin input
66+ */
67+ RotaryEncoder::RotaryEncoder (LatchMode mode) {
68+ _mode = mode;
69+
70+ // No Hardware specific setup here.
71+ // use the ...
72+ _pin1 = _pin2 = NO_PIN;
73+
74+ // start with position 0;
75+ _position = 0 ;
76+ _oldState = 0 ;
77+ _positionExtPrev = _positionExt = 0 ;
78+ _positionExtTimePrev = _positionExtTime = millis ();
79+ } // RotaryEncoder()
80+
81+
82+
83+ /* *
84+ * @brief Constructor that initializes the RotaryEncoder with hardware pin setup.
85+ *
86+ * This constructor creates a RotaryEncoder instance with full default hardware initialization.
87+ * It configures the specified pins, enables internal pull-up resistors, and reads their
88+ * current state to establish the initial encoder position.
89+ *
90+ * @param pin1 First encoder pin (typically pin A). Use a value 0 or greater for a valid pin.
91+ * A negative value or NO_PIN will skip hardware configuration.
92+ * @param pin2 Second encoder pin (typically pin B). Use a value 0 or greater for a valid pin.
93+ * A negative value or NO_PIN will skip hardware configuration.
94+ * @param mode The latch mode defining the encoder sensitivity.
95+ * See RotaryEncoder.h for details on the available modes.
96+ *
97+ * Hardware Setup:
98+ * - Configures both pins as INPUT_PULLUP for reliable signal detection
99+ * - Reads the initial state of pin1 and pin2 using digitalRead()
100+ * - Establishes the initial position based on current pin values
101+ * - Stores pin numbers for use with the non-parameterized tick() method
102+ *
103+ * Interrupt-Safe Usage:
104+ * This constructor is suitable for both polling and interrupt-driven modes:
105+ * - For polling: call tick() periodically in your main loop
106+ * - For interrupts: attach interrupt handlers to these pins and call tick(sig1, sig2)
107+ * from the interrupt handler with explicit pin values for better performance
108+ *
109+ * Initial State:
110+ * - Position counter initialized to 0
111+ * - Internal state variables set based on reading the actual pin values
112+ * - Timestamp tracking initialized for rotation speed calculation
113+ *
114+ * @note If both pins are negative or not in the valid range [0, MAX_PIN], the hardware
115+ * setup is skipped but the encoder still initializes with software defaults.
116+ * @note The pins must support INPUT_PULLUP mode on your microcontroller.
117+ * @note For maximum interrupt responsiveness, consider using the parameterized tick(sig1, sig2)
118+ * variant and reading pins directly in your interrupt handler.
119+ */
120+ RotaryEncoder::RotaryEncoder (int pin1, int pin2, LatchMode mode) : RotaryEncoder(mode) {
43121 int sig1 = 0 ;
44122 int sig2 = 0 ;
45123
46124 // Remember Hardware Setup
47125 _pin1 = pin1;
48126 _pin2 = pin2;
49- _mode = mode;
50127
51128 // Setup the input pins and turn on pullup resistor
52129 if ((pin1 >= 0 ) && (pin2 >= 0 )) {
@@ -56,24 +133,17 @@ RotaryEncoder::RotaryEncoder(int pin1, int pin2, LatchMode mode)
56133 sig1 = digitalRead (_pin1);
57134 sig2 = digitalRead (_pin2);
58135 }
59-
136+
60137 _oldState = sig1 | (sig2 << 1 );
61-
62- // start with position 0;
63- _position = 0 ;
64- _positionExt = 0 ;
65- _positionExtPrev = 0 ;
66- } // RotaryEncoder()
138+ } // RotaryEncoder()
67139
68140
69- long RotaryEncoder::getPosition ()
70- {
141+ long RotaryEncoder::getPosition () {
71142 return _positionExt;
72- } // getPosition()
143+ } // getPosition()
73144
74145
75- RotaryEncoder::Direction RotaryEncoder::getDirection ()
76- {
146+ RotaryEncoder::Direction RotaryEncoder::getDirection () {
77147 RotaryEncoder::Direction ret = Direction::NOROTATION;
78148
79149 if (_positionExtPrev > _positionExt) {
@@ -91,39 +161,37 @@ RotaryEncoder::Direction RotaryEncoder::getDirection()
91161}
92162
93163
94- void RotaryEncoder::setPosition (long newPosition)
95- {
164+ void RotaryEncoder::setPosition (long newPosition) {
96165 switch (_mode) {
97- case LatchMode::FOUR3:
98- case LatchMode::FOUR0:
99- // only adjust the external part of the position.
100- _position = ((newPosition << 2 ) | (_position & 0x03L ));
101- _positionExt = newPosition;
102- _positionExtPrev = newPosition;
103- break ;
166+ case LatchMode::FOUR3:
167+ case LatchMode::FOUR0:
168+ // only adjust the external part of the position.
169+ _position = ((newPosition << 2 ) | (_position & 0x03L ));
170+ _positionExt = newPosition;
171+ _positionExtPrev = newPosition;
172+ break ;
104173
105- case LatchMode::TWO03:
106- // only adjust the external part of the position.
107- _position = ((newPosition << 1 ) | (_position & 0x01L ));
108- _positionExt = newPosition;
109- _positionExtPrev = newPosition;
110- break ;
111- } // switch
174+ case LatchMode::TWO03:
175+ // only adjust the external part of the position.
176+ _position = ((newPosition << 1 ) | (_position & 0x01L ));
177+ _positionExt = newPosition;
178+ _positionExtPrev = newPosition;
179+ break ;
180+ } // switch
112181
113- } // setPosition()
182+ } // setPosition()
114183
115184
116185// Slow, but Simple Variant by directly Read-Out of the Digital State within loop-call
117- void RotaryEncoder::tick (void )
118- {
186+ void RotaryEncoder::tick (void ) {
119187 int sig1 = digitalRead (_pin1);
120188 int sig2 = digitalRead (_pin2);
121189 tick (sig1, sig2);
122- } // tick()
190+ } // tick()
191+
123192
124193// When a faster method than digitalRead is available you can _tick with the 2 values directly.
125- void RotaryEncoder::tick (int sig1, int sig2)
126- {
194+ void RotaryEncoder::tick (int sig1, int sig2) {
127195 unsigned long now = millis ();
128196 int8_t thisState = sig1 | (sig2 << 1 );
129197
@@ -132,44 +200,42 @@ void RotaryEncoder::tick(int sig1, int sig2)
132200 _oldState = thisState;
133201
134202 switch (_mode) {
135- case LatchMode::FOUR3:
136- if (thisState == LATCH3) {
137- // The hardware has 4 steps with a latch on the input state 3
138- _positionExt = _position >> 2 ;
139- _positionExtTimePrev = _positionExtTime;
140- _positionExtTime = now;
141- }
142- break ;
143-
144- case LatchMode::FOUR0:
145- if (thisState == LATCH0) {
146- // The hardware has 4 steps with a latch on the input state 0
147- _positionExt = _position >> 2 ;
148- _positionExtTimePrev = _positionExtTime;
149- _positionExtTime = now;
150- }
151- break ;
152-
153- case LatchMode::TWO03:
154- if ((thisState == LATCH0) || (thisState == LATCH3)) {
155- // The hardware has 2 steps with a latch on the input state 0 and 3
156- _positionExt = _position >> 1 ;
157- _positionExtTimePrev = _positionExtTime;
158- _positionExtTime = now;
159- }
160- break ;
161- } // switch
162- } // if
163- } // tick()
164-
165-
166- unsigned long RotaryEncoder::getMillisBetweenRotations () const
167- {
203+ case LatchMode::FOUR3:
204+ if (thisState == LATCH3) {
205+ // The hardware has 4 steps with a latch on the input state 3
206+ _positionExt = _position >> 2 ;
207+ _positionExtTimePrev = _positionExtTime;
208+ _positionExtTime = now;
209+ }
210+ break ;
211+
212+ case LatchMode::FOUR0:
213+ if (thisState == LATCH0) {
214+ // The hardware has 4 steps with a latch on the input state 0
215+ _positionExt = _position >> 2 ;
216+ _positionExtTimePrev = _positionExtTime;
217+ _positionExtTime = now;
218+ }
219+ break ;
220+
221+ case LatchMode::TWO03:
222+ if ((thisState == LATCH0) || (thisState == LATCH3)) {
223+ // The hardware has 2 steps with a latch on the input state 0 and 3
224+ _positionExt = _position >> 1 ;
225+ _positionExtTimePrev = _positionExtTime;
226+ _positionExtTime = now;
227+ }
228+ break ;
229+ } // switch
230+ } // if
231+ } // tick()
232+
233+
234+ unsigned long RotaryEncoder::getMillisBetweenRotations () const {
168235 return (_positionExtTime - _positionExtTimePrev);
169236}
170237
171- unsigned long RotaryEncoder::getRPM ()
172- {
238+ unsigned long RotaryEncoder::getRPM () {
173239 // calculate max of difference in time between last position changes or last change and now.
174240 unsigned long timeBetweenLastPositions = _positionExtTime - _positionExtTimePrev;
175241 unsigned long timeToLastPosition = millis () - _positionExtTime;
0 commit comments