Skip to content

Commit c8437bd

Browse files
committed
2 parents c2c5938 + a0d8627 commit c8437bd

File tree

3 files changed

+184
-55
lines changed

3 files changed

+184
-55
lines changed

RotaryEncoder.cpp

+15-6
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,23 @@ long RotaryEncoder::getPosition() {
5757
} // getPosition()
5858

5959

60-
int8_t RotaryEncoder::getDirection() {
60+
RotaryEncoder::Direction RotaryEncoder::getDirection() {
6161

62-
int8_t ret = 0;
62+
RotaryEncoder::Direction ret = Direction::NOROTATION;
6363

6464
if( _positionExtPrev > _positionExt )
6565
{
66-
ret = -1;
66+
ret = Direction::COUNTERCLOCKWISE;
6767
_positionExtPrev = _positionExt;
6868
}
6969
else if( _positionExtPrev < _positionExt )
7070
{
71-
ret = 1;
71+
ret = Direction::CLOCKWISE;
7272
_positionExtPrev = _positionExt;
7373
}
7474
else
7575
{
76-
ret = 0;
76+
ret = Direction::NOROTATION;
7777
_positionExtPrev = _positionExt;
7878
}
7979

@@ -99,11 +99,20 @@ void RotaryEncoder::tick(void)
9999
if (_oldState != thisState) {
100100
_position += KNOBDIR[thisState | (_oldState<<2)];
101101

102-
if (thisState == LATCHSTATE)
102+
if (thisState == LATCHSTATE) {
103103
_positionExt = _position >> 2;
104+
_positionExtTimePrev = _positionExtTime;
105+
_positionExtTime = millis();
106+
}
104107

105108
_oldState = thisState;
106109
} // if
107110
} // tick()
108111

112+
unsigned long RotaryEncoder::getMillisBetweenRotations() const
113+
{
114+
return _positionExtTime - _positionExtTimePrev;
115+
}
116+
117+
109118
// End

RotaryEncoder.h

+56-49
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,57 @@
1-
// -----
2-
// RotaryEncoder.h - Library for using rotary encoders.
3-
// This class is implemented for use with the Arduino environment.
4-
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
5-
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
6-
// More information on: http://www.mathertel.de/Arduino
7-
// -----
8-
// 18.01.2014 created by Matthias Hertel
9-
// 16.06.2019 pin initialization using INPUT_PULLUP
10-
// -----
11-
12-
#ifndef RotaryEncoder_h
13-
#define RotaryEncoder_h
14-
15-
#include "Arduino.h"
16-
17-
#define LATCHSTATE 3
18-
19-
class RotaryEncoder
20-
{
21-
public:
22-
// ----- Constructor -----
23-
RotaryEncoder(int pin1, int pin2);
24-
25-
// retrieve the current position
26-
long getPosition();
27-
28-
// simple retrieve of the direction the knob was rotated at. 0 = No rotation, 1 = Clockwise, -1 = Counter Clockwise
29-
int8_t getDirection();
30-
31-
// adjust the current position
32-
void setPosition(long newPosition);
33-
34-
// call this function every some milliseconds or by using an interrupt for handling state changes of the rotary encoder.
35-
void tick(void);
36-
37-
private:
38-
int _pin1, _pin2; // Arduino pins used for the encoder.
39-
40-
volatile int8_t _oldState;
41-
42-
volatile long _position; // Internal position (4 times _positionExt)
43-
volatile long _positionExt; // External position
44-
volatile long _positionExtPrev; // External position (used only for direction checking)
45-
46-
};
47-
48-
#endif
49-
1+
// -----
2+
// RotaryEncoder.h - Library for using rotary encoders.
3+
// This class is implemented for use with the Arduino environment.
4+
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
5+
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
6+
// More information on: http://www.mathertel.de/Arduino
7+
// -----
8+
// 18.01.2014 created by Matthias Hertel
9+
// 16.06.2019 pin initialization using INPUT_PULLUP
10+
// -----
11+
12+
#ifndef RotaryEncoder_h
13+
#define RotaryEncoder_h
14+
15+
#include "Arduino.h"
16+
17+
#define LATCHSTATE 3
18+
19+
class RotaryEncoder
20+
{
21+
public:
22+
enum class Direction { NOROTATION = 0, CLOCKWISE = 1, COUNTERCLOCKWISE = -1};
23+
24+
// ----- Constructor -----
25+
RotaryEncoder(int pin1, int pin2);
26+
27+
// retrieve the current position
28+
long getPosition();
29+
30+
// simple retrieve of the direction the knob was rotated at. 0 = No rotation, 1 = Clockwise, -1 = Counter Clockwise
31+
Direction getDirection();
32+
33+
// adjust the current position
34+
void setPosition(long newPosition);
35+
36+
// call this function every some milliseconds or by using an interrupt for handling state changes of the rotary encoder.
37+
void tick(void);
38+
39+
// Returns the time in milliseconds between the current observed
40+
unsigned long getMillisBetweenRotations() const;
41+
42+
private:
43+
int _pin1, _pin2; // Arduino pins used for the encoder.
44+
45+
volatile int8_t _oldState;
46+
47+
volatile long _position; // Internal position (4 times _positionExt)
48+
volatile long _positionExt; // External position
49+
volatile long _positionExtPrev; // External position (used only for direction checking)
50+
51+
unsigned long _positionExtTime; // The time the last position change was detected.
52+
unsigned long _positionExtTimePrev; // The time the previous position change was detected.
53+
};
54+
55+
#endif
56+
5057
// End
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// -----
2+
// AcceleratedRotator.ino - Example for the RotaryEncoder library.
3+
// This class is implemented for use with the Arduino environment.
4+
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
5+
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
6+
// More information on: http://www.mathertel.de/Arduino
7+
// -----
8+
// 18.01.2014 created by Matthias Hertel
9+
// 13.11.2019 converted to AcceleratedRotator by Damian Philipp
10+
// -----
11+
12+
// This example checks the state of the rotary encoder in the loop() function.
13+
// It then computes an acceleration value and prints the current position when changed.
14+
15+
// Hardware setup:
16+
// Attach a rotary encoder with output pins to A2 and A3.
17+
// The common contact should be attached to ground.
18+
19+
#include <RotaryEncoder.h>
20+
21+
// Setup a RoraryEncoder for pins A2 and A3:
22+
RotaryEncoder encoder(A2, A3);
23+
24+
// Define some constants.
25+
// at 500ms, there should be no acceleration.
26+
constexpr const unsigned long kAccelerationLongCutoffMillis = 500;
27+
// at 4ms, we want to have maximum acceleration
28+
constexpr const unsigned long kAccelerationShortCutffMillis = 4;
29+
// linear acceleration: incline
30+
constexpr static const float m = -0.16;
31+
// linear acceleration: y offset
32+
constexpr static const float c = 84.03;
33+
34+
/* To derive these constants, compute as follows:
35+
* * On an x-y-plane, let x be the time between rotations.
36+
* * On an x-y-plane, let y be the accelerated number of rotations.
37+
* * Select a long cuttoff. If two encoder rotations happen longer apart than the cutoff,
38+
* they are not accelerated anymore. Without the long cutoff, not moving the encoder for a while
39+
* will make the value jump by extreme amounts (possibly in the wrong direction) on the next rotation.
40+
* * Select a short cutoff. This limits the maximum acceleration. While an infinite acceleration is
41+
* not really a problem, it is unrealistic to achieve (how quickly can you tick the encoder?) and
42+
* not having it causes headaches when computing the actual acceleration ;)
43+
* Pick two points defining your acceleration. At x2=(long cutoff), you want y2=1 (No acceleration happened).
44+
* At x1=(short cutoff) you want y1=(maximum accelerated number of ticks).
45+
* Then, compute m and c using high school math (or google for "straight line through two points").
46+
*
47+
* The numbers given in this example are tailored for the following conditions:
48+
* * An encoder with 24 increments per 360 degrees
49+
* * A useful range of 0..1000 rotations
50+
* * Making a 180 degree rotation (12 increments) on the encoder within 50ms will hit
51+
* the opposite end of the range.
52+
*
53+
* Please do not rely on these exact numbers. Recompute them for your usecase and verify
54+
* them with a physical test. A 4ms timing is fairly tight for many controllers. Depending on your
55+
* controller and application, you might not be able to sample the encoder quickly enough to achieve this.
56+
*/
57+
58+
void setup()
59+
{
60+
Serial.begin(57600);
61+
Serial.println("AcceleratedRotator example for the RotaryEncoder library.");
62+
} // setup()
63+
64+
65+
// Read the current position of the encoder and print out when changed.
66+
void loop()
67+
{
68+
static int pos = 0;
69+
static RotaryEncoder::Direction lastMovementDirection = RotaryEncoder::Direction::NOROTATION;
70+
encoder.tick();
71+
72+
int newPos = encoder.getPosition();
73+
if (pos != newPos) {
74+
75+
// compute linear acceleration
76+
RotaryEncoder::Direction currentDirection = encoder.getDirection();
77+
if (currentDirection == lastMovementDirection &&
78+
currentDirection != RotaryEncoder::Direction::NOROTATION &&
79+
lastMovementDirection != RotaryEncoder::Direction::NOROTATION) {
80+
// ... but only of the direction of rotation matched and there
81+
// actually was a previous rotation.
82+
unsigned long deltat = encoder.getMillisBetweenRotations();
83+
84+
if (deltat < kAccelerationLongCutoffMillis) {
85+
if (deltat < kAccelerationShortCutffMillis) {
86+
// limit to maximum acceleration
87+
deltat = kAccelerationShortCutffMillis;
88+
}
89+
90+
float ticksActual_float = m * deltat + c;
91+
// Round by adding 1
92+
// Then again remove 1 to determine the actual delta to the encoder
93+
// value, as the encoder already ticked by 1 tick in the correct
94+
// direction. Thus, just cast to an integer type.
95+
long deltaTicks = (long)ticksActual_float;
96+
97+
// Adjust sign: Needs to be inverted for counterclockwise operation
98+
if (currentDirection == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
99+
deltaTicks = -(deltaTicks);
100+
}
101+
102+
newPos = newPos + deltaTicks;
103+
encoder.setPosition(newPos);
104+
}
105+
}
106+
107+
Serial.print(newPos);
108+
Serial.println();
109+
pos = newPos;
110+
} // if
111+
} // loop ()
112+
113+
// The End

0 commit comments

Comments
 (0)