Skip to content

Commit 434dc95

Browse files
committed
Update changelog and improve documentation for RotaryEncoder constructors
1 parent 82a354c commit 434dc95

4 files changed

Lines changed: 194 additions & 98 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
All notable changes to this project will be documented in this file starting 2021.
44

5-
## [1.6.0] -- 2025-10-25
5+
## [1.6.0] -- 2026-02-21
6+
7+
* Added inline documentation to `RotaryEncoder.cpp` explaining encoder initialization and tick processing
8+
* New constructor `RotaryEncoder(LatchMode mode)` for software-only initialization without hardware pin configuration.
9+
* See pull request #45 and #47
610

711
* New since 1.5.2
812

913
* ADD: Optional debounce support (setDebounceMillis()) to filter contact bounce without external components.
1014
* ADD: New example sketches: RotaryWithButton and AcceleratedRotatorAdvanced (showing debounce and acceleration together).
1115
* CI: Expanded unit/integration tests and updated CI configuration for newer Arduino cores and PlatformIO.
1216
* DOCS: Updated README and examples for IDE 2.x and core compatibility; clarified LatchMode behavior.
17+
* Added ESP32 support with default pins in examples
1318

1419
* Fixes and improvements since 1.5.2
1520

@@ -18,6 +23,7 @@ All notable changes to this project will be documented in this file starting 202
1823
* FIX: Reduced CPU usage in ISR and polling paths (lower interrupt overhead).
1924
* CHANGE: Improved API stability notes and migration guide in README.
2025

26+
* copilot-instructions.md added
2127

2228
## [1.5.2] - 2021-07-04
2329

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ A library for the Arduino environment for using a rotary encoder as an input.
77

88
Here you can find an Arduino compatible library for using rotary encoders.
99

10-
When I was searching a library for using a rotary encoder in my latest project and found a lot of information on this topic but none of the existing libraries did immediately match my expectations so I finally built my own.
10+
When I was searching a library for using a rotary encoder in my latest project and found
11+
a lot of information on this topic but none of the existing libraries did immediately
12+
match my expectations so I finally built my own.
1113

12-
It supports the type of rotary encoder that has a phase change on both input signals when rotating one `notch`.
14+
It supports the type of rotary encoder that has a phase change on both input signals
15+
when rotating one `notch`.
1316

14-
The article on my web site explains the software mechanisms used in detail so you can understand
15-
the coding and might be able to adjust it to your needs if you like:
17+
The article on my web site explains the software mechanisms used in detail so you can
18+
understand the coding and might be able to adjust it to your needs if you like:
1619

1720
<http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx>
1821

19-
There are various aspects when writing a library for rotary encoders and you can also find a lot of the sources I analyzed at the bottom of the article.
22+
There are various aspects when writing a library for rotary encoders and you can also
23+
find a lot of the sources I analyzed at the bottom of the article.

src/RotaryEncoder.cpp

Lines changed: 144 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@
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

2626
const 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

Comments
 (0)