From 2b588f15676d537f70a1125b8380c491d8193f00 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:42:28 +0100 Subject: [PATCH 01/17] CannedMessagePlugin initial commit --- src/plugins/CannedMessagePlugin.cpp | 167 ++++++++++++++++++++++++++++ src/plugins/CannedMessagePlugin.h | 46 ++++++++ src/plugins/Plugins.cpp | 2 + 3 files changed, 215 insertions(+) create mode 100644 src/plugins/CannedMessagePlugin.cpp create mode 100644 src/plugins/CannedMessagePlugin.h diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp new file mode 100644 index 000000000..996e0b696 --- /dev/null +++ b/src/plugins/CannedMessagePlugin.cpp @@ -0,0 +1,167 @@ +#include "configuration.h" +#include "CannedMessagePlugin.h" +#include "MeshService.h" +#include "main.h" + +#include + +#define PIN_PUSH 21 +#define PIN_A 22 +#define PIN_B 23 + +// TODO: add UP-DOWN mode +#define ROTARY_MODE + +CannedMessagePlugin *cannedMessagePlugin; + +void IRAM_ATTR EXT_INT_PUSH() +{ + cannedMessagePlugin->select(); +} + +void IRAM_ATTR EXT_INT_DIRECTION_A() +{ + cannedMessagePlugin->directionA(); +} + +void IRAM_ATTR EXT_INT_DIRECTION_B() +{ + cannedMessagePlugin->directionB(); +} + +CannedMessagePlugin::CannedMessagePlugin() + : 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, bool wantReplies) +{ + MeshPacket *p = allocDataPacket(); + p->to = dest; + const char *replyStr = "This is a canned message"; + p->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply + memcpy(p->decoded.payload.bytes, replyStr, p->decoded.payload.size); + +// PacketId prevPacketId = p->id; // In case we need it later. + + DEBUG_MSG("Sending message id=%d, msg=%.*s\n", + p->id, p->decoded.payload.size, p->decoded.payload.bytes); + + service.sendToMesh(p); +} + +int32_t CannedMessagePlugin::runOnce() +{ + /* + if (this->action == ACTION_PRESSED) + { + sendText(NODENUM_BROADCAST, true); + needSend = false; + } + */ + if (this->action == ACTION_PRESSED) + { + DEBUG_MSG("SELECTED\n"); + } + else if (this->action == ACTION_UP) + { + DEBUG_MSG("MOVE UP\n"); + } + else if (this->action == ACTION_DOWN) + { + DEBUG_MSG("MOVE_DOWN\n"); + } + this->action = ACTION_NONE; + + return UINT32_MAX; +} + +void CannedMessagePlugin::select() +{ + this->action = ACTION_PRESSED; + 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 CannedMessagePlugin::directionA() +{ +#ifdef ROTARY_MODE + // CW rotation (at least on most common rotary encoders) + int currentLevelA = digitalRead(PIN_A); + 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 == (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 + setInterval(30); +} + +void CannedMessagePlugin::directionB() +{ +#ifdef ROTARY_MODE + // CW rotation (at least on most common rotary encoders) + int currentLevelB = digitalRead(PIN_B); + 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 == (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 + setInterval(30); +} diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h new file mode 100644 index 000000000..24e3ac7f9 --- /dev/null +++ b/src/plugins/CannedMessagePlugin.h @@ -0,0 +1,46 @@ +#pragma once +#include "SinglePortPlugin.h" + +enum cannedMessagePluginRotatyStateType +{ + EVENT_OCCURRED, + EVENT_CLEARED +}; + +enum cannedMessagePluginActionType +{ + ACTION_NONE, + ACTION_PRESSED, + ACTION_UP, + ACTION_DOWN +}; + +class CannedMessagePlugin : public SinglePortPlugin, private concurrency::OSThread +{ + public: + CannedMessagePlugin(); + void select(); + void directionA(); + void directionB(); + + protected: + + virtual int32_t runOnce(); + + MeshPacket *preparePacket(); + + void sendText(NodeNum dest, bool wantReplies); + + // TODO: make this configurable + volatile cannedMessagePluginActionType cwRotationMeaning = ACTION_UP; + + bool needSend = false; + volatile cannedMessagePluginActionType action = ACTION_NONE; + volatile cannedMessagePluginRotatyStateType rotaryStateCW = EVENT_CLEARED; + volatile cannedMessagePluginRotatyStateType rotaryStateCCW = EVENT_CLEARED; + volatile int rotaryLevelA = LOW; + volatile int rotaryLevelB = LOW; +// volatile bool enableEvent = true; +}; + +extern CannedMessagePlugin *cannedMessagePlugin; diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 92602c7a8..36eddf5fd 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -8,6 +8,7 @@ #include "plugins/TextMessagePlugin.h" #include "plugins/RoutingPlugin.h" #include "plugins/AdminPlugin.h" +#include "plugins/CannedMessagePlugin.h" #ifndef NO_ESP32 #include "plugins/esp32/SerialPlugin.h" #include "plugins/esp32/EnvironmentalMeasurementPlugin.h" @@ -30,6 +31,7 @@ void setupPlugins() new RemoteHardwarePlugin(); new ReplyPlugin(); + cannedMessagePlugin = new CannedMessagePlugin(); #ifndef NO_ESP32 // Only run on an esp32 based device. From 7b8849493fde5c5568501db47434e93c9715f792 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:43:06 +0100 Subject: [PATCH 02/17] Adding screen frame --- src/graphics/Screen.cpp | 31 ++++++++++++++++ src/graphics/Screen.h | 3 ++ src/plugins/CannedMessagePlugin.cpp | 57 ++++++++++++++++++++++------- src/plugins/CannedMessagePlugin.h | 47 ++++++++++++++++++++---- 4 files changed, 118 insertions(+), 20 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index cdf5ab1a5..22bfb8afb 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -34,6 +34,7 @@ along with this program. If not, see . #include "mesh-pb-constants.h" #include "mesh/Channels.h" #include "plugins/TextMessagePlugin.h" +#include "plugins/CannedMessagePlugin.h" #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -285,6 +286,21 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); } +static void drawCannedMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + displayedNodeNum = 0; // Not currently showing a node pane + + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(0 + x, 0 + y, cannedMessagePlugin->getCurrentSelection()); + + // the max length of this buffer is much longer than we can possibly print +// static char tempBuf[96]; +// snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes); + +// display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); +} + /// Draw a series of fields in a column, wrapping to multiple colums if needed static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) { @@ -820,6 +836,8 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (textMessagePlugin) textMessageObserver.observe(textMessagePlugin); + if (cannedMessagePlugin) + cannedMessageObserver.observe(cannedMessagePlugin); } void Screen::forceDisplay() @@ -996,6 +1014,9 @@ void Screen::setFrames() if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) { normalFrames[numframes++] = drawTextMessageFrame; } + if (cannedMessagePlugin->shouldDraw()) { + normalFrames[numframes++] = drawCannedMessageFrame; + } // then all the nodes // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens @@ -1457,4 +1478,14 @@ int Screen::handleTextMessage(const MeshPacket *packet) return 0; } +int Screen::handleCannedMessage(const meshtastic::Status *packet) +{ + if (showingNormalScreen) { + setFrames(); // Regen the list of screens (will show new text message) + } + ui.switchToFrame(1); + + return 0; +} + } // namespace graphics diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 9334fe43c..68815328e 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -90,6 +90,8 @@ class Screen : public concurrency::OSThread CallbackObserver(this, &Screen::handleStatusUpdate); CallbackObserver textMessageObserver = CallbackObserver(this, &Screen::handleTextMessage); + CallbackObserver cannedMessageObserver = + CallbackObserver(this, &Screen::handleCannedMessage); public: Screen(uint8_t address, int sda = -1, int scl = -1); @@ -218,6 +220,7 @@ class Screen : public concurrency::OSThread int handleStatusUpdate(const meshtastic::Status *arg); int handleTextMessage(const MeshPacket *arg); + int handleCannedMessage(const meshtastic::Status *arg); /// Used to force (super slow) eink displays to draw critical frames void forceDisplay(); diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 996e0b696..b104460b4 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -45,7 +45,9 @@ CannedMessagePlugin::CannedMessagePlugin() #endif } -void CannedMessagePlugin::sendText(NodeNum dest, bool wantReplies) +void CannedMessagePlugin::sendText(NodeNum dest, + const char* message, + bool wantReplies) { MeshPacket *p = allocDataPacket(); p->to = dest; @@ -63,28 +65,57 @@ void CannedMessagePlugin::sendText(NodeNum dest, bool wantReplies) int32_t CannedMessagePlugin::runOnce() { - /* - if (this->action == ACTION_PRESSED) + if ((this->action != ACTION_NONE) + && (this->currentMessageIndex == -1)) { - sendText(NODENUM_BROADCAST, true); - needSend = false; + this->currentMessageIndex = 0; + DEBUG_MSG("First touch. Current message:%s\n", + this->getCurrentSelection()); } - */ - if (this->action == ACTION_PRESSED) + else if (this->action == ACTION_PRESSED) { - DEBUG_MSG("SELECTED\n"); + sendText( + NODENUM_BROADCAST, + cannedMessagePluginMessages[this->currentMessageIndex], + true); } else if (this->action == ACTION_UP) { - DEBUG_MSG("MOVE UP\n"); + if (this->currentMessageIndex <= 0) + { + this->currentMessageIndex = + sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1; + } + else + { + this->currentMessageIndex -= 1; + } + DEBUG_MSG("MOVE UP. Current message:%s\n", + this->getCurrentSelection()); } else if (this->action == ACTION_DOWN) { - DEBUG_MSG("MOVE_DOWN\n"); + if (this->currentMessageIndex >= + (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) + { + this->currentMessageIndex = 0; + } + else + { + this->currentMessageIndex += 1; + } + DEBUG_MSG("MOVE DOWN. Current message:%s\n", + this->getCurrentSelection()); } - this->action = ACTION_NONE; - - return UINT32_MAX; + if (this->action != ACTION_NONE) + { + this->action = ACTION_NONE; + this->notifyObservers(NULL); + } + DEBUG_MSG("Current selection index:%d\n", + this->currentMessageIndex); + + return 3000; } void CannedMessagePlugin::select() diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 24e3ac7f9..437726205 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -1,7 +1,7 @@ #pragma once #include "SinglePortPlugin.h" -enum cannedMessagePluginRotatyStateType +enum cannedMessagePluginRotaryStateType { EVENT_OCCURRED, EVENT_CLEARED @@ -15,13 +15,44 @@ enum cannedMessagePluginActionType ACTION_DOWN }; -class CannedMessagePlugin : public SinglePortPlugin, private concurrency::OSThread +#define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN 50 + +static char cannedMessagePluginMessages[][CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN] = +{ + "I need a helping hand", + "I need help with saw", + "I need an alpinist", + "I need ambulance", + "I'm fine", + "I'm already waiting", + "I will be late", + "I couldn't join", + "We have got company" +}; + +typedef struct _CannedMessagePluginStatus +{ + int dummy; +} CannedMessagePluginStatus; + +class CannedMessagePlugin : + public SinglePortPlugin, + public Observable, + private concurrency::OSThread { public: CannedMessagePlugin(); void select(); void directionA(); void directionB(); + String getCurrentSelection() + { + return cannedMessagePluginMessages[this->currentMessageIndex]; + } + bool shouldDraw() + { + return currentMessageIndex != -1; + } protected: @@ -29,18 +60,20 @@ class CannedMessagePlugin : public SinglePortPlugin, private concurrency::OSThre MeshPacket *preparePacket(); - void sendText(NodeNum dest, bool wantReplies); + void sendText( + NodeNum dest, + const char* message, + bool wantReplies); // TODO: make this configurable volatile cannedMessagePluginActionType cwRotationMeaning = ACTION_UP; - bool needSend = false; volatile cannedMessagePluginActionType action = ACTION_NONE; - volatile cannedMessagePluginRotatyStateType rotaryStateCW = EVENT_CLEARED; - volatile cannedMessagePluginRotatyStateType rotaryStateCCW = EVENT_CLEARED; + volatile cannedMessagePluginRotaryStateType rotaryStateCW = EVENT_CLEARED; + volatile cannedMessagePluginRotaryStateType rotaryStateCCW = EVENT_CLEARED; volatile int rotaryLevelA = LOW; volatile int rotaryLevelB = LOW; -// volatile bool enableEvent = true; + int currentMessageIndex = -1; }; extern CannedMessagePlugin *cannedMessagePlugin; From 4a29aef19ee131b83795c898f73f48cb9b78b170 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 4 Jan 2022 20:41:45 +0100 Subject: [PATCH 03/17] Show previous and next message in list. --- src/graphics/Screen.cpp | 6 ++++- src/plugins/CannedMessagePlugin.cpp | 30 +++++------------------ src/plugins/CannedMessagePlugin.h | 38 ++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 22bfb8afb..8c33e6fb4 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -291,8 +291,12 @@ static void drawCannedMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *sta displayedNodeNum = 0; // Not currently showing a node pane display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage()); display->setFont(FONT_MEDIUM); - display->drawString(0 + x, 0 + y, cannedMessagePlugin->getCurrentSelection()); + display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage()); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage()); // the max length of this buffer is much longer than we can possibly print // static char tempBuf[96]; diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index b104460b4..f880b4548 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -70,7 +70,7 @@ int32_t CannedMessagePlugin::runOnce() { this->currentMessageIndex = 0; DEBUG_MSG("First touch. Current message:%s\n", - this->getCurrentSelection()); + this->getCurrentMessage()); } else if (this->action == ACTION_PRESSED) { @@ -81,41 +81,23 @@ int32_t CannedMessagePlugin::runOnce() } else if (this->action == ACTION_UP) { - if (this->currentMessageIndex <= 0) - { - this->currentMessageIndex = - sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1; - } - else - { - this->currentMessageIndex -= 1; - } + this->currentMessageIndex = getPrevIndex(); DEBUG_MSG("MOVE UP. Current message:%s\n", - this->getCurrentSelection()); + this->getCurrentMessage()); } else if (this->action == ACTION_DOWN) { - if (this->currentMessageIndex >= - (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) - { - this->currentMessageIndex = 0; - } - else - { - this->currentMessageIndex += 1; - } + this->currentMessageIndex = this->getNextIndex(); DEBUG_MSG("MOVE DOWN. Current message:%s\n", - this->getCurrentSelection()); + this->getCurrentMessage()); } if (this->action != ACTION_NONE) { this->action = ACTION_NONE; this->notifyObservers(NULL); } - DEBUG_MSG("Current selection index:%d\n", - this->currentMessageIndex); - return 3000; + return 30000; } void CannedMessagePlugin::select() diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 437726205..4cf9a9120 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -45,10 +45,18 @@ class CannedMessagePlugin : void select(); void directionA(); void directionB(); - String getCurrentSelection() + String getCurrentMessage() { return cannedMessagePluginMessages[this->currentMessageIndex]; } + String getPrevMessage() + { + return cannedMessagePluginMessages[this->getPrevIndex()]; + } + String getNextMessage() + { + return cannedMessagePluginMessages[this->getNextIndex()]; + } bool shouldDraw() { return currentMessageIndex != -1; @@ -58,13 +66,37 @@ class CannedMessagePlugin : virtual int32_t runOnce(); - MeshPacket *preparePacket(); - void sendText( NodeNum dest, const char* message, bool wantReplies); + int getNextIndex() + { + if (this->currentMessageIndex >= + (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) + { + return 0; + } + else + { + return this->currentMessageIndex + 1; + } + } + + int getPrevIndex() + { + 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 cwRotationMeaning = ACTION_UP; From b3ddf16d64776b9f56d966ae1e7dc1eae28d3f66 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 4 Jan 2022 22:02:16 +0100 Subject: [PATCH 04/17] Display sending state. --- src/graphics/Screen.cpp | 29 ++++++++++++++++------------- src/plugins/CannedMessagePlugin.cpp | 16 ++++++++++++---- src/plugins/CannedMessagePlugin.h | 24 +++++++++++++++--------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 8c33e6fb4..b9f0e3ff3 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -290,19 +290,22 @@ static void drawCannedMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *sta { displayedNodeNum = 0; // Not currently showing a node pane - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage()); - display->setFont(FONT_MEDIUM); - display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage()); - display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage()); - - // the max length of this buffer is much longer than we can possibly print -// static char tempBuf[96]; -// snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes); - -// display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); + if (cannedMessagePlugin->getSendingState() == SENDING_STATE_NONE) + { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage()); + display->setFont(FONT_MEDIUM); + display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage()); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage()); + } + else + { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(FONT_MEDIUM); + display->drawString(display->getWidth()/2 + x, 0 + y + 12, "Sending..."); + } } /// Draw a series of fields in a column, wrapping to multiple colums if needed diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index f880b4548..f6abc6742 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -51,9 +51,8 @@ void CannedMessagePlugin::sendText(NodeNum dest, { MeshPacket *p = allocDataPacket(); p->to = dest; - const char *replyStr = "This is a canned message"; - p->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply - memcpy(p->decoded.payload.bytes, replyStr, p->decoded.payload.size); + p->decoded.payload.size = strlen(message); + memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); // PacketId prevPacketId = p->id; // In case we need it later. @@ -65,7 +64,13 @@ void CannedMessagePlugin::sendText(NodeNum dest, int32_t CannedMessagePlugin::runOnce() { - if ((this->action != ACTION_NONE) + if (this->sendingState == SENDING_STATE_ACTIVE) + { + // TODO: might have some feedback of sendig state + this->sendingState = SENDING_STATE_NONE; + this->notifyObservers(NULL); + } + else if ((this->action != ACTION_NONE) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; @@ -78,6 +83,9 @@ int32_t CannedMessagePlugin::runOnce() NODENUM_BROADCAST, cannedMessagePluginMessages[this->currentMessageIndex], true); + this->sendingState = SENDING_STATE_ACTIVE; + this->currentMessageIndex = -1; + return 2000; } else if (this->action == ACTION_UP) { diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 4cf9a9120..0b22ccc13 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -15,26 +15,27 @@ enum cannedMessagePluginActionType ACTION_DOWN }; +enum cannedMessagePluginSendigState +{ + SENDING_STATE_NONE, + SENDING_STATE_ACTIVE +}; + #define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN 50 static char cannedMessagePluginMessages[][CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN] = { - "I need a helping hand", - "I need help with saw", + "Need helping hand", + "Help me with saw", "I need an alpinist", "I need ambulance", "I'm fine", "I'm already waiting", "I will be late", "I couldn't join", - "We have got company" + "We have company" }; -typedef struct _CannedMessagePluginStatus -{ - int dummy; -} CannedMessagePluginStatus; - class CannedMessagePlugin : public SinglePortPlugin, public Observable, @@ -59,7 +60,11 @@ class CannedMessagePlugin : } bool shouldDraw() { - return currentMessageIndex != -1; + return (currentMessageIndex != -1) || (this->sendingState != SENDING_STATE_NONE); + } + cannedMessagePluginSendigState getSendingState() + { + return this->sendingState; } protected: @@ -106,6 +111,7 @@ class CannedMessagePlugin : volatile int rotaryLevelA = LOW; volatile int rotaryLevelB = LOW; int currentMessageIndex = -1; + cannedMessagePluginSendigState sendingState = SENDING_STATE_NONE; }; extern CannedMessagePlugin *cannedMessagePlugin; From 6eb2b33124730432ec67c433ad574ca274ad2bb2 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:52:08 +0100 Subject: [PATCH 05/17] Fix rotary glitch. --- src/plugins/CannedMessagePlugin.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index f6abc6742..e69b742c8 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -74,8 +74,7 @@ int32_t CannedMessagePlugin::runOnce() && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; - DEBUG_MSG("First touch. Current message:%s\n", - this->getCurrentMessage()); + DEBUG_MSG("First touch.\n"); } else if (this->action == ACTION_PRESSED) { @@ -90,14 +89,14 @@ int32_t CannedMessagePlugin::runOnce() else if (this->action == ACTION_UP) { this->currentMessageIndex = getPrevIndex(); - DEBUG_MSG("MOVE UP. Current message:%s\n", - this->getCurrentMessage()); + DEBUG_MSG("MOVE UP. Current message:%ld\n", + millis()); } else if (this->action == ACTION_DOWN) { this->currentMessageIndex = this->getNextIndex(); - DEBUG_MSG("MOVE DOWN. Current message:%s\n", - this->getCurrentMessage()); + DEBUG_MSG("MOVE DOWN. Current message:%ld\n", + millis()); } if (this->action != ACTION_NONE) { @@ -111,6 +110,7 @@ int32_t CannedMessagePlugin::runOnce() void CannedMessagePlugin::select() { this->action = ACTION_PRESSED; + runned(millis()); setInterval(20); } @@ -152,7 +152,8 @@ void CannedMessagePlugin::directionA() this->rotaryStateCCW = EVENT_CLEARED; } #endif - setInterval(30); + runned(millis()); + setInterval(50); } void CannedMessagePlugin::directionB() @@ -184,5 +185,6 @@ void CannedMessagePlugin::directionB() this->rotaryStateCW = EVENT_CLEARED; } #endif - setInterval(30); + runned(millis()); + setInterval(50); } From c7e62142e907d67d95ff07f54a6fd5ce2004c040 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Sat, 8 Jan 2022 13:13:29 +0100 Subject: [PATCH 06/17] RotaryEncoderInterruptBase --- src/plugins/input/HardwareInput.h | 13 ++ .../input/RotaryEncoderInterruptBase.cpp | 142 ++++++++++++++++++ .../input/RotaryEncoderInterruptBase.h | 50 ++++++ 3 files changed, 205 insertions(+) create mode 100644 src/plugins/input/HardwareInput.h create mode 100644 src/plugins/input/RotaryEncoderInterruptBase.cpp create mode 100644 src/plugins/input/RotaryEncoderInterruptBase.h 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 From 772dfe39dc3b9679d6a4a3e87e4d183e4b72cbe0 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Sun, 9 Jan 2022 10:08:31 +0100 Subject: [PATCH 07/17] Combine rotary with canned messages. --- src/plugins/CannedMessagePlugin.cpp | 168 +++++++----------- src/plugins/CannedMessagePlugin.h | 88 +++------ src/plugins/Plugins.cpp | 7 +- .../input/RotaryEncoderInterruptBase.cpp | 67 ++++--- .../input/RotaryEncoderInterruptBase.h | 26 +-- .../input/RotaryEncoderInterruptImpl1.cpp | 30 ++++ .../input/RotaryEncoderInterruptImpl1.h | 16 ++ 7 files changed, 184 insertions(+), 218 deletions(-) create mode 100644 src/plugins/input/RotaryEncoderInterruptImpl1.cpp create mode 100644 src/plugins/input/RotaryEncoderInterruptImpl1.h diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index e69b742c8..a6ebf3bae 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -5,44 +5,43 @@ #include -#define PIN_PUSH 21 -#define PIN_A 22 -#define PIN_B 23 - -// TODO: add UP-DOWN mode -#define ROTARY_MODE - CannedMessagePlugin *cannedMessagePlugin; -void IRAM_ATTR EXT_INT_PUSH() +CannedMessagePlugin::CannedMessagePlugin( + Observable *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() -{ - cannedMessagePlugin->directionB(); -} + if (validEvent) + { + // Let runOnce to be called immediately. + runned(millis()); + setInterval(0); + } -CannedMessagePlugin::CannedMessagePlugin() - : 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 + return 0; } void CannedMessagePlugin::sendText(NodeNum dest, @@ -70,13 +69,13 @@ int32_t CannedMessagePlugin::runOnce() this->sendingState = SENDING_STATE_NONE; this->notifyObservers(NULL); } - else if ((this->action != ACTION_NONE) + else if ((this->action != CANNED_MESSAGE_ACTION_NONE) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; DEBUG_MSG("First touch.\n"); } - else if (this->action == ACTION_PRESSED) + else if (this->action == CANNED_MESSAGE_ACTION_SELECT) { sendText( NODENUM_BROADCAST, @@ -86,105 +85,70 @@ int32_t CannedMessagePlugin::runOnce() this->currentMessageIndex = -1; return 2000; } - else if (this->action == ACTION_UP) + else if (this->action == CANNED_MESSAGE_ACTION_UP) { this->currentMessageIndex = getPrevIndex(); DEBUG_MSG("MOVE UP. Current message:%ld\n", millis()); } - else if (this->action == ACTION_DOWN) + else if (this->action == CANNED_MESSAGE_ACTION_DOWN) { this->currentMessageIndex = this->getNextIndex(); DEBUG_MSG("MOVE DOWN. Current message:%ld\n", 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); } return 30000; } -void CannedMessagePlugin::select() +String CannedMessagePlugin::getCurrentMessage() { - this->action = ACTION_PRESSED; - runned(millis()); - setInterval(20); + return cannedMessagePluginMessages[this->currentMessageIndex]; +} +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; } -/** - * @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() +int CannedMessagePlugin::getNextIndex() { -#ifdef ROTARY_MODE - // CW rotation (at least on most common rotary encoders) - int currentLevelA = digitalRead(PIN_A); - if (this->rotaryLevelA == currentLevelA) + if (this->currentMessageIndex >= + (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) { - return; + return 0; } - this->rotaryLevelA = currentLevelA; - bool pinARaising = currentLevelA == HIGH; - if (pinARaising && (this->rotaryLevelB == LOW)) + else { - if (this->rotaryStateCCW == EVENT_CLEARED) - { - 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; - } - } + return this->currentMessageIndex + 1; } - 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 - // CW rotation (at least on most common rotary encoders) - int currentLevelB = digitalRead(PIN_B); - if (this->rotaryLevelB == currentLevelB) + if (this->currentMessageIndex <= 0) { - return; + return + sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1; } - this->rotaryLevelB = currentLevelB; - bool pinBRaising = currentLevelB == HIGH; - if (pinBRaising && (this->rotaryLevelA == LOW)) + else { - if (this->rotaryStateCW == EVENT_CLEARED) - { - 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; - } - } + return this->currentMessageIndex - 1; } - else if (!pinBRaising && (this->rotaryLevelA == HIGH)) - { - // Logic to prevent bouncing. - this->rotaryStateCW = EVENT_CLEARED; - } -#endif - runned(millis()); - setInterval(50); -} +} \ No newline at end of file diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 0b22ccc13..588716bf8 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -1,18 +1,13 @@ #pragma once #include "SinglePortPlugin.h" - -enum cannedMessagePluginRotaryStateType -{ - EVENT_OCCURRED, - EVENT_CLEARED -}; +#include "input/HardwareInput.h" enum cannedMessagePluginActionType { - ACTION_NONE, - ACTION_PRESSED, - ACTION_UP, - ACTION_DOWN + CANNED_MESSAGE_ACTION_NONE, + CANNED_MESSAGE_ACTION_SELECT, + CANNED_MESSAGE_ACTION_UP, + CANNED_MESSAGE_ACTION_DOWN }; enum cannedMessagePluginSendigState @@ -41,31 +36,20 @@ class CannedMessagePlugin : public Observable, private concurrency::OSThread { + CallbackObserver inputObserver = + CallbackObserver( + this, &CannedMessagePlugin::handleInputEvent); public: - CannedMessagePlugin(); - void select(); - void directionA(); - void directionB(); - String getCurrentMessage() - { - return cannedMessagePluginMessages[this->currentMessageIndex]; - } - String getPrevMessage() - { - 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; - } + CannedMessagePlugin( + Observable *input); + String getCurrentMessage(); + String getPrevMessage(); + String getNextMessage(); + bool shouldDraw(); + cannedMessagePluginSendigState getSendingState(); + void eventUp(); + void eventDown(); + void eventSelect(); protected: @@ -76,40 +60,12 @@ class CannedMessagePlugin : const char* message, bool wantReplies); - int getNextIndex() - { - if (this->currentMessageIndex >= - (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) - { - return 0; - } - else - { - return this->currentMessageIndex + 1; - } - } + int getNextIndex(); + int getPrevIndex(); - int getPrevIndex() - { - if (this->currentMessageIndex <= 0) - { - return - sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1; - } - else - { - return this->currentMessageIndex - 1; - } - } + int handleInputEvent(const InputEvent *event); - // TODO: make this configurable - 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; + volatile cannedMessagePluginActionType action = CANNED_MESSAGE_ACTION_NONE; int currentMessageIndex = -1; cannedMessagePluginSendigState sendingState = SENDING_STATE_NONE; }; diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 36eddf5fd..32b67998d 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -9,6 +9,7 @@ #include "plugins/RoutingPlugin.h" #include "plugins/AdminPlugin.h" #include "plugins/CannedMessagePlugin.h" +#include "plugins/input/RotaryEncoderInterruptImpl1.h" #ifndef NO_ESP32 #include "plugins/esp32/SerialPlugin.h" #include "plugins/esp32/EnvironmentalMeasurementPlugin.h" @@ -31,7 +32,11 @@ void setupPlugins() new RemoteHardwarePlugin(); 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 // Only run on an esp32 based device. diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index 9bee8cae3..2d6236e69 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -6,32 +6,13 @@ #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( + const char *name, 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") + concurrency::OSThread(name) { this->_pinA = pinA; this->_pinB = pinB; @@ -53,19 +34,33 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( int32_t RotaryEncoderInterruptBase::runOnce() { - if (this->action == ACTION_PRESSED) + if (this->action == ROTARY_ACTION_PRESSED) { InputEvent e; - e.inputEvent = INPUT_EVENT_SELECT; + e.inputEvent = this->_eventPressed; 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; } void RotaryEncoderInterruptBase::intPressHandler() { - this->action = ACTION_PRESSED; + this->action = ROTARY_ACTION_PRESSED; runned(millis()); setInterval(20); } @@ -91,20 +86,20 @@ void RotaryEncoderInterruptBase::intAHandler() bool pinARaising = currentLevelA == HIGH; if (pinARaising && (this->rotaryLevelB == LOW)) { - if (this->rotaryStateCCW == EVENT_CLEARED) + if (this->rotaryStateCCW == ROTARY_EVENT_CLEARED) { - this->rotaryStateCCW = EVENT_OCCURRED; - if ((this->action == ACTION_NONE) - || (this->action == ACTION_CCW)) + this->rotaryStateCCW = ROTARY_EVENT_OCCURRED; + if ((this->action == ROTARY_ACTION_NONE) + || (this->action == ROTARY_ACTION_CCW)) { - this->action = ACTION_CW; + this->action = ROTARY_ACTION_CW; } } } else if (!pinARaising && (this->rotaryLevelB == HIGH)) { // Logic to prevent bouncing. - this->rotaryStateCCW = EVENT_CLEARED; + this->rotaryStateCCW = ROTARY_EVENT_CLEARED; } runned(millis()); setInterval(50); @@ -122,20 +117,20 @@ void RotaryEncoderInterruptBase::intBHandler() bool pinBRaising = currentLevelB == HIGH; if (pinBRaising && (this->rotaryLevelA == LOW)) { - if (this->rotaryStateCW == EVENT_CLEARED) + if (this->rotaryStateCW == ROTARY_EVENT_CLEARED) { - this->rotaryStateCW = EVENT_OCCURRED; - if ((this->action == ACTION_NONE) - || (this->action == ACTION_CCW)) + this->rotaryStateCW = ROTARY_EVENT_OCCURRED; + if ((this->action == ROTARY_ACTION_NONE) + || (this->action == ROTARY_ACTION_CCW)) { - this->action = ACTION_CW; + this->action = ROTARY_ACTION_CW; } } } else if (!pinBRaising && (this->rotaryLevelA == HIGH)) { // Logic to prevent bouncing. - this->rotaryStateCW = EVENT_CLEARED; + this->rotaryStateCW = ROTARY_EVENT_CLEARED; } runned(millis()); setInterval(50); diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h index 09d00bcb2..110596cce 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.h +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -1,28 +1,30 @@ #pragma once +//#include +//#include "Observer.h" #include "SinglePortPlugin.h" #include "HardwareInput.h" enum RotaryEncoderInterruptBaseStateType { - EVENT_OCCURRED, - EVENT_CLEARED + ROTARY_EVENT_OCCURRED, + ROTARY_EVENT_CLEARED }; enum RotaryEncoderInterruptBaseActionType { - ACTION_NONE, - ACTION_PRESSED, - ACTION_CW, - ACTION_CCW + ROTARY_ACTION_NONE, + ROTARY_ACTION_PRESSED, + ROTARY_ACTION_CW, + ROTARY_ACTION_CCW }; class RotaryEncoderInterruptBase : - public SinglePortPlugin, public Observable, private concurrency::OSThread { public: RotaryEncoderInterruptBase( + const char *name, uint8_t pinA, uint8_t pinB, uint8_t pinPress, char eventCw, char eventCcw, char eventPressed, // std::function onIntA, std::function onIntB, std::function onIntPress); @@ -33,11 +35,11 @@ class RotaryEncoderInterruptBase : protected: virtual int32_t runOnce(); - volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = EVENT_CLEARED; - volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = EVENT_CLEARED; + volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = ROTARY_EVENT_CLEARED; + volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = ROTARY_EVENT_CLEARED; volatile int rotaryLevelA = LOW; volatile int rotaryLevelB = LOW; - volatile RotaryEncoderInterruptBaseActionType action = ACTION_NONE; + volatile RotaryEncoderInterruptBaseActionType action = ROTARY_ACTION_NONE; private: uint8_t _pinA; @@ -45,6 +47,4 @@ class RotaryEncoderInterruptBase : char _eventCw; char _eventCcw; char _eventPressed; -}; - -RotaryEncoderInterruptBase *RotaryEncoderInterruptBase; \ No newline at end of file +}; \ No newline at end of file diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp new file mode 100644 index 000000000..6c90b18da --- /dev/null +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp @@ -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() +{ + +} diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.h b/src/plugins/input/RotaryEncoderInterruptImpl1.h new file mode 100644 index 000000000..17613e8e3 --- /dev/null +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.h @@ -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; \ No newline at end of file From fbd5b8b721e9c9abd86fe9d4e538d1da12eb947d Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Sun, 9 Jan 2022 21:14:23 +0100 Subject: [PATCH 08/17] Canned message bugfixes. --- src/plugins/CannedMessagePlugin.cpp | 4 ++++ src/plugins/input/RotaryEncoderInterruptBase.cpp | 10 ++++++++-- src/plugins/input/RotaryEncoderInterruptImpl1.cpp | 7 +++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index a6ebf3bae..43a411985 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -20,16 +20,19 @@ int CannedMessagePlugin::handleInputEvent(const InputEvent *event) bool validEvent = false; if (event->inputEvent == INPUT_EVENT_UP) { + DEBUG_MSG("Canned message event UP\n"); this->action = CANNED_MESSAGE_ACTION_UP; validEvent = true; } if (event->inputEvent == INPUT_EVENT_DOWN) { + DEBUG_MSG("Canned message event DOWN\n"); this->action = CANNED_MESSAGE_ACTION_DOWN; validEvent = true; } if (event->inputEvent == INPUT_EVENT_SELECT) { + DEBUG_MSG("Canned message event Select\n"); this->action = CANNED_MESSAGE_ACTION_SELECT; validEvent = true; } @@ -63,6 +66,7 @@ void CannedMessagePlugin::sendText(NodeNum dest, int32_t CannedMessagePlugin::runOnce() { + DEBUG_MSG("Check status\n"); if (this->sendingState == SENDING_STATE_ACTIVE) { // TODO: might have some feedback of sendig state diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index 2d6236e69..49143dedf 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -30,6 +30,8 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( attachInterrupt(this->_pinB, onIntB, CHANGE); this->rotaryLevelA = digitalRead(this->_pinA); this->rotaryLevelB = digitalRead(this->_pinB); + DEBUG_MSG("Rotary initialized (%d, %d, %d)\n", + this->_pinA, this->_pinB, pinPress); } int32_t RotaryEncoderInterruptBase::runOnce() @@ -42,12 +44,14 @@ int32_t RotaryEncoderInterruptBase::runOnce() } else if (this->action == ROTARY_ACTION_CW) { + DEBUG_MSG("Rotary event CW\n"); InputEvent e; e.inputEvent = this->_eventCw; this->notifyObservers(&e); } else if (this->action == ROTARY_ACTION_CCW) { + DEBUG_MSG("Rotary event CW\n"); InputEvent e; e.inputEvent = this->_eventCcw; this->notifyObservers(&e); @@ -90,9 +94,10 @@ void RotaryEncoderInterruptBase::intAHandler() { this->rotaryStateCCW = ROTARY_EVENT_OCCURRED; if ((this->action == ROTARY_ACTION_NONE) - || (this->action == ROTARY_ACTION_CCW)) + || (this->action == ROTARY_ACTION_CW)) { - this->action = ROTARY_ACTION_CW; + this->action = ROTARY_ACTION_CCW; + DEBUG_MSG("Rotary action CCW\n"); } } } @@ -124,6 +129,7 @@ void RotaryEncoderInterruptBase::intBHandler() || (this->action == ROTARY_ACTION_CCW)) { this->action = ROTARY_ACTION_CW; + DEBUG_MSG("Rotary action CW\n"); } } } diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp index 6c90b18da..ebd8e8309 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp @@ -13,18 +13,17 @@ RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1( RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntPressed) { - } void RotaryEncoderInterruptImpl1::handleIntA() { - + rotaryEncoderInterruptImpl1->intAHandler(); } void RotaryEncoderInterruptImpl1::handleIntB() { - + rotaryEncoderInterruptImpl1->intBHandler(); } void RotaryEncoderInterruptImpl1::handleIntPressed() { - + rotaryEncoderInterruptImpl1->intPressHandler(); } From b832b82ec6dcb777c24bf117aafbbad0ab57c424 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 11 Jan 2022 13:12:04 +0100 Subject: [PATCH 09/17] Use init() instead of constructor. --- src/plugins/Plugins.cpp | 3 ++- .../input/RotaryEncoderInterruptBase.cpp | 17 ++++++++++++----- src/plugins/input/RotaryEncoderInterruptBase.h | 7 ++++--- .../input/RotaryEncoderInterruptImpl1.cpp | 17 +++++++++++------ src/plugins/input/RotaryEncoderInterruptImpl1.h | 10 +++++++++- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 32b67998d..cd6df21e9 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -33,7 +33,8 @@ void setupPlugins() new RemoteHardwarePlugin(); new ReplyPlugin(); rotaryEncoderInterruptImpl1 = - new RotaryEncoderInterruptImpl1( + new RotaryEncoderInterruptImpl1(); + rotaryEncoderInterruptImpl1->init( 22, 23, 21, INPUT_EVENT_UP, INPUT_EVENT_DOWN, INPUT_EVENT_SELECT); cannedMessagePlugin = new CannedMessagePlugin(rotaryEncoderInterruptImpl1); diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index 49143dedf..6d775b761 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -7,12 +7,17 @@ */ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( - const char *name, + const char *name) : + concurrency::OSThread(name) +{ + +} + +void RotaryEncoderInterruptBase::init( 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)()) : - concurrency::OSThread(name) +// std::function onIntA, std::function onIntB, std::function onIntPress) : + void (*onIntA)(), void (*onIntB)(), void (*onIntPress)()) { this->_pinA = pinA; this->_pinB = pinB; @@ -20,20 +25,22 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( 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); DEBUG_MSG("Rotary initialized (%d, %d, %d)\n", this->_pinA, this->_pinB, pinPress); } + int32_t RotaryEncoderInterruptBase::runOnce() { if (this->action == ROTARY_ACTION_PRESSED) diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h index 110596cce..7a5cef984 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.h +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -24,9 +24,10 @@ class RotaryEncoderInterruptBase : { public: RotaryEncoderInterruptBase( - const char *name, - uint8_t pinA, uint8_t pinB, uint8_t pinPress, - char eventCw, char eventCcw, char eventPressed, + const char *name); + void init( + 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(); diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp index ebd8e8309..5c66d2c11 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp @@ -2,17 +2,22 @@ RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1; -RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1( - uint8_t pinA, uint8_t pinB, uint8_t pinPress, - char eventCw, char eventCcw, char eventPressed) : +RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1() : RotaryEncoderInterruptBase( - "rotEnc1", + "rotEnc1") +{ +} + +void RotaryEncoderInterruptImpl1::init( + uint8_t pinA, uint8_t pinB, uint8_t pinPress, + char eventCw, char eventCcw, char eventPressed) +{ + RotaryEncoderInterruptBase::init( pinA, pinB, pinPress, eventCw, eventCcw, eventPressed, RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, - RotaryEncoderInterruptImpl1::handleIntPressed) -{ + RotaryEncoderInterruptImpl1::handleIntPressed); } void RotaryEncoderInterruptImpl1::handleIntA() diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.h b/src/plugins/input/RotaryEncoderInterruptImpl1.h index 17613e8e3..61d9195d8 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.h +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.h @@ -1,11 +1,19 @@ #pragma once #include "RotaryEncoderInterruptBase.h" +/** + * @brief The idea behind this class to have static methods for the event handlers. + * Check attachInterrupt() at RotaryEncoderInteruptBase.cpp + * Technically you can have as many rotary encoders hardver attached + * to your device as you wish, but you always need to have separate event + * handlers, thus you need to have a RotaryEncoderInterrupt implementation. + */ class RotaryEncoderInterruptImpl1 : public RotaryEncoderInterruptBase { public: - RotaryEncoderInterruptImpl1( + RotaryEncoderInterruptImpl1(); + void init( uint8_t pinA, uint8_t pinB, uint8_t pinPress, char eventCw, char eventCcw, char eventPressed); static void handleIntA(); From f5004a66a1a83e784c1ec186142d4e6e6733d610 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:02:55 +0100 Subject: [PATCH 10/17] Introduce InputBroker --- src/plugins/CannedMessagePlugin.cpp | 14 ++++++++++---- src/plugins/CannedMessagePlugin.h | 3 +-- src/plugins/Plugins.cpp | 4 +++- src/plugins/input/HardwareInput.h | 2 ++ src/plugins/input/InputBroker.cpp | 17 +++++++++++++++++ src/plugins/input/InputBroker.h | 19 +++++++++++++++++++ .../input/RotaryEncoderInterruptBase.cpp | 17 +++++++++++------ .../input/RotaryEncoderInterruptBase.h | 6 +++--- .../input/RotaryEncoderInterruptImpl1.cpp | 2 ++ 9 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 src/plugins/input/InputBroker.cpp create mode 100644 src/plugins/input/InputBroker.h diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 43a411985..4591761e5 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -1,22 +1,28 @@ #include "configuration.h" #include "CannedMessagePlugin.h" #include "MeshService.h" -#include "main.h" +#include "input/InputBroker.h" #include CannedMessagePlugin *cannedMessagePlugin; -CannedMessagePlugin::CannedMessagePlugin( - Observable *input) +CannedMessagePlugin::CannedMessagePlugin() : SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessagePlugin") { - this->inputObserver.observe(input); + if (true) // if module enabled + { + this->inputObserver.observe(inputBroker); + } } int CannedMessagePlugin::handleInputEvent(const InputEvent *event) { + if (false) // test event->origin + { + return 0; + } bool validEvent = false; if (event->inputEvent == INPUT_EVENT_UP) { diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 588716bf8..93bdc2cce 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -40,8 +40,7 @@ class CannedMessagePlugin : CallbackObserver( this, &CannedMessagePlugin::handleInputEvent); public: - CannedMessagePlugin( - Observable *input); + CannedMessagePlugin(); String getCurrentMessage(); String getPrevMessage(); String getNextMessage(); diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index cd6df21e9..09416a724 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -9,6 +9,7 @@ #include "plugins/RoutingPlugin.h" #include "plugins/AdminPlugin.h" #include "plugins/CannedMessagePlugin.h" +#include "plugins/input/InputBroker.h" #include "plugins/input/RotaryEncoderInterruptImpl1.h" #ifndef NO_ESP32 #include "plugins/esp32/SerialPlugin.h" @@ -22,6 +23,7 @@ */ void setupPlugins() { + inputBroker = new InputBroker(); adminPlugin = new AdminPlugin(); nodeInfoPlugin = new NodeInfoPlugin(); positionPlugin = new PositionPlugin(); @@ -37,7 +39,7 @@ void setupPlugins() rotaryEncoderInterruptImpl1->init( 22, 23, 21, INPUT_EVENT_UP, INPUT_EVENT_DOWN, INPUT_EVENT_SELECT); - cannedMessagePlugin = new CannedMessagePlugin(rotaryEncoderInterruptImpl1); + cannedMessagePlugin = new CannedMessagePlugin(); #ifndef NO_ESP32 // Only run on an esp32 based device. diff --git a/src/plugins/input/HardwareInput.h b/src/plugins/input/HardwareInput.h index ebc04447c..22ab84615 100644 --- a/src/plugins/input/HardwareInput.h +++ b/src/plugins/input/HardwareInput.h @@ -1,5 +1,6 @@ #pragma once +#define INPUT_EVENT_NULL 0 #define INPUT_EVENT_UP 17 #define INPUT_EVENT_DOWN 18 #define INPUT_EVENT_LEFT 19 @@ -9,5 +10,6 @@ #define INPUT_EVENT_CANCEL 24 typedef struct _InputEvent { + const char* origin; char inputEvent; } InputEvent; \ No newline at end of file diff --git a/src/plugins/input/InputBroker.cpp b/src/plugins/input/InputBroker.cpp new file mode 100644 index 000000000..165f16f75 --- /dev/null +++ b/src/plugins/input/InputBroker.cpp @@ -0,0 +1,17 @@ +#include "InputBroker.h" + +InputBroker *inputBroker; + +InputBroker::InputBroker() +{ +}; + +void InputBroker::registerOrigin(Observable *origin) +{ + this->inputEventObserver.observe(origin); +} + +int InputBroker::handleInputEvent(const InputEvent *event) +{ + this->notifyObservers(event); +} \ No newline at end of file diff --git a/src/plugins/input/InputBroker.h b/src/plugins/input/InputBroker.h new file mode 100644 index 000000000..2493cfedf --- /dev/null +++ b/src/plugins/input/InputBroker.h @@ -0,0 +1,19 @@ +#pragma once +#include "Observer.h" +#include "HardwareInput.h" + +class InputBroker : + public Observable +{ + CallbackObserver inputEventObserver = + CallbackObserver(this, &InputBroker::handleInputEvent); + + public: + InputBroker(); + void registerOrigin(Observable *origin); + + protected: + int handleInputEvent(const InputEvent *event); +}; + +extern InputBroker *inputBroker; \ No newline at end of file diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index 6d775b761..31dfe0142 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -10,7 +10,7 @@ RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( const char *name) : concurrency::OSThread(name) { - + this->_originName = name; } void RotaryEncoderInterruptBase::init( @@ -43,26 +43,31 @@ void RotaryEncoderInterruptBase::init( int32_t RotaryEncoderInterruptBase::runOnce() { + InputEvent e; + e.inputEvent = INPUT_EVENT_NULL; + e.origin = this->_originName; + if (this->action == ROTARY_ACTION_PRESSED) { - InputEvent e; + DEBUG_MSG("Rotary event Press\n"); e.inputEvent = this->_eventPressed; - this->notifyObservers(&e); } else if (this->action == ROTARY_ACTION_CW) { DEBUG_MSG("Rotary event CW\n"); - InputEvent e; e.inputEvent = this->_eventCw; - this->notifyObservers(&e); } else if (this->action == ROTARY_ACTION_CCW) { DEBUG_MSG("Rotary event CW\n"); - InputEvent e; e.inputEvent = this->_eventCcw; + } + + if (e.inputEvent != INPUT_EVENT_NULL) + { this->notifyObservers(&e); } + this->action = ROTARY_ACTION_NONE; return 30000; diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h index 7a5cef984..1f07f58b4 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.h +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -1,7 +1,6 @@ #pragma once -//#include -//#include "Observer.h" -#include "SinglePortPlugin.h" + +#include "SinglePortPlugin.h" // TODO: what header file to include? #include "HardwareInput.h" enum RotaryEncoderInterruptBaseStateType @@ -48,4 +47,5 @@ class RotaryEncoderInterruptBase : char _eventCw; char _eventCcw; char _eventPressed; + const char *_originName; }; \ No newline at end of file diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp index 5c66d2c11..638868c3a 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp @@ -1,4 +1,5 @@ #include "RotaryEncoderInterruptImpl1.h" +#include "InputBroker.h" RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1; @@ -18,6 +19,7 @@ void RotaryEncoderInterruptImpl1::init( RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntPressed); + inputBroker->registerOrigin(this); } void RotaryEncoderInterruptImpl1::handleIntA() From 3fa00f603b90d9fbbe7c3123fdc5c05966de51e2 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Wed, 12 Jan 2022 09:26:42 +0100 Subject: [PATCH 11/17] Make all variables configurable. --- src/mesh/generated/radioconfig.pb.h | 49 +++++++++ src/plugins/CannedMessagePlugin.cpp | 101 ++++++++++++++---- src/plugins/CannedMessagePlugin.h | 21 ++-- src/plugins/Plugins.cpp | 4 +- src/plugins/input/HardwareInput.h | 15 --- src/plugins/input/InputBroker.cpp | 1 + src/plugins/input/InputBroker.h | 5 +- .../input/RotaryEncoderInterruptBase.cpp | 9 +- .../input/RotaryEncoderInterruptBase.h | 2 +- .../input/RotaryEncoderInterruptImpl1.cpp | 21 +++- .../input/RotaryEncoderInterruptImpl1.h | 4 +- 11 files changed, 165 insertions(+), 67 deletions(-) delete mode 100644 src/plugins/input/HardwareInput.h diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index 255b5a88e..732467274 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -79,6 +79,17 @@ typedef enum _PositionFlags { PositionFlags_POS_TIMESTAMP = 256 } PositionFlags; +typedef enum _InputEventChar { + InputEventChar_NULL = 0, + InputEventChar_UP = 17, + InputEventChar_DOWN = 18, + InputEventChar_LEFT = 19, + InputEventChar_RIGHT = 20, + InputEventChar_SELECT = 10, + InputEventChar_BACK = 27, + InputEventChar_CANCEL = 24 +} InputEventChar; + typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType { RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0, RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 = 1 @@ -157,6 +168,17 @@ typedef struct _RadioConfig_UserPreferences { char mqtt_password[32]; bool is_lora_tx_disabled; bool is_power_saving; + bool rotary1_enabled; + uint32_t rotary1_pin_a; + uint32_t rotary1_pin_b; + uint32_t rotary1_pin_press; + InputEventChar rotary1_event_cw; + InputEventChar rotary1_event_ccw; + InputEventChar rotary1_event_press; + bool canned_message_plugin_enabled; + char canned_message_plugin_allow_input_origin[16]; + char canned_message_plugin_messages[1024]; + bool canned_message_plugin_send_bell; } RadioConfig_UserPreferences; typedef struct _RadioConfig { @@ -190,6 +212,10 @@ typedef struct _RadioConfig { #define _PositionFlags_MAX PositionFlags_POS_TIMESTAMP #define _PositionFlags_ARRAYSIZE ((PositionFlags)(PositionFlags_POS_TIMESTAMP+1)) +#define _InputEventChar_MIN InputEventChar_NULL +#define _InputEventChar_MAX InputEventChar_BACK +#define _InputEventChar_ARRAYSIZE ((InputEventChar)(InputEventChar_BACK+1)) + #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20+1)) @@ -276,6 +302,17 @@ extern "C" { #define RadioConfig_UserPreferences_mqtt_password_tag 156 #define RadioConfig_UserPreferences_is_lora_tx_disabled_tag 157 #define RadioConfig_UserPreferences_is_power_saving_tag 158 +#define RadioConfig_UserPreferences_rotary1_enabled_tag 160 +#define RadioConfig_UserPreferences_rotary1_pin_a_tag 161 +#define RadioConfig_UserPreferences_rotary1_pin_b_tag 162 +#define RadioConfig_UserPreferences_rotary1_pin_press_tag 163 +#define RadioConfig_UserPreferences_rotary1_event_cw_tag 164 +#define RadioConfig_UserPreferences_rotary1_event_ccw_tag 165 +#define RadioConfig_UserPreferences_rotary1_event_press_tag 166 +#define RadioConfig_UserPreferences_canned_message_plugin_enabled_tag 170 +#define RadioConfig_UserPreferences_canned_message_plugin_allow_input_origin_tag 171 +#define RadioConfig_UserPreferences_canned_message_plugin_messages_tag 172 +#define RadioConfig_UserPreferences_canned_message_plugin_send_bell_tag 173 #define RadioConfig_preferences_tag 1 /* Struct field encoding specification for nanopb */ @@ -356,6 +393,18 @@ X(a, STATIC, SINGULAR, STRING, mqtt_username, 155) \ X(a, STATIC, SINGULAR, STRING, mqtt_password, 156) \ X(a, STATIC, SINGULAR, BOOL, is_lora_tx_disabled, 157) \ X(a, STATIC, SINGULAR, BOOL, is_power_saving, 158) +X(a, STATIC, SINGULAR, STRING, mqtt_password, 156) \ +X(a, STATIC, SINGULAR, BOOL, rotary1_enabled, 160) \ +X(a, STATIC, SINGULAR, UINT32, rotary1_pin_a, 161) \ +X(a, STATIC, SINGULAR, UINT32, rotary1_pin_b, 162) \ +X(a, STATIC, SINGULAR, UINT32, rotary1_pin_press, 163) \ +X(a, STATIC, SINGULAR, UENUM, rotary1_event_cw, 164) \ +X(a, STATIC, SINGULAR, UENUM, rotary1_event_ccw, 165) \ +X(a, STATIC, SINGULAR, UENUM, rotary1_event_press, 166) \ +X(a, STATIC, SINGULAR, BOOL, canned_message_plugin_enabled, 170) \ +X(a, STATIC, SINGULAR, STRING, canned_message_plugin_allow_input_origin, 171) \ +X(a, STATIC, SINGULAR, STRING, canned_message_plugin_messages, 172) \ +X(a, STATIC, SINGULAR, BOOL, canned_message_plugin_send_bell, 173) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 4591761e5..25cdff6d2 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -1,7 +1,6 @@ #include "configuration.h" #include "CannedMessagePlugin.h" #include "MeshService.h" -#include "input/InputBroker.h" #include @@ -11,32 +10,89 @@ CannedMessagePlugin::CannedMessagePlugin() : SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessagePlugin") { - if (true) // if module enabled + if (radioConfig.preferences.canned_message_plugin_enabled) { + if(this->splitConfiguredMessages() <= 0) + { + radioConfig.preferences.canned_message_plugin_enabled = false; + DEBUG_MSG("CannedMessagePlugin: No messages are configured. Plugin is disabled\n"); + return; + } this->inputObserver.observe(inputBroker); } } +/** + * @brief Items in array this->messages will be set to be pointing on the right + * starting points of the string radioConfig.preferences.canned_message_plugin_messages + * + * @return int Returns the number of messages found. + */ +int CannedMessagePlugin::splitConfiguredMessages() +{ + int messageIndex = 0; + int i = 0; + this->messages[messageIndex++] = radioConfig.preferences.canned_message_plugin_messages; + int upTo = strlen(radioConfig.preferences.canned_message_plugin_messages) - 1; + + while (i < upTo) + { + if (radioConfig.preferences.canned_message_plugin_messages[i] == '|') + { + // Message ending found, replace it with string-end character. + radioConfig.preferences.canned_message_plugin_messages[i] = '\0'; + DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex-1, this->messages[messageIndex-1]); + + if (messageIndex >= CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT) + { + this->messagesCount = messageIndex; + return this->messagesCount; + } + + // Next message starts after pipe (|) just found. + this->messages[messageIndex++] = + (radioConfig.preferences.canned_message_plugin_messages + i + 1); + } + i += 1; + } + if (strlen(this->messages[messageIndex-1]) > 0) + { + DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex-1, this->messages[messageIndex-1]); + this->messagesCount = messageIndex; + } + else + { + this->messagesCount = messageIndex-1; + } + + return this->messagesCount; +} + int CannedMessagePlugin::handleInputEvent(const InputEvent *event) { - if (false) // test event->origin + if ( + (strlen(radioConfig.preferences.canned_message_plugin_allow_input_origin) > 0) && + (strcmp(radioConfig.preferences.canned_message_plugin_allow_input_origin, event->origin) != 0) && + (strcmp(radioConfig.preferences.canned_message_plugin_allow_input_origin, "_any") != 0)) { + // Event origin is not accepted. return 0; } + bool validEvent = false; - if (event->inputEvent == INPUT_EVENT_UP) + if (event->inputEvent == static_cast(InputEventChar_UP)) { DEBUG_MSG("Canned message event UP\n"); this->action = CANNED_MESSAGE_ACTION_UP; validEvent = true; } - if (event->inputEvent == INPUT_EVENT_DOWN) + if (event->inputEvent == static_cast(InputEventChar_DOWN)) { DEBUG_MSG("Canned message event DOWN\n"); this->action = CANNED_MESSAGE_ACTION_DOWN; validEvent = true; } - if (event->inputEvent == INPUT_EVENT_SELECT) + if (event->inputEvent == static_cast(InputEventChar_SELECT)) { DEBUG_MSG("Canned message event Select\n"); this->action = CANNED_MESSAGE_ACTION_SELECT; @@ -61,6 +117,13 @@ void CannedMessagePlugin::sendText(NodeNum dest, p->to = dest; p->decoded.payload.size = strlen(message); memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); + if (radioConfig.preferences.canned_message_plugin_send_bell) + { + p->decoded.payload.bytes[p->decoded.payload.size-1] = 7; // Bell character + p->decoded.payload.bytes[p->decoded.payload.size] = '\0'; // Bell character + p->decoded.payload.size++; + } + // PacketId prevPacketId = p->id; // In case we need it later. @@ -72,6 +135,10 @@ void CannedMessagePlugin::sendText(NodeNum dest, int32_t CannedMessagePlugin::runOnce() { + if (!radioConfig.preferences.canned_message_plugin_enabled) + { + return 30000; // TODO: should return MAX_VAL + } DEBUG_MSG("Check status\n"); if (this->sendingState == SENDING_STATE_ACTIVE) { @@ -89,7 +156,7 @@ int32_t CannedMessagePlugin::runOnce() { sendText( NODENUM_BROADCAST, - cannedMessagePluginMessages[this->currentMessageIndex], + this->messages[this->currentMessageIndex], true); this->sendingState = SENDING_STATE_ACTIVE; this->currentMessageIndex = -1; @@ -98,14 +165,12 @@ int32_t CannedMessagePlugin::runOnce() else if (this->action == CANNED_MESSAGE_ACTION_UP) { this->currentMessageIndex = getPrevIndex(); - DEBUG_MSG("MOVE UP. Current message:%ld\n", - millis()); + DEBUG_MSG("MOVE UP"); } else if (this->action == CANNED_MESSAGE_ACTION_DOWN) { this->currentMessageIndex = this->getNextIndex(); - DEBUG_MSG("MOVE DOWN. Current message:%ld\n", - millis()); + DEBUG_MSG("MOVE DOWN"); } if (this->action != CANNED_MESSAGE_ACTION_NONE) { @@ -113,20 +178,20 @@ int32_t CannedMessagePlugin::runOnce() this->notifyObservers(NULL); } - return 30000; + return 30000; // TODO: should return MAX_VAL } String CannedMessagePlugin::getCurrentMessage() { - return cannedMessagePluginMessages[this->currentMessageIndex]; + return this->messages[this->currentMessageIndex]; } String CannedMessagePlugin::getPrevMessage() { - return cannedMessagePluginMessages[this->getPrevIndex()]; + return this->messages[this->getPrevIndex()]; } String CannedMessagePlugin::getNextMessage() { - return cannedMessagePluginMessages[this->getNextIndex()]; + return this->messages[this->getNextIndex()]; } bool CannedMessagePlugin::shouldDraw() { @@ -139,8 +204,7 @@ cannedMessagePluginSendigState CannedMessagePlugin::getSendingState() int CannedMessagePlugin::getNextIndex() { - if (this->currentMessageIndex >= - (sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN) - 1) + if (this->currentMessageIndex >= (this->messagesCount -1)) { return 0; } @@ -154,8 +218,7 @@ int CannedMessagePlugin::getPrevIndex() { if (this->currentMessageIndex <= 0) { - return - sizeof(cannedMessagePluginMessages) / CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN - 1; + return this->messagesCount - 1; } else { diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 93bdc2cce..a4efecc95 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -1,6 +1,6 @@ #pragma once #include "SinglePortPlugin.h" -#include "input/HardwareInput.h" +#include "input/InputBroker.h" enum cannedMessagePluginActionType { @@ -16,20 +16,7 @@ enum cannedMessagePluginSendigState SENDING_STATE_ACTIVE }; -#define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN 50 - -static char cannedMessagePluginMessages[][CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_LEN] = -{ - "Need helping hand", - "Help me with saw", - "I need an alpinist", - "I need ambulance", - "I'm fine", - "I'm already waiting", - "I will be late", - "I couldn't join", - "We have company" -}; +#define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT 50 class CannedMessagePlugin : public SinglePortPlugin, @@ -59,6 +46,7 @@ class CannedMessagePlugin : const char* message, bool wantReplies); + int splitConfiguredMessages(); int getNextIndex(); int getPrevIndex(); @@ -67,6 +55,9 @@ class CannedMessagePlugin : volatile cannedMessagePluginActionType action = CANNED_MESSAGE_ACTION_NONE; int currentMessageIndex = -1; cannedMessagePluginSendigState sendingState = SENDING_STATE_NONE; + + char *messages[CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT]; + int messagesCount = 0; }; extern CannedMessagePlugin *cannedMessagePlugin; diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 09416a724..55c0fd84a 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -36,9 +36,7 @@ void setupPlugins() new ReplyPlugin(); rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1(); - rotaryEncoderInterruptImpl1->init( - 22, 23, 21, - INPUT_EVENT_UP, INPUT_EVENT_DOWN, INPUT_EVENT_SELECT); + rotaryEncoderInterruptImpl1->init(); cannedMessagePlugin = new CannedMessagePlugin(); #ifndef NO_ESP32 diff --git a/src/plugins/input/HardwareInput.h b/src/plugins/input/HardwareInput.h deleted file mode 100644 index 22ab84615..000000000 --- a/src/plugins/input/HardwareInput.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#define INPUT_EVENT_NULL 0 -#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 { - const char* origin; - char inputEvent; -} InputEvent; \ No newline at end of file diff --git a/src/plugins/input/InputBroker.cpp b/src/plugins/input/InputBroker.cpp index 165f16f75..a71da5778 100644 --- a/src/plugins/input/InputBroker.cpp +++ b/src/plugins/input/InputBroker.cpp @@ -14,4 +14,5 @@ void InputBroker::registerOrigin(Observable *origin) int InputBroker::handleInputEvent(const InputEvent *event) { this->notifyObservers(event); + return 0; } \ No newline at end of file diff --git a/src/plugins/input/InputBroker.h b/src/plugins/input/InputBroker.h index 2493cfedf..a79855131 100644 --- a/src/plugins/input/InputBroker.h +++ b/src/plugins/input/InputBroker.h @@ -1,7 +1,10 @@ #pragma once #include "Observer.h" -#include "HardwareInput.h" +typedef struct _InputEvent { + const char* origin; + char inputEvent; +} InputEvent; class InputBroker : public Observable { diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index 31dfe0142..e21ec3a14 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -1,11 +1,6 @@ #include "configuration.h" #include "RotaryEncoderInterruptBase.h" -/*#define PIN_PUSH 21 -#define PIN_A 22 -#define PIN_B 23 -*/ - RotaryEncoderInterruptBase::RotaryEncoderInterruptBase( const char *name) : concurrency::OSThread(name) @@ -44,7 +39,7 @@ void RotaryEncoderInterruptBase::init( int32_t RotaryEncoderInterruptBase::runOnce() { InputEvent e; - e.inputEvent = INPUT_EVENT_NULL; + e.inputEvent = InputEventChar_NULL; e.origin = this->_originName; if (this->action == ROTARY_ACTION_PRESSED) @@ -63,7 +58,7 @@ int32_t RotaryEncoderInterruptBase::runOnce() e.inputEvent = this->_eventCcw; } - if (e.inputEvent != INPUT_EVENT_NULL) + if (e.inputEvent != InputEventChar_NULL) { this->notifyObservers(&e); } diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h index 1f07f58b4..607d6e8bd 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.h +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -1,7 +1,7 @@ #pragma once #include "SinglePortPlugin.h" // TODO: what header file to include? -#include "HardwareInput.h" +#include "InputBroker.h" enum RotaryEncoderInterruptBaseStateType { diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp index 638868c3a..1f1434ab4 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.cpp @@ -9,10 +9,25 @@ RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1() : { } -void RotaryEncoderInterruptImpl1::init( - uint8_t pinA, uint8_t pinB, uint8_t pinPress, - char eventCw, char eventCcw, char eventPressed) +void RotaryEncoderInterruptImpl1::init() { + if (!radioConfig.preferences.rotary1_enabled) + { + // Input device is disabled. + return; + } + + uint8_t pinA = radioConfig.preferences.rotary1_pin_a; + uint8_t pinB = radioConfig.preferences.rotary1_pin_b; + uint8_t pinPress = radioConfig.preferences.rotary1_pin_press; + char eventCw = + static_cast(radioConfig.preferences.rotary1_event_cw); + char eventCcw = + static_cast(radioConfig.preferences.rotary1_event_ccw); + char eventPressed = + static_cast(radioConfig.preferences.rotary1_event_press); + + //radioConfig.preferences.ext_notification_plugin_output RotaryEncoderInterruptBase::init( pinA, pinB, pinPress, eventCw, eventCcw, eventPressed, diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.h b/src/plugins/input/RotaryEncoderInterruptImpl1.h index 61d9195d8..aeafeeca7 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.h +++ b/src/plugins/input/RotaryEncoderInterruptImpl1.h @@ -13,9 +13,7 @@ class RotaryEncoderInterruptImpl1 : { public: RotaryEncoderInterruptImpl1(); - void init( - uint8_t pinA, uint8_t pinB, uint8_t pinPress, - char eventCw, char eventCcw, char eventPressed); + void init(); static void handleIntA(); static void handleIntB(); static void handleIntPressed(); From 0f1c424731ce79d653c39c99a7631e82d6797f27 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Wed, 12 Jan 2022 22:50:37 +0100 Subject: [PATCH 12/17] Rotary get rid of duplicate methods. --- .../input/RotaryEncoderInterruptBase.cpp | 80 +++++++++---------- .../input/RotaryEncoderInterruptBase.h | 6 ++ 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index e21ec3a14..bff01edb2 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -76,15 +76,6 @@ void RotaryEncoderInterruptBase::intPressHandler() 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) @@ -94,27 +85,11 @@ void RotaryEncoderInterruptBase::intAHandler() return; } this->rotaryLevelA = currentLevelA; - bool pinARaising = currentLevelA == HIGH; - if (pinARaising && (this->rotaryLevelB == LOW)) - { - if (this->rotaryStateCCW == ROTARY_EVENT_CLEARED) - { - this->rotaryStateCCW = ROTARY_EVENT_OCCURRED; - if ((this->action == ROTARY_ACTION_NONE) - || (this->action == ROTARY_ACTION_CW)) - { - this->action = ROTARY_ACTION_CCW; - DEBUG_MSG("Rotary action CCW\n"); - } - } - } - else if (!pinARaising && (this->rotaryLevelB == HIGH)) - { - // Logic to prevent bouncing. - this->rotaryStateCCW = ROTARY_EVENT_CLEARED; - } - runned(millis()); - setInterval(50); + intHandler( + currentLevelA == HIGH, + this->rotaryLevelB, + ROTARY_ACTION_CCW, + this->rotaryStateCCW); } void RotaryEncoderInterruptBase::intBHandler() @@ -126,25 +101,50 @@ void RotaryEncoderInterruptBase::intBHandler() return; } this->rotaryLevelB = currentLevelB; - bool pinBRaising = currentLevelB == HIGH; - if (pinBRaising && (this->rotaryLevelA == LOW)) + this->rotaryStateCW = intHandler( + currentLevelB == HIGH, + this->rotaryLevelA, + ROTARY_ACTION_CW, + this->rotaryStateCW); +} + +/** + * @brief Rotary action implementation. + * We assume, the following pin setup: + * A --|| + * GND --||]======== + * B --|| + * + * @return The new state for rotary pin. + */ +RotaryEncoderInterruptBaseStateType RotaryEncoderInterruptBase::intHandler( + bool actualPinRaising, + int otherPinLevel, + RotaryEncoderInterruptBaseActionType action, + RotaryEncoderInterruptBaseStateType state) +{ + RotaryEncoderInterruptBaseStateType newState = + state; + if (actualPinRaising && (otherPinLevel == LOW)) { - if (this->rotaryStateCW == ROTARY_EVENT_CLEARED) + if (state == ROTARY_EVENT_CLEARED) { - this->rotaryStateCW = ROTARY_EVENT_OCCURRED; - if ((this->action == ROTARY_ACTION_NONE) - || (this->action == ROTARY_ACTION_CCW)) + newState = ROTARY_EVENT_OCCURRED; + if ((this->action != ROTARY_ACTION_PRESSED) + && (this->action != action)) { - this->action = ROTARY_ACTION_CW; - DEBUG_MSG("Rotary action CW\n"); + this->action = action; + DEBUG_MSG("Rotary action\n"); } } } - else if (!pinBRaising && (this->rotaryLevelA == HIGH)) + else if (!actualPinRaising && (otherPinLevel == HIGH)) { // Logic to prevent bouncing. - this->rotaryStateCW = ROTARY_EVENT_CLEARED; + newState = ROTARY_EVENT_CLEARED; } runned(millis()); setInterval(50); + + return newState; } diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/plugins/input/RotaryEncoderInterruptBase.h index 607d6e8bd..ae4af5262 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.h +++ b/src/plugins/input/RotaryEncoderInterruptBase.h @@ -35,6 +35,12 @@ class RotaryEncoderInterruptBase : protected: virtual int32_t runOnce(); + RotaryEncoderInterruptBaseStateType intHandler( + bool actualPinRaising, + int otherPinLevel, + RotaryEncoderInterruptBaseActionType action, + RotaryEncoderInterruptBaseStateType state); + volatile RotaryEncoderInterruptBaseStateType rotaryStateCW = ROTARY_EVENT_CLEARED; volatile RotaryEncoderInterruptBaseStateType rotaryStateCCW = ROTARY_EVENT_CLEARED; volatile int rotaryLevelA = LOW; From f7c8cabdfe6889e9a466b69239390df4144fc995 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Thu, 13 Jan 2022 08:08:16 +0100 Subject: [PATCH 13/17] Screen drawing routine goes to Plugin. --- src/graphics/Screen.cpp | 30 ++++------------------- src/plugins/CannedMessagePlugin.cpp | 37 ++++++++++++++++++++++++++++- src/plugins/CannedMessagePlugin.h | 4 ++++ 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b9f0e3ff3..4e4a98454 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -286,28 +286,6 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); } -static void drawCannedMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - displayedNodeNum = 0; // Not currently showing a node pane - - if (cannedMessagePlugin->getSendingState() == SENDING_STATE_NONE) - { - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage()); - display->setFont(FONT_MEDIUM); - display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage()); - display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage()); - } - else - { - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(FONT_MEDIUM); - display->drawString(display->getWidth()/2 + x, 0 + y + 12, "Sending..."); - } -} - /// Draw a series of fields in a column, wrapping to multiple colums if needed static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) { @@ -843,6 +821,7 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (textMessagePlugin) textMessageObserver.observe(textMessagePlugin); + // TODO: find a nicer way to notify screen about refresh if (cannedMessagePlugin) cannedMessageObserver.observe(cannedMessagePlugin); } @@ -1021,9 +1000,6 @@ void Screen::setFrames() if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) { normalFrames[numframes++] = drawTextMessageFrame; } - if (cannedMessagePlugin->shouldDraw()) { - normalFrames[numframes++] = drawCannedMessageFrame; - } // then all the nodes // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens @@ -1490,7 +1466,9 @@ int Screen::handleCannedMessage(const meshtastic::Status *packet) if (showingNormalScreen) { setFrames(); // Regen the list of screens (will show new text message) } - ui.switchToFrame(1); + // TODO: We might also want switch to corresponding frame, + // but we don't know the exact frame number. + //ui.switchToFrame(0); return 0; } diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 25cdff6d2..251a9c2f5 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -4,6 +4,12 @@ #include +// TODO: reuse defined from Screen.cpp +#define FONT_SMALL ArialMT_Plain_10 +#define FONT_MEDIUM ArialMT_Plain_16 +#define FONT_LARGE ArialMT_Plain_24 + + CannedMessagePlugin *cannedMessagePlugin; CannedMessagePlugin::CannedMessagePlugin() @@ -160,6 +166,7 @@ int32_t CannedMessagePlugin::runOnce() true); this->sendingState = SENDING_STATE_ACTIVE; this->currentMessageIndex = -1; + this->notifyObservers(NULL); return 2000; } else if (this->action == CANNED_MESSAGE_ACTION_UP) @@ -195,6 +202,10 @@ String CannedMessagePlugin::getNextMessage() } bool CannedMessagePlugin::shouldDraw() { + if (!radioConfig.preferences.canned_message_plugin_enabled) + { + return false; + } return (currentMessageIndex != -1) || (this->sendingState != SENDING_STATE_NONE); } cannedMessagePluginSendigState CannedMessagePlugin::getSendingState() @@ -224,4 +235,28 @@ int CannedMessagePlugin::getPrevIndex() { return this->currentMessageIndex - 1; } -} \ No newline at end of file +} + +void CannedMessagePlugin::drawFrame( + OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + displayedNodeNum = 0; // Not currently showing a node pane + + if (cannedMessagePlugin->getSendingState() == SENDING_STATE_NONE) + { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage()); + display->setFont(FONT_MEDIUM); + display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage()); + display->setFont(FONT_SMALL); + display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage()); + } + else + { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(FONT_MEDIUM); + display->drawString(display->getWidth()/2 + x, 0 + y + 12, "Sending..."); + } +} + diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index a4efecc95..91c394466 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -1,4 +1,5 @@ #pragma once +#include #include "SinglePortPlugin.h" #include "input/InputBroker.h" @@ -51,6 +52,9 @@ class CannedMessagePlugin : int getPrevIndex(); int handleInputEvent(const InputEvent *event); + virtual bool wantUIFrame() { return this->shouldDraw(); } + virtual void drawFrame( + OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); volatile cannedMessagePluginActionType action = CANNED_MESSAGE_ACTION_NONE; int currentMessageIndex = -1; From c5b95ed3c0daf55d1f459e3bb94fc0fe330d3989 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:19:36 +0100 Subject: [PATCH 14/17] Screen update event. --- src/graphics/Screen.cpp | 24 ++++++++++++------- src/graphics/Screen.h | 7 +++--- src/mesh/MeshPlugin.cpp | 15 ++++++++++++ src/mesh/MeshPlugin.h | 10 ++++++++ src/plugins/CannedMessagePlugin.cpp | 8 ++++--- src/plugins/CannedMessagePlugin.h | 4 +++- .../input/RotaryEncoderInterruptBase.cpp | 6 ++--- 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 4e4a98454..c7bc0fe36 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -34,7 +34,6 @@ along with this program. If not, see . #include "mesh-pb-constants.h" #include "mesh/Channels.h" #include "plugins/TextMessagePlugin.h" -#include "plugins/CannedMessagePlugin.h" #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -821,9 +820,9 @@ void Screen::setup() nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (textMessagePlugin) textMessageObserver.observe(textMessagePlugin); - // TODO: find a nicer way to notify screen about refresh - if (cannedMessagePlugin) - cannedMessageObserver.observe(cannedMessagePlugin); + + // Plugins can notify screen about refresh + MeshPlugin::observeUIEvents(&uiFrameEventObserver); } void Screen::forceDisplay() @@ -1461,14 +1460,21 @@ int Screen::handleTextMessage(const MeshPacket *packet) return 0; } -int Screen::handleCannedMessage(const meshtastic::Status *packet) +int Screen::handleUIFrameEvent(const UIFrameEvent *event) { if (showingNormalScreen) { - setFrames(); // Regen the list of screens (will show new text message) + if (event->frameChanged) + { + setFrames(); // Regen the list of screens (will show new text message) + } + else if (event->needRedraw) + { + setFastFramerate(); + // TODO: We might also want switch to corresponding frame, + // but we don't know the exact frame number. + //ui.switchToFrame(0); + } } - // TODO: We might also want switch to corresponding frame, - // but we don't know the exact frame number. - //ui.switchToFrame(0); return 0; } diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 68815328e..10b2b6ff7 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -40,6 +40,7 @@ class Screen #include "concurrency/OSThread.h" #include "power.h" #include +#include "mesh/MeshPlugin.h" // 0 to 255, though particular variants might define different defaults #ifndef BRIGHTNESS_DEFAULT @@ -90,8 +91,8 @@ class Screen : public concurrency::OSThread CallbackObserver(this, &Screen::handleStatusUpdate); CallbackObserver textMessageObserver = CallbackObserver(this, &Screen::handleTextMessage); - CallbackObserver cannedMessageObserver = - CallbackObserver(this, &Screen::handleCannedMessage); + CallbackObserver uiFrameEventObserver = + CallbackObserver(this, &Screen::handleUIFrameEvent); public: Screen(uint8_t address, int sda = -1, int scl = -1); @@ -220,7 +221,7 @@ class Screen : public concurrency::OSThread int handleStatusUpdate(const meshtastic::Status *arg); int handleTextMessage(const MeshPacket *arg); - int handleCannedMessage(const meshtastic::Status *arg); + int handleUIFrameEvent(const UIFrameEvent *arg); /// Used to force (super slow) eink displays to draw critical frames void forceDisplay(); diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 85988ec7b..985a7060f 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -235,3 +235,18 @@ std::vector MeshPlugin::GetMeshPluginsWithUIFrames() } return pluginsWithUIFrames; } + +void MeshPlugin::observeUIEvents( + Observer *observer) +{ + std::vector pluginsWithUIFrames; + for (auto i = plugins->begin(); i != plugins->end(); ++i) { + auto &pi = **i; + Observable *observable = + pi.getUIFrameObservable(); + if (observable != NULL) { + DEBUG_MSG("Plugin wants a UI Frame\n"); + observer->observe(observable); + } + } +} diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 0143814a7..b6bcf22b8 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -21,6 +21,14 @@ enum class ProcessMessage STOP = 1, }; +/* + * This struct is used by Screen to figure out whether screen frame should be updated. + */ +typedef struct _UIFrameEvent { + bool frameChanged; + bool needRedraw; +} UIFrameEvent; + /** A baseclass for any mesh "plugin". * * A plugin allows you to add new features to meshtastic device code, without needing to know messaging details. @@ -48,6 +56,7 @@ class MeshPlugin static void callPlugins(const MeshPacket &mp, RxSource src = RX_SRC_RADIO); static std::vector GetMeshPluginsWithUIFrames(); + static void observeUIEvents(Observer *observer); #ifndef NO_SCREEN virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } #endif @@ -119,6 +128,7 @@ class MeshPlugin * @return true if you want to be alloced a UI screen frame */ virtual bool wantUIFrame() { return false; } + virtual Observable* getUIFrameObservable() { return NULL; } MeshPacket *allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 251a9c2f5..26ef6cce8 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -146,17 +146,19 @@ int32_t CannedMessagePlugin::runOnce() return 30000; // TODO: should return MAX_VAL } DEBUG_MSG("Check status\n"); + UIFrameEvent e = {false, true}; if (this->sendingState == SENDING_STATE_ACTIVE) { // TODO: might have some feedback of sendig state this->sendingState = SENDING_STATE_NONE; - this->notifyObservers(NULL); + this->notifyObservers(&e); } else if ((this->action != CANNED_MESSAGE_ACTION_NONE) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; DEBUG_MSG("First touch.\n"); + e.frameChanged = true; } else if (this->action == CANNED_MESSAGE_ACTION_SELECT) { @@ -166,7 +168,7 @@ int32_t CannedMessagePlugin::runOnce() true); this->sendingState = SENDING_STATE_ACTIVE; this->currentMessageIndex = -1; - this->notifyObservers(NULL); + this->notifyObservers(&e); return 2000; } else if (this->action == CANNED_MESSAGE_ACTION_UP) @@ -182,7 +184,7 @@ int32_t CannedMessagePlugin::runOnce() if (this->action != CANNED_MESSAGE_ACTION_NONE) { this->action = CANNED_MESSAGE_ACTION_NONE; - this->notifyObservers(NULL); + this->notifyObservers(&e); } return 30000; // TODO: should return MAX_VAL diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 91c394466..10b8f6742 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -17,11 +17,12 @@ enum cannedMessagePluginSendigState SENDING_STATE_ACTIVE }; + #define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT 50 class CannedMessagePlugin : public SinglePortPlugin, - public Observable, + public Observable, private concurrency::OSThread { CallbackObserver inputObserver = @@ -53,6 +54,7 @@ class CannedMessagePlugin : int handleInputEvent(const InputEvent *event); virtual bool wantUIFrame() { return this->shouldDraw(); } + virtual Observable* getUIFrameObservable() { return this; } virtual void drawFrame( OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index bff01edb2..e4635eb1a 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -65,7 +65,7 @@ int32_t RotaryEncoderInterruptBase::runOnce() this->action = ROTARY_ACTION_NONE; - return 30000; + return 30000; // TODO: technically this can be MAX_INT } @@ -73,7 +73,7 @@ void RotaryEncoderInterruptBase::intPressHandler() { this->action = ROTARY_ACTION_PRESSED; runned(millis()); - setInterval(20); + setInterval(20); // TODO: this modifies a non-volatile variable! } void RotaryEncoderInterruptBase::intAHandler() @@ -144,7 +144,7 @@ RotaryEncoderInterruptBaseStateType RotaryEncoderInterruptBase::intHandler( newState = ROTARY_EVENT_CLEARED; } runned(millis()); - setInterval(50); + setInterval(50); // TODO: this modifies a non-volatile variable! return newState; } From 33f08364e400387e4c12d96528729eb697f53f53 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:51:36 +0100 Subject: [PATCH 15/17] Reset plugin in case of inactivity. --- src/plugins/CannedMessagePlugin.cpp | 32 ++++++++++++++----- src/plugins/CannedMessagePlugin.h | 4 +-- .../input/RotaryEncoderInterruptBase.cpp | 6 ++-- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 26ef6cce8..2107a8768 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -2,13 +2,13 @@ #include "CannedMessagePlugin.h" #include "MeshService.h" -#include - // TODO: reuse defined from Screen.cpp #define FONT_SMALL ArialMT_Plain_10 #define FONT_MEDIUM ArialMT_Plain_16 #define FONT_LARGE ArialMT_Plain_24 +// Remove Canned message screen if no action is taken for some milliseconds +#define INACTIVATE_AFTER_MS 20000 CannedMessagePlugin *cannedMessagePlugin; @@ -38,8 +38,10 @@ int CannedMessagePlugin::splitConfiguredMessages() { int messageIndex = 0; int i = 0; - this->messages[messageIndex++] = radioConfig.preferences.canned_message_plugin_messages; - int upTo = strlen(radioConfig.preferences.canned_message_plugin_messages) - 1; + this->messages[messageIndex++] = + radioConfig.preferences.canned_message_plugin_messages; + int upTo = + strlen(radioConfig.preferences.canned_message_plugin_messages) - 1; while (i < upTo) { @@ -47,7 +49,8 @@ int CannedMessagePlugin::splitConfiguredMessages() { // Message ending found, replace it with string-end character. radioConfig.preferences.canned_message_plugin_messages[i] = '\0'; - DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex-1, this->messages[messageIndex-1]); + DEBUG_MSG("CannedMessage %d is: '%s'\n", + messageIndex-1, this->messages[messageIndex-1]); if (messageIndex >= CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT) { @@ -63,7 +66,8 @@ int CannedMessagePlugin::splitConfiguredMessages() } if (strlen(this->messages[messageIndex-1]) > 0) { - DEBUG_MSG("CannedMessage %d is: '%s'\n", messageIndex-1, this->messages[messageIndex-1]); + DEBUG_MSG("CannedMessage %d is: '%s'\n", + messageIndex-1, this->messages[messageIndex-1]); this->messagesCount = messageIndex; } else @@ -108,8 +112,7 @@ int CannedMessagePlugin::handleInputEvent(const InputEvent *event) if (validEvent) { // Let runOnce to be called immediately. - runned(millis()); - setInterval(0); + setIntervalFromNow(0); } return 0; @@ -121,6 +124,7 @@ void CannedMessagePlugin::sendText(NodeNum dest, { MeshPacket *p = allocDataPacket(); p->to = dest; + p->want_ack = true; p->decoded.payload.size = strlen(message); memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); if (radioConfig.preferences.canned_message_plugin_send_bell) @@ -151,6 +155,7 @@ int32_t CannedMessagePlugin::runOnce() { // TODO: might have some feedback of sendig state this->sendingState = SENDING_STATE_NONE; + e.frameChanged = true; this->notifyObservers(&e); } else if ((this->action != CANNED_MESSAGE_ACTION_NONE) @@ -183,8 +188,19 @@ int32_t CannedMessagePlugin::runOnce() } if (this->action != CANNED_MESSAGE_ACTION_NONE) { + this->lastTouchMillis = millis(); this->action = CANNED_MESSAGE_ACTION_NONE; this->notifyObservers(&e); + return INACTIVATE_AFTER_MS; + } + if ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS) + { + // Reset plugin + DEBUG_MSG("Reset due the lack of activity.\n"); + e.frameChanged = true; + this->currentMessageIndex = -1; + this->sendingState = SENDING_STATE_NONE; + this->notifyObservers(&e); } return 30000; // TODO: should return MAX_VAL diff --git a/src/plugins/CannedMessagePlugin.h b/src/plugins/CannedMessagePlugin.h index 10b8f6742..eab516b85 100644 --- a/src/plugins/CannedMessagePlugin.h +++ b/src/plugins/CannedMessagePlugin.h @@ -1,5 +1,4 @@ #pragma once -#include #include "SinglePortPlugin.h" #include "input/InputBroker.h" @@ -41,7 +40,7 @@ class CannedMessagePlugin : protected: - virtual int32_t runOnce(); + virtual int32_t runOnce(); void sendText( NodeNum dest, @@ -64,6 +63,7 @@ class CannedMessagePlugin : char *messages[CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT]; int messagesCount = 0; + unsigned long lastTouchMillis = 0; }; extern CannedMessagePlugin *cannedMessagePlugin; diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/plugins/input/RotaryEncoderInterruptBase.cpp index e4635eb1a..2186936e4 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/plugins/input/RotaryEncoderInterruptBase.cpp @@ -72,8 +72,7 @@ int32_t RotaryEncoderInterruptBase::runOnce() void RotaryEncoderInterruptBase::intPressHandler() { this->action = ROTARY_ACTION_PRESSED; - runned(millis()); - setInterval(20); // TODO: this modifies a non-volatile variable! + setIntervalFromNow(20); // TODO: this modifies a non-volatile variable! } void RotaryEncoderInterruptBase::intAHandler() @@ -143,8 +142,7 @@ RotaryEncoderInterruptBaseStateType RotaryEncoderInterruptBase::intHandler( // Logic to prevent bouncing. newState = ROTARY_EVENT_CLEARED; } - runned(millis()); - setInterval(50); // TODO: this modifies a non-volatile variable! + setIntervalFromNow(50); // TODO: this modifies a non-volatile variable! return newState; } From 41de8a1309a7b5059611cb32975cb19e84aaf984 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:06:10 +0100 Subject: [PATCH 16/17] Relocate 'input' folder. --- src/{plugins => }/input/InputBroker.cpp | 4 ++-- src/{plugins => }/input/InputBroker.h | 4 ++-- src/{plugins => }/input/RotaryEncoderInterruptBase.cpp | 2 +- src/{plugins => }/input/RotaryEncoderInterruptBase.h | 0 src/{plugins => }/input/RotaryEncoderInterruptImpl1.cpp | 2 +- src/{plugins => }/input/RotaryEncoderInterruptImpl1.h | 0 src/plugins/CannedMessagePlugin.cpp | 4 ++-- src/plugins/Plugins.cpp | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) rename src/{plugins => }/input/InputBroker.cpp (62%) rename src/{plugins => }/input/InputBroker.h (84%) rename src/{plugins => }/input/RotaryEncoderInterruptBase.cpp (99%) rename src/{plugins => }/input/RotaryEncoderInterruptBase.h (100%) rename src/{plugins => }/input/RotaryEncoderInterruptImpl1.cpp (97%) rename src/{plugins => }/input/RotaryEncoderInterruptImpl1.h (100%) diff --git a/src/plugins/input/InputBroker.cpp b/src/input/InputBroker.cpp similarity index 62% rename from src/plugins/input/InputBroker.cpp rename to src/input/InputBroker.cpp index a71da5778..85e5a087f 100644 --- a/src/plugins/input/InputBroker.cpp +++ b/src/input/InputBroker.cpp @@ -6,9 +6,9 @@ InputBroker::InputBroker() { }; -void InputBroker::registerOrigin(Observable *origin) +void InputBroker::registerSource(Observable *source) { - this->inputEventObserver.observe(origin); + this->inputEventObserver.observe(source); } int InputBroker::handleInputEvent(const InputEvent *event) diff --git a/src/plugins/input/InputBroker.h b/src/input/InputBroker.h similarity index 84% rename from src/plugins/input/InputBroker.h rename to src/input/InputBroker.h index a79855131..e75b0c407 100644 --- a/src/plugins/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -2,7 +2,7 @@ #include "Observer.h" typedef struct _InputEvent { - const char* origin; + const char* source; char inputEvent; } InputEvent; class InputBroker : @@ -13,7 +13,7 @@ class InputBroker : public: InputBroker(); - void registerOrigin(Observable *origin); + void registerSource(Observable *source); protected: int handleInputEvent(const InputEvent *event); diff --git a/src/plugins/input/RotaryEncoderInterruptBase.cpp b/src/input/RotaryEncoderInterruptBase.cpp similarity index 99% rename from src/plugins/input/RotaryEncoderInterruptBase.cpp rename to src/input/RotaryEncoderInterruptBase.cpp index 2186936e4..ba825080f 100644 --- a/src/plugins/input/RotaryEncoderInterruptBase.cpp +++ b/src/input/RotaryEncoderInterruptBase.cpp @@ -40,7 +40,7 @@ int32_t RotaryEncoderInterruptBase::runOnce() { InputEvent e; e.inputEvent = InputEventChar_NULL; - e.origin = this->_originName; + e.source = this->_originName; if (this->action == ROTARY_ACTION_PRESSED) { diff --git a/src/plugins/input/RotaryEncoderInterruptBase.h b/src/input/RotaryEncoderInterruptBase.h similarity index 100% rename from src/plugins/input/RotaryEncoderInterruptBase.h rename to src/input/RotaryEncoderInterruptBase.h diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp b/src/input/RotaryEncoderInterruptImpl1.cpp similarity index 97% rename from src/plugins/input/RotaryEncoderInterruptImpl1.cpp rename to src/input/RotaryEncoderInterruptImpl1.cpp index 1f1434ab4..e0e904a0d 100644 --- a/src/plugins/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/input/RotaryEncoderInterruptImpl1.cpp @@ -34,7 +34,7 @@ void RotaryEncoderInterruptImpl1::init() RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntPressed); - inputBroker->registerOrigin(this); + inputBroker->registerSource(this); } void RotaryEncoderInterruptImpl1::handleIntA() diff --git a/src/plugins/input/RotaryEncoderInterruptImpl1.h b/src/input/RotaryEncoderInterruptImpl1.h similarity index 100% rename from src/plugins/input/RotaryEncoderInterruptImpl1.h rename to src/input/RotaryEncoderInterruptImpl1.h diff --git a/src/plugins/CannedMessagePlugin.cpp b/src/plugins/CannedMessagePlugin.cpp index 2107a8768..ba3cb02c9 100644 --- a/src/plugins/CannedMessagePlugin.cpp +++ b/src/plugins/CannedMessagePlugin.cpp @@ -82,10 +82,10 @@ int CannedMessagePlugin::handleInputEvent(const InputEvent *event) { if ( (strlen(radioConfig.preferences.canned_message_plugin_allow_input_origin) > 0) && - (strcmp(radioConfig.preferences.canned_message_plugin_allow_input_origin, event->origin) != 0) && + (strcmp(radioConfig.preferences.canned_message_plugin_allow_input_origin, event->source) != 0) && (strcmp(radioConfig.preferences.canned_message_plugin_allow_input_origin, "_any") != 0)) { - // Event origin is not accepted. + // Event source is not accepted. return 0; } diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 55c0fd84a..03ab1a8a0 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -1,4 +1,6 @@ #include "configuration.h" +#include "input/InputBroker.h" +#include "input/RotaryEncoderInterruptImpl1.h" #include "plugins/ExternalNotificationPlugin.h" #include "plugins/NodeInfoPlugin.h" #include "plugins/PositionPlugin.h" @@ -9,8 +11,6 @@ #include "plugins/RoutingPlugin.h" #include "plugins/AdminPlugin.h" #include "plugins/CannedMessagePlugin.h" -#include "plugins/input/InputBroker.h" -#include "plugins/input/RotaryEncoderInterruptImpl1.h" #ifndef NO_ESP32 #include "plugins/esp32/SerialPlugin.h" #include "plugins/esp32/EnvironmentalMeasurementPlugin.h" From a1f80f024e54af22e6f06d7a019f9f8f534b80a5 Mon Sep 17 00:00:00 2001 From: Balazs Kelemen <10376327+prampec@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:19:55 +0100 Subject: [PATCH 17/17] CannedMessagePlugin merge fix --- src/mesh/generated/admin.pb.h | 2 +- src/mesh/generated/radioconfig.pb.h | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index 2fced6a29..2468faa85 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -86,7 +86,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define AdminMessage_fields &AdminMessage_msg /* Maximum encoded size of messages (where known) */ -#define AdminMessage_size 535 +#define AdminMessage_size 1619 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index 732467274..3e6eea360 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -227,9 +227,9 @@ extern "C" { /* Initializer values for message structs */ #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0} +#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", "", 0} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0} +#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", "", 0} /* Field tags (for use in manual encoding/decoding) */ #define RadioConfig_UserPreferences_position_broadcast_secs_tag 1 @@ -392,8 +392,7 @@ X(a, STATIC, SINGULAR, UINT32, hop_limit, 154) \ X(a, STATIC, SINGULAR, STRING, mqtt_username, 155) \ X(a, STATIC, SINGULAR, STRING, mqtt_password, 156) \ X(a, STATIC, SINGULAR, BOOL, is_lora_tx_disabled, 157) \ -X(a, STATIC, SINGULAR, BOOL, is_power_saving, 158) -X(a, STATIC, SINGULAR, STRING, mqtt_password, 156) \ +X(a, STATIC, SINGULAR, BOOL, is_power_saving, 158) \ X(a, STATIC, SINGULAR, BOOL, rotary1_enabled, 160) \ X(a, STATIC, SINGULAR, UINT32, rotary1_pin_a, 161) \ X(a, STATIC, SINGULAR, UINT32, rotary1_pin_b, 162) \ @@ -416,8 +415,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg; #define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg /* Maximum encoded size of messages (where known) */ -#define RadioConfig_size 532 -#define RadioConfig_UserPreferences_size 529 +#define RadioConfig_size 1616 +#define RadioConfig_UserPreferences_size 1613 #ifdef __cplusplus } /* extern "C" */