From d303a57f3c9912a7c414680c97682ed184ee0b74 Mon Sep 17 00:00:00 2001 From: sabeechen Date: Mon, 30 Apr 2018 14:21:04 -0700 Subject: [PATCH] Added polling and multiple encoder support. Added: -polling support -multiple encoder support -a better readme --- .gitignore | 1 + examples/encoder_basic/encoder_basic.ino | 36 +++++++++++++ .../encoder_without_interrupts.ino | 36 +++++++++++++ .../multiple_encoders/multiple_encoders.ino | 43 +++++++++++++++ src/RotaryEncoder.cpp | 53 +++++++++++++++---- src/RotaryEncoder.h | 50 ++++++++++++++++- 6 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 examples/encoder_basic/encoder_basic.ino create mode 100644 examples/encoder_without_interrupts/encoder_without_interrupts.ino create mode 100644 examples/multiple_encoders/multiple_encoders.ino diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/examples/encoder_basic/encoder_basic.ino b/examples/encoder_basic/encoder_basic.ino new file mode 100644 index 0000000..16b1620 --- /dev/null +++ b/examples/encoder_basic/encoder_basic.ino @@ -0,0 +1,36 @@ +#include + +#define ENCODER_PIN1 13 +#define ENCODER_PIN2 14 +#define BUTTON_PIN 12 + +RotaryEncoder encoder(ENCODER_PIN1, ENCODER_PIN2, BUTTON_PIN); + +void buttonDown() { + Serial.println("button down"); +} + +void buttonUp() { + Serial.println("button up"); +} + +void movedClockwise() { + Serial.println("moved clockwise"); +} + +void movedCounterClockwise() { + Serial.println("moved counter-clockwise"); +} + +void setup() { + Serial.begin(115200); + encoder.setup(); + encoder.setupInterrupts(); + encoder.setButtonDownCallback(buttonDown); + encoder.setButtonUpCallback(buttonUp); + encoder.setRotateCWCallback(movedClockwise); + encoder.setRotateCCWCallback(movedCounterClockwise); +} + +void loop() { +} \ No newline at end of file diff --git a/examples/encoder_without_interrupts/encoder_without_interrupts.ino b/examples/encoder_without_interrupts/encoder_without_interrupts.ino new file mode 100644 index 0000000..263d21b --- /dev/null +++ b/examples/encoder_without_interrupts/encoder_without_interrupts.ino @@ -0,0 +1,36 @@ +#include + +#define ENCODER_PIN1 13 +#define ENCODER_PIN2 14 +#define BUTTON_PIN 12 + +RotaryEncoder encoder(ENCODER_PIN1, ENCODER_PIN2, BUTTON_PIN); + +void buttonDown() { + Serial.println("button down"); +} + +void buttonUp() { + Serial.println("button up"); +} + +void movedClockwise() { + Serial.println("moved clockwise"); +} + +void movedCounterClockwise() { + Serial.println("moved counter-clockwise"); +} + +void setup() { + Serial.begin(115200); + encoder.setup(); + encoder.setButtonDownCallback(buttonDown); + encoder.setButtonUpCallback(buttonUp); + encoder.setRotateCWCallback(movedClockwise); + encoder.setRotateCCWCallback(movedCounterClockwise); +} + +void loop() { + encoder.loop(); +} \ No newline at end of file diff --git a/examples/multiple_encoders/multiple_encoders.ino b/examples/multiple_encoders/multiple_encoders.ino new file mode 100644 index 0000000..c29d916 --- /dev/null +++ b/examples/multiple_encoders/multiple_encoders.ino @@ -0,0 +1,43 @@ +#include + +#define ENCODER1_PIN1 13 +#define ENCODER1_PIN2 14 + +#define ENCODER2_PIN1 12 +#define ENCODER2_PIN2 11 + +RotaryEncoder encoder1(ENCODER1_PIN1, ENCODER1_PIN2); +RotaryEncoder encoder2(ENCODER2_PIN1, ENCODER2_PIN2); + +void movedClockwise() { + Serial.println("moved clockwise"); +} + +void movedCounterClockwise() { + Serial.println("moved counter-clockwise"); +} + +void encoder1Interrupt() { + encoder1.loop(); +} + +void encoder2Interrupt() { + encoder2.loop(); +} + +void setup() { + Serial.begin(115200); + + encoder1.setup(); + encoder1.setupInterrupts(encoder1Interrupt); + encoder1.setRotateCWCallback(movedClockwise); + encoder1.setRotateCCWCallback(movedCounterClockwise); + + encoder2.setup(); + encoder2.setupInterrupts(encoder2Interrupt); + encoder2.setRotateCWCallback(movedClockwise); + encoder2.setRotateCCWCallback(movedCounterClockwise); +} + +void loop() { +} \ No newline at end of file diff --git a/src/RotaryEncoder.cpp b/src/RotaryEncoder.cpp index d0b89af..fa4db62 100644 --- a/src/RotaryEncoder.cpp +++ b/src/RotaryEncoder.cpp @@ -2,7 +2,7 @@ RotaryEncoder* theEncoder; -void rotaryEncoderIsr() { +void veryHackyRotaryEncoderIsr() { theEncoder->loop(); } @@ -12,18 +12,39 @@ RotaryEncoder::RotaryEncoder(int rotaryPin1, int rotaryPin2, int buttonPin) { buttonpin = buttonPin; } +RotaryEncoder::RotaryEncoder(int rotaryPin1, int rotaryPin2) { + rotarypin1 = rotaryPin1; + rotarypin2 = rotaryPin2; + buttonpin = -1; +} + void RotaryEncoder::setup() { pinMode(rotarypin1, INPUT_PULLUP); pinMode(rotarypin2, INPUT_PULLUP); pinMode(buttonpin, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(rotarypin1), rotaryEncoderIsr, CHANGE); - attachInterrupt(digitalPinToInterrupt(rotarypin2), rotaryEncoderIsr, CHANGE); - attachInterrupt(digitalPinToInterrupt(buttonpin), rotaryEncoderIsr, CHANGE); - buttonWasDown = digitalRead(buttonpin) == LOW; - encoderLastState = (digitalRead(rotarypin1) << 1) | digitalRead(rotarypin2); + if (buttonpin >= 0) { + buttonWasDown = digitalRead(buttonpin) == LOW; + } + if (rotarypin1 >=0 && rotarypin2 >= 0) { + encoderLastState = (digitalRead(rotarypin1) << 1) | digitalRead(rotarypin2); + } +} + +void RotaryEncoder::setupInterrupts() { + setupInterrupts(veryHackyRotaryEncoderIsr); theEncoder = this; } +void RotaryEncoder::setupInterrupts(void (*cb)()) { + if (rotarypin1 >=0 && rotarypin2 >= 0) { + attachInterrupt(digitalPinToInterrupt(rotarypin1), cb, CHANGE); + attachInterrupt(digitalPinToInterrupt(rotarypin2), cb, CHANGE); + } + if (buttonpin >= 0) { + attachInterrupt(digitalPinToInterrupt(buttonpin), cb, CHANGE); + } +} + void RotaryEncoder::setButtonDownCallback(void (*cb)()) { buttondowncb = cb; } @@ -40,13 +61,18 @@ void RotaryEncoder::setRotateCCWCallback(void (*cb)()) { ccwcb = cb; } +bool RotaryEncoder::buttonIsDown() { + return buttonWasDown; +} + void RotaryEncoder::loop() { - if ((digitalRead(buttonpin) == LOW) != buttonWasDown) { + if (buttonpin >= 0 && (digitalRead(buttonpin) == LOW) != buttonWasDown) { if (buttonWasDown && buttonupcb != NULL) buttonupcb(); else if (buttondowncb != NULL) buttondowncb(); buttonWasDown = !buttonWasDown; } + if (rotarypin1 < 0 || rotarypin2 < 0) return; int rotaryState = (digitalRead(rotarypin1) << 1) | digitalRead(rotarypin2); if (encoderLastState != rotaryState) { if (pattern[encoderLastState] == rotaryState @@ -55,14 +81,23 @@ void RotaryEncoder::loop() { && lastReportedState != rotaryState) { cwcb(); lastReportedState = rotaryState; - } + lastWasClockwise = true; + } else if (pattern[3-encoderLastState] == rotaryState && ccwcb != NULL && (rotaryState == 0 || rotaryState == 3) && lastReportedState != rotaryState) { ccwcb(); lastReportedState = rotaryState; - } + lastWasClockwise = false; + } else if (rotaryState == 0 || rotaryState == 3) { + // Either the switch malfunctioned or we didn't get updates fast enough to catch the + // transitions, so do our best to infer what the rotation direction was from the last + // rotation. + if (lastWasClockwise) cwcb(); + else ccwcb(); + } + encoderLastState = rotaryState; } } diff --git a/src/RotaryEncoder.h b/src/RotaryEncoder.h index df97961..77c73ad 100644 --- a/src/RotaryEncoder.h +++ b/src/RotaryEncoder.h @@ -16,14 +16,62 @@ class RotaryEncoder { bool buttonWasDown; int encoderLastState; int lastReportedState; + bool lastWasClockwise = true; public: + /* Configures this to use a rotary encoder with an additional button. */ RotaryEncoder(int rotaryPin1, int rotaryPin2, int buttonPin); - void setup() ; + + /* Configures this to use a rotary encoder without a button. */ + RotaryEncoder(int rotaryPin1, int rotaryPin2); + + /* Must be called before other methods, sets up pullup mode for each pin. */ + void setup(); + + /* + * Configures the encoder to use interrupts. Using interrupts mitigates the need to call + * loop() periodically. + */ + void setupInterrupts(); + + /* + * Configures the encoder to use interrupts. The passed in callback must call this->loop(). + */ + void setupInterrupts(void (*cb)()); + + /* + * Sets the callback to be called when the button is pushed down. + * Expects a no argument method returning void. + */ void setButtonDownCallback(void (*cb)()); + + /* + * Sets the callback to be called when the button is released. + * Expects a no argument method returning void. + */ void setButtonUpCallback(void (*cb)()); + + /* + * Sets the callback to be called when the encoder is rotated clockwise. + * Expects a no argument method returning void. + */ void setRotateCWCallback(void (*cb)()); + + /* + * Sets the callback to be called when the encoder is counter-rotated clockwise. + * Expects a no argument method returning void. + */ void setRotateCCWCallback(void (*cb)()); + + /* + * Returns true if the button ahs been pressed. + */ + bool buttonIsDown(); + + /* + * If using interrupts, this method can be ignored. If not, call this method very frequently + * from your loop to decet the encoder state and handle events. + */ void loop(); };