Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mathertel committed Nov 13, 2019
2 parents c2c5938 + a0d8627 commit c8437bd
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 55 deletions.
21 changes: 15 additions & 6 deletions RotaryEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,23 @@ long RotaryEncoder::getPosition() {
} // getPosition()


int8_t RotaryEncoder::getDirection() {
RotaryEncoder::Direction RotaryEncoder::getDirection() {

int8_t ret = 0;
RotaryEncoder::Direction ret = Direction::NOROTATION;

if( _positionExtPrev > _positionExt )
{
ret = -1;
ret = Direction::COUNTERCLOCKWISE;
_positionExtPrev = _positionExt;
}
else if( _positionExtPrev < _positionExt )
{
ret = 1;
ret = Direction::CLOCKWISE;
_positionExtPrev = _positionExt;
}
else
{
ret = 0;
ret = Direction::NOROTATION;
_positionExtPrev = _positionExt;
}

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

if (thisState == LATCHSTATE)
if (thisState == LATCHSTATE) {
_positionExt = _position >> 2;
_positionExtTimePrev = _positionExtTime;
_positionExtTime = millis();
}

_oldState = thisState;
} // if
} // tick()

unsigned long RotaryEncoder::getMillisBetweenRotations() const
{
return _positionExtTime - _positionExtTimePrev;
}


// End
105 changes: 56 additions & 49 deletions RotaryEncoder.h
Original file line number Diff line number Diff line change
@@ -1,50 +1,57 @@
// -----
// RotaryEncoder.h - Library for using rotary encoders.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// 16.06.2019 pin initialization using INPUT_PULLUP
// -----

#ifndef RotaryEncoder_h
#define RotaryEncoder_h

#include "Arduino.h"

#define LATCHSTATE 3

class RotaryEncoder
{
public:
// ----- Constructor -----
RotaryEncoder(int pin1, int pin2);

// retrieve the current position
long getPosition();

// simple retrieve of the direction the knob was rotated at. 0 = No rotation, 1 = Clockwise, -1 = Counter Clockwise
int8_t getDirection();

// adjust the current position
void setPosition(long newPosition);

// call this function every some milliseconds or by using an interrupt for handling state changes of the rotary encoder.
void tick(void);

private:
int _pin1, _pin2; // Arduino pins used for the encoder.

volatile int8_t _oldState;

volatile long _position; // Internal position (4 times _positionExt)
volatile long _positionExt; // External position
volatile long _positionExtPrev; // External position (used only for direction checking)

};

#endif

// -----
// RotaryEncoder.h - Library for using rotary encoders.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// 16.06.2019 pin initialization using INPUT_PULLUP
// -----

#ifndef RotaryEncoder_h
#define RotaryEncoder_h

#include "Arduino.h"

#define LATCHSTATE 3

class RotaryEncoder
{
public:
enum class Direction { NOROTATION = 0, CLOCKWISE = 1, COUNTERCLOCKWISE = -1};

// ----- Constructor -----
RotaryEncoder(int pin1, int pin2);

// retrieve the current position
long getPosition();

// simple retrieve of the direction the knob was rotated at. 0 = No rotation, 1 = Clockwise, -1 = Counter Clockwise
Direction getDirection();

// adjust the current position
void setPosition(long newPosition);

// call this function every some milliseconds or by using an interrupt for handling state changes of the rotary encoder.
void tick(void);

// Returns the time in milliseconds between the current observed
unsigned long getMillisBetweenRotations() const;

private:
int _pin1, _pin2; // Arduino pins used for the encoder.

volatile int8_t _oldState;

volatile long _position; // Internal position (4 times _positionExt)
volatile long _positionExt; // External position
volatile long _positionExtPrev; // External position (used only for direction checking)

unsigned long _positionExtTime; // The time the last position change was detected.
unsigned long _positionExtTimePrev; // The time the previous position change was detected.
};

#endif

// End
113 changes: 113 additions & 0 deletions examples/AcceleratedRotator/AcceleratedRotator.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// -----
// AcceleratedRotator.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// 13.11.2019 converted to AcceleratedRotator by Damian Philipp
// -----

// This example checks the state of the rotary encoder in the loop() function.
// It then computes an acceleration value and prints the current position when changed.

// Hardware setup:
// Attach a rotary encoder with output pins to A2 and A3.
// The common contact should be attached to ground.

#include <RotaryEncoder.h>

// Setup a RoraryEncoder for pins A2 and A3:
RotaryEncoder encoder(A2, A3);

// Define some constants.
// at 500ms, there should be no acceleration.
constexpr const unsigned long kAccelerationLongCutoffMillis = 500;
// at 4ms, we want to have maximum acceleration
constexpr const unsigned long kAccelerationShortCutffMillis = 4;
// linear acceleration: incline
constexpr static const float m = -0.16;
// linear acceleration: y offset
constexpr static const float c = 84.03;

/* To derive these constants, compute as follows:
* * On an x-y-plane, let x be the time between rotations.
* * On an x-y-plane, let y be the accelerated number of rotations.
* * Select a long cuttoff. If two encoder rotations happen longer apart than the cutoff,
* they are not accelerated anymore. Without the long cutoff, not moving the encoder for a while
* will make the value jump by extreme amounts (possibly in the wrong direction) on the next rotation.
* * Select a short cutoff. This limits the maximum acceleration. While an infinite acceleration is
* not really a problem, it is unrealistic to achieve (how quickly can you tick the encoder?) and
* not having it causes headaches when computing the actual acceleration ;)
* Pick two points defining your acceleration. At x2=(long cutoff), you want y2=1 (No acceleration happened).
* At x1=(short cutoff) you want y1=(maximum accelerated number of ticks).
* Then, compute m and c using high school math (or google for "straight line through two points").
*
* The numbers given in this example are tailored for the following conditions:
* * An encoder with 24 increments per 360 degrees
* * A useful range of 0..1000 rotations
* * Making a 180 degree rotation (12 increments) on the encoder within 50ms will hit
* the opposite end of the range.
*
* Please do not rely on these exact numbers. Recompute them for your usecase and verify
* them with a physical test. A 4ms timing is fairly tight for many controllers. Depending on your
* controller and application, you might not be able to sample the encoder quickly enough to achieve this.
*/

void setup()
{
Serial.begin(57600);
Serial.println("AcceleratedRotator example for the RotaryEncoder library.");
} // setup()


// Read the current position of the encoder and print out when changed.
void loop()
{
static int pos = 0;
static RotaryEncoder::Direction lastMovementDirection = RotaryEncoder::Direction::NOROTATION;
encoder.tick();

int newPos = encoder.getPosition();
if (pos != newPos) {

// compute linear acceleration
RotaryEncoder::Direction currentDirection = encoder.getDirection();
if (currentDirection == lastMovementDirection &&
currentDirection != RotaryEncoder::Direction::NOROTATION &&
lastMovementDirection != RotaryEncoder::Direction::NOROTATION) {
// ... but only of the direction of rotation matched and there
// actually was a previous rotation.
unsigned long deltat = encoder.getMillisBetweenRotations();

if (deltat < kAccelerationLongCutoffMillis) {
if (deltat < kAccelerationShortCutffMillis) {
// limit to maximum acceleration
deltat = kAccelerationShortCutffMillis;
}

float ticksActual_float = m * deltat + c;
// Round by adding 1
// Then again remove 1 to determine the actual delta to the encoder
// value, as the encoder already ticked by 1 tick in the correct
// direction. Thus, just cast to an integer type.
long deltaTicks = (long)ticksActual_float;

// Adjust sign: Needs to be inverted for counterclockwise operation
if (currentDirection == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
deltaTicks = -(deltaTicks);
}

newPos = newPos + deltaTicks;
encoder.setPosition(newPos);
}
}

Serial.print(newPos);
Serial.println();
pos = newPos;
} // if
} // loop ()

// The End

0 comments on commit c8437bd

Please sign in to comment.