Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 28 additions & 18 deletions src/graphics/draw/NodeListRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,12 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int

int nameMaxWidth = getNodeNameMaxWidth(columnWidth, columnWidth - 25);
int barsOffset = (currentResolution == ScreenResolution::High) ? (isLeftCol ? 20 : 24) : (isLeftCol ? 15 : 19);
int hopOffset = (currentResolution == ScreenResolution::High) ? (isLeftCol ? 21 : 29) : (isLeftCol ? 13 : 17);
constexpr int kBarCount = 4;
constexpr int kBarWidth = 2;
constexpr int kBarGap = 1;

int barsXOffset = columnWidth - barsOffset;
int barsRightEdge = x + barsXOffset + ((kBarCount - 1) * (kBarWidth + kBarGap)) + kBarWidth;

const int nameX = x + ((currentResolution == ScreenResolution::High) ? 6 : 3);
char nodeName[96];
Expand All @@ -304,28 +307,35 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
}
}

// Draw signal strength bars
int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0;
int barWidth = 2;
int barStartX = x + barsXOffset;
int barStartY = y + 1 + (FONT_HEIGHT_SMALL / 2) + 2;
const bool isZeroHop = node->has_hops_away && node->hops_away == 0;

for (int b = 0; b < 4; b++) {
if (b < bars) {
int height = (b * 2);
display->fillRect(barStartX + (b * (barWidth + 1)), barStartY - height, barWidth, height);
// Show signal only for direct neighbors (0 hops)
if (isZeroHop) {
int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0;
Comment thread
HarukiToreda marked this conversation as resolved.
int barStartX = x + barsXOffset;
int barStartY = y + 1 + (FONT_HEIGHT_SMALL / 2) + 2;

for (int b = 0; b < kBarCount; b++) {
if (b < bars) {
int height = (b * 2);
display->fillRect(barStartX + (b * (kBarWidth + kBarGap)), barStartY - height, kBarWidth, height);
}
}
}

// Draw hop count
char hopStr[6] = "";
if (node->has_hops_away && node->hops_away > 0)
snprintf(hopStr, sizeof(hopStr), "[%d]", node->hops_away);
// Draw hop count + hop icon
if (node->has_hops_away && node->hops_away > 0) {
char hopCount[6];
snprintf(hopCount, sizeof(hopCount), "%d", node->hops_away);

const int hopCountWidth = display->getStringWidth(hopCount);
const int gap = 1;
const int totalWidth = hopCountWidth + gap + hop_width;
const int hopX = barsRightEdge - totalWidth;
const int iconY = y + (FONT_HEIGHT_SMALL - hop_height) / 2;

if (hopStr[0] != '\0') {
int rightEdge = x + columnWidth - hopOffset;
int textWidth = display->getStringWidth(hopStr);
display->drawString(rightEdge - textWidth, y, hopStr);
display->drawString(hopX, y, hopCount);
display->drawXbm(hopX + hopCountWidth + gap, iconY, hop_width, hop_height, hop);
}
}

Expand Down
120 changes: 44 additions & 76 deletions src/graphics/draw/UIRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,10 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
}
#endif

// === 2. Signal and Hops (combined on one line, if available) ===
char signalHopsStr[32] = "";
// === 2. Signal/Hops line (if available) ===
bool haveSignal = false;
int bars = 0;
const char *qualityLabel = nullptr;

// Helper to get SNR limit based on modem preset
auto getSnrLimit = [](meshtastic_Config_LoRaConfig_ModemPreset preset) -> float {
Expand All @@ -420,81 +420,52 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
}
};

// Calculate signal grade using modem preset and SNR only
float snrLimit = getSnrLimit(config.lora.modem_preset);
float snr = node->snr;

// Determine signal quality label and bars using SNR-only grading
const char *qualityLabel = nullptr;

if (snr > snrLimit + 10) {
qualityLabel = "Good";
bars = 4;
} else if (snr > snrLimit + 6) {
qualityLabel = "Good";
bars = 3;
} else if (snr > snrLimit + 2) {
qualityLabel = "Good";
bars = 2;
} else if (snr > snrLimit - 4) {
qualityLabel = "Fair";
bars = 1;
} else {
qualityLabel = "Bad";
bars = 1;
}

// Add extra spacing on the left if we have an API connection to account for the common footer icons
const char *leftSideSpacing =
graphics::isAPIConnected(service->api_state) ? (currentResolution == ScreenResolution::High ? " " : " ") : " ";
const bool isZeroHop = node->has_hops_away && node->hops_away == 0;

// Signal text/bars are only for direct (zero-hop) nodes with valid SNR.
if (isZeroHop) {
float snr = node->snr;
if (snr > -100 && snr != 0) {
float snrLimit = getSnrLimit(config.lora.modem_preset);
// Determine signal quality label and bars using SNR-only grading.
if (snr > snrLimit + 10) {
qualityLabel = "Good";
bars = 4;
} else if (snr > snrLimit + 6) {
qualityLabel = "Good";
bars = 3;
} else if (snr > snrLimit + 2) {
qualityLabel = "Good";
bars = 2;
} else if (snr > snrLimit - 4) {
qualityLabel = "Fair";
bars = 1;
} else {
qualityLabel = "Bad";
bars = 1;
}

// --- Build the Signal/Hops line ---
// Only show signal if we have valid SNR
if (snr > -100 && snr != 0) {
snprintf(signalHopsStr, sizeof(signalHopsStr), "%sSig:%s", leftSideSpacing, qualityLabel);
haveSignal = true;
}

if (node->hops_away > 0) {
size_t len = strlen(signalHopsStr);
if (haveSignal) {
snprintf(signalHopsStr + len, sizeof(signalHopsStr) - len, " [#]");
} else {
snprintf(signalHopsStr, sizeof(signalHopsStr), "[#]");
haveSignal = true;
}
}

if (signalHopsStr[0]) {
int yPos = getTextPositions(display)[line++];
int curX = x;

// Split combined string into signal text and hop suffix
char sigPart[20] = "";
const char *hopPart = nullptr;

char *bracket = strchr(signalHopsStr, '[');
if (bracket) {
size_t n = (size_t)(bracket - signalHopsStr);
if (n >= sizeof(sigPart))
n = sizeof(sigPart) - 1;
memcpy(sigPart, signalHopsStr, n);
sigPart[n] = '\0';

// Trim trailing spaces
while (strlen(sigPart) && sigPart[strlen(sigPart) - 1] == ' ') {
sigPart[strlen(sigPart) - 1] = '\0';
}
const bool showHops = node->has_hops_away && node->hops_away > 0;

hopPart = bracket; // "[n Hop(s)]"
} else {
strncpy(sigPart, signalHopsStr, sizeof(sigPart) - 1);
sigPart[sizeof(sigPart) - 1] = '\0';
if (haveSignal || showHops) {
int yPos = getTextPositions(display)[line++];
int curX = x + display->getStringWidth(leftSideSpacing);

// Draw signal quality text for zero-hop nodes when present.
if (haveSignal && qualityLabel) {
char signalLabel[20];
snprintf(signalLabel, sizeof(signalLabel), "Sig:%s", qualityLabel);
display->drawString(curX, yPos, signalLabel);
curX += display->getStringWidth(signalLabel) + 4;
}

// Draw signal quality text
display->drawString(curX, yPos, sigPart);
curX += display->getStringWidth(sigPart) + 4;

// Draw signal bars (skip on UltraLow, text only)
if (currentResolution != ScreenResolution::UltraLow && haveSignal && bars > 0) {
const int kMaxBars = 4;
Expand Down Expand Up @@ -532,12 +503,12 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
curX += (kMaxBars * barWidth) + ((kMaxBars - 1) * barGap) + 2;
}

// Draw hops AFTER the bars as: [ number + hop icon ]
if (hopPart && node->hops_away > 0) {

// open bracket
display->drawString(curX, yPos, "[");
curX += display->getStringWidth("[") + 1;
// Draw hops for non-zero-hop nodes as: number + hop icon.
// This path is mutually exclusive with the zero-hop signal-bars path above.
if (showHops) {
// hop label
display->drawString(curX, yPos, "Hop:");
curX += display->getStringWidth("Hop:") + 2;

// hop count
char hopCount[6];
Expand All @@ -549,9 +520,6 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
const int iconY = yPos + (FONT_HEIGHT_SMALL - hop_height) / 2;
display->drawXbm(curX, iconY, hop_width, hop_height, hop);
curX += hop_width + 1;

// closing bracket
display->drawString(curX, yPos, "]");
}
}

Expand Down
Loading