Show specific frame when updating screen (#4264)

* Updated setFrames in Screen.cpp

Added code to attempt to revert back to the same frame that user was on prior to setFrame reload.

* Space added Screen.cpp

* Update Screen.cpp

Make screen to revert to Frame 0 if the originally displayed frame is no longer there.

* Update Screen.cpp

Inserted boolean holdPosition into setFrames to indicate the requirement to stay on the same frame ( if =true) or else it will switch to new frame .
Only Screen::handleStatusUpdate calls with setFrame(true). ( For Node Updates)
All other types of updates call as before setFrame(), so it will change focus as needed.

* Hold position, even if number of frames increases

* Hold position, if handling an outgoing text message

* Update Screen.cpp

* Reverted chnages related to devicestate.has_rx_text_message

* Reset to master

* CannedMessages only handles routing packets when waiting for ACK
Previously, this was calling Screen::setFrames at unexpected times

* Gather position info about screen frames while regenerating

* Make admin module observable
Notify only when relevant. Currently: only to handle remove_nodenum.

* Optionally specify which frame to focus when setFrames runs

* UIFrameEvent uses enum instead of multiple booleans

* Allow modules to request their own frame to be focussed
This is done internally by calling MeshModule::requestFocus()
Easier this way, insteady of passing the info in the UIFrameEvent:
* Modules don't always know whether they should be focussed until after the UIFrameEvent has been raised, in dramFrame
* Don't have to pass reference to module instance as parameter though several methods

* E-Ink screensaver uses FOCUS_PRESERVE
Previously, it had its own basic implementation of this.

* Spelling: regional variant

* trunk

* Fix HAS_SCREEN guarding

* More HAS_SCREEN guarding

---------

Co-authored-by: BIST <77391720+slash-bit@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: slash-bit <v-b2@live.com>
This commit is contained in:
todd-herbert 2024-07-12 11:51:26 +12:00 committed by GitHub
parent df194ca0f0
commit eabec5ae34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 239 additions and 51 deletions

View File

@ -41,6 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Channels.h" #include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h" #include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h" #include "meshUtils.h"
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h" #include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#include "sleep.h" #include "sleep.h"
@ -1725,6 +1726,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus); powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus);
adminMessageObserver.observe(adminModule);
if (textMessageModule) if (textMessageModule)
textMessageObserver.observe(textMessageModule); textMessageObserver.observe(textMessageModule);
if (inputBroker) if (inputBroker)
@ -1953,9 +1955,6 @@ void Screen::setWelcomeFrames()
/// Determine which screensaver frame to use, then set the FrameCallback /// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver) void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
{ {
// Remember current frame, restore position at power-on
uint8_t frameNumber = ui->getUiState()->currentFrame;
// Retain specified frame / overlay callback beyond scope of this method // Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame; static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay; static OverlayCallback screensaverOverlay;
@ -1994,8 +1993,7 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
// Prepare now for next frame, shown when display wakes // Prepare now for next frame, shown when display wakes
ui->setOverlays(NULL, 0); // Clear overlay ui->setOverlays(NULL, 0); // Clear overlay
setFrames(); // Return to normal display updates setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally
ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
// Pick a refresh method, for when display wakes // Pick a refresh method, for when display wakes
#ifdef EINK_HASQUIRK_GHOSTING #ifdef EINK_HASQUIRK_GHOSTING
@ -2006,9 +2004,13 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
} }
#endif #endif
// restore our regular frame list // Regenerate the normal set of frames, focusing a specific frame if requested
void Screen::setFrames() // Called when a frame should be added / removed, or custom frames should be cleared
void Screen::setFrames(FrameFocus focus)
{ {
uint8_t originalPosition = ui->getUiState()->currentFrame;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
LOG_DEBUG("showing standard frames\n"); LOG_DEBUG("showing standard frames\n");
showingNormalScreen = true; showingNormalScreen = true;
@ -2042,20 +2044,33 @@ void Screen::setFrames()
// is the same offset into the moduleFrames vector // is the same offset into the moduleFrames vector
// so that we can invoke the module's callback // so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) { for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
normalFrames[numframes++] = drawModuleFrame; // Draw the module frame, using the hack described above
normalFrames[numframes] = drawModuleFrame;
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
if (m->isRequestingFocus())
fsi.positions.focusedModule = numframes;
numframes++;
} }
LOG_DEBUG("Added modules. numframes: %d\n", numframes); LOG_DEBUG("Added modules. numframes: %d\n", numframes);
// If we have a critical fault, show it first // If we have a critical fault, show it first
if (error_code) fsi.positions.fault = numframes;
if (error_code) {
normalFrames[numframes++] = drawCriticalFaultFrame; normalFrames[numframes++] = drawCriticalFaultFrame;
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
}
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
#endif #endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules // If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
fsi.positions.textMessage = numframes;
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) { if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame; normalFrames[numframes++] = drawTextMessageFrame;
} }
@ -2070,11 +2085,14 @@ void Screen::setFrames()
// //
// Since frames are basic function pointers, we have to use a helper to // Since frames are basic function pointers, we have to use a helper to
// call a method on debugInfo object. // call a method on debugInfo object.
fsi.positions.log = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
fsi.positions.settings = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
fsi.positions.wifi = numframes;
#if HAS_WIFI && !defined(ARCH_PORTDUINO) #if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (isWifiAvailable()) { if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
@ -2082,6 +2100,7 @@ void Screen::setFrames()
} }
#endif #endif
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes); LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
ui->setFrames(normalFrames, numframes); ui->setFrames(normalFrames, numframes);
@ -2095,6 +2114,55 @@ void Screen::setFrames()
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed) // just changed)
// Focus on a specific frame, in the frame set we just created
switch (focus) {
case FOCUS_DEFAULT:
ui->switchToFrame(0); // First frame
break;
case FOCUS_FAULT:
ui->switchToFrame(fsi.positions.fault);
break;
case FOCUS_TEXTMESSAGE:
ui->switchToFrame(fsi.positions.textMessage);
break;
case FOCUS_MODULE:
// Whichever frame was marked by MeshModule::requestFocus(), if any
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.focusedModule);
break;
case FOCUS_PRESERVE:
// If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
FramesetInfo &oldFsi = this->framesetInfo;
if (originalPosition == oldFsi.positions.log)
ui->switchToFrame(fsi.positions.log);
else if (originalPosition == oldFsi.positions.settings)
ui->switchToFrame(fsi.positions.settings);
else if (originalPosition == oldFsi.positions.wifi)
ui->switchToFrame(fsi.positions.wifi);
// If frame count has decreased
else if (fsi.frameCount < oldFsi.frameCount) {
uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
// Move n frames backwards
if (numDropped <= originalPosition)
ui->switchToFrame(originalPosition - numDropped);
// Unless that would put us "out of bounds" (< 0)
else
ui->switchToFrame(0);
}
// If we're not sure exactly which frame we were on, at least return to the same frame number
// (node frames; module frames)
else
ui->switchToFrame(originalPosition);
break;
}
// Store the info about this frameset, for future setFrames calls
this->framesetInfo = fsi;
setFastFramerate(); // Draw ASAP setFastFramerate(); // Draw ASAP
} }
@ -2549,7 +2617,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) { switch (arg->getStatusType()) {
case STATUS_TYPE_NODE: case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens setFrames(FOCUS_PRESERVE); // Regen the list of screen frames (returning to same frame, if possible)
} }
nodeDB->updateGUI = false; nodeDB->updateGUI = false;
break; break;
@ -2561,23 +2629,33 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{ {
if (showingNormalScreen) { if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message) // Outgoing message
if (packet->from == 0)
setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
// Incoming message
else
setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
} }
return 0; return 0;
} }
// Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleUIFrameEvent(const UIFrameEvent *event)
{ {
if (showingNormalScreen) { if (showingNormalScreen) {
if (event->frameChanged) { // Regenerate the frameset, potentially honoring a module's internal requestFocus() call
setFrames(); // Regen the list of screens (will show new text message) if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
} else if (event->needRedraw) { setFrames(FOCUS_MODULE);
// Regenerate the frameset, while attempting to maintain focus on the current frame
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
setFrames(FOCUS_PRESERVE);
// Don't regenerate the frameset, just re-draw whatever is on screen ASAP
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
setFastFramerate(); setFastFramerate();
// TODO: We might also want switch to corresponding frame,
// but we don't know the exact frame number.
// ui->switchToFrame(0);
}
} }
return 0; return 0;
@ -2612,6 +2690,24 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0; return 0;
} }
int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
{
// Note: only selected admin messages notify this observer
// If you wish to handle a new type of message, you should modify AdminModule.cpp first
switch (arg->which_payload_variant) {
// Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag:
setFrames(FOCUS_PRESERVE);
break;
// Default no-op, in case the admin message observable gets used by other classes in future
default:
break;
}
return 0;
}
} // namespace graphics } // namespace graphics
#else #else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}

View File

@ -173,9 +173,11 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver = CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage); CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver = CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver<Screen, const InputEvent *> inputObserver = CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent); CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);
public: public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY); explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@ -394,6 +396,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg); int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg); int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg); int handleInputEvent(const InputEvent *arg);
int handleAdminMessage(const meshtastic_AdminMessage *arg);
/// Used to force (super slow) eink displays to draw critical frames /// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false); void forceDisplay(bool forceUiUpdate = false);
@ -450,8 +453,34 @@ class Screen : public concurrency::OSThread
void handleShowPrevFrame(); void handleShowPrevFrame();
void handlePrint(const char *text); void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen(); void handleStartFirmwareUpdateScreen();
/// Rebuilds our list of frames (screens) to default ones.
void setFrames(); // Info collected by setFrames method.
// Index location of specific frames. Used to apply the FrameFocus parameter of setFrames
struct FramesetInfo {
struct FramePositions {
uint8_t fault = 0;
uint8_t textMessage = 0;
uint8_t focusedModule = 0;
uint8_t log = 0;
uint8_t settings = 0;
uint8_t wifi = 0;
} positions;
uint8_t frameCount = 0;
} framesetInfo;
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
enum FrameFocus : uint8_t {
FOCUS_DEFAULT, // No specific frame
FOCUS_PRESERVE, // Return to the previous frame
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
};
// Regenerate the normal set of frames, focusing a specific frame if requested
// Call when a frame should be added / removed, or custom frames should be cleared
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
/// Try to start drawing ASAP /// Try to start drawing ASAP
void setFastFramerate(); void setFastFramerate();

View File

@ -285,3 +285,16 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht
} }
return handled; return handled;
} }
#if HAS_SCREEN
// Would our module like its frame to be focused after Screen::setFrames has regenerated the list of frames?
// Only considered if setFrames is triggered by a UIFrameEvent
bool MeshModule::isRequestingFocus()
{
if (_requestingFocus) {
_requestingFocus = false; // Consume the request
return true;
} else
return false;
}
#endif

View File

@ -35,10 +35,16 @@ enum class AdminMessageHandleResult {
/* /*
* This struct is used by Screen to figure out whether screen frame should be updated. * This struct is used by Screen to figure out whether screen frame should be updated.
*/ */
typedef struct _UIFrameEvent { struct UIFrameEvent {
bool frameChanged; // What do we actually want to happen?
bool needRedraw; enum Action {
} UIFrameEvent; REDRAW_ONLY, // Don't change which frames are show, just redraw, asap
REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus()
REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, attempting to remain on the same frame throughout
} action = REDRAW_ONLY;
// We might want to pass additional data inside this struct at some point
};
/** A baseclass for any mesh "module". /** A baseclass for any mesh "module".
* *
@ -73,6 +79,7 @@ class MeshModule
meshtastic_AdminMessage *response); meshtastic_AdminMessage *response);
#if HAS_SCREEN #if HAS_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
#endif #endif
protected: protected:
const char *name; const char *name;
@ -176,6 +183,19 @@ class MeshModule
return AdminMessageHandleResult::NOT_HANDLED; return AdminMessageHandleResult::NOT_HANDLED;
}; };
#if HAS_SCREEN
/** Request that our module's screen frame be focused when Screen::setFrames runs
* Only considered if Screen::setFrames is triggered via a UIFrameEvent
*
* Having this as a separate call, instead of part of the UIFrameEvent, allows the module to delay decision
* until drawFrame() is called. This required less restructuring.
*/
bool _requestingFocus = false;
void requestFocus() { _requestingFocus = true; }
#else
void requestFocus(){}; // No-op
#endif
private: private:
/** /**
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow

View File

@ -200,6 +200,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
case meshtastic_AdminMessage_remove_by_nodenum_tag: { case meshtastic_AdminMessage_remove_by_nodenum_tag: {
LOG_INFO("Client is receiving a remove_nodenum command.\n"); LOG_INFO("Client is receiving a remove_nodenum command.\n");
nodeDB->removeNodeByNum(r->remove_by_nodenum); nodeDB->removeNodeByNum(r->remove_by_nodenum);
this->notifyObservers(r); // Observed by screen
break; break;
} }
case meshtastic_AdminMessage_set_favorite_node_tag: { case meshtastic_AdminMessage_set_favorite_node_tag: {

View File

@ -7,7 +7,7 @@
/** /**
* Admin module for admin messages * Admin module for admin messages
*/ */
class AdminModule : public ProtobufModule<meshtastic_AdminMessage> class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Observable<const meshtastic_AdminMessage *>
{ {
public: public:
/** Constructor /** Constructor

View File

@ -148,8 +148,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
if (this->currentMessageIndex == 0) { if (this->currentMessageIndex == 0) {
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
UIFrameEvent e = {false, true}; requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
e.frameChanged = true; UIFrameEvent e;
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e); this->notifyObservers(&e);
return 0; return 0;
@ -166,8 +167,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
} }
} }
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
UIFrameEvent e = {false, true}; UIFrameEvent e;
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
#if !defined(T_WATCH_S3) && !defined(RAK14014) #if !defined(T_WATCH_S3) && !defined(RAK14014)
@ -353,6 +354,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
} }
if (validEvent) { if (validEvent) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
// Let runOnce to be called immediately. // Let runOnce to be called immediately.
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough. setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
@ -378,6 +381,11 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
p->decoded.payload.size++; p->decoded.payload.size++;
} }
// Only receive routing messages when expecting ACK for a canned message
// Prevents the canned message module from regenerating the screen's frameset at unexpected times,
// or raising a UIFrameEvent before another module has the chance
this->waitingForAck = true;
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh( service.sendToMesh(
@ -393,13 +401,13 @@ int32_t CannedMessageModule::runOnce()
return INT32_MAX; return INT32_MAX;
} }
// LOG_DEBUG("Check status\n"); // LOG_DEBUG("Check status\n");
UIFrameEvent e = {false, true}; UIFrameEvent e;
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) || if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) { (this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) {
// TODO: might have some feedback of sending state // TODO: might have some feedback of sending state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
temporaryMessage = ""; temporaryMessage = "";
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@ -412,7 +420,7 @@ int32_t CannedMessageModule::runOnce()
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
// Reset module // Reset module
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@ -449,7 +457,7 @@ int32_t CannedMessageModule::runOnce()
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
} }
} }
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@ -463,7 +471,7 @@ int32_t CannedMessageModule::runOnce()
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
this->currentMessageIndex = 0; this->currentMessageIndex = 0;
LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
if (this->messagesCount > 0) { if (this->messagesCount > 0) {
@ -567,7 +575,7 @@ int32_t CannedMessageModule::runOnce()
break; break;
} }
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
// display back to the default window // display back to the default window
case 0x08: // backspace case 0x08: // backspace
@ -706,8 +714,8 @@ int CannedMessageModule::getPrevIndex()
void CannedMessageModule::showTemporaryMessage(const String &message) void CannedMessageModule::showTemporaryMessage(const String &message)
{ {
temporaryMessage = message; temporaryMessage = message;
UIFrameEvent e = {false, true}; UIFrameEvent e;
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
notifyObservers(&e); notifyObservers(&e);
runState = CANNED_MESSAGE_RUN_STATE_MESSAGE; runState = CANNED_MESSAGE_RUN_STATE_MESSAGE;
// run this loop again in 2 seconds, next iteration will clear the display // run this loop again in 2 seconds, next iteration will clear the display
@ -914,11 +922,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
char buffer[50]; char buffer[50];
if (temporaryMessage.length() != 0) { if (temporaryMessage.length() != 0) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str()); LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str());
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage);
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
String displayString; String displayString;
@ -940,6 +950,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi); display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
} }
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending..."); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
@ -948,7 +959,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
#if defined(T_WATCH_S3) || defined(RAK14014) #if defined(T_WATCH_S3) || defined(RAK14014)
drawKeyboard(display, state, 0, 0); drawKeyboard(display, state, 0, 0);
#else #else
@ -1030,16 +1041,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp) ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{ {
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) { if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) {
// look for a request_id // look for a request_id
if (mp.decoded.request_id != 0) { if (mp.decoded.request_id != 0) {
UIFrameEvent e = {false, true}; UIFrameEvent e;
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED; this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id); this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
meshtastic_Routing decoded = meshtastic_Routing_init_default; meshtastic_Routing decoded = meshtastic_Routing_init_default;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
waitingForAck = false; // No longer want routing packets
this->notifyObservers(&e); this->notifyObservers(&e);
// run the next time 2 seconds later // run the next time 2 seconds later
setIntervalFromNow(2000); setIntervalFromNow(2000);

View File

@ -81,9 +81,8 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
} }
switch (p->decoded.portnum) { switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP: case meshtastic_PortNum_ROUTING_APP:
return true; return waitingForAck;
default: default:
return false; return false;
} }
@ -141,6 +140,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0}; ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0};
NodeNum incoming = NODENUM_BROADCAST; NodeNum incoming = NODENUM_BROADCAST;
bool ack = false; // True means ACK, false means NAK (error_reason != NONE) bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
bool waitingForAck = false; // Are currently interested in routing packets?
float lastRxSnr = 0; float lastRxSnr = 0;
int32_t lastRxRssi = 0; int32_t lastRxRssi = 0;

View File

@ -16,15 +16,31 @@ ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp)
auto &p = mp.decoded; auto &p = mp.decoded;
LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
#endif #endif
UIFrameEvent e = {true, true};
// We only store/display messages destined for us. // We only store/display messages destined for us.
// Keep a copy of the most recent text message. // Keep a copy of the most recent text message.
devicestate.rx_waypoint = mp; devicestate.rx_waypoint = mp;
devicestate.has_rx_waypoint = true; devicestate.has_rx_waypoint = true;
powerFSM.trigger(EVENT_RECEIVED_MSG); powerFSM.trigger(EVENT_RECEIVED_MSG);
#if HAS_SCREEN
UIFrameEvent e;
// New or updated waypoint: focus on this frame next time Screen::setFrames runs
if (shouldDraw()) {
requestFocus();
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
}
// Deleting an old waypoint: remove the frame quietly, don't change frame position if possible
else
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND;
notifyObservers(&e); notifyObservers(&e);
#endif
return ProcessMessage::CONTINUE; // Let others look at this message also if they want return ProcessMessage::CONTINUE; // Let others look at this message also if they want
} }

View File

@ -190,13 +190,13 @@ int32_t AudioModule::runOnce()
firstTime = false; firstTime = false;
} else { } else {
UIFrameEvent e = {false, true}; UIFrameEvent e;
// Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive.
if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) {
if (radio_state == RadioState::rx) { if (radio_state == RadioState::rx) {
LOG_INFO("PTT pressed, switching to TX\n"); LOG_INFO("PTT pressed, switching to TX\n");
radio_state = RadioState::tx; radio_state = RadioState::tx;
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e); this->notifyObservers(&e);
} }
} else { } else {
@ -209,7 +209,7 @@ int32_t AudioModule::runOnce()
} }
tx_encode_frame_index = sizeof(tx_header); tx_encode_frame_index = sizeof(tx_header);
radio_state = RadioState::rx; radio_state = RadioState::rx;
e.frameChanged = true; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e); this->notifyObservers(&e);
} }
} }