|
7 | 7 | // -----
|
8 | 8 | // 18.01.2014 created by Matthias Hertel
|
9 | 9 | // 13.11.2019 converted to AcceleratedRotator by Damian Philipp
|
| 10 | +// 06.02.2021 conditions and settings added for ESP8266 |
10 | 11 | // -----
|
11 | 12 |
|
12 | 13 | // This example checks the state of the rotary encoder in the loop() function.
|
13 | 14 | // It then computes an acceleration value and prints the current position when changed.
|
| 15 | +// There is detailed output given to the Serial. |
| 16 | +// You may play around with the constants to fit to your needs. |
14 | 17 |
|
15 | 18 | // Hardware setup:
|
16 |
| -// Attach a rotary encoder with output pins to A2 and A3. |
| 19 | +// Attach a rotary encoder with output pins to |
| 20 | +// * 2 and 3 on Arduino UNO. |
| 21 | +// * A2 and A3 can be used when directly using the ISR interrupts, see comments below. |
| 22 | +// * D5 and D6 on ESP8266 board (e.g. NodeMCU). |
| 23 | +// Swap the pins when direction is detected wrong. |
17 | 24 | // The common contact should be attached to ground.
|
18 | 25 |
|
| 26 | +#include <Arduino.h> |
19 | 27 | #include <RotaryEncoder.h>
|
20 | 28 |
|
21 |
| -// Setup a RoraryEncoder for pins A2 and A3: |
22 |
| -RotaryEncoder encoder(A2, A3); |
| 29 | +#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY) |
| 30 | +// Example for Arduino UNO with input signals on pin 2 and 3 |
| 31 | +#define PIN_IN1 2 |
| 32 | +#define PIN_IN2 3 |
| 33 | + |
| 34 | +#elif defined(ESP8266) |
| 35 | +// Example for ESP8266 NodeMCU with input signals on pin D5 and D6 |
| 36 | +#define PIN_IN1 D5 |
| 37 | +#define PIN_IN2 D6 |
| 38 | + |
| 39 | +#endif |
| 40 | + |
| 41 | +// Setup a RotaryEncoder with 4 steps per latch for the 2 signal input pins: |
| 42 | +// RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::FOUR3); |
| 43 | + |
| 44 | +// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins: |
| 45 | +RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03); |
23 | 46 |
|
24 | 47 | // 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 |
| - */ |
| 48 | + |
| 49 | +// the maximum acceleration is 10 times. |
| 50 | +constexpr float m = 10; |
| 51 | + |
| 52 | +// at 200ms or slower, there should be no acceleration. (factor 1) |
| 53 | +constexpr float longCutoff = 50; |
| 54 | + |
| 55 | +// at 5 ms, we want to have maximum acceleration (factor m) |
| 56 | +constexpr float shortCutoff = 5; |
| 57 | + |
| 58 | +// To derive the calc. constants, compute as follows: |
| 59 | +// On an x(ms) - y(factor) plane resolve a linear formular factor(ms) = a * ms + b; |
| 60 | +// where f(4)=10 and f(200)=1 |
| 61 | + |
| 62 | +constexpr float a = (m - 1) / (shortCutoff - longCutoff); |
| 63 | +constexpr float b = 1 - longCutoff * a; |
| 64 | + |
| 65 | +// a global variables to hold the last position |
| 66 | +static int lastPos = 0; |
57 | 67 |
|
58 | 68 | void setup()
|
59 | 69 | {
|
60 |
| - Serial.begin(57600); |
| 70 | + Serial.begin(115200); |
| 71 | + while (!Serial) |
| 72 | + ; |
| 73 | + |
61 | 74 | Serial.println("AcceleratedRotator example for the RotaryEncoder library.");
|
| 75 | + Serial.print("a="); |
| 76 | + Serial.println(a); |
| 77 | + Serial.print("b="); |
| 78 | + Serial.println(b); |
62 | 79 | } // setup()
|
63 | 80 |
|
64 | 81 |
|
65 | 82 | // Read the current position of the encoder and print out when changed.
|
66 | 83 | void loop()
|
67 | 84 | {
|
68 |
| - static int pos = 0; |
69 |
| - static RotaryEncoder::Direction lastMovementDirection = RotaryEncoder::Direction::NOROTATION; |
70 | 85 | encoder.tick();
|
71 | 86 |
|
72 | 87 | 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 |
| - lastMovementDirection = currentDirection; |
| 88 | + if (lastPos != newPos) { |
| 89 | + |
| 90 | + // accelerate when there was a previous rotation in the same direction. |
| 91 | + |
| 92 | + unsigned long ms = encoder.getMillisBetweenRotations(); |
| 93 | + |
| 94 | + if (ms < longCutoff) { |
| 95 | + // do some acceleration using factors a and b |
| 96 | + |
| 97 | + // limit to maximum acceleration |
| 98 | + if (ms < shortCutoff) { |
| 99 | + ms = shortCutoff; |
105 | 100 | }
|
| 101 | + |
| 102 | + float ticksActual_float = a * ms + b; |
| 103 | + Serial.print(" f= "); |
| 104 | + Serial.println(ticksActual_float); |
| 105 | + |
| 106 | + long deltaTicks = (long)ticksActual_float * (newPos - lastPos); |
| 107 | + Serial.print(" d= "); |
| 108 | + Serial.println(deltaTicks); |
| 109 | + |
| 110 | + newPos = newPos + deltaTicks; |
| 111 | + encoder.setPosition(newPos); |
106 | 112 | }
|
107 | 113 |
|
108 | 114 | Serial.print(newPos);
|
109 |
| - Serial.println(); |
110 |
| - pos = newPos; |
| 115 | + Serial.print(" ms: "); |
| 116 | + Serial.println(ms); |
| 117 | + lastPos = newPos; |
111 | 118 | } // if
|
112 | 119 | } // loop ()
|
113 | 120 |
|
|
0 commit comments