Combine rotary with canned messages.

This commit is contained in:
Balazs Kelemen 2022-01-09 10:08:31 +01:00
parent c7e62142e9
commit 772dfe39dc
7 changed files with 184 additions and 218 deletions

View File

@ -5,44 +5,43 @@
#include <assert.h> #include <assert.h>
#define PIN_PUSH 21
#define PIN_A 22
#define PIN_B 23
// TODO: add UP-DOWN mode
#define ROTARY_MODE
CannedMessagePlugin *cannedMessagePlugin; CannedMessagePlugin *cannedMessagePlugin;
void IRAM_ATTR EXT_INT_PUSH() CannedMessagePlugin::CannedMessagePlugin(
Observable<const InputEvent *> *input)
: SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP),
concurrency::OSThread("CannedMessagePlugin")
{ {
cannedMessagePlugin->select(); this->inputObserver.observe(input);
} }
void IRAM_ATTR EXT_INT_DIRECTION_A() int CannedMessagePlugin::handleInputEvent(const InputEvent *event)
{ {
cannedMessagePlugin->directionA(); bool validEvent = false;
} if (event->inputEvent == INPUT_EVENT_UP)
{
this->action = CANNED_MESSAGE_ACTION_UP;
validEvent = true;
}
if (event->inputEvent == INPUT_EVENT_DOWN)
{
this->action = CANNED_MESSAGE_ACTION_DOWN;
validEvent = true;
}
if (event->inputEvent == INPUT_EVENT_SELECT)
{
this->action = CANNED_MESSAGE_ACTION_SELECT;
validEvent = true;
}
void IRAM_ATTR EXT_INT_DIRECTION_B() if (validEvent)
{ {
cannedMessagePlugin->directionB(); // Let runOnce to be called immediately.
} runned(millis());
setInterval(0);
}
CannedMessagePlugin::CannedMessagePlugin() return 0;
: SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessagePlugin")
{
// TODO: make pins configurable
pinMode(PIN_PUSH, INPUT_PULLUP);
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
attachInterrupt(PIN_PUSH, EXT_INT_PUSH, RISING);
#ifdef ROTARY_MODE
attachInterrupt(PIN_A, EXT_INT_DIRECTION_A, CHANGE);
attachInterrupt(PIN_B, EXT_INT_DIRECTION_B, CHANGE);
this->rotaryLevelA = digitalRead(PIN_A);
this->rotaryLevelB = digitalRead(PIN_B);
#endif
} }
void CannedMessagePlugin::sendText(NodeNum dest, void CannedMessagePlugin::sendText(NodeNum dest,
@ -70,13 +69,13 @@ int32_t CannedMessagePlugin::runOnce()
this->sendingState = SENDING_STATE_NONE; this->sendingState = SENDING_STATE_NONE;
this->notifyObservers(NULL); this->notifyObservers(NULL);
} }
else if ((this->action != ACTION_NONE) else if ((this->action != CANNED_MESSAGE_ACTION_NONE)
&& (this->currentMessageIndex == -1)) && (this->currentMessageIndex == -1))
{ {
this->currentMessageIndex = 0; this->currentMessageIndex = 0;
DEBUG_MSG("First touch.\n"); DEBUG_MSG("First touch.\n");
} }
else if (this->action == ACTION_PRESSED) else if (this->action == CANNED_MESSAGE_ACTION_SELECT)
{ {
sendText( sendText(
NODENUM_BROADCAST, NODENUM_BROADCAST,
@ -86,105 +85,70 @@ int32_t CannedMessagePlugin::runOnce()
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
return 2000; return 2000;
} }
else if (this->action == ACTION_UP) else if (this->action == CANNED_MESSAGE_ACTION_UP)
{ {
this->currentMessageIndex = getPrevIndex(); this->currentMessageIndex = getPrevIndex();
DEBUG_MSG("MOVE UP. Current message:%ld\n", DEBUG_MSG("MOVE UP. Current message:%ld\n",
millis()); millis());
} }
else if (this->action == ACTION_DOWN) else if (this->action == CANNED_MESSAGE_ACTION_DOWN)
{ {
this->currentMessageIndex = this->getNextIndex(); this->currentMessageIndex = this->getNextIndex();
DEBUG_MSG("MOVE DOWN. Current message:%ld\n", DEBUG_MSG("MOVE DOWN. Current message:%ld\n",
millis()); millis());
} }
if (this->action != ACTION_NONE) if (this->action != CANNED_MESSAGE_ACTION_NONE)
{ {
this->action = ACTION_NONE; this->action = CANNED_MESSAGE_ACTION_NONE;
this->notifyObservers(NULL); this->notifyObservers(NULL);
} }
return 30000; return 30000;
} }
void CannedMessagePlugin::select() String CannedMessagePlugin::getCurrentMessage()
{ {
this->action = ACTION_PRESSED; return cannedMessagePluginMessages[this->currentMessageIndex];
runned(millis()); }
setInterval(20); String CannedMessagePlugin::getPrevMessage()
{
return cannedMessagePluginMessages[this->getPrevIndex()];
}
String CannedMessagePlugin::getNextMessage()
{
return cannedMessagePluginMessages[this->getNextIndex()];
}
bool CannedMessagePlugin::shouldDraw()
{
return (currentMessageIndex != -1) || (this->sendingState != SENDING_STATE_NONE);
}
cannedMessagePluginSendigState CannedMessagePlugin::getSendingState()
{
return this->sendingState;
} }
/** int CannedMessagePlugin::getNextIndex()
* @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 CannedMessagePlugin::directionA()
{ {
#ifdef ROTARY_MODE if (this->currentMessageIndex >=
// CW rotation (at least on most common rotary encoders) (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1)
int currentLevelA = digitalRead(PIN_A);
if (this->rotaryLevelA == currentLevelA)
{ {
return; return 0;
} }
this->rotaryLevelA = currentLevelA; else
bool pinARaising = currentLevelA == HIGH;
if (pinARaising && (this->rotaryLevelB == LOW))
{ {
if (this->rotaryStateCCW == EVENT_CLEARED) return this->currentMessageIndex + 1;
{
this->rotaryStateCCW = EVENT_OCCURRED;
if ((this->action == ACTION_NONE)
|| (this->action == (cwRotationMeaning == ACTION_UP ? ACTION_UP : ACTION_DOWN)))
{
this->action = cwRotationMeaning == ACTION_UP ? ACTION_DOWN : ACTION_UP;
}
}
} }
else if (!pinARaising && (this->rotaryLevelB == HIGH))
{
// Logic to prevent bouncing.
this->rotaryStateCCW = EVENT_CLEARED;
}
#endif
runned(millis());
setInterval(50);
} }
void CannedMessagePlugin::directionB() int CannedMessagePlugin::getPrevIndex()
{ {
#ifdef ROTARY_MODE if (this->currentMessageIndex <= 0)
// CW rotation (at least on most common rotary encoders)
int currentLevelB = digitalRead(PIN_B);
if (this->rotaryLevelB == currentLevelB)
{ {
return; return
sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1;
} }
this->rotaryLevelB = currentLevelB; else
bool pinBRaising = currentLevelB == HIGH;
if (pinBRaising && (this->rotaryLevelA == LOW))
{ {
if (this->rotaryStateCW == EVENT_CLEARED) return this->currentMessageIndex - 1;
{
this->rotaryStateCW = EVENT_OCCURRED;
if ((this->action == ACTION_NONE)
|| (this->action == (cwRotationMeaning == ACTION_UP ? ACTION_DOWN : ACTION_UP)))
{
this->action = cwRotationMeaning == ACTION_UP ? ACTION_UP : ACTION_DOWN;
}
}
} }
else if (!pinBRaising && (this->rotaryLevelA == HIGH))
{
// Logic to prevent bouncing.
this->rotaryStateCW = EVENT_CLEARED;
}
#endif
runned(millis());
setInterval(50);
} }

View File

@ -1,18 +1,13 @@
#pragma once #pragma once
#include "SinglePortPlugin.h" #include "SinglePortPlugin.h"
#include "input/HardwareInput.h"
enum cannedMessagePluginRotaryStateType
{
EVENT_OCCURRED,
EVENT_CLEARED
};
enum cannedMessagePluginActionType enum cannedMessagePluginActionType
{ {
ACTION_NONE, CANNED_MESSAGE_ACTION_NONE,
ACTION_PRESSED, CANNED_MESSAGE_ACTION_SELECT,
ACTION_UP, CANNED_MESSAGE_ACTION_UP,
ACTION_DOWN CANNED_MESSAGE_ACTION_DOWN
}; };
enum cannedMessagePluginSendigState enum cannedMessagePluginSendigState
@ -41,31 +36,20 @@ class CannedMessagePlugin :
public Observable<const meshtastic::Status *>, public Observable<const meshtastic::Status *>,
private concurrency::OSThread private concurrency::OSThread
{ {
CallbackObserver<CannedMessagePlugin, const InputEvent *> inputObserver =
CallbackObserver<CannedMessagePlugin, const InputEvent *>(
this, &CannedMessagePlugin::handleInputEvent);
public: public:
CannedMessagePlugin(); CannedMessagePlugin(
void select(); Observable<const InputEvent *> *input);
void directionA(); String getCurrentMessage();
void directionB(); String getPrevMessage();
String getCurrentMessage() String getNextMessage();
{ bool shouldDraw();
return cannedMessagePluginMessages[this->currentMessageIndex]; cannedMessagePluginSendigState getSendingState();
} void eventUp();
String getPrevMessage() void eventDown();
{ void eventSelect();
return cannedMessagePluginMessages[this->getPrevIndex()];
}
String getNextMessage()
{
return cannedMessagePluginMessages[this->getNextIndex()];
}
bool shouldDraw()
{
return (currentMessageIndex != -1) || (this->sendingState != SENDING_STATE_NONE);
}
cannedMessagePluginSendigState getSendingState()
{
return this->sendingState;
}
protected: protected:
@ -76,40 +60,12 @@ class CannedMessagePlugin :
const char* message, const char* message,
bool wantReplies); bool wantReplies);
int getNextIndex() int getNextIndex();
{ int getPrevIndex();
if (this->currentMessageIndex >=
(sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1)
{
return 0;
}
else
{
return this->currentMessageIndex + 1;
}
}
int getPrevIndex() int handleInputEvent(const InputEvent *event);
{
if (this->currentMessageIndex <= 0)
{
return
sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1;
}
else
{
return this->currentMessageIndex - 1;
}
}
// TODO: make this configurable volatile cannedMessagePluginActionType action = CANNED_MESSAGE_ACTION_NONE;
volatile cannedMessagePluginActionType cwRotationMeaning = ACTION_UP;
volatile cannedMessagePluginActionType action = ACTION_NONE;
volatile cannedMessagePluginRotaryStateType rotaryStateCW = EVENT_CLEARED;
volatile cannedMessagePluginRotaryStateType rotaryStateCCW = EVENT_CLEARED;
volatile int rotaryLevelA = LOW;
volatile int rotaryLevelB = LOW;
int currentMessageIndex = -1; int currentMessageIndex = -1;
cannedMessagePluginSendigState sendingState = SENDING_STATE_NONE; cannedMessagePluginSendigState sendingState = SENDING_STATE_NONE;
}; };

View File

@ -9,6 +9,7 @@
#include "plugins/RoutingPlugin.h" #include "plugins/RoutingPlugin.h"
#include "plugins/AdminPlugin.h" #include "plugins/AdminPlugin.h"
#include "plugins/CannedMessagePlugin.h" #include "plugins/CannedMessagePlugin.h"
#include "plugins/input/RotaryEncoderInterruptImpl1.h"
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "plugins/esp32/SerialPlugin.h" #include "plugins/esp32/SerialPlugin.h"
#include "plugins/esp32/EnvironmentalMeasurementPlugin.h" #include "plugins/esp32/EnvironmentalMeasurementPlugin.h"
@ -31,7 +32,11 @@ void setupPlugins()
new RemoteHardwarePlugin(); new RemoteHardwarePlugin();
new ReplyPlugin(); new ReplyPlugin();
cannedMessagePlugin = new CannedMessagePlugin(); rotaryEncoderInterruptImpl1 =
new RotaryEncoderInterruptImpl1(
22, 23, 21,
INPUT_EVENT_UP, INPUT_EVENT_DOWN, INPUT_EVENT_SELECT);
cannedMessagePlugin = new CannedMessagePlugin(rotaryEncoderInterruptImpl1);
#ifndef NO_ESP32 #ifndef NO_ESP32
// Only run on an esp32 based device. // Only run on an esp32 based device.

View File

@ -6,32 +6,13 @@
#define PIN_B 23 #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( RotaryEncoderInterruptBase::RotaryEncoderInterruptBase(
const char *name,
uint8_t pinA, uint8_t pinB, uint8_t pinPress, uint8_t pinA, uint8_t pinB, uint8_t pinPress,
char eventCw, char eventCcw, char eventPressed, char eventCw, char eventCcw, char eventPressed,
// std::function<void(void)> onIntA, std::function<void(void)> onIntB, std::function<void(void)> onIntPress) : // std::function<void(void)> onIntA, std::function<void(void)> onIntB, std::function<void(void)> onIntPress) :
void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()) : void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()) :
SinglePortPlugin("rotaryi", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(name)
concurrency::OSThread("RotaryEncoderInterruptBase")
{ {
this->_pinA = pinA; this->_pinA = pinA;
this->_pinB = pinB; this->_pinB = pinB;
@ -53,19 +34,33 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase(
int32_t RotaryEncoderInterruptBase::runOnce() int32_t RotaryEncoderInterruptBase::runOnce()
{ {
if (this->action == ACTION_PRESSED) if (this->action == ROTARY_ACTION_PRESSED)
{ {
InputEvent e; InputEvent e;
e.inputEvent = INPUT_EVENT_SELECT; e.inputEvent = this->_eventPressed;
this->notifyObservers(&e); this->notifyObservers(&e);
} }
else if (this->action == ROTARY_ACTION_CW)
{
InputEvent e;
e.inputEvent = this->_eventCw;
this->notifyObservers(&e);
}
else if (this->action == ROTARY_ACTION_CCW)
{
InputEvent e;
e.inputEvent = this->_eventCcw;
this->notifyObservers(&e);
}
this->action = ROTARY_ACTION_NONE;
return 30000; return 30000;
} }
void RotaryEncoderInterruptBase::intPressHandler() void RotaryEncoderInterruptBase::intPressHandler()
{ {
this->action = ACTION_PRESSED; this->action = ROTARY_ACTION_PRESSED;
runned(millis()); runned(millis());
setInterval(20); setInterval(20);
} }
@ -91,20 +86,20 @@ void RotaryEncoderInterruptBase::intAHandler()
bool pinARaising = currentLevelA == HIGH; bool pinARaising = currentLevelA == HIGH;
if (pinARaising && (this->rotaryLevelB == LOW)) if (pinARaising && (this->rotaryLevelB == LOW))
{ {
if (this->rotaryStateCCW == EVENT_CLEARED) if (this->rotaryStateCCW == ROTARY_EVENT_CLEARED)
{ {
this->rotaryStateCCW = EVENT_OCCURRED; this->rotaryStateCCW = ROTARY_EVENT_OCCURRED;
if ((this->action == ACTION_NONE) if ((this->action == ROTARY_ACTION_NONE)
|| (this->action == ACTION_CCW)) || (this->action == ROTARY_ACTION_CCW))
{ {
this->action = ACTION_CW; this->action = ROTARY_ACTION_CW;
} }
} }
} }
else if (!pinARaising && (this->rotaryLevelB == HIGH)) else if (!pinARaising && (this->rotaryLevelB == HIGH))
{ {
// Logic to prevent bouncing. // Logic to prevent bouncing.
this->rotaryStateCCW = EVENT_CLEARED; this->rotaryStateCCW = ROTARY_EVENT_CLEARED;
} }
runned(millis()); runned(millis());
setInterval(50); setInterval(50);
@ -122,20 +117,20 @@ void RotaryEncoderInterruptBase::intBHandler()
bool pinBRaising = currentLevelB == HIGH; bool pinBRaising = currentLevelB == HIGH;
if (pinBRaising && (this->rotaryLevelA == LOW)) if (pinBRaising && (this->rotaryLevelA == LOW))
{ {
if (this->rotaryStateCW == EVENT_CLEARED) if (this->rotaryStateCW == ROTARY_EVENT_CLEARED)
{ {
this->rotaryStateCW = EVENT_OCCURRED; this->rotaryStateCW = ROTARY_EVENT_OCCURRED;
if ((this->action == ACTION_NONE) if ((this->action == ROTARY_ACTION_NONE)
|| (this->action == ACTION_CCW)) || (this->action == ROTARY_ACTION_CCW))
{ {
this->action = ACTION_CW; this->action = ROTARY_ACTION_CW;
} }
} }
} }
else if (!pinBRaising && (this->rotaryLevelA == HIGH)) else if (!pinBRaising && (this->rotaryLevelA == HIGH))
{ {
// Logic to prevent bouncing. // Logic to prevent bouncing.
this->rotaryStateCW = EVENT_CLEARED; this->rotaryStateCW = ROTARY_EVENT_CLEARED;
} }
runned(millis()); runned(millis());
setInterval(50); setInterval(50);

View File

@ -1,28 +1,30 @@
#pragma once #pragma once
//#include <Arduino.h>
//#include "Observer.h"
#include "SinglePortPlugin.h" #include "SinglePortPlugin.h"
#include "HardwareInput.h" #include "HardwareInput.h"
enum RotaryEncoderInterruptBaseStateType enum RotaryEncoderInterruptBaseStateType
{ {
EVENT_OCCURRED, ROTARY_EVENT_OCCURRED,
EVENT_CLEARED ROTARY_EVENT_CLEARED
}; };
enum RotaryEncoderInterruptBaseActionType enum RotaryEncoderInterruptBaseActionType
{ {
ACTION_NONE, ROTARY_ACTION_NONE,
ACTION_PRESSED, ROTARY_ACTION_PRESSED,
ACTION_CW, ROTARY_ACTION_CW,
ACTION_CCW ROTARY_ACTION_CCW
}; };
class RotaryEncoderInterruptBase : class RotaryEncoderInterruptBase :
public SinglePortPlugin,
public Observable<const InputEvent *>, public Observable<const InputEvent *>,
private concurrency::OSThread private concurrency::OSThread
{ {
public: public:
RotaryEncoderInterruptBase( RotaryEncoderInterruptBase(
const char *name,
uint8_t pinA, uint8_t pinB, uint8_t pinPress, uint8_t pinA, uint8_t pinB, uint8_t pinPress,
char eventCw, char eventCcw, char eventPressed, char eventCw, char eventCcw, char eventPressed,
// std::function<void(void)> onIntA, std::function<void(void)> onIntB, std::function<void(void)> onIntPress); // std::function<void(void)> onIntA, std::function<void(void)> onIntB, std::function<void(void)> onIntPress);
@ -33,11 +35,11 @@ class RotaryEncoderInterruptBase :
protected: protected:
virtual int32_t runOnce(); virtual int32_t runOnce();
volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = EVENT_CLEARED; volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = ROTARY_EVENT_CLEARED;
volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = EVENT_CLEARED; volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = ROTARY_EVENT_CLEARED;
volatile int rotaryLevelA = LOW; volatile int rotaryLevelA = LOW;
volatile int rotaryLevelB = LOW; volatile int rotaryLevelB = LOW;
volatile RotaryEncoderInterruptBaseActionType action = ACTION_NONE; volatile RotaryEncoderInterruptBaseActionType action = ROTARY_ACTION_NONE;
private: private:
uint8_t _pinA; uint8_t _pinA;
@ -46,5 +48,3 @@ class RotaryEncoderInterruptBase :
char _eventCcw; char _eventCcw;
char _eventPressed; char _eventPressed;
}; };
RotaryEncoderInterruptBase *RotaryEncoderInterruptBase;

View File

@ -0,0 +1,30 @@
#include "RotaryEncoderInterruptImpl1.h"
RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1(
uint8_t pinA, uint8_t pinB, uint8_t pinPress,
char eventCw, char eventCcw, char eventPressed) :
RotaryEncoderInterruptBase(
"rotEnc1",
pinA, pinB, pinPress,
eventCw, eventCcw, eventPressed,
RotaryEncoderInterruptImpl1::handleIntA,
RotaryEncoderInterruptImpl1::handleIntB,
RotaryEncoderInterruptImpl1::handleIntPressed)
{
}
void RotaryEncoderInterruptImpl1::handleIntA()
{
}
void RotaryEncoderInterruptImpl1::handleIntB()
{
}
void RotaryEncoderInterruptImpl1::handleIntPressed()
{
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "RotaryEncoderInterruptBase.h"
class RotaryEncoderInterruptImpl1 :
public RotaryEncoderInterruptBase
{
public:
RotaryEncoderInterruptImpl1(
uint8_t pinA, uint8_t pinB, uint8_t pinPress,
char eventCw, char eventCcw, char eventPressed);
static void handleIntA();
static void handleIntB();
static void handleIntPressed();
};
extern RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;