diff --git a/src/plugins/input/HardwareInput.h b/src/plugins/input/HardwareInput.h new file mode 100644 index 000000000..ebc04447c --- /dev/null +++ b/src/plugins/input/HardwareInput.h @@ -0,0 +1,13 @@ +#pragma once + +#define INPUT_EVENT_UP 17 +#define INPUT_EVENT_DOWN 18 +#define INPUT_EVENT_LEFT 19 +#define INPUT_EVENT_RIGHT 20 +#define INPUT_EVENT_SELECT '\n' +#define INPUT_EVENT_BACK 27 +#define INPUT_EVENT_CANCEL 24 + +typedef struct _InputEvent { + char inputEvent; +} InputEvent; \ No newline at end of file diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp new file mode 100644 index 000000000..9bee8cae3 --- /dev/null +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -0,0 +1,142 @@ +#include "configuration.h" +#include "RotaryEncoderInterruptBase.h" + +/*#define PIN_PUSH 21 +#define PIN_A 22 +#define PIN_B 23 +*/ + +/* +RotaryEncoderInterruptBase *cannedMessagePlugin; + +void IRAM_ATTR EXT_INT_PUSH() +{ + cannedMessagePlugin->pressed(); +} + +void IRAM_ATTR EXT_INT_DIRECTION_A() +{ + cannedMessagePlugin->directionA(); +} + +void IRAM_ATTR EXT_INT_DIRECTION_B() +{ + cannedMessagePlugin->directionB(); +} +*/ + +RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( + uint8_t pinA, uint8_t pinB, uint8_t pinPress, + char eventCw, char eventCcw, char eventPressed, +// std::function onIntA, std::function onIntB, std::function onIntPress) : + void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()) : + SinglePortPlugin("rotaryi", PortNum_TEXT_MESSAGE_APP), + concurrency::OSThread("RotaryEncoderInterruptBase") +{ + this->_pinA = pinA; + this->_pinB = pinB; + this->_eventCw = eventCw; + this->_eventCcw = eventCcw; + this->_eventPressed = eventPressed; + + // TODO: make pins configurable + pinMode(pinPress, INPUT_PULLUP); + pinMode(this->_pinA, INPUT_PULLUP); + pinMode(this->_pinB, INPUT_PULLUP); +// attachInterrupt(pinPress, onIntPress, RISING); + attachInterrupt(pinPress, onIntPress, RISING); + attachInterrupt(this->_pinA, onIntA, CHANGE); + attachInterrupt(this->_pinB, onIntB, CHANGE); + this->rotaryLevelA = digitalRead(this->_pinA); + this->rotaryLevelB = digitalRead(this->_pinB); +} + +int32_t RotaryEncoderInterruptBase::runOnce() +{ + if (this->action == ACTION_PRESSED) + { + InputEvent e; + e.inputEvent = INPUT_EVENT_SELECT; + this->notifyObservers(&e); + } + return 30000; +} + + +void RotaryEncoderInterruptBase::intPressHandler() +{ + this->action = ACTION_PRESSED; + runned(millis()); + setInterval(20); +} + +/** + * @brief Rotary action implementation. + * We assume, the following pin setup: + * A --|| + * GND --||]======== + * B --|| + * + * @return The new level of the actual pin (that is actualPinCurrentLevel). + */ +void RotaryEncoderInterruptBase::intAHandler() +{ + // CW rotation (at least on most common rotary encoders) + int currentLevelA = digitalRead(this->_pinA); + if (this->rotaryLevelA == currentLevelA) + { + return; + } + this->rotaryLevelA = currentLevelA; + bool pinARaising = currentLevelA == HIGH; + if (pinARaising && (this->rotaryLevelB == LOW)) + { + if (this->rotaryStateCCW == EVENT_CLEARED) + { + this->rotaryStateCCW = EVENT_OCCURRED; + if ((this->action == ACTION_NONE) + || (this->action == ACTION_CCW)) + { + this->action = ACTION_CW; + } + } + } + else if (!pinARaising && (this->rotaryLevelB == HIGH)) + { + // Logic to prevent bouncing. + this->rotaryStateCCW = EVENT_CLEARED; + } + runned(millis()); + setInterval(50); +} + +void RotaryEncoderInterruptBase::intBHandler() +{ + // CW rotation (at least on most common rotary encoders) + int currentLevelB = digitalRead(this->_pinB); + if (this->rotaryLevelB == currentLevelB) + { + return; + } + this->rotaryLevelB = currentLevelB; + bool pinBRaising = currentLevelB == HIGH; + if (pinBRaising && (this->rotaryLevelA == LOW)) + { + if (this->rotaryStateCW == EVENT_CLEARED) + { + this->rotaryStateCW = EVENT_OCCURRED; + if ((this->action == ACTION_NONE) + || (this->action == ACTION_CCW)) + { + this->action = ACTION_CW; + } + } + } + else if (!pinBRaising && (this->rotaryLevelA == HIGH)) + { + // Logic to prevent bouncing. + this->rotaryStateCW = EVENT_CLEARED; + } + runned(millis()); + setInterval(50); +} diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h new file mode 100644 index 000000000..09d00bcb2 --- /dev/null +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -0,0 +1,50 @@ +#pragma once +#include "SinglePortPlugin.h" +#include "HardwareInput.h" + +enum RotaryEncoderInterruptBaseStateType +{ + EVENT_OCCURRED, + EVENT_CLEARED +}; + +enum RotaryEncoderInterruptBaseActionType +{ + ACTION_NONE, + ACTION_PRESSED, + ACTION_CW, + ACTION_CCW +}; + +class RotaryEncoderInterruptBase : + public SinglePortPlugin, + public Observable, + private concurrency::OSThread +{ + public: + RotaryEncoderInterruptBase( + uint8_t pinA, uint8_t pinB, uint8_t pinPress, + char eventCw, char eventCcw, char eventPressed, +// std::function onIntA, std::function onIntB, std::function onIntPress); + void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()); + void intPressHandler(); + void intAHandler(); + void intBHandler(); + + protected: + virtual int32_t runOnce(); + volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = EVENT_CLEARED; + volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = EVENT_CLEARED; + volatile int rotaryLevelA = LOW; + volatile int rotaryLevelB = LOW; + volatile RotaryEncoderInterruptBaseActionType action = ACTION_NONE; + + private: + uint8_t _pinA; + uint8_t _pinB; + char _eventCw; + char _eventCcw; + char _eventPressed; +}; + +RotaryEncoderInterruptBase *RotaryEncoderInterruptBase; \ No newline at end of file