Skip to content
Open
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
78f29c0
Work through implementation of Status Message
Xaositek Feb 1, 2026
523906d
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 2, 2026
62f897e
Change drawNodeInfo to drawFavoriteNode
Xaositek Feb 2, 2026
0b8b757
Rename variable, set max status to 20, added Node List View.
Xaositek Feb 2, 2026
697dd2b
Truncate overflow on Favorite frame
Xaositek Feb 2, 2026
d8a0b6a
Reduce MAX_RECENT_STATUSMESSAGES to 5 to meet memory usage targets
Xaositek Feb 3, 2026
0d57a49
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 3, 2026
6426e25
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 4, 2026
34314d1
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 4, 2026
44efc9b
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 6, 2026
7251879
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 11, 2026
7c080f4
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 11, 2026
81d5354
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 11, 2026
20b664e
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 13, 2026
f051718
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 14, 2026
b5f6b76
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 14, 2026
e5e470c
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 14, 2026
f416c2d
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 15, 2026
f3fe1e2
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 16, 2026
6620d2b
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 16, 2026
d432aab
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 18, 2026
efc1e55
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 19, 2026
6711bdc
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 20, 2026
a604353
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 20, 2026
955b63b
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 20, 2026
a1b2770
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 21, 2026
7bc6865
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 22, 2026
8fef43e
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 23, 2026
5cc1465
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 24, 2026
8fbcd62
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 24, 2026
7f4d063
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 25, 2026
89744bc
Merge branch 'develop' into baseui_statusmessage
Xaositek Feb 26, 2026
401619a
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 2, 2026
061b0f9
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 4, 2026
3d6a67a
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 4, 2026
12d6130
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 6, 2026
85d945a
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 6, 2026
ce435de
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 7, 2026
3b0a4ef
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 11, 2026
bdf4d76
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 12, 2026
56dde7c
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 16, 2026
2e2f410
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 17, 2026
2ea0b11
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 18, 2026
8cd15d0
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 20, 2026
edef9e9
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 20, 2026
d74559c
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 20, 2026
74bc5ab
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 22, 2026
65ae078
Merge branch 'develop' into baseui_statusmessage
Xaositek Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/graphics/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ void Screen::setFrames(FrameFocus focus)
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
favoriteFrames.push_back(graphics::UIRenderer::drawFavoriteNode);
}
}

Expand Down Expand Up @@ -1217,7 +1217,7 @@ void Screen::setFrames(FrameFocus focus)
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));

prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list just changed)
prevFrame = -1; // Force drawFavoriteNode to pick a new node (because our list just changed)

// Focus on a specific frame, in the frame set we just created
switch (focus) {
Expand Down
40 changes: 38 additions & 2 deletions src/graphics/draw/NodeListRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include "CompassRenderer.h"
#include "NodeDB.h"
#include "NodeListRenderer.h"
#if !MESHTASTIC_EXCLUDE_STATUS
#include "modules/StatusMessageModule.h"
#endif
#include "UIRenderer.h"
#include "gps/GeoCoord.h"
#include "gps/RTC.h" // for getTime() function
Expand Down Expand Up @@ -92,8 +95,41 @@ std::string getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node,

// 1) Choose target candidate (long vs short) only if present
const char *raw = nullptr;
if (node && node->has_user) {
raw = config.display.use_long_node_name ? node->user.long_name : node->user.short_name;

#if !MESHTASTIC_EXCLUDE_STATUS
// If long-name mode is enabled, and we have a recent status for this node,
// prefer "(short_name) statusText" as the raw candidate.
std::string composedFromStatus;
if (config.display.use_long_node_name && node && node->has_user && statusMessageModule) {
const auto &recent = statusMessageModule->getRecentReceived();
const StatusMessageModule::RecentStatus *found = nullptr;
for (auto it = recent.rbegin(); it != recent.rend(); ++it) {
if (it->fromNodeId == node->num && !it->statusText.empty()) {
found = &(*it);
break;
}
}

if (found) {
const char *shortName = node->user.short_name;
composedFromStatus.reserve(4 + (shortName ? std::strlen(shortName) : 0) + 1 + found->statusText.size());
composedFromStatus += "(";
if (shortName && *shortName) {
composedFromStatus += shortName;
}
composedFromStatus += ") ";
composedFromStatus += found->statusText;

raw = composedFromStatus.c_str(); // safe for now; we'll sanitize immediately into std::string
}
}
#endif

// If we didn't compose from status, use normal long/short selection
if (!raw) {
if (node && node->has_user) {
raw = config.display.use_long_node_name ? node->user.long_name : node->user.short_name;
}
}

// 2) Preserve UTF-8 names so emotes can be detected and rendered.
Expand Down
56 changes: 55 additions & 1 deletion src/graphics/draw/UIRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "NodeListRenderer.h"
#if !MESHTASTIC_EXCLUDE_STATUS
#include "modules/StatusMessageModule.h"
#endif
#include "UIRenderer.h"
#include "airtime.h"
#include "gps/GeoCoord.h"
Expand Down Expand Up @@ -290,7 +293,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
// * Favorite Node Info *
// **********************
// cppcheck-suppress constParameterPointer; signature must match FrameCallback typedef from OLEDDisplayUi library
void UIRenderer::drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
if (favoritedNodes.empty())
return;
Expand Down Expand Up @@ -342,6 +345,57 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, i
UIRenderer::drawStringWithEmotes(display, x, getTextPositions(display)[line++], username, FONT_HEIGHT_SMALL, 1, false);
}

#if !MESHTASTIC_EXCLUDE_STATUS
// === Optional: Last received StatusMessage line for this node ===
// Display it directly under the username line (if we have one).
if (statusMessageModule) {
const auto &recent = statusMessageModule->getRecentReceived();
const StatusMessageModule::RecentStatus *found = nullptr;

// Search newest-to-oldest
for (auto it = recent.rbegin(); it != recent.rend(); ++it) {
if (it->fromNodeId == node->num && !it->statusText.empty()) {
found = &(*it);
break;
}
}

if (found) {
std::string statusLine = std::string(" Status: ") + found->statusText;
{
const int screenW = display->getWidth();
const int ellipseW = display->getStringWidth("...");
int w = display->getStringWidth(statusLine.c_str());

// Only do work if it overflows
if (w > screenW) {
bool truncated = false;
if (ellipseW > screenW) {
statusLine.clear();
} else {
while (!statusLine.empty()) {
// remove one char (byte) at a time
statusLine.pop_back();
truncated = true;

// Measure candidate with ellipsis appended
std::string candidate = statusLine + "...";
if (display->getStringWidth(candidate.c_str()) <= screenW) {
statusLine = std::move(candidate);
break;
}
}
if (statusLine.empty() && ellipseW <= screenW) {
statusLine = "...";
}
}
}
}
display->drawString(x, getTextPositions(display)[line++], statusLine.c_str());
}
}
#endif

// === 2. Signal and Hops (combined on one line, if available) ===
char signalHopsStr[32] = "";
bool haveSignal = false;
Expand Down
2 changes: 1 addition & 1 deletion src/graphics/draw/UIRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class UIRenderer
// Navigation bar overlay
static void drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *state);

static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static void drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);

static void drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);

Expand Down
15 changes: 14 additions & 1 deletion src/modules/StatusMessageModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,23 @@ int32_t StatusMessageModule::runOnce()
ProcessMessage StatusMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
meshtastic_StatusMessage incomingMessage;
meshtastic_StatusMessage incomingMessage = meshtastic_StatusMessage_init_zero;

if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_StatusMessage_fields,
&incomingMessage)) {

LOG_INFO("Received a NodeStatus message %s", incomingMessage.status);

RecentStatus entry;
entry.fromNodeId = mp.from;
entry.statusText = incomingMessage.status;

recentReceived.push_back(std::move(entry));

// Keep only last MAX_RECENT_STATUSMESSAGES
if (recentReceived.size() > MAX_RECENT_STATUSMESSAGES) {
recentReceived.erase(recentReceived.begin()); // drop oldest
}
}
}
return ProcessMessage::CONTINUE;
Expand Down
15 changes: 14 additions & 1 deletion src/modules/StatusMessageModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
#if !MESHTASTIC_EXCLUDE_STATUS
#include "SinglePortModule.h"
#include "configuration.h"
#include <string>
#include <vector>

class StatusMessageModule : public SinglePortModule, private concurrency::OSThread
{

public:
/** Constructor
* name is for debugging output
Expand All @@ -19,16 +20,28 @@ class StatusMessageModule : public SinglePortModule, private concurrency::OSThre
this->setInterval(1000 * 12 * 60 * 60);
}
// TODO: If we have a string, set the initial delay (15 minutes maybe)

// Keep vector from reallocating as we fill up to MAX_RECENT_STATUSMESSAGES
recentReceived.reserve(MAX_RECENT_STATUSMESSAGES);
}

virtual int32_t runOnce() override;

struct RecentStatus {
uint32_t fromNodeId; // mp.from
std::string statusText; // incomingMessage.status
};

const std::vector<RecentStatus> &getRecentReceived() const { return recentReceived; }

protected:
/** Called to handle a particular incoming message
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;

private:
static constexpr size_t MAX_RECENT_STATUSMESSAGES = 5;
std::vector<RecentStatus> recentReceived;
};

extern StatusMessageModule *statusMessageModule;
Expand Down
Loading