You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
10 KiB
435 lines
10 KiB
///////////////////////////////////////////////////////////////// |
|
/* |
|
ESP8266/Arduino Library for reading rotary encoder values. |
|
Copyright 2017-2022 Lennart Hennigs. |
|
*/ |
|
///////////////////////////////////////////////////////////////// |
|
|
|
#include "ESPRotary.h" |
|
|
|
///////////////////////////////////////////////////////////////// |
|
// initialize static counter |
|
|
|
int ESPRotary::_nextID = 0; |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::_setID() |
|
{ |
|
id = _nextID; |
|
_nextID++; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
ESPRotary::ESPRotary() |
|
{ |
|
_setID(); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
ESPRotary::ESPRotary(byte pin1, byte pin2, byte steps_per_click /* = 1 */, int lower_bound /* = INT16_MIN */, int upper_bound /* = INT16_MAX */, int inital_pos /* = 0 */, int increment /* = 1 */) |
|
{ |
|
ESPRotary(); |
|
begin(pin1, pin2, steps_per_click, lower_bound, upper_bound, inital_pos, increment); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::begin(byte pin1, byte pin2, byte steps_per_click /* = 1 */, int lower_bound /* = INT16_MIN */, int upper_bound /* = INT16_MAX */, int inital_pos /* = 0 */, int increment /* = 1 */) |
|
{ |
|
this->pin1 = pin1; |
|
this->pin2 = pin2; |
|
pinMode(pin1, INPUT_PULLUP); |
|
pinMode(pin2, INPUT_PULLUP); |
|
|
|
setUpperBound(upper_bound); |
|
setLowerBound(lower_bound); |
|
setIncrement(increment); |
|
setStepsPerClick(steps_per_click); |
|
|
|
loop(); |
|
steps = inital_pos * steps_per_click; |
|
last_event = rotary_event::none; |
|
dir = rotary_direction::undefined; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setUpperBound(int upper) |
|
{ |
|
upper_bound = (lower_bound < upper) ? upper : lower_bound; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setLowerBound(int lower) |
|
{ |
|
lower_bound = (lower < upper_bound) ? lower : upper_bound; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getUpperBound() const |
|
{ |
|
return upper_bound; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getLowerBound() const |
|
{ |
|
return lower_bound; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setChangedHandler(CallbackFunction f) |
|
{ |
|
change_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setRightRotationHandler(CallbackFunction f) |
|
{ |
|
right_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setLeftRotationHandler(CallbackFunction f) |
|
{ |
|
left_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setUpperOverflowHandler(CallbackFunction f) |
|
{ |
|
upper_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setLowerOverflowHandler(CallbackFunction f) |
|
{ |
|
lower_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setSpeedupStartedHandler(CallbackFunction f) |
|
{ |
|
speedup_start_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setSpeedupEndedHandler(CallbackFunction f) |
|
{ |
|
speedup_end_cb = f; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::resetPosition(int p /* = 0 */, bool fireCallback /* = true */) |
|
{ |
|
// change position? |
|
if (p == getPosition()) |
|
return; |
|
// yes... |
|
steps = p * steps_per_click; |
|
_isWithinBounds(); |
|
if (fireCallback) |
|
_callCallback(change_cb); |
|
last_event = rotary_event::none; |
|
dir = rotary_direction::undefined; |
|
in_speedup = false; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setIncrement(int increment) |
|
{ |
|
this->increment = increment; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getIncrement() const |
|
{ |
|
return increment; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setStepsPerClick(int steps) |
|
{ |
|
steps_per_click = (steps < 1) ? 1 : steps; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getStepsPerClick() const |
|
{ |
|
return steps_per_click; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
rotary_direction ESPRotary::getDirection() const |
|
{ |
|
return dir; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
String ESPRotary::directionToString(rotary_direction dir) const |
|
{ |
|
return (dir == rotary_direction::right) ? "right" : "left"; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getPosition() const |
|
{ |
|
return steps / steps_per_click; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getID() const |
|
{ |
|
return id; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setID(int newID) |
|
{ |
|
id = newID; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
bool ESPRotary::operator==(ESPRotary &rhs) |
|
{ |
|
return (this == &rhs); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::loop() |
|
{ |
|
unsigned long now = millis(); |
|
// did it change (enough)? |
|
if (!_wasRotated()) |
|
return; |
|
dir = (steps > last_steps) ? rotary_direction::right : rotary_direction::left; |
|
// shall I speedup things |
|
if (enable_speedup) |
|
_checkForSpeedup(now); |
|
// are we out of bounds? |
|
if (_isWithinBounds(true)) |
|
{ |
|
// trigger rotation event |
|
_setEvent((dir == rotary_direction::right) ? rotary_event::right_rotation : rotary_event::left_rotation); |
|
last_turn = now; |
|
} |
|
last_steps = steps; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
bool ESPRotary::_wasRotated() |
|
{ |
|
static const int8_t factors[] = {0, 1, -1, 2, -1, 0, -2, 1, 1, -2, 0, -1, 2, -1, 1, 0}; |
|
int encoderState = (state & 3) | digitalRead(pin1) << 2 | digitalRead(pin2) << 3; |
|
steps += factors[encoderState] * increment; |
|
state = (encoderState >> 2); |
|
int stepDifference = abs(steps - last_steps); |
|
return stepDifference >= (steps_per_click * increment); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::_checkForSpeedup(unsigned long now) |
|
{ |
|
if (now - last_turn > speedup_interval) |
|
{ |
|
if (in_speedup) |
|
_setEvent(rotary_event::speedup_ended); |
|
return; |
|
} |
|
steps += ((dir == rotary_direction::right ? 1 : -1) * (speedup_increment - increment) * steps_per_click); |
|
int pos = getPosition(); |
|
// only trigger speedup when you are not "on a wall" |
|
if (pos > lower_bound && pos < upper_bound) |
|
{ |
|
if (!in_speedup) |
|
_setEvent(rotary_event::speedup_started); |
|
} |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::triggerOnBounds(bool triggerEvents /* = true */) |
|
{ |
|
boundsTrigger = triggerEvents; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::_callCallback(CallbackFunction callback) |
|
{ |
|
if (callback != NULL) |
|
callback(*this); |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
bool ESPRotary::_isWithinBounds(bool triggerAlerts /* = false */) |
|
{ |
|
int pos = getPosition(); |
|
if (pos > lower_bound && pos < upper_bound) |
|
return true; |
|
|
|
if (pos >= upper_bound) |
|
{ |
|
steps = upper_bound * steps_per_click; |
|
if (in_speedup) |
|
_setEvent(rotary_event::speedup_ended); |
|
if (triggerAlerts) |
|
_setEvent(rotary_event::upper_bound_hit); |
|
} |
|
else if (pos <= lower_bound) |
|
{ |
|
steps = lower_bound * steps_per_click; |
|
if (in_speedup) |
|
_setEvent(rotary_event::speedup_ended); |
|
if (triggerAlerts) |
|
_setEvent(rotary_event::lower_bound_hit); |
|
} |
|
|
|
return false; |
|
} |
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::_setEvent(rotary_event event) |
|
{ |
|
switch (event) |
|
{ |
|
case rotary_event::left_rotation: |
|
_callCallback(left_cb); |
|
_callCallback(change_cb); |
|
break; |
|
|
|
case rotary_event::right_rotation: |
|
_callCallback(right_cb); |
|
_callCallback(change_cb); |
|
break; |
|
|
|
case rotary_event::speedup_started: |
|
_callCallback(speedup_start_cb); |
|
in_speedup = true; |
|
break; |
|
|
|
case rotary_event::speedup_ended: |
|
_callCallback(speedup_end_cb); |
|
in_speedup = false; |
|
break; |
|
|
|
case rotary_event::upper_bound_hit: |
|
if (last_event == rotary_event::upper_bound_hit && !retrigger_event) |
|
return; |
|
if (boundsTrigger) |
|
{ |
|
_callCallback(right_cb); |
|
_callCallback(change_cb); |
|
} |
|
_callCallback(upper_cb); |
|
break; |
|
|
|
case rotary_event::lower_bound_hit: |
|
if (last_event == rotary_event::lower_bound_hit && !retrigger_event) |
|
return; |
|
if (boundsTrigger) |
|
{ |
|
_callCallback(left_cb); |
|
_callCallback(change_cb); |
|
} |
|
_callCallback(lower_cb); |
|
break; |
|
|
|
case rotary_event::none: |
|
break; |
|
} |
|
last_event = event; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setSpeedupInterval(int interval) |
|
{ |
|
speedup_interval = interval; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getSpeedupInterval() const |
|
{ |
|
return speedup_interval; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::setSpeedupIncrement(int increment) |
|
{ |
|
speedup_increment = increment; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
int ESPRotary::getSpeedupIncrement() const |
|
{ |
|
return speedup_increment; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::enableSpeedup(bool enable) |
|
{ |
|
enable_speedup = enable; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
bool ESPRotary::isSpeedupEnabled() const |
|
{ |
|
return enable_speedup; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
rotary_event ESPRotary::getLastEvent() const |
|
{ |
|
return last_event; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
void ESPRotary::retriggerEvent(bool retrigger) |
|
{ |
|
retrigger_event = retrigger; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////// |
|
|
|
bool ESPRotary::isInSpeedup() const |
|
{ |
|
return in_speedup; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|