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