diff --git a/src/main.cpp b/src/main.cpp index 5d813ee..e8fad02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -519,6 +519,7 @@ void setup() { announceManager = new AnnounceManager(); announceManager->setStorage(&sdStore, &flash); announceManager->setLocalDestHash(rns.destination().hash()); + if (rns.loraInterface()) announceManager->setLoRaInterface(rns.loraInterface()); announceManager->loadContacts(); announceManager->loadNameCache(); announceHandler = RNS::HAnnounceHandler(announceManager); @@ -718,6 +719,7 @@ void setup() { lvNodesScreen.setAnnounceManager(announceManager); lvNodesScreen.setUIManager(&ui); + lvNodesScreen.setUserConfig(&userConfig); lvNodesScreen.setNodeSelectedCallback([](const std::string& peerHex) { lvMessageView.setPeerHex(peerHex); ui.lvTabBar().setActiveTab(LvTabBar::TAB_MSGS); diff --git a/src/reticulum/AnnounceManager.cpp b/src/reticulum/AnnounceManager.cpp index f6ccd59..f044084 100644 --- a/src/reticulum/AnnounceManager.cpp +++ b/src/reticulum/AnnounceManager.cpp @@ -3,6 +3,7 @@ #include "config/Config.h" #include "storage/SDStore.h" #include "storage/FlashStore.h" +#include "transport/LoRaInterface.h" #include #include @@ -154,6 +155,7 @@ void AnnounceManager::received_announce( if (!idHex.empty()) node.identityHex = idHex; node.lastSeen = now; node.hops = RNS::Transport::hops_to(destination_hash); + if (_loraIf) { node.rssi = _loraIf->lastRxRssi(); node.snr = _loraIf->lastRxSnr(); } if (node.saved) _contactsDirty = true; // Only compute toHex for log + name cache when node actually updated std::string destHex = destination_hash.toHex(); @@ -228,6 +230,7 @@ void AnnounceManager::received_announce( node.identityHex = idHex; node.lastSeen = millis(); node.hops = RNS::Transport::hops_to(destination_hash); + if (_loraIf) { node.rssi = _loraIf->lastRxRssi(); node.snr = _loraIf->lastRxSnr(); } _hashIndex[key] = (int)_nodes.size(); _nodes.push_back(node); } diff --git a/src/reticulum/AnnounceManager.h b/src/reticulum/AnnounceManager.h index 6141e21..88194a4 100644 --- a/src/reticulum/AnnounceManager.h +++ b/src/reticulum/AnnounceManager.h @@ -10,6 +10,7 @@ class SDStore; class FlashStore; +class LoRaInterface; struct DiscoveredNode { RNS::Bytes hash; @@ -33,6 +34,7 @@ public: const RNS::Bytes& app_data) override; void setStorage(SDStore* sd, FlashStore* flash); + void setLoRaInterface(LoRaInterface* li) { _loraIf = li; } void setLocalDestHash(const RNS::Bytes& hash) { _localDestHash = hash; } void saveContacts(); void loadContacts(); @@ -61,6 +63,7 @@ private: std::vector _nodes; SDStore* _sd = nullptr; FlashStore* _flash = nullptr; + LoRaInterface* _loraIf = nullptr; RNS::Bytes _localDestHash; bool _contactsDirty = false; bool _nameCacheDirty = false; diff --git a/src/transport/LoRaInterface.cpp b/src/transport/LoRaInterface.cpp index 40403e6..a3fb4f0 100644 --- a/src/transport/LoRaInterface.cpp +++ b/src/transport/LoRaInterface.cpp @@ -195,6 +195,10 @@ void LoRaInterface::loop() { uint8_t raw[MAX_PACKET_SIZE]; memcpy(raw, _radio->packetBuffer(), packetSize); + // Capture signal quality before any further processing + _lastRxRssi = _radio->packetRssi(); + _lastRxSnr = _radio->packetSnr(); + uint8_t header = raw[0]; int payloadSize = packetSize - RNODE_HEADER_L; uint8_t seq = header & RNODE_NIBBLE_SEQ; @@ -210,13 +214,13 @@ void LoRaInterface::loop() { _splitRxTimestamp = millis(); Serial.printf("[LORA_IF] RX SPLIT frame 1: %d bytes (seq=0x%02X), RSSI=%d, SNR=%.1f\n", - payloadSize, seq, _radio->packetRssi(), _radio->packetSnr()); + payloadSize, seq, _lastRxRssi, _lastRxSnr); _radio->receive(); return; } else if (seq == _splitRxSeq) { // Second frame matches — reassemble Serial.printf("[LORA_IF] RX SPLIT frame 2: %d bytes (seq=0x%02X), RSSI=%d, SNR=%.1f\n", - payloadSize, seq, _radio->packetRssi(), _radio->packetSnr()); + payloadSize, seq, _lastRxRssi, _lastRxSnr); _splitRxBuffer.append(raw + RNODE_HEADER_L, payloadSize); int totalSize = _splitRxBuffer.size(); @@ -252,7 +256,7 @@ void LoRaInterface::loop() { Serial.printf("[LORA_IF] RX %d bytes (hdr=0x%02X, payload=%d), RSSI=%d, SNR=%.1f\n", packetSize, header, payloadSize, - _radio->packetRssi(), _radio->packetSnr()); + _lastRxRssi, _lastRxSnr); RNS::Bytes buf(payloadSize); memcpy(buf.writable(payloadSize), raw + RNODE_HEADER_L, payloadSize); diff --git a/src/transport/LoRaInterface.h b/src/transport/LoRaInterface.h index aad2958..032700f 100644 --- a/src/transport/LoRaInterface.h +++ b/src/transport/LoRaInterface.h @@ -19,6 +19,10 @@ public: float airtimeUtilization() const; + // Last received packet signal quality + int lastRxRssi() const { return _lastRxRssi; } + float lastRxSnr() const { return _lastRxSnr; } + protected: virtual void send_outgoing(const RNS::Bytes& data) override; @@ -45,6 +49,9 @@ private: RNS::Bytes _splitRxBuffer; unsigned long _splitRxTimestamp = 0; + int _lastRxRssi = 0; + float _lastRxSnr = 0; + unsigned long _airtimeWindowStart = 0; float _airtimeAccumMs = 0; static constexpr unsigned long AIRTIME_WINDOW_MS = 60000; diff --git a/src/ui/screens/LvNodesScreen.cpp b/src/ui/screens/LvNodesScreen.cpp index 4e97603..a5a2cf0 100644 --- a/src/ui/screens/LvNodesScreen.cpp +++ b/src/ui/screens/LvNodesScreen.cpp @@ -3,6 +3,7 @@ #include "ui/LvTheme.h" #include "ui/UIManager.h" #include "reticulum/AnnounceManager.h" +#include "config/UserConfig.h" #include #include @@ -227,20 +228,21 @@ void LvNodesScreen::syncVisibleRows() { lv_obj_set_style_border_width(_poolRows[i], 0, 0); lv_obj_set_style_border_side(_poolRows[i], LV_BORDER_SIDE_NONE, 0); - // Name + hash - std::string truncName = node.name.substr(0, 15); + // Name + hash (use smaller font to reduce truncation) + bool devMode = _cfg && _cfg->settings().devMode; + std::string truncName = node.name.substr(0, devMode ? 12 : 15); std::string displayHash = node.hash.toHex().substr(0, 12); char buf[64]; snprintf(buf, sizeof(buf), "%s [%s]", truncName.c_str(), displayHash.c_str()); - lv_obj_set_style_text_font(_poolNameLabels[i], &lv_font_montserrat_14, 0); + lv_obj_set_style_text_font(_poolNameLabels[i], &lv_font_montserrat_12, 0); lv_obj_set_style_text_color(_poolNameLabels[i], lv_color_hex( node.saved ? Theme::ACCENT : Theme::PRIMARY), 0); lv_label_set_text(_poolNameLabels[i], buf); lv_obj_align(_poolNameLabels[i], LV_ALIGN_LEFT_MID, 8, 0); - // Hops + age + // Hops + age + optional RSSI (dev mode) unsigned long ageSec = (millis() - node.lastSeen) / 1000; - char infoBuf[24]; + char infoBuf[32]; if (node.hops < 128) { if (ageSec < 60) snprintf(infoBuf, sizeof(infoBuf), "%dhop %lus", node.hops, ageSec); else snprintf(infoBuf, sizeof(infoBuf), "%dhop %lum", node.hops, ageSec / 60); @@ -248,6 +250,11 @@ void LvNodesScreen::syncVisibleRows() { if (ageSec < 60) snprintf(infoBuf, sizeof(infoBuf), "%lus", ageSec); else snprintf(infoBuf, sizeof(infoBuf), "%lum", ageSec / 60); } + if (devMode && node.rssi != 0) { + char rssiBuf[16]; + snprintf(rssiBuf, sizeof(rssiBuf), " %ddB", node.rssi); + strncat(infoBuf, rssiBuf, sizeof(infoBuf) - strlen(infoBuf) - 1); + } lv_label_set_text(_poolInfoLabels[i], infoBuf); lv_obj_align(_poolInfoLabels[i], LV_ALIGN_RIGHT_MID, -4, 0); } diff --git a/src/ui/screens/LvNodesScreen.h b/src/ui/screens/LvNodesScreen.h index 56ecc7c..03b8603 100644 --- a/src/ui/screens/LvNodesScreen.h +++ b/src/ui/screens/LvNodesScreen.h @@ -6,6 +6,7 @@ #include class AnnounceManager; +class UserConfig; class LvNodesScreen : public LvScreen { public: @@ -19,6 +20,7 @@ public: void setAnnounceManager(AnnounceManager* am) { _am = am; } void setNodeSelectedCallback(NodeSelectedCallback cb) { _onSelect = cb; } void setUIManager(class UIManager* ui) { _ui = ui; } + void setUserConfig(UserConfig* cfg) { _cfg = cfg; } bool handleLongPress() override; const char* title() const override { return "Nodes"; } @@ -31,6 +33,7 @@ private: AnnounceManager* _am = nullptr; class UIManager* _ui = nullptr; + UserConfig* _cfg = nullptr; NodeSelectedCallback _onSelect; bool _confirmDelete = false; int _lastNodeCount = -1;