From df194ca0f06bb3546c6fb25c4ab1a40ec2702b74 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 11 Jul 2024 14:08:31 -0500 Subject: [PATCH 1/3] WM1110 SDK kit enter serial DFU and add deployment packages (#4266) * Switch default upload protocol to nrfutil so that pio generates zip deploy packages * Enter serial DFU on SDK board * Remove guard for DFU zip from SDK build * NRF_USE_SERIAL_DFU macro instead --- bin/build-nrf52.sh | 10 +++------- src/platform/nrf52/main-nrf52.cpp | 5 +++++ variants/wio-sdk-wm1110/platformio.ini | 2 +- variants/wio-sdk-wm1110/variant.h | 2 ++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index 077e2af35..cf4ca60cb 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -25,13 +25,9 @@ pio run --environment $1 # -v SRCELF=.pio/build/$1/firmware.elf cp $SRCELF $OUTDIR/$basename.elf -if (echo $1 | grep -q "wio-sdk-wm1110"); then - echo "Skipping dfu file" -else - echo "Generating NRF52 dfu file" - DFUPKG=.pio/build/$1/firmware.zip - cp $DFUPKG $OUTDIR/$basename-ota.zip -fi +echo "Generating NRF52 dfu file" +DFUPKG=.pio/build/$1/firmware.zip +cp $DFUPKG $OUTDIR/$basename-ota.zip echo "Generating NRF52 uf2 file" SRCHEX=.pio/build/$1/firmware.hex diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index c5b217f0e..7334f3a04 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -286,5 +286,10 @@ void clearBonds() void enterDfuMode() { +// SDK kit does not have native USB like almost all other NRF52 boards +#ifdef NRF_USE_SERIAL_DFU + enterSerialDfu(); +#else enterUf2Dfu(); +#endif } \ No newline at end of file diff --git a/variants/wio-sdk-wm1110/platformio.ini b/variants/wio-sdk-wm1110/platformio.ini index dc7d47310..766717428 100644 --- a/variants/wio-sdk-wm1110/platformio.ini +++ b/variants/wio-sdk-wm1110/platformio.ini @@ -20,7 +20,7 @@ debug_tool = jlink ; No need to reflash if the binary hasn't changed debug_load_mode = modified ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -upload_protocol = jlink +upload_protocol = nrfutil ;upload_protocol = stlink ; we prefer to stop in setup() because we are an 'ardiuno' app debug_init_break = tbreak setup diff --git a/variants/wio-sdk-wm1110/variant.h b/variants/wio-sdk-wm1110/variant.h index 8ad8c769a..8f66b1f8c 100644 --- a/variants/wio-sdk-wm1110/variant.h +++ b/variants/wio-sdk-wm1110/variant.h @@ -107,6 +107,8 @@ extern "C" { #define LR1110_GNSS_ANT_PIN (32 + 5) // P1.05 37 +#define NRF_USE_SERIAL_DFU + #ifdef __cplusplus } #endif From eabec5ae3451c0edc33979453c8af635d538c538 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 12 Jul 2024 11:51:26 +1200 Subject: [PATCH 2/3] 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 Co-authored-by: slash-bit --- src/graphics/Screen.cpp | 134 ++++++++++++++++++++++++---- src/graphics/Screen.h | 35 +++++++- src/mesh/MeshModule.cpp | 15 +++- src/mesh/MeshModule.h | 28 +++++- src/modules/AdminModule.cpp | 1 + src/modules/AdminModule.h | 2 +- src/modules/CannedMessageModule.cpp | 45 ++++++---- src/modules/CannedMessageModule.h | 6 +- src/modules/WaypointModule.cpp | 18 +++- src/modules/esp32/AudioModule.cpp | 6 +- 10 files changed, 239 insertions(+), 51 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 7f9c905a8..b2059b71c 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -41,6 +41,7 @@ along with this program. If not, see . #include "mesh/Channels.h" #include "mesh/generated/meshtastic/deviceonly.pb.h" #include "meshUtils.h" +#include "modules/AdminModule.h" #include "modules/ExternalNotificationModule.h" #include "modules/TextMessageModule.h" #include "sleep.h" @@ -1725,6 +1726,7 @@ void Screen::setup() powerStatusObserver.observe(&powerStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus); + adminMessageObserver.observe(adminModule); if (textMessageModule) textMessageObserver.observe(textMessageModule); if (inputBroker) @@ -1953,9 +1955,6 @@ void Screen::setWelcomeFrames() /// Determine which screensaver frame to use, then set the FrameCallback 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 static FrameCallback screensaverFrame; static OverlayCallback screensaverOverlay; @@ -1993,9 +1992,8 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver) #endif // Prepare now for next frame, shown when display wakes - ui->setOverlays(NULL, 0); // Clear overlay - setFrames(); // Return to normal display updates - ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on + ui->setOverlays(NULL, 0); // Clear overlay + setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally // Pick a refresh method, for when display wakes #ifdef EINK_HASQUIRK_GHOSTING @@ -2006,9 +2004,13 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver) } #endif -// restore our regular frame list -void Screen::setFrames() +// Regenerate the normal set of frames, focusing a specific frame if requested +// 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"); showingNormalScreen = true; @@ -2042,20 +2044,33 @@ void Screen::setFrames() // is the same offset into the moduleFrames vector // so that we can invoke the module's callback 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); // If we have a critical fault, show it first - if (error_code) + fsi.positions.fault = numframes; + if (error_code) { normalFrames[numframes++] = drawCriticalFaultFrame; + focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame + } #ifdef T_WATCH_S3 normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; #endif // 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)) { normalFrames[numframes++] = drawTextMessageFrame; } @@ -2070,11 +2085,14 @@ void Screen::setFrames() // // Since frames are basic function pointers, we have to use a helper to // call a method on debugInfo object. + fsi.positions.log = numframes; normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline; // call a method on debugInfoScreen object (for more details) + fsi.positions.settings = numframes; normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; + fsi.positions.wifi = numframes; #if HAS_WIFI && !defined(ARCH_PORTDUINO) if (isWifiAvailable()) { // call a method on debugInfoScreen object (for more details) @@ -2082,6 +2100,7 @@ void Screen::setFrames() } #endif + fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE LOG_DEBUG("Finished building frames. numframes: %d\n", numframes); ui->setFrames(normalFrames, numframes); @@ -2095,6 +2114,55 @@ void Screen::setFrames() prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list // 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 } @@ -2549,7 +2617,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) switch (arg->getStatusType()) { case STATUS_TYPE_NODE: 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; break; @@ -2561,23 +2629,33 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) { 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; } +// Triggered by MeshModules int Screen::handleUIFrameEvent(const UIFrameEvent *event) { if (showingNormalScreen) { - if (event->frameChanged) { - setFrames(); // Regen the list of screens (will show new text message) - } else if (event->needRedraw) { + // Regenerate the frameset, potentially honoring a module's internal requestFocus() call + if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET) + 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(); - // TODO: We might also want switch to corresponding frame, - // but we don't know the exact frame number. - // ui->switchToFrame(0); - } } return 0; @@ -2612,6 +2690,24 @@ int Screen::handleInputEvent(const InputEvent *event) 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 #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index b89a2917e..93e5f2ef7 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -173,9 +173,11 @@ class Screen : public concurrency::OSThread CallbackObserver textMessageObserver = CallbackObserver(this, &Screen::handleTextMessage); CallbackObserver uiFrameEventObserver = - CallbackObserver(this, &Screen::handleUIFrameEvent); + CallbackObserver(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules CallbackObserver inputObserver = CallbackObserver(this, &Screen::handleInputEvent); + CallbackObserver adminMessageObserver = + CallbackObserver(this, &Screen::handleAdminMessage); public: 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 handleUIFrameEvent(const UIFrameEvent *arg); int handleInputEvent(const InputEvent *arg); + int handleAdminMessage(const meshtastic_AdminMessage *arg); /// Used to force (super slow) eink displays to draw critical frames void forceDisplay(bool forceUiUpdate = false); @@ -450,8 +453,34 @@ class Screen : public concurrency::OSThread void handleShowPrevFrame(); void handlePrint(const char *text); 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 void setFastFramerate(); diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 04fa250bf..1ef4f60d8 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -284,4 +284,17 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht } } return handled; -} \ No newline at end of file +} + +#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 \ No newline at end of file diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index 2e2af33e0..c341b301a 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -35,10 +35,16 @@ enum class AdminMessageHandleResult { /* * This struct is used by Screen to figure out whether screen frame should be updated. */ -typedef struct _UIFrameEvent { - bool frameChanged; - bool needRedraw; -} UIFrameEvent; +struct UIFrameEvent { + // What do we actually want to happen? + enum Action { + 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". * @@ -73,6 +79,7 @@ class MeshModule meshtastic_AdminMessage *response); #if HAS_SCREEN virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } + virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset #endif protected: const char *name; @@ -176,6 +183,19 @@ class MeshModule 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: /** * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index e24c62712..cab63e559 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -200,6 +200,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta case meshtastic_AdminMessage_remove_by_nodenum_tag: { LOG_INFO("Client is receiving a remove_nodenum command.\n"); nodeDB->removeNodeByNum(r->remove_by_nodenum); + this->notifyObservers(r); // Observed by screen break; } case meshtastic_AdminMessage_set_favorite_node_tag: { diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index 6ecc88829..a5ffeb7d6 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -7,7 +7,7 @@ /** * Admin module for admin messages */ -class AdminModule : public ProtobufModule +class AdminModule : public ProtobufModule, public Observable { public: /** Constructor diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index be414dce1..84b5a3260 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -148,8 +148,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) if (this->currentMessageIndex == 0) { this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; - UIFrameEvent e = {false, true}; - e.frameChanged = true; + requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs + UIFrameEvent e; + e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->notifyObservers(&e); return 0; @@ -166,8 +167,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) } } if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { - UIFrameEvent e = {false, true}; - e.frameChanged = true; + UIFrameEvent e; + e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->currentMessageIndex = -1; #if !defined(T_WATCH_S3) && !defined(RAK14014) @@ -353,6 +354,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) } if (validEvent) { + requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs + // Let runOnce to be called immediately. if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { 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++; } + // 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); service.sendToMesh( @@ -393,13 +401,13 @@ int32_t CannedMessageModule::runOnce() return INT32_MAX; } // LOG_DEBUG("Check status\n"); - UIFrameEvent e = {false, true}; + UIFrameEvent e; 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)) { // TODO: might have some feedback of sending state this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; 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->freetext = ""; // clear freetext 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)) && ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { // 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->freetext = ""; // clear freetext this->cursor = 0; @@ -449,7 +457,7 @@ int32_t CannedMessageModule::runOnce() 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->freetext = ""; // clear freetext this->cursor = 0; @@ -463,7 +471,7 @@ int32_t CannedMessageModule::runOnce() } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; 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; } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { if (this->messagesCount > 0) { @@ -567,7 +575,7 @@ int32_t CannedMessageModule::runOnce() break; } 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 // display back to the default window case 0x08: // backspace @@ -706,8 +714,8 @@ int CannedMessageModule::getPrevIndex() void CannedMessageModule::showTemporaryMessage(const String &message) { temporaryMessage = message; - UIFrameEvent e = {false, true}; - e.frameChanged = true; + UIFrameEvent e; + e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen notifyObservers(&e); runState = CANNED_MESSAGE_RUN_STATE_MESSAGE; // 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]; if (temporaryMessage.length() != 0) { + requestFocus(); // Tell Screen::setFrames to move to our module's frame LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str()); display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); } 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->setFont(FONT_MEDIUM); 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); } } 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->setFont(FONT_MEDIUM); 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->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); } 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) drawKeyboard(display, state, 0, 0); #else @@ -1030,16 +1041,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st 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 if (mp.decoded.request_id != 0) { - UIFrameEvent e = {false, true}; - e.frameChanged = true; + UIFrameEvent e; + 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->incoming = service.getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; 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; + waitingForAck = false; // No longer want routing packets this->notifyObservers(&e); // run the next time 2 seconds later setIntervalFromNow(2000); diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 00e8c2bf9..797b9f7cf 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -81,9 +81,8 @@ class CannedMessageModule : public SinglePortModule, public Observabledecoded.portnum) { - case meshtastic_PortNum_TEXT_MESSAGE_APP: case meshtastic_PortNum_ROUTING_APP: - return true; + return waitingForAck; default: return false; } @@ -140,7 +139,8 @@ class CannedMessageModule : public SinglePortModule, public ObservablenotifyObservers(&e); } } else { @@ -209,7 +209,7 @@ int32_t AudioModule::runOnce() } tx_encode_frame_index = sizeof(tx_header); 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); } } From 699d37b04c5a87a07e0444dbd724ad0b3ef7a7b2 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 12 Jul 2024 09:24:42 -0500 Subject: [PATCH 3/3] Move up telemetry defaults to every 30 minutes (#4274) --- src/mesh/Default.h | 1 + src/modules/Telemetry/AirQualityTelemetry.cpp | 3 ++- src/modules/Telemetry/DeviceTelemetry.cpp | 3 ++- src/modules/Telemetry/EnvironmentTelemetry.cpp | 6 ++++-- src/modules/Telemetry/PowerTelemetry.cpp | 6 ++++-- src/modules/esp32/PaxcounterModule.cpp | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 95723744b..cc3927914 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -5,6 +5,7 @@ #define ONE_MINUTE_MS 60 * 1000 #define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60) +#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60) #define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60) #define default_wait_bluetooth_secs IF_ROUTER(1, 60) #define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index ba043feab..43b0ac46c 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -47,7 +47,8 @@ int32_t AirQualityTelemetryModule::runOnce() uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval))) && + ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 9cc4bf6ea..9fe679b41 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -17,7 +17,8 @@ int32_t DeviceTelemetryModule::runOnce() { refreshUptime(); if (((lastSentToMesh == 0) || - ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) && + ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval, + default_telemetry_broadcast_interval_secs))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 23200fd00..a9740879d 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -63,7 +63,8 @@ int32_t EnvironmentTelemetryModule::runOnce() { if (sleepOnNextExecution == true) { sleepOnNextExecution = false; - uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval); + uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval, + default_telemetry_broadcast_interval_secs); LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); doDeepSleep(nightyNightMs, true); } @@ -144,7 +145,8 @@ int32_t EnvironmentTelemetryModule::runOnce() uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval))) && + ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval, + default_telemetry_broadcast_interval_secs))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index fb5aee375..6915d67e3 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -24,7 +24,8 @@ int32_t PowerTelemetryModule::runOnce() { if (sleepOnNextExecution == true) { sleepOnNextExecution = false; - uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval); + uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, + default_telemetry_broadcast_interval_secs); LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); doDeepSleep(nightyNightMs, true); } @@ -70,7 +71,8 @@ int32_t PowerTelemetryModule::runOnce() uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval))) && + ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, + default_telemetry_broadcast_interval_secs))) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); lastSentToMesh = now; diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 0bae515df..34d6fb1d0 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -101,7 +101,7 @@ int32_t PaxcounterModule::runOnce() sendInfo(NODENUM_BROADCAST); } return Default::getConfiguredOrDefaultMs(moduleConfig.paxcounter.paxcounter_update_interval, - default_broadcast_interval_secs); + default_telemetry_broadcast_interval_secs); } else { return disable(); }