diff --git a/src/GPSStatus.h b/src/GPSStatus.h index a1a9f2c56..80e3c54f1 100644 --- a/src/GPSStatus.h +++ b/src/GPSStatus.h @@ -53,8 +53,8 @@ class GPSStatus : public Status int32_t getLatitude() const { if (config.position.fixed_position) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - return node->position.latitude_i; + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + return node ? node->latitude_i : 0; } else { return p.latitude_i; } @@ -63,8 +63,8 @@ class GPSStatus : public Status int32_t getLongitude() const { if (config.position.fixed_position) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - return node->position.longitude_i; + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + return node ? node->longitude_i : 0; } else { return p.longitude_i; } @@ -73,8 +73,8 @@ class GPSStatus : public Status int32_t getAltitude() const { if (config.position.fixed_position) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - return node->position.altitude; + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + return node ? node->altitude : 0; } else { return p.altitude; } diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 86599d5b3..a96719b49 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1106,8 +1106,8 @@ void Screen::setFrames(FrameFocus focus) std::vector favoriteFrames; 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) { + const meshtastic_NodeDetail *nodeDetail = nodeDB->getMeshNodeByIndex(i); + if (nodeDetail && nodeDetail->num != nodeDB->getNodeNum() && detailIsFavorite(*nodeDetail)) { favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo); } } @@ -1460,10 +1460,10 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) forceDisplay(); // Forces screen redraw } // === Prepare banner content === - const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(packet->from); const meshtastic_Channel channel = channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex()); - const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; + const char *longName = (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) ? node->long_name : nullptr; const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index bd647c3d8..aa00c1547 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -1191,8 +1191,8 @@ void menuHandler::removeFavoriteMenu() BannerOverlayOptions bannerOptions; std::string message = "Unfavorite This Node?\n"; auto node = nodeDB->getMeshNode(graphics::UIRenderer::currentFavoriteNodeNum); - if (node && node->has_user) { - message += sanitizeString(node->user.long_name).substr(0, 15); + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) { + message += sanitizeString(node->long_name).substr(0, 15); } bannerOptions.message = message.c_str(); bannerOptions.optionsArrayPtr = optionsArray; @@ -1459,8 +1459,10 @@ void menuHandler::keyVerificationFinalPrompt() options.notificationType = graphics::notificationTypeEnum::selection_picker; options.bannerCallback = [=](int selected) { if (selected == 1) { - auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode()); - remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + meshtastic_NodeDetail *remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode()); + if (remoteNodePtr) { + detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED, true); + } } }; screen->showOverlayBanner(options); diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index da6ec7abc..eeb43ee3a 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -218,18 +218,18 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 } // === Header Construction === - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp)); + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp)); char headerStr[80]; const char *sender = "???"; #if defined(M5STACK_UNITC6L) - if (node && node->has_user) - sender = node->user.short_name; + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) + sender = node->short_name; #else - if (node && node->has_user) { - if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) { - sender = node->user.long_name; + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) { + if (SCREEN_WIDTH >= 200 && node->long_name[0] != '\0') { + sender = node->long_name; } else { - sender = node->user.short_name; + sender = node->short_name; } } #endif diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp index 1a36a6188..2776312c1 100644 --- a/src/graphics/draw/NodeListRenderer.cpp +++ b/src/graphics/draw/NodeListRenderer.cpp @@ -53,23 +53,29 @@ static int scrollIndex = 0; // Utility Functions // ============================= -const char *getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node) +const char *getSafeNodeName(OLEDDisplay *display, const meshtastic_NodeDetail *node) { - const char *name = NULL; static char nodeName[16] = "?"; - if (config.display.use_long_node_name == true) { - if (node->has_user && strlen(node->user.long_name) > 0) { - name = node->user.long_name; - } else { - snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); + if (!node) { + strncpy(nodeName, "?", sizeof(nodeName)); + return nodeName; + } + + const bool hasUser = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER); + const char *name = nullptr; + if (config.display.use_long_node_name) { + if (hasUser && node->long_name[0] != '\0') { + name = node->long_name; } } else { - if (node->has_user && strlen(node->user.short_name) > 0) { - name = node->user.short_name; - } else { - snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); + if (hasUser && node->short_name[0] != '\0') { + name = node->short_name; } } + if (!name) { + snprintf(nodeName, sizeof(nodeName), "(%04X)", static_cast(node->num & 0xFFFF)); + name = nodeName; + } // Use sanitizeString() function and copy directly into nodeName std::string sanitized_name = sanitizeString(name ? name : ""); @@ -164,7 +170,7 @@ void drawScrollbar(OLEDDisplay *display, int visibleNodeRows, int totalEntries, // Entry Renderers // ============================= -void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth) { bool isLeftCol = (x < SCREEN_WIDTH / 2); int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7); @@ -189,7 +195,7 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawString(x + ((isHighResolution) ? 6 : 3), y, nodeName); - if (node->is_favorite) { + if (node && detailIsFavorite(*node)) { if (isHighResolution) { drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display); } else { @@ -204,7 +210,7 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int display->drawString(rightEdge - textWidth, y, timeStr); } -void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth) { bool isLeftCol = (x < SCREEN_WIDTH / 2); @@ -220,7 +226,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int display->setFont(FONT_SMALL); display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName); - if (node->is_favorite) { + if (node && detailIsFavorite(*node)) { if (isHighResolution) { drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display); } else { @@ -243,7 +249,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int // Draw hop count char hopStr[6] = ""; - if (node->has_hops_away && node->hops_away > 0) + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > 0) snprintf(hopStr, sizeof(hopStr), "[%d]", node->hops_away); if (hopStr[0] != '\0') { @@ -253,7 +259,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int } } -void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth) { bool isLeftCol = (x < SCREEN_WIDTH / 2); int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22)); @@ -261,12 +267,12 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 const char *nodeName = getSafeNodeName(display, node); char distStr[10] = ""; - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) { - double lat1 = ourNode->position.latitude_i * 1e-7; - double lon1 = ourNode->position.longitude_i * 1e-7; - double lat2 = node->position.latitude_i * 1e-7; - double lon2 = node->position.longitude_i * 1e-7; + double lat1 = ourNode->latitude_i * 1e-7; + double lon1 = ourNode->longitude_i * 1e-7; + double lat2 = node->latitude_i * 1e-7; + double lon2 = node->longitude_i * 1e-7; double earthRadiusKm = 6371.0; double dLat = (lat2 - lat1) * DEG_TO_RAD; @@ -312,7 +318,7 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName); - if (node->is_favorite) { + if (node && detailIsFavorite(*node)) { if (isHighResolution) { drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display); } else { @@ -329,7 +335,7 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 } } -void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth) { switch (currentMode) { case MODE_LAST_HEARD: @@ -346,7 +352,7 @@ void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 } } -void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth) { bool isLeftCol = (x < SCREEN_WIDTH / 2); @@ -358,7 +364,7 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName); - if (node->is_favorite) { + if (node && detailIsFavorite(*node)) { if (isHighResolution) { drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display); } else { @@ -367,7 +373,7 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 } } -void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading, +void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth, float myHeading, double userLat, double userLon) { if (!nodeDB->hasValidPosition(node)) @@ -379,8 +385,8 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 int centerX = x + columnWidth - arrowXOffset; int centerY = y + FONT_HEIGHT_SMALL / 2; - double nodeLat = node->position.latitude_i * 1e-7; - double nodeLon = node->position.longitude_i * 1e-7; + double nodeLat = node->latitude_i * 1e-7; + double nodeLon = node->longitude_i * 1e-7; float bearing = GeoCoord::bearing(userLat, userLon, nodeLat, nodeLon); float bearingToNode = RAD_TO_DEG * bearing; float relativeBearing = fmod((bearingToNode - myHeading + 360), 360); @@ -466,16 +472,17 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t int rowCount = 0; for (int i = startIndex; i < endIndex; ++i) { - if (locationScreen && !nodeDB->getMeshNodeByIndex(i)->has_position) { + meshtastic_NodeDetail *candidate = nodeDB->getMeshNodeByIndex(i); + if (locationScreen && candidate && !detailHasFlag(*candidate, NODEDETAIL_FLAG_HAS_POSITION)) { numskipped++; continue; } int xPos = x + (col * columnWidth); int yPos = y + yOffset; - renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth); + renderer(display, candidate, xPos, yPos, columnWidth); if (extras) { - extras(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth, heading, lat, lon); + extras(display, candidate, xPos, yPos, columnWidth, heading, lat, lon); } lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL); @@ -578,9 +585,12 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, { float heading = 0; bool validHeading = false; - auto ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - double lat = DegD(ourNode->position.latitude_i); - double lon = DegD(ourNode->position.longitude_i); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + if (!ourNode || !nodeDB->hasValidPosition(ourNode)) { + return; + } + double lat = DegD(ourNode->latitude_i); + double lon = DegD(ourNode->longitude_i); #if defined(M5STACK_UNITC6L) display->clear(); diff --git a/src/graphics/draw/NodeListRenderer.h b/src/graphics/draw/NodeListRenderer.h index ea8df8bd9..3c0cec14c 100644 --- a/src/graphics/draw/NodeListRenderer.h +++ b/src/graphics/draw/NodeListRenderer.h @@ -20,8 +20,8 @@ class Screen; namespace NodeListRenderer { // Entry renderer function types -typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int); -typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double); +typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeDetail *, int16_t, int16_t, int); +typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeDetail *, int16_t, int16_t, int, float, double, double); // Node list mode enumeration enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 }; @@ -32,14 +32,14 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t double lon = 0); // Entry renderers -void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); -void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); -void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); -void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); -void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); +void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth); +void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth); +void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth); +void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth); +void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth); // Extras renderers -void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading, +void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth, float myHeading, double userLat, double userLon); // Screen frame functions @@ -51,7 +51,7 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, // Utility functions const char *getCurrentModeTitle(int screenWidth); -const char *getSafeNodeName(meshtastic_NodeInfoLite *node); +const char *getSafeNodeName(OLEDDisplay *display, const meshtastic_NodeDetail *node); void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields); // Bitmap drawing function diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 26bfe8447..cccbf3261 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -300,14 +300,17 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta int scratchLineNum = 0; for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) { char temp_name[16] = {0}; - if (nodeDB->getMeshNodeByIndex(i + 1)->has_user) { - std::string sanitized = sanitizeString(nodeDB->getMeshNodeByIndex(i + 1)->user.long_name); + meshtastic_NodeDetail *candidate = nodeDB->getMeshNodeByIndex(i + 1); + if (candidate && detailHasFlag(*candidate, NODEDETAIL_FLAG_HAS_USER) && candidate->long_name[0] != '\0') { + std::string sanitized = sanitizeString(candidate->long_name); strncpy(temp_name, sanitized.c_str(), sizeof(temp_name) - 1); + } else if (candidate) { + snprintf(temp_name, sizeof(temp_name), "(%04X)", static_cast(candidate->num & 0xFFFF)); } else { - snprintf(temp_name, sizeof(temp_name), "(%04X)", (uint16_t)(nodeDB->getMeshNodeByIndex(i + 1)->num & 0xFFFF)); + strncpy(temp_name, "(?)", sizeof(temp_name) - 1); } - if (i == curSelected) { - selectedNodenum = nodeDB->getMeshNodeByIndex(i + 1)->num; + if (candidate && i == curSelected) { + selectedNodenum = candidate->num; if (isHighResolution) { strncpy(scratchLineBuffer[scratchLineNum], "> ", 3); strncpy(scratchLineBuffer[scratchLineNum] + 2, temp_name, 36); diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index c50fe5cf1..092f2de12 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -27,22 +27,22 @@ static uint32_t lastSwitchTime = 0; namespace graphics { NodeNum UIRenderer::currentFavoriteNodeNum = 0; -std::vector graphics::UIRenderer::favoritedNodes; +std::vector graphics::UIRenderer::favoritedNodes; void graphics::UIRenderer::rebuildFavoritedNodes() { favoritedNodes.clear(); size_t total = nodeDB->getNumMeshNodes(); for (size_t i = 0; i < total; i++) { - meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); + meshtastic_NodeDetail *n = nodeDB->getMeshNodeByIndex(i); if (!n || n->num == nodeDB->getNodeNum()) continue; - if (n->is_favorite) + if (detailIsFavorite(*n)) favoritedNodes.push_back(n); } std::sort(favoritedNodes.begin(), favoritedNodes.end(), - [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; }); + [](const meshtastic_NodeDetail *a, const meshtastic_NodeDetail *b) { return a->num < b->num; }); } #if !MESHTASTIC_EXCLUDE_GPS @@ -289,8 +289,8 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st if (nodeIndex < 0 || nodeIndex >= (int)favoritedNodes.size()) return; - meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex]; - if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite) + meshtastic_NodeDetail *node = favoritedNodes[nodeIndex]; + if (!node || node->num == nodeDB->getNodeNum() || !detailIsFavorite(*node)) return; display->clear(); #if defined(M5STACK_UNITC6L) @@ -303,7 +303,9 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st #endif currentFavoriteNodeNum = node->num; // === Create the shortName and title string === - const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node"; + const bool hasUser = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER); + const char *shortNameCandidate = (hasUser && node->short_name[0] != '\0') ? node->short_name : nullptr; + const char *shortName = (shortNameCandidate && haveGlyphs(shortNameCandidate)) ? shortNameCandidate : "Node"; char titlestr[32] = {0}; snprintf(titlestr, sizeof(titlestr), "Fav: %s", shortName); @@ -321,9 +323,9 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st std::string usernameStr; // === 1. Long Name (always try to show first) === #if defined(M5STACK_UNITC6L) - const char *username = (node->has_user && node->user.long_name[0]) ? node->user.short_name : nullptr; + const char *username = (hasUser && node->long_name[0]) ? node->short_name : nullptr; #else - const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr; + const char *username = (hasUser && node->long_name[0]) ? node->long_name : nullptr; #endif if (username) { @@ -349,7 +351,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st haveSignal = true; } // If hops is valid (>0), show right after signal - if (node->hops_away > 0) { + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > 0) { size_t len = strlen(signalHopsStr); // Decide between "1 Hop" and "N Hops" if (haveSignal) { @@ -383,23 +385,23 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st #if !defined(M5STACK_UNITC6L) // === 4. Uptime (only show if metric is present) === char uptimeStr[32] = ""; - if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) { - getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr)); + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_UPTIME)) { + getUptimeStr(node->uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr)); } if (uptimeStr[0] && line < 5) { display->drawString(x, getTextPositions(display)[line++], uptimeStr); } // === 5. Distance (only if both nodes have GPS position) === - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); char distStr[24] = ""; // Make buffer big enough for any string bool haveDistance = false; if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) { - double lat1 = ourNode->position.latitude_i * 1e-7; - double lon1 = ourNode->position.longitude_i * 1e-7; - double lat2 = node->position.latitude_i * 1e-7; - double lon2 = node->position.longitude_i * 1e-7; + double lat1 = ourNode->latitude_i * 1e-7; + double lon1 = ourNode->longitude_i * 1e-7; + double lat2 = node->latitude_i * 1e-7; + double lon2 = node->longitude_i * 1e-7; double earthRadiusKm = 6371.0; double dLat = (lat2 - lat1) * DEG_TO_RAD; double dLon = (lon2 - lon1) * DEG_TO_RAD; @@ -457,6 +459,10 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st showCompass = true; } if (showCompass) { + const double ourLatDeg = DegD(ourNode->latitude_i); + const double ourLonDeg = DegD(ourNode->longitude_i); + const double nodeLatDeg = DegD(node->latitude_i); + const double nodeLonDeg = DegD(node->longitude_i); const int16_t topY = getTextPositions(display)[1]; const int16_t bottomY = SCREEN_HEIGHT - (FONT_HEIGHT_SMALL - 1); const int16_t usableHeight = bottomY - topY - 5; @@ -467,16 +473,10 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8; const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2; - const auto &op = ourNode->position; - float myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180 - : screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); + float myHeading = + screen->hasHeading() ? screen->getHeading() * PI / 180 : screen->estimatedHeading(ourLatDeg, ourLonDeg); - const auto &p = node->position; - /* unused - float d = - GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - */ - float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i)); + float bearing = GeoCoord::bearing(ourLatDeg, ourLonDeg, nodeLatDeg, nodeLonDeg); if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) { myHeading = 0; } else { @@ -520,20 +520,21 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st int compassX = x + SCREEN_WIDTH / 2; int compassY = yBelowContent + availableHeight / 2; - const auto &op = ourNode->position; + const double ourLatDeg = DegD(ourNode->latitude_i); + const double ourLonDeg = DegD(ourNode->longitude_i); float myHeading = 0; if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) { - myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180 - : screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); + myHeading = + screen->hasHeading() ? screen->getHeading() * PI / 180 : screen->estimatedHeading(ourLatDeg, ourLonDeg); } graphics::CompassRenderer::drawCompassNorth(display, compassX, compassY, myHeading, compassRadius); - const auto &p = node->position; + const double nodeLatDeg = DegD(node->latitude_i); + const double nodeLonDeg = DegD(node->longitude_i); /* unused - float d = - GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + float d = GeoCoord::latLongToMeter(nodeLatDeg, nodeLonDeg, ourLatDeg, ourLonDeg); */ - float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i)); + float bearing = GeoCoord::bearing(ourLatDeg, ourLonDeg, nodeLatDeg, nodeLonDeg); if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) bearing -= myHeading; graphics::CompassRenderer::drawNodeHeading(display, compassX, compassY, compassRadius * 2, bearing); @@ -555,7 +556,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); int line = 1; - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); // === Header === #if defined(M5STACK_UNITC6L) @@ -723,8 +724,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta int yOffset = (isHighResolution) ? 0 : 5; std::string longNameStr; - if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) { - longNameStr = sanitizeString(ourNode->user.long_name); + if (ourNode && detailHasFlag(*ourNode, NODEDETAIL_FLAG_HAS_USER) && ourNode->long_name[0] != '\0') { + longNameStr = sanitizeString(ourNode->long_name); } char shortnameble[35]; snprintf(shortnameble, sizeof(shortnameble), "%s", @@ -981,8 +982,6 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU config.display.heading_bold = false; const char *displayLine = ""; // Initialize to empty string by default - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) { if (config.position.fixed_position) { displayLine = "Fixed GPS"; diff --git a/src/graphics/draw/UIRenderer.h b/src/graphics/draw/UIRenderer.h index 438d56cc2..54bdc8d79 100644 --- a/src/graphics/draw/UIRenderer.h +++ b/src/graphics/draw/UIRenderer.h @@ -63,7 +63,7 @@ class UIRenderer static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static NodeNum currentFavoriteNodeNum; - static std::vector favoritedNodes; + static std::vector favoritedNodes; static void rebuildFavoritedNodes(); // OEM screens diff --git a/src/graphics/niche/InkHUD/Applet.cpp b/src/graphics/niche/InkHUD/Applet.cpp index 1e89ebe1b..b1f64f432 100644 --- a/src/graphics/niche/InkHUD/Applet.cpp +++ b/src/graphics/niche/InkHUD/Applet.cpp @@ -5,6 +5,7 @@ #include "main.h" #include "RTC.h" +#include "mesh/NodeDB.h" using namespace NicheGraphics; @@ -333,13 +334,13 @@ std::string InkHUD::Applet::parse(std::string text) // Get the best version of a node's short name available to us // Parses any non-ascii chars // Swaps for last-four of node-id if the real short name is unknown or can't be rendered (emoji) -std::string InkHUD::Applet::parseShortName(meshtastic_NodeInfoLite *node) +std::string InkHUD::Applet::parseShortName(meshtastic_NodeDetail *node) { assert(node); // Use the true shortname if known, and doesn't contain any unprintable characters (emoji, etc.) - if (node->has_user) { - std::string parsed = parse(node->user.short_name); + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->short_name[0] != '\0') { + std::string parsed = parse(node->short_name); if (isPrintable(parsed)) return parsed; } @@ -640,7 +641,9 @@ uint16_t InkHUD::Applet::getActiveNodeCount() // For each node in db for (uint16_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); + if (!node) + continue; // Check if heard recently, and not our own node if (sinceLastSeen(node) < settings->recentlyActiveSeconds && node->num != nodeDB->getNodeNum()) diff --git a/src/graphics/niche/InkHUD/Applet.h b/src/graphics/niche/InkHUD/Applet.h index 802186e6e..288307eef 100644 --- a/src/graphics/niche/InkHUD/Applet.h +++ b/src/graphics/niche/InkHUD/Applet.h @@ -16,6 +16,7 @@ #include // GFXRoot drawing lib #include "mesh/MeshTypes.h" +#include "mesh/generated/meshtastic/deviceonly.pb.h" #include "./AppletFont.h" #include "./Applets/System/Notification/Notification.h" // The notification object, not the applet @@ -133,15 +134,15 @@ class Applet : public GFX void drawLogo(int16_t centerX, int16_t centerY, uint16_t width, uint16_t height, Color color = BLACK); // Draw the Meshtastic logo - std::string hexifyNodeNum(NodeNum num); // Style as !0123abdc - SignalStrength getSignalStrength(float snr, float rssi); // Interpret SNR and RSSI, as an easy to understand value - std::string getTimeString(uint32_t epochSeconds); // Human readable - std::string getTimeString(); // Current time, human readable - uint16_t getActiveNodeCount(); // Duration determined by user, in onscreen menu - std::string localizeDistance(uint32_t meters); // Human readable distance, imperial or metric - std::string parse(std::string text); // Handle text which might contain special chars - std::string parseShortName(meshtastic_NodeInfoLite *node); // Get the shortname, or a substitute if has unprintable chars - bool isPrintable(std::string); // Check for characters which the font can't print + std::string hexifyNodeNum(NodeNum num); // Style as !0123abdc + SignalStrength getSignalStrength(float snr, float rssi); // Interpret SNR and RSSI, as an easy to understand value + std::string getTimeString(uint32_t epochSeconds); // Human readable + std::string getTimeString(); // Current time, human readable + uint16_t getActiveNodeCount(); // Duration determined by user, in onscreen menu + std::string localizeDistance(uint32_t meters); // Human readable distance, imperial or metric + std::string parse(std::string text); // Handle text which might contain special chars + std::string parseShortName(meshtastic_NodeDetail *node); // Get the shortname, or a substitute if has unprintable chars + bool isPrintable(std::string); // Check for characters which the font can't print // Convenient references diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp index d383a11e4..67c1a8d34 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp @@ -2,6 +2,8 @@ #include "./MapApplet.h" +#include "mesh/NodeDB.h" + using namespace NicheGraphics; void InkHUD::MapApplet::onRender() @@ -136,9 +138,9 @@ void InkHUD::MapApplet::onRender() printAt(vertBarX + (bottomLabelW / 2) + 1, bottomLabelY + (bottomLabelH / 2), vertBottomLabel, CENTER, MIDDLE); // Draw our node LAST with full white fill + outline - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (ourNode && nodeDB->hasValidPosition(ourNode)) { - Marker self = calculateMarker(ourNode->position.latitude_i * 1e-7, ourNode->position.longitude_i * 1e-7, false, 0); + Marker self = calculateMarker(ourNode->latitude_i * 1e-7f, ourNode->longitude_i * 1e-7f, false, 0); int16_t centerX = X(0.5) + (self.eastMeters * metersToPx); int16_t centerY = Y(0.5) - (self.northMeters * metersToPx); @@ -166,10 +168,10 @@ void InkHUD::MapApplet::onRender() void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) { // If we have a valid position for our own node, use that as the anchor - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (ourNode && nodeDB->hasValidPosition(ourNode)) { - *lat = ourNode->position.latitude_i * 1e-7; - *lng = ourNode->position.longitude_i * 1e-7; + *lat = ourNode->latitude_i * 1e-7f; + *lng = ourNode->longitude_i * 1e-7f; } else { // Find mean lat long coords // ============================ @@ -189,7 +191,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) // For each node in db for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); // Skip if no position if (!nodeDB->hasValidPosition(node)) @@ -200,8 +202,8 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) continue; // Latitude and Longitude of node, in radians - float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD; - float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD; + float latRad = node->latitude_i * (1e-7f) * DEG_TO_RAD; + float lngRad = node->longitude_i * (1e-7f) * DEG_TO_RAD; // Convert to cartesian points, with center of earth at 0, 0, 0 // Exact distance from center is irrelevant, as we're only interested in the vector @@ -288,7 +290,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) float westernmost = lngCenter; for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); // Skip if no position if (!nodeDB->hasValidPosition(node)) @@ -299,12 +301,12 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) continue; // Check for a new top or bottom latitude - float latNode = node->position.latitude_i * 1e-7; + float latNode = node->latitude_i * 1e-7f; northernmost = max(northernmost, latNode); southernmost = min(southernmost, latNode); // Longitude is trickier - float lngNode = node->position.longitude_i * 1e-7; + float lngNode = node->longitude_i * 1e-7f; float degEastward = fmod(((lngNode - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node float degWestward = abs(fmod(((lngNode - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node if (degEastward < degWestward) @@ -366,14 +368,14 @@ InkHUD::MapApplet::Marker InkHUD::MapApplet::calculateMarker(float lat, float ln return m; } // Draw a marker on the map for a node, with a shortname label, and backing box -void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node) +void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeDetail *node) { // Find x and y position based on node's position in nodeDB assert(nodeDB->hasValidPosition(node)); - Marker m = calculateMarker(node->position.latitude_i * 1e-7, // Lat, converted from Meshtastic's internal int32 style - node->position.longitude_i * 1e-7, // Long, converted from Meshtastic's internal int32 style - node->has_hops_away, // Is the hopsAway number valid - node->hops_away // Hops away + Marker m = calculateMarker(node->latitude_i * 1e-7f, // Lat, converted from Meshtastic's internal int32 style + node->longitude_i * 1e-7f, // Long, converted from Meshtastic's internal int32 style + detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY), // Is the hopsAway number valid + node->hops_away // Hops away ); // Convert to pixel coords @@ -396,9 +398,9 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node) uint16_t labelH; uint8_t markerSize; - bool tooManyHops = node->hops_away > config.lora.hop_limit; + bool tooManyHops = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > config.lora.hop_limit; bool isOurNode = node->num == nodeDB->getNodeNum(); - bool unknownHops = !node->has_hops_away && !isOurNode; + bool unknownHops = !detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && !isOurNode; // Parse any non-ascii chars in the short name, // and use last 4 instead if unknown / can't render @@ -476,7 +478,7 @@ bool InkHUD::MapApplet::enoughMarkers() { size_t count = 0; for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); // Count nodes if (nodeDB->hasValidPosition(node) && shouldDrawNode(node)) @@ -499,7 +501,7 @@ void InkHUD::MapApplet::calculateAllMarkers() // For each node in db for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); // Skip if no position if (!nodeDB->hasValidPosition(node)) @@ -515,12 +517,11 @@ void InkHUD::MapApplet::calculateAllMarkers() continue; // Calculate marker and store it - markers.push_back( - calculateMarker(node->position.latitude_i * 1e-7, // Lat, converted from Meshtastic's internal int32 style - node->position.longitude_i * 1e-7, // Long, converted from Meshtastic's internal int32 style - node->has_hops_away, // Is the hopsAway number valid - node->hops_away // Hops away - )); + markers.push_back(calculateMarker(node->latitude_i * 1e-7f, // Lat, converted from Meshtastic's internal int32 style + node->longitude_i * 1e-7f, // Long, converted from Meshtastic's internal int32 style + detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY), // Is the hopsAway number valid + node->hops_away // Hops away + )); } } diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h index f45a36071..8dadc2086 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h +++ b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.h @@ -30,12 +30,12 @@ class MapApplet : public Applet void onRender() override; protected: - virtual bool shouldDrawNode(meshtastic_NodeInfoLite *node) { return true; } // Allow derived applets to filter the nodes + virtual bool shouldDrawNode(meshtastic_NodeDetail *node) { return true; } // Allow derived applets to filter the nodes virtual void getMapCenter(float *lat, float *lng); virtual void getMapSize(uint32_t *widthMeters, uint32_t *heightMeters); - bool enoughMarkers(); // Anything to draw? - void drawLabeledMarker(meshtastic_NodeInfoLite *node); // Highlight a specific marker + bool enoughMarkers(); // Anything to draw? + void drawLabeledMarker(meshtastic_NodeDetail *node); // Highlight a specific marker private: // Position and size of a marker to be drawn diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp index 5c9906fba..920d59eb3 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp @@ -3,7 +3,7 @@ #include "RTC.h" #include "GeoCoord.h" -#include "NodeDB.h" +#include "mesh/NodeDB.h" #include "./NodeListApplet.h" @@ -50,19 +50,19 @@ ProcessMessage InkHUD::NodeListApplet::handleReceived(const meshtastic_MeshPacke c.signal = getSignalStrength(mp.rx_snr, mp.rx_rssi); // Assemble info: from nodeDB (needed to detect changes) - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(c.nodeNum); - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(c.nodeNum); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (node) { - if (node->has_hops_away) + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY)) c.hopsAway = node->hops_away; if (nodeDB->hasValidPosition(node) && nodeDB->hasValidPosition(ourNode)) { // Get lat and long as float // Meshtastic stores these as integers internally - float ourLat = ourNode->position.latitude_i * 1e-7; - float ourLong = ourNode->position.longitude_i * 1e-7; - float theirLat = node->position.latitude_i * 1e-7; - float theirLong = node->position.longitude_i * 1e-7; + float ourLat = ourNode->latitude_i * 1e-7f; + float ourLong = ourNode->longitude_i * 1e-7f; + float theirLat = node->latitude_i * 1e-7f; + float theirLong = node->longitude_i * 1e-7f; c.distanceMeters = (int32_t)GeoCoord::latLongToMeter(theirLat, theirLong, ourLat, ourLong); } @@ -144,7 +144,7 @@ void InkHUD::NodeListApplet::onRender() std::string distance; // handled below; uint8_t &hopsAway = card->hopsAway; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeNum); // Skip deleted nodes if (!node) { @@ -162,8 +162,8 @@ void InkHUD::NodeListApplet::onRender() // -- Longname -- // Parse special chars in long name // Use node id if unknown - if (node && node->has_user) - longName = parse(node->user.long_name); // Found in nodeDB + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) + longName = parse(node->long_name); // Found in nodeDB else { // Not found in nodeDB, show a hex nodeid instead longName = hexifyNodeNum(nodeNum); diff --git a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp index ecaa7cea3..010b3d699 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp @@ -14,10 +14,10 @@ InkHUD::LogoApplet::LogoApplet() : concurrency::OSThread("LogoApplet") // During onboarding, show the default short name as well as the version string // This behavior assists manufacturers during mass production, and should not be modified without good reason if (!settings->tips.safeShutdownSeen) { - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); fontTitle = fontMedium; textLeft = xstr(APP_VERSION_SHORT); - textRight = parseShortName(ourNode); + textRight = ourNode ? parseShortName(ourNode) : ""; textTitle = "Meshtastic"; } else { fontTitle = fontSmall; @@ -146,10 +146,10 @@ void InkHUD::LogoApplet::onShutdown() // Prepare for the powered-off screen now // We can change these values because the initial "shutting down" screen has already rendered at this point - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); textLeft = ""; textRight = ""; - textTitle = parseShortName(ourNode); + textTitle = ourNode ? parseShortName(ourNode) : ""; fontTitle = fontMedium; // This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp index 09f76ed46..829233e4e 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp @@ -8,6 +8,7 @@ #include "Router.h" #include "airtime.h" #include "main.h" +#include "mesh/NodeDB.h" #include "power.h" #if !MESHTASTIC_EXCLUDE_GPS @@ -616,7 +617,8 @@ void InkHUD::MenuApplet::populateRecipientPage() // Count favorites for (uint32_t i = 0; i < nodeCount; i++) { - if (nodeDB->getMeshNodeByIndex(i)->is_favorite) + const meshtastic_NodeDetail *detail = nodeDB->getMeshNodeByIndex(i); + if (detail && detailIsFavorite(*detail)) favoriteCount++; } @@ -624,10 +626,12 @@ void InkHUD::MenuApplet::populateRecipientPage() // Don't want some monstrous list that takes 100 clicks to reach exit if (favoriteCount < 20) { for (uint32_t i = 0; i < nodeCount; i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); + if (!node) + continue; // Skip node if not a favorite - if (!node->is_favorite) + if (!detailIsFavorite(*node)) continue; CannedMessages::RecipientItem r; @@ -637,8 +641,8 @@ void InkHUD::MenuApplet::populateRecipientPage() // Set a label for the menu item r.label = "DM: "; - if (node->has_user) - r.label += parse(node->user.long_name); + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->long_name[0] != '\0') + r.label += parse(node->long_name); else r.label += hexifyNodeNum(node->num); // Unsure if it's possible to favorite a node without NodeInfo? diff --git a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp index ae0836d19..a394f379e 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Notification/NotificationApplet.cpp @@ -5,6 +5,7 @@ #include "./Notification.h" #include "graphics/niche/InkHUD/Persistence.h" +#include "mesh/NodeDB.h" #include "meshUtils.h" #include "modules/TextMessageModule.h" @@ -206,13 +207,13 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila isBroadcast ? &inkhud->persistence->latestMessage.broadcast : &inkhud->persistence->latestMessage.dm; // Find info about the sender - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(message->sender); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(message->sender); // Leading tag (channel vs. DM) text += isBroadcast ? "From:" : "DM: "; // Sender id - if (node && node->has_user) + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) text += parseShortName(node); else text += hexifyNodeNum(message->sender); @@ -226,7 +227,7 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila text += isBroadcast ? "Msg from " : "DM from "; // Sender id - if (node && node->has_user) + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) text += parseShortName(node); else text += hexifyNodeNum(message->sender); diff --git a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp index 7c6232f3b..23820cf41 100644 --- a/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/AllMessage/AllMessageApplet.cpp @@ -69,11 +69,11 @@ void InkHUD::AllMessageApplet::onRender() // Sender's id // - short name and long name, if available, or // - node id - meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(message->sender); - if (sender && sender->has_user) { + meshtastic_NodeDetail *sender = nodeDB->getMeshNode(message->sender); + if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER)) { header += parseShortName(sender); // May be last-four of node if unprintable (emoji, etc) header += " ("; - header += parse(sender->user.long_name); + header += parse(sender->long_name); header += ")"; } else header += hexifyNodeNum(message->sender); diff --git a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp index a3b9615a5..93b05e144 100644 --- a/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/DM/DMApplet.cpp @@ -65,11 +65,11 @@ void InkHUD::DMApplet::onRender() // Sender's id // - shortname and long name, if available, or // - node id - meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(latestMessage->dm.sender); - if (sender && sender->has_user) { + meshtastic_NodeDetail *sender = nodeDB->getMeshNode(latestMessage->dm.sender); + if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER)) { header += parseShortName(sender); // May be last-four of node if unprintable (emoji, etc) header += " ("; - header += parse(sender->user.long_name); + header += parse(sender->long_name); header += ")"; } else header += hexifyNodeNum(latestMessage->dm.sender); diff --git a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp index 5a659c606..79fb991c6 100644 --- a/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/Heard/HeardApplet.cpp @@ -6,6 +6,8 @@ #include "./HeardApplet.h" +#include "mesh/NodeDB.h" + using namespace NicheGraphics; void InkHUD::HeardApplet::onActivate() @@ -61,7 +63,7 @@ void InkHUD::HeardApplet::handleParsed(CardInfo c) void InkHUD::HeardApplet::populateFromNodeDB() { // Fill a collection with pointers to each node in db - std::vector ordered; + std::vector ordered; for (auto mn = nodeDB->meshNodes->begin(); mn != nodeDB->meshNodes->end(); ++mn) { // Only copy if valid, and not our own node if (mn->num != 0 && mn->num != nodeDB->getNodeNum()) @@ -69,7 +71,7 @@ void InkHUD::HeardApplet::populateFromNodeDB() } // Sort the collection by age - std::sort(ordered.begin(), ordered.end(), [](meshtastic_NodeInfoLite *top, meshtastic_NodeInfoLite *bottom) -> bool { + std::sort(ordered.begin(), ordered.end(), [](const meshtastic_NodeDetail *top, const meshtastic_NodeDetail *bottom) -> bool { return (top->last_heard > bottom->last_heard); }); @@ -79,21 +81,21 @@ void InkHUD::HeardApplet::populateFromNodeDB() ordered.resize(maxCards()); // Create card info for these (stale) node observations - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - for (meshtastic_NodeInfoLite *node : ordered) { + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + for (meshtastic_NodeDetail *node : ordered) { CardInfo c; c.nodeNum = node->num; - if (node->has_hops_away) + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY)) c.hopsAway = node->hops_away; if (nodeDB->hasValidPosition(node) && nodeDB->hasValidPosition(ourNode)) { // Get lat and long as float // Meshtastic stores these as integers internally - float ourLat = ourNode->position.latitude_i * 1e-7; - float ourLong = ourNode->position.longitude_i * 1e-7; - float theirLat = node->position.latitude_i * 1e-7; - float theirLong = node->position.longitude_i * 1e-7; + float ourLat = ourNode->latitude_i * 1e-7f; + float ourLong = ourNode->longitude_i * 1e-7f; + float theirLat = node->latitude_i * 1e-7f; + float theirLong = node->longitude_i * 1e-7f; c.distanceMeters = (int32_t)GeoCoord::latLongToMeter(theirLat, theirLong, ourLat, ourLong); } diff --git a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp index 88bed998d..d711a1ba4 100644 --- a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp @@ -14,9 +14,9 @@ void InkHUD::PositionsApplet::onRender() // We might be rendering because we got a position packet from them // We might be rendering because our own position updated // Either way, we still highlight which node most recently sent us a position packet - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(lastFrom); + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(lastFrom); if (node && nodeDB->hasValidPosition(node) && enoughMarkers()) - drawLabeledMarker(node); + drawLabeledMarker(const_cast(node)); } // Determine if we need to redraw the map, when we receive a new position packet diff --git a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp index fdb5a168d..d729e69b9 100644 --- a/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/ThreadedMessage/ThreadedMessageApplet.cpp @@ -108,8 +108,8 @@ void InkHUD::ThreadedMessageApplet::onRender() info += "Me"; else { // Check if sender is node db - meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(m.sender); - if (sender) + meshtastic_NodeDetail *sender = nodeDB->getMeshNode(m.sender); + if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER)) info += parseShortName(sender); // Handle any unprintable chars in short name else info += hexifyNodeNum(m.sender); // No node info at all. Print the node num diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 1b2af082d..fcea30a49 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -86,12 +86,15 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp) nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio bool isPreferredRebroadcaster = config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER; + meshtastic_NodeDetail *fromNode = nodeDB->getMeshNode(mp->from); + if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) { LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo"); // ignore our request for its NodeInfo - } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user && - nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) { + } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && fromNode && + !detailHasFlag(*fromNode, NODEDETAIL_FLAG_HAS_USER) && nodeInfoModule && !isPreferredRebroadcaster && + !nodeDB->isFull()) { if (airTime->isTxAllowedChannelUtil(true)) { // Hops used by the request. If somebody in between running modified firmware modified it, ignore it auto hopStart = mp->hop_start; @@ -269,7 +272,7 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh bool MeshService::trySendPosition(NodeNum dest, bool wantReplies) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); assert(node); @@ -376,24 +379,25 @@ void MeshService::sendClientNotification(meshtastic_ClientNotification *n) fromNum++; } -meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode() +meshtastic_NodeDetail *MeshService::refreshLocalMeshNode() { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); assert(node); - // We might not have a position yet for our local node, in that case, at least try to send the time - if (!node->has_position) { - memset(&node->position, 0, sizeof(node->position)); - node->has_position = true; + // Ensure we have a position container so time fields stay fresh even without GPS fixes + if (!detailHasFlag(*node, NODEDETAIL_FLAG_HAS_POSITION)) { + detailSetFlag(*node, NODEDETAIL_FLAG_HAS_POSITION, true); + node->latitude_i = 0; + node->longitude_i = 0; + node->altitude = 0; + node->position_source = _meshtastic_Position_LocSource_MIN; } - meshtastic_PositionLite &position = node->position; - // Update our local node info with our time (even if we don't decide to update anyone else) node->last_heard = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid - position.time = getValidTime(RTCQualityFromNet); + node->position_time = getValidTime(RTCQualityFromNet); if (powerStatus->getHasBattery() == 1) { updateBatteryLevel(powerStatus->getBatteryChargePercent()); @@ -406,7 +410,7 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode() int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) { // Update our local node info with our position (even if we don't decide to update anyone else) - const meshtastic_NodeInfoLite *node = refreshLocalMeshNode(); + const meshtastic_NodeDetail *node = refreshLocalMeshNode(); meshtastic_Position pos = meshtastic_Position_init_default; if (newStatus->getHasLock()) { @@ -421,7 +425,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) // Used fixed position if configured regardless of GPS lock if (config.position.fixed_position) { LOG_WARN("Use fixed position"); - pos = TypeConversions::ConvertToPosition(node->position); + pos = TypeConversions::ConvertToPosition(detailToPositionLite(*node)); } // Add a fresh timestamp diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 71fb544a0..72a0da8de 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -170,7 +170,7 @@ class MeshService bool cancelSending(PacketId id); /// Pull the latest power and time info into my nodeinfo - meshtastic_NodeInfoLite *refreshLocalMeshNode(); + meshtastic_NodeDetail *refreshLocalMeshNode(); /// Send a packet to the phone void sendToPhone(meshtastic_MeshPacket *p); diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index afdb4d096..0e715d4de 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -94,7 +94,7 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast // is not 0 (means implicit ACK) and original packet was also relayed by this node, or we sent it directly to the // destination if (p->from != 0) { - meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from); + meshtastic_NodeDetail *origTx = nodeDB->getMeshNode(p->from); if (origTx) { // Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came // directly from the destination @@ -176,7 +176,7 @@ uint8_t NextHopRouter::getNextHop(NodeNum to, uint8_t relay_node) if (isBroadcast(to)) return NO_NEXT_HOP_PREFERENCE; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(to); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(to); if (node && node->next_hop) { // We are careful not to return the relay node as the next hop if (node->next_hop != relay_node) { @@ -296,7 +296,7 @@ int32_t NextHopRouter::doRetransmissions() // Last retransmission, reset next_hop (fallback to FloodingRouter) p.packet->next_hop = NO_NEXT_HOP_PREFERENCE; // Also reset it in the nodeDB - meshtastic_NodeInfoLite *sentTo = nodeDB->getMeshNode(p.packet->to); + meshtastic_NodeDetail *sentTo = nodeDB->getMeshNode(p.packet->to); if (sentTo) { LOG_INFO("Resetting next hop for packet with dest 0x%x\n", p.packet->to); sentTo->next_hop = NO_NEXT_HOP_PREFERENCE; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index bb3fc6dca..39e1f5c31 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -24,6 +24,7 @@ #include "modules/NeighborInfoModule.h" #include #include +#include #include #include #include @@ -152,18 +153,19 @@ uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { if (ostream) { - std::vector const *vec = (std::vector *)field->pData; - for (auto item : *vec) { + const auto *vec = reinterpret_cast *>(field->pData); + for (const auto &item : *vec) { if (!pb_encode_tag_for_field(ostream, field)) return false; - pb_encode_submessage(ostream, meshtastic_NodeInfoLite_fields, &item); + if (!pb_encode_submessage(ostream, meshtastic_NodeDetail_fields, &item)) + return false; } } if (istream) { - meshtastic_NodeInfoLite node; // this gets good data - std::vector *vec = (std::vector *)field->pData; + meshtastic_NodeDetail node = meshtastic_NodeDetail_init_default; // this gets good data + auto *vec = reinterpret_cast *>(field->pData); - if (istream->bytes_left && pb_decode(istream, meshtastic_NodeInfoLite_fields, &node)) + if (istream->bytes_left && pb_decode(istream, meshtastic_NodeDetail_fields, &node)) vec->push_back(node); } return true; @@ -295,9 +297,8 @@ NodeDB::NodeDB() } #endif // Include our owner in the node db under our nodenum - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); - info->user = TypeConversions::ConvertToUserLite(owner); - info->has_user = true; + meshtastic_NodeDetail *info = getOrCreateMeshNode(getNodeNum()); + applyUserToDetail(*info, owner); // If node database has not been saved for the first time, save it now #ifdef FSCom @@ -520,7 +521,7 @@ void NodeDB::installDefaultNodeDatabase() { LOG_DEBUG("Install default NodeDatabase"); nodeDatabase.version = DEVICESTATE_CUR_VER; - nodeDatabase.nodes = std::vector(MAX_NUM_NODES); + nodeDatabase.nodes = std::vector(MAX_NUM_NODES, makeDefaultDetail()); numMeshNodes = 0; meshNodes = &nodeDatabase.nodes; } @@ -992,16 +993,16 @@ void NodeDB::resetNodes(bool keepFavorites) if (keepFavorites) { LOG_INFO("Clearing node database - preserving favorites"); for (size_t i = 0; i < meshNodes->size(); i++) { - meshtastic_NodeInfoLite &node = meshNodes->at(i); - if (i > 0 && !node.is_favorite) { - node = meshtastic_NodeInfoLite(); + meshtastic_NodeDetail &detail = meshNodes->at(i); + if (i > 0 && !detailIsFavorite(detail)) { + detail = makeDefaultDetail(); } else { numMeshNodes += 1; } }; } else { LOG_INFO("Clearing node database - removing favorites"); - std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite()); + std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), makeDefaultDetail()); } devicestate.has_rx_text_message = false; devicestate.has_rx_waypoint = false; @@ -1021,19 +1022,17 @@ void NodeDB::removeNodeByNum(NodeNum nodeNum) removed++; } numMeshNodes -= removed; - std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + 1, - meshtastic_NodeInfoLite()); + std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + 1, makeDefaultDetail()); LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed); saveNodeDatabaseToDisk(); } void NodeDB::clearLocalPosition() { - meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum()); - node->position.latitude_i = 0; - node->position.longitude_i = 0; - node->position.altitude = 0; - node->position.time = 0; + meshtastic_NodeDetail *detail = getMeshNode(nodeDB->getNodeNum()); + if (detail) { + clearPositionFromDetail(*detail); + } setLocalPosition(meshtastic_Position_init_default); } @@ -1041,10 +1040,10 @@ void NodeDB::cleanupMeshDB() { int newPos = 0, removed = 0; for (int i = 0; i < numMeshNodes; i++) { - if (meshNodes->at(i).has_user) { - if (meshNodes->at(i).user.public_key.size > 0) { - if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) { - meshNodes->at(i).user.public_key.size = 0; + if (detailHasFlag(meshNodes->at(i), NODEDETAIL_FLAG_HAS_USER)) { + if (meshNodes->at(i).public_key.size > 0) { + if (memfll(meshNodes->at(i).public_key.bytes, 0, meshNodes->at(i).public_key.size)) { + meshNodes->at(i).public_key.size = 0; } } if (newPos != i) @@ -1057,7 +1056,7 @@ void NodeDB::cleanupMeshDB() } numMeshNodes -= removed; std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + removed, - meshtastic_NodeInfoLite()); + makeDefaultDetail()); LOG_DEBUG("cleanupMeshDB purged %d entries", removed); } @@ -1109,14 +1108,14 @@ void NodeDB::pickNewNodeNum() nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]; } - meshtastic_NodeInfoLite *found; - while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) || + meshtastic_NodeDetail *found; + while (((found = getMeshNode(nodeNum)) && memcmp(found->macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) || (nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) { NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice if (found) LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so " "trying for 0x%x", - nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate); + nodeNum, found->macaddr[4], found->macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate); nodeNum = candidate; } LOG_DEBUG("Use nodenum 0x%x ", nodeNum); @@ -1415,7 +1414,7 @@ bool NodeDB::saveDeviceStateToDisk() FSCom.mkdir("/prefs"); spiLock->unlock(); #endif - // Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB + // Note: if MAX_NUM_NODES=100 and meshtastic_NodeDetail_size=182, node storage alone is roughly 18KB of data // Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this return saveProto(deviceStateFileName, meshtastic_DeviceState_size, &meshtastic_DeviceState_msg, &devicestate, true); } @@ -1508,7 +1507,7 @@ bool NodeDB::saveToDisk(int saveWhat) return success; } -const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex) +const meshtastic_NodeDetail *NodeDB::readNextMeshNode(uint32_t &readIndex) { if (readIndex < numMeshNodes) return &meshNodes->at(readIndex++); @@ -1517,7 +1516,7 @@ const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex) } /// Given a node, return how many seconds in the past (vs now) that we last heard from it -uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n) +uint32_t sinceLastSeen(const meshtastic_NodeDetail *n) { uint32_t now = getTime(); @@ -1547,9 +1546,10 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) // FIXME this implementation is kinda expensive for (int i = 0; i < numMeshNodes; i++) { - if (localOnly && meshNodes->at(i).via_mqtt) + const auto &detail = meshNodes->at(i); + if (localOnly && detailViaMqtt(detail)) continue; - if (sinceLastSeen(&meshNodes->at(i)) < NUM_ONLINE_SECS) + if (sinceLastSeen(&detail) < NUM_ONLINE_SECS) numseen++; } @@ -1563,8 +1563,8 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) */ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src) { - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); - if (!info) { + meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId); + if (!detail) { return; } @@ -1574,12 +1574,13 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou p.altitude); setLocalPosition(p); - info->position = TypeConversions::ConvertToPositionLite(p); + applyPositionToDetail(*detail, p); } else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) { // FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO // (stop-gap fix for issue #900) LOG_DEBUG("updatePosition SPECIAL time setting time=%u", p.time); - info->position.time = p.time; + detailSetFlag(*detail, NODEDETAIL_FLAG_HAS_POSITION, true); + detail->position_time = p.time; } else { // Be careful to only update fields that have been set by the REMOTE sender // A lot of position reports don't have time populated. In that case, be careful to not blow away the time we @@ -1589,17 +1590,16 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d", nodeId, p.time, p.latitude_i, p.longitude_i); // First, back up fields that we want to protect from overwrite - uint32_t tmp_time = info->position.time; + uint32_t tmp_time = detail->position_time; // Next, update atomically - info->position = TypeConversions::ConvertToPositionLite(p); + applyPositionToDetail(*detail, p); // Last, restore any fields that may have been overwritten - if (!info->position.time) - info->position.time = tmp_time; + if (!detail->position_time) + detail->position_time = tmp_time; } - info->has_position = true; - updateGUIforNode = info; + updateGUIforNode = detail; notifyObservers(true); // Force an update whether or not our node counts have changed } @@ -1608,9 +1608,9 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou */ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src) { - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); + meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId); // Environment metrics should never go to NodeDb but we'll safegaurd anyway - if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) { + if (!detail || t.which_variant != meshtastic_Telemetry_device_metrics_tag) { return; } @@ -1620,9 +1620,8 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS } else { LOG_DEBUG("updateTelemetry REMOTE node=0x%x ", nodeId); } - info->device_metrics = t.variant.device_metrics; - info->has_device_metrics = true; - updateGUIforNode = info; + applyMetricsToDetail(*detail, t.variant.device_metrics); + updateGUIforNode = detail; notifyObservers(true); // Force an update whether or not our node counts have changed } @@ -1631,32 +1630,32 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS */ void NodeDB::addFromContact(meshtastic_SharedContact contact) { - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num); - if (!info || !contact.has_user) { + meshtastic_NodeDetail *detail = getOrCreateMeshNode(contact.node_num); + if (!detail || !contact.has_user) { return; } // If the local node has this node marked as manually verified // and the client does not, do not allow the client to update the // saved public key. - if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) { - if (contact.user.public_key.size != info->user.public_key.size || - memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) { + if (detailHasFlag(*detail, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED) && !contact.manually_verified) { + if (contact.user.public_key.size != detail->public_key.size || + memcmp(contact.user.public_key.bytes, detail->public_key.bytes, detail->public_key.size) != 0) { return; } } - info->num = contact.node_num; - info->has_user = true; - info->user = TypeConversions::ConvertToUserLite(contact.user); + detail->num = contact.node_num; + applyUserToDetail(*detail, contact.user); if (contact.should_ignore) { // If should_ignore is set, // we need to clear the public key and other cruft, in addition to setting the node as ignored - info->is_ignored = true; - info->is_favorite = false; - info->has_device_metrics = false; - info->has_position = false; - info->user.public_key.size = 0; - info->user.public_key.bytes[0] = 0; + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_IGNORED, true); + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, false); + clearMetricsFromDetail(*detail); + clearPositionFromDetail(*detail); + detail->public_key.size = 0; + memset(detail->public_key.bytes, 0, sizeof(detail->public_key.bytes)); } else { + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_IGNORED, false); /* Clients are sending add_contact before every text message DM (because clients may hold a larger node database with * public keys than the radio holds). However, we don't want to update last_heard just because we sent someone a DM! */ @@ -1670,19 +1669,19 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact) // without the user doing so deliberately. We don't normally expect users to use a CLIENT_BASE to send DMs or to add // contacts, but we should make sure it doesn't auto-favorite in case they do. Instead, as a workaround, we'll set // last_heard to now, so that the add_contact node doesn't immediately get evicted. - info->last_heard = getTime(); + detail->last_heard = getTime(); } else { // Normal case: set is_favorite to prevent expiration. // last_heard will remain as-is (or remain 0 if this entry wasn't in the nodeDB). - info->is_favorite = true; + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, true); } // As the clients will begin sending the contact with DMs, we want to strictly check if the node is manually verified if (contact.manually_verified) { - info->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED, true); } // Mark the node's key as manually verified to indicate trustworthiness. - updateGUIforNode = info; + updateGUIforNode = detail; sortMeshDB(); notifyObservers(true); // Force an update whether or not our node counts have changed } @@ -1693,8 +1692,8 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact) */ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex) { - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); - if (!info) { + meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId); + if (!detail) { return false; } @@ -1719,9 +1718,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } } - if (info->user.public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one + if (detail->public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one // if the key doesn't match, don't update nodeDB at all. - if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) { + if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, detail->public_key.bytes, 32) != 0)) { LOG_WARN("Public Key mismatch, dropping NodeInfo"); return false; } @@ -1734,22 +1733,38 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde // Always ensure user.id is derived from nodeId, regardless of what was received snprintf(p.id, sizeof(p.id), "!%08x", nodeId); - // Both of info->user and p start as filled with zero so I think this is okay - auto lite = TypeConversions::ConvertToUserLite(p); - bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex); + meshtastic_NodeDetail before = *detail; - info->user = lite; - if (info->user.public_key.size == 32) { - printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); + applyUserToDetail(*detail, p); + + if (detail->public_key.size == 32) { + printBytes("Saved Pubkey: ", detail->public_key.bytes, 32); } - if (nodeId != getNodeNum()) - info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, info->user.long_name, info->user.short_name, nodeId, - info->channel); - info->has_user = true; + + if (nodeId != getNodeNum()) { + detail->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) + } + + bool userChanged = strncmp(before.long_name, detail->long_name, sizeof(detail->long_name)) != 0 || + strncmp(before.short_name, detail->short_name, sizeof(detail->short_name)) != 0 || + memcmp(before.macaddr, detail->macaddr, sizeof(detail->macaddr)) != 0 || + before.hw_model != detail->hw_model || before.role != detail->role || + before.public_key.size != detail->public_key.size || + memcmp(before.public_key.bytes, detail->public_key.bytes, detail->public_key.size) != 0; + + uint32_t flagDelta = before.flags ^ detail->flags; + bool flagChange = + (flagDelta & (NODEDETAIL_FLAG_IS_LICENSED | NODEDETAIL_FLAG_HAS_UNMESSAGABLE | NODEDETAIL_FLAG_IS_UNMESSAGABLE)) != 0; + + bool channelChanged = (nodeId != getNodeNum()) && (before.channel != detail->channel); + + bool changed = userChanged || flagChange || channelChanged; + + LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, detail->long_name, detail->short_name, nodeId, + detail->channel); if (changed) { - updateGUIforNode = info; + updateGUIforNode = detail; notifyObservers(true); // Force an update whether or not our node counts have changed // We just changed something about a User, @@ -1777,23 +1792,24 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) { LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time); - meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp)); - if (!info) { + meshtastic_NodeDetail *detail = getOrCreateMeshNode(getFrom(&mp)); + if (!detail) { return; } if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard - info->last_heard = mp.rx_time; + detail->last_heard = mp.rx_time; if (mp.rx_snr) - info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. + detail->snr = mp.rx_snr; // keep the most recent SNR we received for this node. - info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT + detailSetFlag(*detail, NODEDETAIL_FLAG_VIA_MQTT, mp.via_mqtt); // Store if we received this packet via MQTT // If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) { - info->has_hops_away = true; - info->hops_away = mp.hop_start - mp.hop_limit; + detailSetFlag(*detail, NODEDETAIL_FLAG_HAS_HOPS_AWAY, true); + uint32_t hopDelta = mp.hop_start - mp.hop_limit; + detail->hops_away = static_cast(std::min(hopDelta, UINT8_MAX)); } sortMeshDB(); } @@ -1801,9 +1817,9 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId) { - meshtastic_NodeInfoLite *lite = getMeshNode(nodeId); - if (lite && lite->is_favorite != is_favorite) { - lite->is_favorite = is_favorite; + meshtastic_NodeDetail *detail = getMeshNode(nodeId); + if (detail && detailIsFavorite(*detail) != is_favorite) { + detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, is_favorite); sortMeshDB(); saveNodeDatabaseToDisk(); } @@ -1817,10 +1833,10 @@ bool NodeDB::isFavorite(uint32_t nodeId) if (nodeId == NODENUM_BROADCAST) return false; - meshtastic_NodeInfoLite *lite = getMeshNode(nodeId); + meshtastic_NodeDetail *detail = getMeshNode(nodeId); - if (lite) { - return lite->is_favorite; + if (detail) { + return detailIsFavorite(*detail); } return false; } @@ -1836,23 +1852,21 @@ bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p) if (p.to == NODENUM_BROADCAST) return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from - meshtastic_NodeInfoLite *lite = NULL; - bool seenFrom = false; bool seenTo = false; for (int i = 0; i < numMeshNodes; i++) { - lite = &meshNodes->at(i); + auto *detail = &meshNodes->at(i); - if (lite->num == p.from) { - if (lite->is_favorite) + if (detail->num == p.from) { + if (detailIsFavorite(*detail)) return true; seenFrom = true; } - if (lite->num == p.to) { - if (lite->is_favorite) + if (detail->num == p.to) { + if (detailIsFavorite(*detail)) return true; seenTo = true; @@ -1888,10 +1902,10 @@ void NodeDB::sortMeshDB() // TODO: Look for at(i-1) also matching own node num, and throw the DB in the trash std::swap(meshNodes->at(i), meshNodes->at(i - 1)); changed = true; - } else if (meshNodes->at(i).is_favorite && !meshNodes->at(i - 1).is_favorite) { + } else if (detailIsFavorite(meshNodes->at(i)) && !detailIsFavorite(meshNodes->at(i - 1))) { std::swap(meshNodes->at(i), meshNodes->at(i - 1)); changed = true; - } else if (!meshNodes->at(i).is_favorite && meshNodes->at(i - 1).is_favorite) { + } else if (!detailIsFavorite(meshNodes->at(i)) && detailIsFavorite(meshNodes->at(i - 1))) { // noop } else if (meshNodes->at(i).last_heard > meshNodes->at(i - 1).last_heard) { std::swap(meshNodes->at(i), meshNodes->at(i - 1)); @@ -1905,11 +1919,11 @@ void NodeDB::sortMeshDB() uint8_t NodeDB::getMeshNodeChannel(NodeNum n) { - const meshtastic_NodeInfoLite *info = getMeshNode(n); - if (!info) { + const meshtastic_NodeDetail *detail = getMeshNode(n); + if (!detail) { return 0; // defaults to PRIMARY } - return info->channel; + return detail->channel; } std::string NodeDB::getNodeId() const @@ -1921,7 +1935,7 @@ std::string NodeDB::getNodeId() const /// Find a node in our DB, return null for missing /// NOTE: This function might be called from an ISR -meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n) +meshtastic_NodeDetail *NodeDB::getMeshNode(NodeNum n) { for (int i = 0; i < numMeshNodes; i++) if (meshNodes->at(i).num == n) @@ -1937,11 +1951,11 @@ bool NodeDB::isFull() } /// Find a node in our DB, create an empty NodeInfo if missing -meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) +meshtastic_NodeDetail *NodeDB::getOrCreateMeshNode(NodeNum n) { - meshtastic_NodeInfoLite *lite = getMeshNode(n); + meshtastic_NodeDetail *detail = getMeshNode(n); - if (!lite) { + if (!detail) { if (isFull()) { LOG_INFO("Node database full with %i nodes and %u bytes free. Erasing oldest entry", numMeshNodes, memGet.getFreeHeap()); @@ -1952,14 +1966,14 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { // Simply the oldest non-favorite, non-ignored, non-verified node - if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && - !(meshNodes->at(i).bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && - meshNodes->at(i).last_heard < oldest) { + const auto &candidate = meshNodes->at(i); + if (!detailIsFavorite(candidate) && !detailIsIgnored(candidate) && + !detailHasFlag(candidate, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED) && meshNodes->at(i).last_heard < oldest) { oldest = meshNodes->at(i).last_heard; oldestIndex = i; } // The oldest "boring" node - if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).user.public_key.size == 0 && + if (!detailIsFavorite(candidate) && !detailIsIgnored(candidate) && candidate.public_key.size == 0 && meshNodes->at(i).last_heard < oldestBoring) { oldestBoring = meshNodes->at(i).last_heard; oldestBoringIndex = i; @@ -1979,33 +1993,33 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) } } // add the node at the end - lite = &meshNodes->at((numMeshNodes)++); + detail = &meshNodes->at((numMeshNodes)++); // everything is missing except the nodenum - memset(lite, 0, sizeof(*lite)); - lite->num = n; + *detail = makeDefaultDetail(); + detail->num = n; LOG_INFO("Adding node to database with %i nodes and %u bytes free!", numMeshNodes, memGet.getFreeHeap()); } - return lite; + return detail; } /// Sometimes we will have Position objects that only have a time, so check for /// valid lat/lon -bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n) +bool NodeDB::hasValidPosition(const meshtastic_NodeDetail *n) { - return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); + return detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION) && (n->latitude_i != 0 || n->longitude_i != 0); } /// If we have a node / user and they report is_licensed = true /// we consider them licensed UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum) { - meshtastic_NodeInfoLite *info = getMeshNode(nodeNum); - if (!info || !info->has_user) { + meshtastic_NodeDetail *detail = getMeshNode(nodeNum); + if (!detail || !detailHasFlag(*detail, NODEDETAIL_FLAG_HAS_USER)) { return UserLicenseStatus::NotKnown; } - return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed; + return detailHasFlag(*detail, NODEDETAIL_FLAG_IS_LICENSED) ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed; } #if !defined(MESHTASTIC_EXCLUDE_PKI) diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 306acc0a5..e6626ea4c 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -104,8 +106,13 @@ static constexpr const char *moduleConfigFileName = "/prefs/module.proto"; static constexpr const char *channelFileName = "/prefs/channels.proto"; static constexpr const char *backupFileName = "/backups/backup.proto"; +template inline T clampValue(T value, T low, T high) +{ + return std::max(low, std::min(value, high)); +} + /// Given a node, return how many seconds in the past (vs now) that we last heard from it -uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n); +uint32_t sinceLastSeen(const meshtastic_NodeDetail *n); /// Given a packet, return how many seconds in the past (vs now) it was received uint32_t sinceReceived(const meshtastic_MeshPacket *p); @@ -135,9 +142,9 @@ class NodeDB // Note: these two references just point into our static array we serialize to/from disk public: - std::vector *meshNodes; + std::vector *meshNodes; bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled - meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI + meshtastic_NodeDetail *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI Observable newStatus; pb_size_t numMeshNodes; @@ -241,15 +248,15 @@ class NodeDB void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role); - const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex); + const meshtastic_NodeDetail *readNextMeshNode(uint32_t &readIndex); - meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x) + meshtastic_NodeDetail *getMeshNodeByIndex(size_t x) { assert(x < numMeshNodes); return &meshNodes->at(x); } - virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n); + virtual meshtastic_NodeDetail *getMeshNode(NodeNum n); size_t getNumMeshNodes() { return numMeshNodes; } UserLicenseStatus getLicenseStatus(uint32_t nodeNum); @@ -260,7 +267,7 @@ class NodeDB emptyNodeDatabase.version = DEVICESTATE_CUR_VER; size_t nodeDatabaseSize; pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase); - return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size); + return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeDetail_size); } // returns true if the maximum number of nodes is reached or we are running low on memory @@ -281,7 +288,7 @@ class NodeDB localPosition = position; } - bool hasValidPosition(const meshtastic_NodeInfoLite *n); + bool hasValidPosition(const meshtastic_NodeDetail *n); #if !defined(MESHTASTIC_EXCLUDE_PKI) bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest); @@ -304,8 +311,8 @@ class NodeDB uint32_t lastNodeDbSave = 0; // when we last saved our db to flash uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually uint32_t lastSort = 0; // When last sorted the nodeDB - /// Find a node in our DB, create an empty NodeInfoLite if missing - meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n); + /// Find a node in our DB, create an empty NodeDetail if missing + meshtastic_NodeDetail *getOrCreateMeshNode(NodeNum n); /* * Internal boolean to track sorting paused @@ -370,6 +377,262 @@ extern uint32_t error_address; #define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT 0 #define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK (1 << NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT) +enum NodeDetailFlag : uint32_t { + NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED = 1u << 0, + NODEDETAIL_FLAG_HAS_USER = 1u << 1, + NODEDETAIL_FLAG_HAS_POSITION = 1u << 2, + NODEDETAIL_FLAG_HAS_DEVICE_METRICS = 1u << 3, + NODEDETAIL_FLAG_VIA_MQTT = 1u << 4, + NODEDETAIL_FLAG_IS_FAVORITE = 1u << 5, + NODEDETAIL_FLAG_IS_IGNORED = 1u << 6, + NODEDETAIL_FLAG_HAS_HOPS_AWAY = 1u << 7, + NODEDETAIL_FLAG_IS_LICENSED = 1u << 8, + NODEDETAIL_FLAG_HAS_UNMESSAGABLE = 1u << 9, + NODEDETAIL_FLAG_IS_UNMESSAGABLE = 1u << 10, + NODEDETAIL_FLAG_HAS_BATTERY_LEVEL = 1u << 11, + NODEDETAIL_FLAG_HAS_VOLTAGE = 1u << 12, + NODEDETAIL_FLAG_HAS_CHANNEL_UTIL = 1u << 13, + NODEDETAIL_FLAG_HAS_AIR_UTIL_TX = 1u << 14, + NODEDETAIL_FLAG_HAS_UPTIME = 1u << 15 +}; + +inline bool detailHasFlag(const meshtastic_NodeDetail &detail, NodeDetailFlag flag) +{ + return (detail.flags & static_cast(flag)) != 0; +} + +inline void detailSetFlag(meshtastic_NodeDetail &detail, NodeDetailFlag flag, bool value = true) +{ + if (value) { + detail.flags |= static_cast(flag); + } else { + detail.flags &= ~static_cast(flag); + } +} + +inline meshtastic_NodeDetail makeDefaultDetail() +{ + meshtastic_NodeDetail detail = meshtastic_NodeDetail_init_default; + return detail; +} + +inline void clearUserFromDetail(meshtastic_NodeDetail &detail) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_USER, false); + detailSetFlag(detail, NODEDETAIL_FLAG_IS_LICENSED, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, false); + detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, false); + detail.long_name[0] = '\0'; + detail.short_name[0] = '\0'; + memset(detail.macaddr, 0, sizeof(detail.macaddr)); + detail.hw_model = _meshtastic_HardwareModel_MIN; + detail.role = _meshtastic_Config_DeviceConfig_Role_MIN; + detail.public_key.size = 0; + memset(detail.public_key.bytes, 0, sizeof(detail.public_key.bytes)); +} + +inline void applyUserLiteToDetail(meshtastic_NodeDetail &detail, const meshtastic_UserLite &userLite) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_USER, true); + strncpy(detail.long_name, userLite.long_name, sizeof(detail.long_name)); + detail.long_name[sizeof(detail.long_name) - 1] = '\0'; + strncpy(detail.short_name, userLite.short_name, sizeof(detail.short_name)); + detail.short_name[sizeof(detail.short_name) - 1] = '\0'; + memcpy(detail.macaddr, userLite.macaddr, sizeof(detail.macaddr)); + detail.hw_model = userLite.hw_model; + detail.role = userLite.role; + + const pb_size_t keySize = std::min(userLite.public_key.size, static_cast(sizeof(detail.public_key.bytes))); + memcpy(detail.public_key.bytes, userLite.public_key.bytes, keySize); + detail.public_key.size = keySize; + + detailSetFlag(detail, NODEDETAIL_FLAG_IS_LICENSED, userLite.is_licensed); + + if (userLite.has_is_unmessagable) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, true); + detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, userLite.is_unmessagable); + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, false); + detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, false); + } +} + +inline void applyUserToDetail(meshtastic_NodeDetail &detail, const meshtastic_User &user) +{ + meshtastic_UserLite lite = meshtastic_UserLite_init_default; + strncpy(lite.long_name, user.long_name, sizeof(lite.long_name)); + lite.long_name[sizeof(lite.long_name) - 1] = '\0'; + strncpy(lite.short_name, user.short_name, sizeof(lite.short_name)); + lite.short_name[sizeof(lite.short_name) - 1] = '\0'; + lite.hw_model = user.hw_model; + lite.role = user.role; + lite.is_licensed = user.is_licensed; + memcpy(lite.macaddr, user.macaddr, sizeof(lite.macaddr)); + const pb_size_t keySize = std::min(user.public_key.size, static_cast(sizeof(lite.public_key.bytes))); + memcpy(lite.public_key.bytes, user.public_key.bytes, keySize); + lite.public_key.size = keySize; + lite.has_is_unmessagable = user.has_is_unmessagable; + lite.is_unmessagable = user.is_unmessagable; + applyUserLiteToDetail(detail, lite); +} + +inline void clearPositionFromDetail(meshtastic_NodeDetail &detail) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_POSITION, false); + detail.latitude_i = 0; + detail.longitude_i = 0; + detail.altitude = 0; + detail.position_time = 0; + detail.position_source = _meshtastic_Position_LocSource_MIN; +} + +inline void applyPositionLiteToDetail(meshtastic_NodeDetail &detail, const meshtastic_PositionLite &positionLite) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_POSITION, true); + detail.latitude_i = positionLite.latitude_i; + detail.longitude_i = positionLite.longitude_i; + detail.altitude = positionLite.altitude; + detail.position_time = positionLite.time; + detail.position_source = positionLite.location_source; +} + +inline void applyPositionToDetail(meshtastic_NodeDetail &detail, const meshtastic_Position &position) +{ + meshtastic_PositionLite lite = meshtastic_PositionLite_init_default; + lite.latitude_i = position.latitude_i; + lite.longitude_i = position.longitude_i; + lite.altitude = position.altitude; + lite.location_source = position.location_source; + lite.time = position.time; + applyPositionLiteToDetail(detail, lite); +} + +inline void clearMetricsFromDetail(meshtastic_NodeDetail &detail) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_DEVICE_METRICS, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, false); + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, false); + detail.battery_level = 0; + detail.voltage_millivolts = 0; + detail.channel_utilization_permille = 0; + detail.air_util_tx_permille = 0; + detail.uptime_seconds = 0; +} + +inline void applyMetricsToDetail(meshtastic_NodeDetail &detail, const meshtastic_DeviceMetrics &metrics) +{ + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_DEVICE_METRICS, true); + + if (metrics.has_battery_level) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, true); + uint32_t battery = metrics.battery_level; + if (battery > 255u) { + battery = 255u; + } + detail.battery_level = static_cast(battery); + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, false); + } + + if (metrics.has_voltage) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, true); + double limitedVoltage = clampValue(static_cast(metrics.voltage), 0.0, 65.535); + int millivolts = static_cast(std::lround(limitedVoltage * 1000.0)); + millivolts = clampValue(millivolts, 0, 0xFFFF); + detail.voltage_millivolts = static_cast(millivolts); + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, false); + detail.voltage_millivolts = 0; + } + + if (metrics.has_channel_utilization) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, true); + double limitedUtil = clampValue(static_cast(metrics.channel_utilization), 0.0, 100.0); + int permille = static_cast(std::lround(limitedUtil * 10.0)); + permille = clampValue(permille, 0, 1000); + detail.channel_utilization_permille = static_cast(permille); + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, false); + detail.channel_utilization_permille = 0; + } + + if (metrics.has_air_util_tx) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, true); + double limitedAirUtil = clampValue(static_cast(metrics.air_util_tx), 0.0, 100.0); + int permille = static_cast(std::lround(limitedAirUtil * 10.0)); + permille = clampValue(permille, 0, 1000); + detail.air_util_tx_permille = static_cast(permille); + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, false); + detail.air_util_tx_permille = 0; + } + + if (metrics.has_uptime_seconds) { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, true); + detail.uptime_seconds = metrics.uptime_seconds; + } else { + detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, false); + detail.uptime_seconds = 0; + } +} + +inline bool detailIsFavorite(const meshtastic_NodeDetail &detail) +{ + return detailHasFlag(detail, NODEDETAIL_FLAG_IS_FAVORITE); +} + +inline bool detailIsIgnored(const meshtastic_NodeDetail &detail) +{ + return detailHasFlag(detail, NODEDETAIL_FLAG_IS_IGNORED); +} + +inline bool detailViaMqtt(const meshtastic_NodeDetail &detail) +{ + return detailHasFlag(detail, NODEDETAIL_FLAG_VIA_MQTT); +} + +inline meshtastic_PositionLite detailToPositionLite(const meshtastic_NodeDetail &detail) +{ + meshtastic_PositionLite lite = meshtastic_PositionLite_init_default; + if (!detailHasFlag(detail, NODEDETAIL_FLAG_HAS_POSITION)) { + return lite; + } + + lite.latitude_i = detail.latitude_i; + lite.longitude_i = detail.longitude_i; + lite.altitude = detail.altitude; + lite.time = detail.position_time; + lite.location_source = detail.position_source; + return lite; +} + +inline meshtastic_UserLite detailToUserLite(const meshtastic_NodeDetail &detail) +{ + meshtastic_UserLite lite = meshtastic_UserLite_init_default; + if (!detailHasFlag(detail, NODEDETAIL_FLAG_HAS_USER)) { + return lite; + } + + strncpy(lite.long_name, detail.long_name, sizeof(lite.long_name)); + lite.long_name[sizeof(lite.long_name) - 1] = '\0'; + strncpy(lite.short_name, detail.short_name, sizeof(lite.short_name)); + lite.short_name[sizeof(lite.short_name) - 1] = '\0'; + lite.hw_model = detail.hw_model; + lite.role = detail.role; + lite.is_licensed = detailHasFlag(detail, NODEDETAIL_FLAG_IS_LICENSED); + memcpy(lite.macaddr, detail.macaddr, sizeof(lite.macaddr)); + lite.public_key.size = std::min(static_cast(sizeof(lite.public_key.bytes)), detail.public_key.size); + memcpy(lite.public_key.bytes, detail.public_key.bytes, lite.public_key.size); + if (detailHasFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE)) { + lite.has_is_unmessagable = true; + lite.is_unmessagable = detailHasFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE); + } + return lite; +} + #define Module_Config_size \ (ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \ ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \ diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 9050ee89d..8230a7a4d 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -267,9 +267,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_OWN_NODEINFO: { LOG_DEBUG("Send My NodeInfo"); - auto us = nodeDB->readNextMeshNode(readIndex); + const meshtastic_NodeDetail *us = nodeDB->readNextMeshNode(readIndex); if (us) { - auto info = TypeConversions::ConvertToNodeInfo(us); + auto info = TypeConversions::ConvertToNodeInfo(*us); info.has_hops_away = false; info.is_favorite = true; { @@ -633,11 +633,11 @@ void PhoneAPI::prefetchNodeInfos() { concurrency::LockGuard guard(&nodeInfoMutex); while (nodeInfoQueue.size() < kNodePrefetchDepth) { - auto nextNode = nodeDB->readNextMeshNode(readIndex); + const meshtastic_NodeDetail *nextNode = nodeDB->readNextMeshNode(readIndex); if (!nextNode) break; - auto info = TypeConversions::ConvertToNodeInfo(nextNode); + auto info = TypeConversions::ConvertToNodeInfo(*nextNode); bool isUs = info.num == nodeDB->getNodeNum(); info.hops_away = isUs ? 0 : info.hops_away; info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard; diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h index 725477eae..134e56c93 100644 --- a/src/mesh/ProtobufModule.h +++ b/src/mesh/ProtobufModule.h @@ -58,7 +58,7 @@ template class ProtobufModule : protected SinglePortModule const char *getSenderShortName(const meshtastic_MeshPacket &mp) { auto node = nodeDB->getMeshNode(getFrom(&mp)); - const char *sender = (node) ? node->user.short_name : "???"; + const char *sender = (node) ? node->short_name : "???"; return sender; } diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 00066a7a3..14ff57667 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -119,11 +119,16 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas // stop the immediate relayer's retransmissions. sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0); } - } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && - (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { - LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY"); - sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), - routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0) { + const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(p->from); + if (!fromDetail || fromDetail->public_key.size == 0) { + LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY"); + sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + } else { + sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + } } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 05f47d7f4..0e6081a33 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -100,21 +100,20 @@ bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p) // Optimized search for favorite routers with matching last byte // Check ordering optimized for IoT devices (cheapest checks first) for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); if (!node) continue; // Check 1: is_favorite (cheapest - single bool) - if (!node->is_favorite) + if (!detailIsFavorite(*node)) continue; // Check 2: has_user (cheap - single bool) - if (!node->has_user) + if (!detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) continue; // Check 3: role check (moderate cost - multiple comparisons) - if (!IS_ONE_OF(node->user.role, meshtastic_Config_DeviceConfig_Role_ROUTER, - meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) { + if (!IS_ONE_OF(node->role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) { continue; } @@ -261,7 +260,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) // don't override if a channel was requested and no need to set it when PKI is enforced if (!p->channel && !p->pki_encrypted && !isBroadcast(p->to)) { - meshtastic_NodeInfoLite const *node = nodeDB->getMeshNode(p->to); + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->to); if (node) { p->channel = node->channel; LOG_DEBUG("localSend to channel %d", p->channel); @@ -409,8 +408,11 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) { concurrency::LockGuard g(cryptLock); + const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(p->from); + const meshtastic_NodeDetail *toDetail = nodeDB->getMeshNode(p->to); + if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY && - (nodeDB->getMeshNode(p->from) == NULL || !nodeDB->getMeshNode(p->from)->has_user)) { + (!fromDetail || !detailHasFlag(*fromDetail, NODEDETAIL_FLAG_HAS_USER))) { LOG_DEBUG("Node 0x%x not in nodeDB-> Rebroadcast mode KNOWN_ONLY will ignore packet", p->from); return DecodeState::DECODE_FAILURE; } @@ -427,33 +429,38 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p) ChannelIndex chIndex = 0; #if !(MESHTASTIC_EXCLUDE_PKI) // Attempt PKI decryption first - if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr && - nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && + if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && fromDetail != nullptr && toDetail != nullptr && rawSize > MESHTASTIC_PKC_OVERHEAD) { - LOG_DEBUG("Attempt PKI decryption"); + meshtastic_UserLite fromLite = meshtastic_UserLite_init_default; + meshtastic_UserLite toLite = meshtastic_UserLite_init_default; + fromLite = detailToUserLite(*fromDetail); + toLite = detailToUserLite(*toDetail); - if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, p->encrypted.bytes, - bytes)) { - LOG_INFO("PKI Decryption worked!"); + if (fromLite.public_key.size == 32 && toLite.public_key.size == 32) { + LOG_DEBUG("Attempt PKI decryption"); - meshtastic_Data decodedtmp; - memset(&decodedtmp, 0, sizeof(decodedtmp)); - rawSize -= MESHTASTIC_PKC_OVERHEAD; - if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) && - decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) { - decrypted = true; - LOG_INFO("Packet decrypted using PKI!"); - p->pki_encrypted = true; - memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); - p->public_key.size = 32; - p->decoded = decodedtmp; - p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded + if (crypto->decryptCurve25519(p->from, fromLite.public_key, p->id, rawSize, p->encrypted.bytes, bytes)) { + LOG_INFO("PKI Decryption worked!"); + + meshtastic_Data decodedtmp; + memset(&decodedtmp, 0, sizeof(decodedtmp)); + rawSize -= MESHTASTIC_PKC_OVERHEAD; + if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) && + decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) { + decrypted = true; + LOG_INFO("Packet decrypted using PKI!"); + p->pki_encrypted = true; + memcpy(&p->public_key.bytes, fromLite.public_key.bytes, 32); + p->public_key.size = 32; + p->decoded = decodedtmp; + p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded + } else { + LOG_ERROR("PKC Decrypted, but pb_decode failed!"); + return DecodeState::DECODE_FAILURE; + } } else { - LOG_ERROR("PKC Decrypted, but pb_decode failed!"); - return DecodeState::DECODE_FAILURE; + LOG_WARN("PKC decrypt attempted but failed!"); } - } else { - LOG_WARN("PKC decrypt attempted but failed!"); } } #endif @@ -596,7 +603,11 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it #if !(MESHTASTIC_EXCLUDE_PKI) - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->to); + meshtastic_UserLite destLite = meshtastic_UserLite_init_default; + if (node) { + destLite = detailToUserLite(*node); + } // We may want to retool things so we can send a PKC packet when the client specifies a key and nodenum, even if the node // is not in the local nodedb // First, only PKC encrypt packets we are originating @@ -613,7 +624,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Check for valid keys and single node destination config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr && // Check for a known public key for the destination - (node->user.public_key.size == 32) && + (destLite.public_key.size == 32) && // Some portnums either make no sense to send with PKC p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { @@ -621,12 +632,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN) return meshtastic_Routing_Error_TOO_LARGE; if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && - memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { + memcmp(p->public_key.bytes, destLite.public_key.bytes, 32) != 0) { LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x", *p->public_key.bytes, - *node->user.public_key.bytes); + *destLite.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } - crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, p->encrypted.bytes); + crypto->encryptCurve25519(p->to, getFrom(p), destLite.public_key, p->id, numbytes, bytes, p->encrypted.bytes); numbytes += MESHTASTIC_PKC_OVERHEAD; p->channel = 0; p->pki_encrypted = true; @@ -776,8 +787,8 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) return; } - meshtastic_NodeInfoLite const *node = nodeDB->getMeshNode(p->from); - if (node != NULL && node->is_ignored) { + const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->from); + if (node != NULL && detailIsIgnored(*node)) { LOG_DEBUG("Ignore msg, 0x%x is ignored", p->from); packetPool.release(p); return; diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 17cd92851..66f736232 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -1,6 +1,9 @@ #include "TypeConversions.h" #include "mesh/generated/meshtastic/deviceonly.pb.h" #include "mesh/generated/meshtastic/mesh.pb.h" +#include +#include +#include meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite) { @@ -45,6 +48,196 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo return info; } +meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeDetail &detail) +{ + meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default; + + info.num = detail.num; + info.snr = detail.snr; + info.last_heard = detail.last_heard; + info.channel = detail.channel; + info.via_mqtt = detail.flags & NODEDETAIL_FLAG_VIA_MQTT; + info.is_favorite = detail.flags & NODEDETAIL_FLAG_IS_FAVORITE; + info.is_ignored = detail.flags & NODEDETAIL_FLAG_IS_IGNORED; + info.is_key_manually_verified = detail.flags & NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED; + + if (detail.flags & NODEDETAIL_FLAG_HAS_HOPS_AWAY) { + info.has_hops_away = true; + info.hops_away = detail.hops_away; + } + + if (detail.flags & NODEDETAIL_FLAG_HAS_POSITION) { + info.has_position = true; + info.position = meshtastic_Position_init_default; + if (detail.latitude_i != 0) { + info.position.has_latitude_i = true; + } + info.position.latitude_i = detail.latitude_i; + if (detail.longitude_i != 0) { + info.position.has_longitude_i = true; + } + info.position.longitude_i = detail.longitude_i; + if (detail.altitude != 0) { + info.position.has_altitude = true; + } + info.position.altitude = detail.altitude; + info.position.location_source = detail.position_source; + info.position.time = detail.position_time; + } + + if (detail.flags & NODEDETAIL_FLAG_HAS_USER) { + info.has_user = true; + meshtastic_User user = meshtastic_User_init_default; + snprintf(user.id, sizeof(user.id), "!%08x", detail.num); + strncpy(user.long_name, detail.long_name, sizeof(user.long_name)); + user.long_name[sizeof(user.long_name) - 1] = '\0'; + strncpy(user.short_name, detail.short_name, sizeof(user.short_name)); + user.short_name[sizeof(user.short_name) - 1] = '\0'; + user.hw_model = detail.hw_model; + user.role = detail.role; + user.is_licensed = detail.flags & NODEDETAIL_FLAG_IS_LICENSED; + memcpy(user.macaddr, detail.macaddr, sizeof(user.macaddr)); + const pb_size_t keySize = std::min(detail.public_key.size, static_cast(sizeof(user.public_key.bytes))); + memcpy(user.public_key.bytes, detail.public_key.bytes, keySize); + user.public_key.size = keySize; + if (detail.flags & NODEDETAIL_FLAG_HAS_UNMESSAGABLE) { + user.has_is_unmessagable = true; + user.is_unmessagable = detail.flags & NODEDETAIL_FLAG_IS_UNMESSAGABLE; + } + info.user = user; + } + + if (detail.flags & NODEDETAIL_FLAG_HAS_DEVICE_METRICS) { + info.has_device_metrics = true; + meshtastic_DeviceMetrics metrics = meshtastic_DeviceMetrics_init_default; + + if (detail.flags & NODEDETAIL_FLAG_HAS_BATTERY_LEVEL) { + metrics.has_battery_level = true; + metrics.battery_level = detail.battery_level; + } + if (detail.flags & NODEDETAIL_FLAG_HAS_VOLTAGE) { + metrics.has_voltage = true; + metrics.voltage = static_cast(detail.voltage_millivolts) / 1000.0f; + } + if (detail.flags & NODEDETAIL_FLAG_HAS_CHANNEL_UTIL) { + metrics.has_channel_utilization = true; + metrics.channel_utilization = static_cast(detail.channel_utilization_permille) / 10.0f; + } + if (detail.flags & NODEDETAIL_FLAG_HAS_AIR_UTIL_TX) { + metrics.has_air_util_tx = true; + metrics.air_util_tx = static_cast(detail.air_util_tx_permille) / 10.0f; + } + if (detail.flags & NODEDETAIL_FLAG_HAS_UPTIME) { + metrics.has_uptime_seconds = true; + metrics.uptime_seconds = detail.uptime_seconds; + } + + info.device_metrics = metrics; + } + + return info; +} + +meshtastic_NodeDetail TypeConversions::ConvertToNodeDetail(const meshtastic_NodeInfoLite &lite) +{ + meshtastic_NodeDetail detail = meshtastic_NodeDetail_init_default; + + detail.num = lite.num; + detail.snr = lite.snr; + detail.last_heard = lite.last_heard; + detail.channel = lite.channel; + detail.next_hop = lite.next_hop; + if (lite.has_hops_away) { + detail.flags |= NODEDETAIL_FLAG_HAS_HOPS_AWAY; + detail.hops_away = lite.hops_away; + } + if (lite.via_mqtt) { + detail.flags |= NODEDETAIL_FLAG_VIA_MQTT; + } + if (lite.is_favorite) { + detail.flags |= NODEDETAIL_FLAG_IS_FAVORITE; + } + if (lite.is_ignored) { + detail.flags |= NODEDETAIL_FLAG_IS_IGNORED; + } + if (lite.bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) { + detail.flags |= NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED; + } + + if (lite.has_user) { + detail.flags |= NODEDETAIL_FLAG_HAS_USER; + strncpy(detail.long_name, lite.user.long_name, sizeof(detail.long_name)); + detail.long_name[sizeof(detail.long_name) - 1] = '\0'; + strncpy(detail.short_name, lite.user.short_name, sizeof(detail.short_name)); + detail.short_name[sizeof(detail.short_name) - 1] = '\0'; + detail.hw_model = lite.user.hw_model; + detail.role = lite.user.role; + memcpy(detail.macaddr, lite.user.macaddr, sizeof(detail.macaddr)); + const pb_size_t keySize = std::min(lite.user.public_key.size, static_cast(sizeof(detail.public_key.bytes))); + memcpy(detail.public_key.bytes, lite.user.public_key.bytes, keySize); + detail.public_key.size = keySize; + if (lite.user.is_licensed) { + detail.flags |= NODEDETAIL_FLAG_IS_LICENSED; + } + if (lite.user.has_is_unmessagable) { + detail.flags |= NODEDETAIL_FLAG_HAS_UNMESSAGABLE; + if (lite.user.is_unmessagable) { + detail.flags |= NODEDETAIL_FLAG_IS_UNMESSAGABLE; + } + } + } + + if (lite.has_position) { + detail.flags |= NODEDETAIL_FLAG_HAS_POSITION; + detail.latitude_i = lite.position.latitude_i; + detail.longitude_i = lite.position.longitude_i; + detail.altitude = lite.position.altitude; + detail.position_time = lite.position.time; + detail.position_source = lite.position.location_source; + } + + if (lite.has_device_metrics) { + detail.flags |= NODEDETAIL_FLAG_HAS_DEVICE_METRICS; + const meshtastic_DeviceMetrics &metrics = lite.device_metrics; + + if (metrics.has_battery_level) { + detail.flags |= NODEDETAIL_FLAG_HAS_BATTERY_LEVEL; + uint32_t battery = metrics.battery_level; + if (battery > 255u) { + battery = 255u; + } + detail.battery_level = static_cast(battery); + } + if (metrics.has_voltage) { + detail.flags |= NODEDETAIL_FLAG_HAS_VOLTAGE; + double limitedVoltage = clampValue(static_cast(metrics.voltage), 0.0, 65.535); + int millivolts = static_cast(std::lround(limitedVoltage * 1000.0)); + millivolts = clampValue(millivolts, 0, 0xFFFF); + detail.voltage_millivolts = static_cast(millivolts); + } + if (metrics.has_channel_utilization) { + detail.flags |= NODEDETAIL_FLAG_HAS_CHANNEL_UTIL; + double limitedUtil = clampValue(static_cast(metrics.channel_utilization), 0.0, 100.0); + int permille = static_cast(std::lround(limitedUtil * 10.0)); + permille = clampValue(permille, 0, 1000); + detail.channel_utilization_permille = static_cast(permille); + } + if (metrics.has_air_util_tx) { + detail.flags |= NODEDETAIL_FLAG_HAS_AIR_UTIL_TX; + double limitedAirUtil = clampValue(static_cast(metrics.air_util_tx), 0.0, 100.0); + int permille = static_cast(std::lround(limitedAirUtil * 10.0)); + permille = clampValue(permille, 0, 1000); + detail.air_util_tx_permille = static_cast(permille); + } + if (metrics.has_uptime_seconds) { + detail.flags |= NODEDETAIL_FLAG_HAS_UPTIME; + detail.uptime_seconds = metrics.uptime_seconds; + } + } + + return detail; +} + meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Position position) { meshtastic_PositionLite lite = meshtastic_PositionLite_init_default; diff --git a/src/mesh/TypeConversions.h b/src/mesh/TypeConversions.h index 19e471f98..93a335c11 100644 --- a/src/mesh/TypeConversions.h +++ b/src/mesh/TypeConversions.h @@ -8,6 +8,8 @@ class TypeConversions { public: static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite); + static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeDetail &detail); + static meshtastic_NodeDetail ConvertToNodeDetail(const meshtastic_NodeInfoLite &lite); static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position); static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite); static meshtastic_UserLite ConvertToUserLite(meshtastic_User user); diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 5a9695702..db2dbedec 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -15,6 +15,9 @@ PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO) PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO) +PB_BIND(meshtastic_NodeDetail, meshtastic_NodeDetail, AUTO) + + PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 2) diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 7fab82ff7..b5466d815 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -101,6 +101,49 @@ typedef struct _meshtastic_NodeInfoLite { uint32_t bitfield; } meshtastic_NodeInfoLite; +typedef PB_BYTES_ARRAY_T(32) meshtastic_NodeDetail_public_key_t; +/* Flattened node representation used for the compact NodeDB rewrite. + Uses integer scaling where possible and a single flags bitfield for booleans. */ +typedef struct _meshtastic_NodeDetail { + /* The node number */ + uint32_t num; + /* 48-bit hardware identifier copied from the radio */ + pb_byte_t macaddr[6]; + /* Cached long display name */ + char long_name[40]; + /* Cached short display name */ + char short_name[5]; + /* Hardware model reported by the node */ + meshtastic_HardwareModel hw_model; + /* Role assigned to the node */ + meshtastic_Config_DeviceConfig_Role role; + /* Public key broadcast by the node */ + meshtastic_NodeDetail_public_key_t public_key; + /* Position data flattened from PositionLite */ + int32_t latitude_i; + int32_t longitude_i; + int32_t altitude; + uint32_t position_time; + meshtastic_Position_LocSource position_source; + /* Radio performance metrics */ + float snr; + /* Last packet timestamp */ + uint32_t last_heard; + /* Mesh routing metadata */ + uint8_t channel; + uint8_t hops_away; + uint8_t next_hop; + /* Device metrics cached using integer scaling */ + uint8_t battery_level; + uint32_t uptime_seconds; + uint16_t channel_utilization_permille; + uint16_t air_util_tx_permille; + uint16_t voltage_millivolts; + /* Bitset storing boolean flags and presence markers. + See NodeDetailFlag shifts for decoded meaning. */ + uint32_t flags; +} meshtastic_NodeDetail; + /* This message is never sent over the wire, but it is used for serializing DB state to flash in the device code FIXME, since we write this each time we enter deep sleep (and have infinite @@ -148,7 +191,7 @@ typedef struct _meshtastic_NodeDatabase { NodeDB.cpp in the device code. */ uint32_t version; /* New lite version of NodeDB to decrease memory footprint */ - std::vector nodes; + std::vector nodes; } meshtastic_NodeDatabase; /* The on-disk saved channels */ @@ -191,6 +234,7 @@ extern "C" { #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0} #define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0} +#define meshtastic_NodeDetail_init_default {0, {0}, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, 0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}} #define meshtastic_NodeDatabase_init_default {0, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} @@ -198,6 +242,7 @@ extern "C" { #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0} #define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0} +#define meshtastic_NodeDetail_init_zero {0, {0}, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, 0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}} #define meshtastic_NodeDatabase_init_zero {0, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} @@ -230,6 +275,29 @@ extern "C" { #define meshtastic_NodeInfoLite_is_ignored_tag 11 #define meshtastic_NodeInfoLite_next_hop_tag 12 #define meshtastic_NodeInfoLite_bitfield_tag 13 +#define meshtastic_NodeDetail_num_tag 1 +#define meshtastic_NodeDetail_macaddr_tag 2 +#define meshtastic_NodeDetail_long_name_tag 3 +#define meshtastic_NodeDetail_short_name_tag 4 +#define meshtastic_NodeDetail_hw_model_tag 5 +#define meshtastic_NodeDetail_role_tag 6 +#define meshtastic_NodeDetail_public_key_tag 7 +#define meshtastic_NodeDetail_latitude_i_tag 8 +#define meshtastic_NodeDetail_longitude_i_tag 9 +#define meshtastic_NodeDetail_altitude_tag 10 +#define meshtastic_NodeDetail_position_time_tag 11 +#define meshtastic_NodeDetail_position_source_tag 12 +#define meshtastic_NodeDetail_snr_tag 13 +#define meshtastic_NodeDetail_last_heard_tag 14 +#define meshtastic_NodeDetail_channel_tag 15 +#define meshtastic_NodeDetail_hops_away_tag 16 +#define meshtastic_NodeDetail_next_hop_tag 17 +#define meshtastic_NodeDetail_battery_level_tag 18 +#define meshtastic_NodeDetail_uptime_seconds_tag 19 +#define meshtastic_NodeDetail_channel_utilization_permille_tag 20 +#define meshtastic_NodeDetail_air_util_tx_permille_tag 21 +#define meshtastic_NodeDetail_voltage_millivolts_tag 22 +#define meshtastic_NodeDetail_flags_tag 23 #define meshtastic_DeviceState_my_node_tag 2 #define meshtastic_DeviceState_owner_tag 3 #define meshtastic_DeviceState_receive_queue_tag 5 @@ -292,6 +360,33 @@ X(a, STATIC, SINGULAR, UINT32, bitfield, 13) #define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite #define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics +#define meshtastic_NodeDetail_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, num, 1) \ +X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 2) \ +X(a, STATIC, SINGULAR, STRING, long_name, 3) \ +X(a, STATIC, SINGULAR, STRING, short_name, 4) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \ +X(a, STATIC, SINGULAR, UENUM, role, 6) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 7) \ +X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 8) \ +X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 9) \ +X(a, STATIC, SINGULAR, INT32, altitude, 10) \ +X(a, STATIC, SINGULAR, FIXED32, position_time, 11) \ +X(a, STATIC, SINGULAR, UENUM, position_source, 12) \ +X(a, STATIC, SINGULAR, FLOAT, snr, 13) \ +X(a, STATIC, SINGULAR, FIXED32, last_heard, 14) \ +X(a, STATIC, SINGULAR, UINT32, channel, 15) \ +X(a, STATIC, SINGULAR, UINT32, hops_away, 16) \ +X(a, STATIC, SINGULAR, UINT32, next_hop, 17) \ +X(a, STATIC, SINGULAR, UINT32, battery_level, 18) \ +X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 19) \ +X(a, STATIC, SINGULAR, UINT32, channel_utilization_permille, 20) \ +X(a, STATIC, SINGULAR, UINT32, air_util_tx_permille, 21) \ +X(a, STATIC, SINGULAR, UINT32, voltage_millivolts, 22) \ +X(a, STATIC, SINGULAR, UINT32, flags, 23) +#define meshtastic_NodeDetail_CALLBACK NULL +#define meshtastic_NodeDetail_DEFAULT NULL + #define meshtastic_DeviceState_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \ @@ -317,7 +412,7 @@ X(a, CALLBACK, REPEATED, MESSAGE, nodes, 2) extern bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); #define meshtastic_NodeDatabase_CALLBACK meshtastic_NodeDatabase_callback #define meshtastic_NodeDatabase_DEFAULT NULL -#define meshtastic_NodeDatabase_nodes_MSGTYPE meshtastic_NodeInfoLite +#define meshtastic_NodeDatabase_nodes_MSGTYPE meshtastic_NodeDetail #define meshtastic_ChannelFile_FIELDLIST(X, a) \ X(a, STATIC, REPEATED, MESSAGE, channels, 1) \ @@ -343,6 +438,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, owner, 6) extern const pb_msgdesc_t meshtastic_PositionLite_msg; extern const pb_msgdesc_t meshtastic_UserLite_msg; extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg; +extern const pb_msgdesc_t meshtastic_NodeDetail_msg; extern const pb_msgdesc_t meshtastic_DeviceState_msg; extern const pb_msgdesc_t meshtastic_NodeDatabase_msg; extern const pb_msgdesc_t meshtastic_ChannelFile_msg; @@ -352,6 +448,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg; #define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg #define meshtastic_UserLite_fields &meshtastic_UserLite_msg #define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg +#define meshtastic_NodeDetail_fields &meshtastic_NodeDetail_msg #define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg #define meshtastic_NodeDatabase_fields &meshtastic_NodeDatabase_msg #define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg @@ -363,6 +460,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg; #define meshtastic_BackupPreferences_size 2277 #define meshtastic_ChannelFile_size 718 #define meshtastic_DeviceState_size 1737 +#define meshtastic_NodeDetail_size 182 #define meshtastic_NodeInfoLite_size 196 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 98 diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 7b7ebb595..e4ad46872 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -738,9 +738,9 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res) JSONArray nodesArray; uint32_t readIndex = 0; - const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); + const meshtastic_NodeDetail *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); while (tempNodeInfo != NULL) { - if (tempNodeInfo->has_user) { + if (detailHasFlag(*tempNodeInfo, NODEDETAIL_FLAG_HAS_USER)) { JSONObject node; char id[16]; @@ -748,26 +748,25 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res) node["id"] = new JSONValue(id); node["snr"] = new JSONValue(tempNodeInfo->snr); - node["via_mqtt"] = new JSONValue(BoolToString(tempNodeInfo->via_mqtt)); + node["via_mqtt"] = new JSONValue(BoolToString(detailViaMqtt(*tempNodeInfo))); node["last_heard"] = new JSONValue((int)tempNodeInfo->last_heard); node["position"] = new JSONValue(); if (nodeDB->hasValidPosition(tempNodeInfo)) { JSONObject position; - position["latitude"] = new JSONValue((float)tempNodeInfo->position.latitude_i * 1e-7); - position["longitude"] = new JSONValue((float)tempNodeInfo->position.longitude_i * 1e-7); - position["altitude"] = new JSONValue((int)tempNodeInfo->position.altitude); + position["latitude"] = new JSONValue(static_cast(tempNodeInfo->latitude_i) * 1e-7f); + position["longitude"] = new JSONValue(static_cast(tempNodeInfo->longitude_i) * 1e-7f); + position["altitude"] = new JSONValue(static_cast(tempNodeInfo->altitude)); node["position"] = new JSONValue(position); } - node["long_name"] = new JSONValue(tempNodeInfo->user.long_name); - node["short_name"] = new JSONValue(tempNodeInfo->user.short_name); + node["long_name"] = new JSONValue(tempNodeInfo->long_name); + node["short_name"] = new JSONValue(tempNodeInfo->short_name); char macStr[18]; - snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->user.macaddr[0], - tempNodeInfo->user.macaddr[1], tempNodeInfo->user.macaddr[2], tempNodeInfo->user.macaddr[3], - tempNodeInfo->user.macaddr[4], tempNodeInfo->user.macaddr[5]); + snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->macaddr[0], tempNodeInfo->macaddr[1], + tempNodeInfo->macaddr[2], tempNodeInfo->macaddr[3], tempNodeInfo->macaddr[4], tempNodeInfo->macaddr[5]); node["mac_address"] = new JSONValue(macStr); - node["hw_model"] = new JSONValue(tempNodeInfo->user.hw_model); + node["hw_model"] = new JSONValue(tempNodeInfo->hw_model); nodesArray.push_back(new JSONValue(node)); } diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index e4f65aa28..a2cad4947 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -39,7 +39,7 @@ /// Verify baseline assumption of node size. If it increases, we need to reevaluate /// the impact of its memory footprint, notably on MAX_NUM_NODES. -static_assert(sizeof(meshtastic_NodeInfoLite) <= 200, "NodeInfoLite size increased. Reconsider impact on MAX_NUM_NODES."); +static_assert(sizeof(meshtastic_NodeDetail) <= 200, "NodeDetail size increased. Reconsider impact on MAX_NUM_NODES."); /// max number of nodes allowed in the nodeDB #ifndef MAX_NUM_NODES diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index aa510a86d..9f2f4011e 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -107,14 +107,14 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta // Automatically favorite the node that is using the admin key auto remoteNode = nodeDB->getMeshNode(mp.from); - if (remoteNode && !remoteNode->is_favorite) { + if (remoteNode && !detailIsFavorite(*remoteNode)) { if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) { // Special case for CLIENT_BASE: is_favorite has special meaning, and we don't want to automatically set it // without the user doing so deliberately. LOG_INFO("PKC admin valid, but not auto-favoriting node %x because role==CLIENT_BASE", mp.from); } else { LOG_INFO("PKC admin valid. Auto-favoriting node %x", mp.from); - remoteNode->is_favorite = true; + nodeDB->set_favorite(true, mp.from); } } } else { @@ -341,9 +341,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } case meshtastic_AdminMessage_set_favorite_node_tag: { LOG_INFO("Client received set_favorite_node command"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->set_favorite_node); if (node != NULL) { - node->is_favorite = true; + detailSetFlag(*node, NODEDETAIL_FLAG_IS_FAVORITE, true); saveChanges(SEGMENT_NODEDATABASE, false); if (screen) screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens @@ -352,9 +352,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } case meshtastic_AdminMessage_remove_favorite_node_tag: { LOG_INFO("Client received remove_favorite_node command"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->remove_favorite_node); if (node != NULL) { - node->is_favorite = false; + detailSetFlag(*node, NODEDETAIL_FLAG_IS_FAVORITE, false); saveChanges(SEGMENT_NODEDATABASE, false); if (screen) screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens @@ -363,31 +363,32 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } case meshtastic_AdminMessage_set_ignored_node_tag: { LOG_INFO("Client received set_ignored_node command"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_ignored_node); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->set_ignored_node); if (node != NULL) { - node->is_ignored = true; - node->has_device_metrics = false; - node->has_position = false; - node->user.public_key.size = 0; - node->user.public_key.bytes[0] = 0; + detailSetFlag(*node, NODEDETAIL_FLAG_IS_IGNORED, true); + clearMetricsFromDetail(*node); + clearPositionFromDetail(*node); + node->public_key.size = 0; + memset(node->public_key.bytes, 0, sizeof(node->public_key.bytes)); saveChanges(SEGMENT_NODEDATABASE, false); } break; } case meshtastic_AdminMessage_remove_ignored_node_tag: { LOG_INFO("Client received remove_ignored_node command"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_ignored_node); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->remove_ignored_node); if (node != NULL) { - node->is_ignored = false; + detailSetFlag(*node, NODEDETAIL_FLAG_IS_IGNORED, false); saveChanges(SEGMENT_NODEDATABASE, false); } break; } case meshtastic_AdminMessage_set_fixed_position_tag: { LOG_INFO("Client received set_fixed_position command"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - node->has_position = true; - node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + if (node) { + applyPositionToDetail(*node, r->set_fixed_position); + } nodeDB->setLocalPosition(r->set_fixed_position); config.position.fixed_position = true; saveChanges(SEGMENT_NODEDATABASE | SEGMENT_CONFIG, false); diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index f435f6060..f6b485900 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -131,9 +131,9 @@ void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t } static bool returnToCannedList = false; -bool hasKeyForNode(const meshtastic_NodeInfoLite *node) +bool hasKeyForNode(const meshtastic_NodeDetail *node) { - return node && node->has_user && node->user.public_key.size > 0; + return node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->public_key.size > 0; } /** * @brief Items in array this->messages will be set to be pointing on the right @@ -254,11 +254,11 @@ void CannedMessageModule::updateDestinationSelectionList() this->filteredNodes.reserve(numMeshNodes); for (size_t i = 0; i < numMeshNodes; ++i) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); - if (!node || node->num == myNodeNum || !node->has_user || node->user.public_key.size != 32) + meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i); + if (!node || node->num == myNodeNum || !detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) || node->public_key.size != 32) continue; - const String &nodeName = node->user.long_name; + const String nodeName = node->long_name; if (searchQuery.length() == 0) { this->filteredNodes.push_back({node, sinceLastSeen(node)}); @@ -525,7 +525,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event } else { int nodeIndex = destIndex - static_cast(activeChannelIndices.size()); if (nodeIndex >= 0 && nodeIndex < static_cast(filteredNodes.size())) { - const meshtastic_NodeInfoLite *selectedNode = filteredNodes[nodeIndex].node; + const meshtastic_NodeDetail *selectedNode = filteredNodes[nodeIndex].node; if (selectedNode) { dest = selectedNode->num; channel = selectedNode->channel; @@ -1253,9 +1253,9 @@ const char *CannedMessageModule::getNodeName(NodeNum node) if (node == NODENUM_BROADCAST) return "Broadcast"; - meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(node); - if (info && info->has_user && strlen(info->user.long_name) > 0) { - return info->user.long_name; + meshtastic_NodeDetail *info = nodeDB->getMeshNode(node); + if (info && detailHasFlag(*info, NODEDETAIL_FLAG_HAS_USER) && strlen(info->long_name) > 0) { + return info->long_name; } static char fallback[12]; @@ -1565,18 +1565,18 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O else { int nodeIndex = itemIndex - numActiveChannels; if (nodeIndex >= 0 && nodeIndex < static_cast(this->filteredNodes.size())) { - meshtastic_NodeInfoLite *node = this->filteredNodes[nodeIndex].node; + meshtastic_NodeDetail *node = this->filteredNodes[nodeIndex].node; if (node) { - if (node->is_favorite) { + if (detailIsFavorite(*node)) { #if defined(M5STACK_UNITC6L) - snprintf(entryText, sizeof(entryText), "* %s", node->user.short_name); + snprintf(entryText, sizeof(entryText), "* %s", node->short_name); } else { - snprintf(entryText, sizeof(entryText), "%s", node->user.short_name); + snprintf(entryText, sizeof(entryText), "%s", node->short_name); } #else - snprintf(entryText, sizeof(entryText), "* %s", node->user.long_name); + snprintf(entryText, sizeof(entryText), "* %s", node->long_name); } else { - snprintf(entryText, sizeof(entryText), "%s", node->user.long_name); + snprintf(entryText, sizeof(entryText), "%s", node->long_name); } #endif } @@ -1601,7 +1601,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O if (itemIndex >= numActiveChannels) { int nodeIndex = itemIndex - numActiveChannels; if (nodeIndex >= 0 && nodeIndex < static_cast(this->filteredNodes.size())) { - const meshtastic_NodeInfoLite *node = this->filteredNodes[nodeIndex].node; + const meshtastic_NodeDetail *node = this->filteredNodes[nodeIndex].node; if (node && hasKeyForNode(node)) { int iconX = display->getWidth() - key_symbol_width - 15; int iconY = yOffset + (FONT_HEIGHT_SMALL - key_symbol_height) / 2; diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 5b0481ac7..40ea31608 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -45,7 +45,7 @@ struct Letter { }; struct NodeEntry { - meshtastic_NodeInfoLite *node; + meshtastic_NodeDetail *node; uint32_t lastHeard; }; diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index 3b8225763..4f4f2bd6e 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -1,6 +1,7 @@ #if !MESHTASTIC_EXCLUDE_PKI #include "KeyVerificationModule.h" #include "MeshService.h" +#include "NodeDB.h" #include "RTC.h" #include "graphics/draw/MenuHandler.h" #include "main.h" @@ -36,8 +37,9 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons } else if (request->key_verification.message_type == meshtastic_KeyVerificationAdmin_MessageType_DO_VERIFY && request->key_verification.nonce == currentNonce) { - auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); - remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + if (auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode)) { + detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED); + } resetToIdle(); } else if (request->key_verification.message_type == meshtastic_KeyVerificationAdmin_MessageType_DO_NOT_VERIFY) { resetToIdle(); @@ -72,8 +74,9 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & sprintf(cn->message, "Enter Security Number for Key Verification"); cn->which_payload_variant = meshtastic_ClientNotification_key_verification_number_request_tag; cn->payload_variant.key_verification_number_request.nonce = currentNonce; - strncpy(cn->payload_variant.key_verification_number_request.remote_longname, // should really check for nulls, etc - nodeDB->getMeshNode(currentRemoteNode)->user.long_name, + const meshtastic_NodeDetail *remoteNode = nodeDB->getMeshNode(currentRemoteNode); + const char *remoteLongName = (remoteNode && remoteNode->long_name[0] != '\0') ? remoteNode->long_name : ""; + strncpy(cn->payload_variant.key_verification_number_request.remote_longname, remoteLongName, sizeof(cn->payload_variant.key_verification_number_request.remote_longname)); service->sendClientNotification(cn); LOG_INFO("Received hash2"); @@ -94,8 +97,9 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & options.bannerCallback = [=](int selected) { if (selected == 1) { - auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); - remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + if (auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode)) { + detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED); + } } }; screen->showOverlayBanner(options);) @@ -104,8 +108,10 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message); cn->which_payload_variant = meshtastic_ClientNotification_key_verification_final_tag; cn->payload_variant.key_verification_final.nonce = currentNonce; - strncpy(cn->payload_variant.key_verification_final.remote_longname, // should really check for nulls, etc - nodeDB->getMeshNode(currentRemoteNode)->user.long_name, + const meshtastic_NodeDetail *remoteNodeFinal = nodeDB->getMeshNode(currentRemoteNode); + const char *remoteFinalLongName = + (remoteNodeFinal && remoteNodeFinal->long_name[0] != '\0') ? remoteNodeFinal->long_name : ""; + strncpy(cn->payload_variant.key_verification_final.remote_longname, remoteFinalLongName, sizeof(cn->payload_variant.key_verification_final.remote_longname)); cn->payload_variant.key_verification_final.isSender = false; service->sendClientNotification(cn); @@ -202,8 +208,9 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply() currentSecurityNumber % 1000); cn->which_payload_variant = meshtastic_ClientNotification_key_verification_number_inform_tag; cn->payload_variant.key_verification_number_inform.nonce = currentNonce; - strncpy(cn->payload_variant.key_verification_number_inform.remote_longname, // should really check for nulls, etc - nodeDB->getMeshNode(currentRemoteNode)->user.long_name, + meshtastic_NodeDetail *remoteNode = nodeDB->getMeshNode(currentRemoteNode); + const char *remoteName = (remoteNode && detailHasFlag(*remoteNode, NODEDETAIL_FLAG_HAS_USER)) ? remoteNode->long_name : ""; + strncpy(cn->payload_variant.key_verification_number_inform.remote_longname, remoteName, sizeof(cn->payload_variant.key_verification_number_inform.remote_longname)); cn->payload_variant.key_verification_number_inform.security_number = currentSecurityNumber; service->sendClientNotification(cn); @@ -217,9 +224,9 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) NodeNum ourNodeNum = nodeDB->getNodeNum(); uint8_t scratch_hash[32] = {0}; LOG_WARN("received security number: %u", incomingNumber); - meshtastic_NodeInfoLite *remoteNodePtr = nullptr; - remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); - if (remoteNodePtr == nullptr || !remoteNodePtr->has_user || remoteNodePtr->user.public_key.size != 32) { + meshtastic_NodeDetail *remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); + if (remoteNodePtr == nullptr || !detailHasFlag(*remoteNodePtr, NODEDETAIL_FLAG_HAS_USER) || + remoteNodePtr->public_key.size != 32) { currentState = KEY_VERIFICATION_IDLE; return; // should we throw an error here? } @@ -232,7 +239,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) hash.update(¤tRemoteNode, sizeof(currentRemoteNode)); hash.update(owner.public_key.bytes, owner.public_key.size); - hash.update(remoteNodePtr->user.public_key.bytes, remoteNodePtr->user.public_key.size); + hash.update(remoteNodePtr->public_key.bytes, remoteNodePtr->public_key.size); hash.finalize(hash1, 32); hash.reset(); @@ -265,8 +272,10 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message); cn->which_payload_variant = meshtastic_ClientNotification_key_verification_final_tag; cn->payload_variant.key_verification_final.nonce = currentNonce; - strncpy(cn->payload_variant.key_verification_final.remote_longname, // should really check for nulls, etc - nodeDB->getMeshNode(currentRemoteNode)->user.long_name, + meshtastic_NodeDetail *remoteNodeFinal = nodeDB->getMeshNode(currentRemoteNode); + const char *finalRemoteName = + (remoteNodeFinal && detailHasFlag(*remoteNodeFinal, NODEDETAIL_FLAG_HAS_USER)) ? remoteNodeFinal->long_name : ""; + strncpy(cn->payload_variant.key_verification_final.remote_longname, finalRemoteName, sizeof(cn->payload_variant.key_verification_final.remote_longname)); cn->payload_variant.key_verification_final.isSender = true; service->sendClientNotification(cn); diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 8b6a9f19c..ace1b77d8 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -169,8 +169,9 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket() return nullptr; } - meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position - assert(node->has_position); + meshtastic_NodeDetail *node = service->refreshLocalMeshNode(); // should guarantee there is now a position + assert(detailHasFlag(*node, NODEDETAIL_FLAG_HAS_POSITION)); + meshtastic_PositionLite nodePosition = detailToPositionLite(*node); // configuration of POSITION packet // consider making this a function argument? @@ -180,7 +181,7 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket() meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure // if localPosition is totally empty, put our last saved position (lite) in there if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) { - nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(node->position)); + nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(nodePosition)); } localPosition.seq_number++; @@ -401,7 +402,7 @@ int32_t PositionModule::runOnce() doDeepSleep(nightyNightMs, false, false); } - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (node == nullptr) return RUNONCE_INTERVAL; @@ -420,8 +421,9 @@ int32_t PositionModule::runOnce() if (nodeDB->hasValidPosition(node)) { lastGpsSend = now; - lastGpsLatitude = node->position.latitude_i; - lastGpsLongitude = node->position.longitude_i; + meshtastic_PositionLite nodePosition = detailToPositionLite(*node); + lastGpsLatitude = nodePosition.latitude_i; + lastGpsLongitude = nodePosition.longitude_i; sendOurPosition(); if (config.device.role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) { @@ -429,12 +431,11 @@ int32_t PositionModule::runOnce() } } } else if (config.position.position_broadcast_smart_enabled) { - const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position + const meshtastic_NodeDetail *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position if (nodeDB->hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. - - auto smartPosition = getDistanceTraveledSinceLastSend(node->position); + auto smartPosition = getDistanceTraveledSinceLastSend(detailToPositionLite(*node)); msSinceLastSend = now - lastGpsSend; if (smartPosition.hasTraveledOverThreshold && @@ -448,8 +449,9 @@ int32_t PositionModule::runOnce() msSinceLastSend, minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send - lastGpsLatitude = node->position.latitude_i; - lastGpsLongitude = node->position.longitude_i; + meshtastic_PositionLite nodePosition = detailToPositionLite(*node); + lastGpsLatitude = nodePosition.latitude_i; + lastGpsLongitude = nodePosition.longitude_i; } } } @@ -489,11 +491,11 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic void PositionModule::handleNewPosition() { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position + meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + const meshtastic_NodeDetail *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate if (nodeDB->hasValidPosition(node2)) { - auto smartPosition = getDistanceTraveledSinceLastSend(node->position); + auto smartPosition = getDistanceTraveledSinceLastSend(detailToPositionLite(*node)); uint32_t msSinceLastSend = millis() - lastGpsSend; if (smartPosition.hasTraveledOverThreshold && Throttle::execute( @@ -505,8 +507,9 @@ void PositionModule::handleNewPosition() minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send - lastGpsLatitude = node->position.latitude_i; - lastGpsLongitude = node->position.longitude_i; + meshtastic_PositionLite nodePosition = detailToPositionLite(*node); + lastGpsLatitude = nodePosition.latitude_i; + lastGpsLongitude = nodePosition.longitude_i; } } } diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index 026b3028d..0d6070737 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -151,7 +151,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket } /* - NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp)); + meshtastic_NodeDetail *n = nodeDB->getMeshNode(getFrom(&mp)); LOG_DEBUG("-----------------------------------------"); LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes); @@ -189,7 +189,7 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #ifdef ARCH_ESP32 auto &p = mp.decoded; - meshtastic_NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp)); + meshtastic_NodeDetail *n = nodeDB->getMeshNode(getFrom(&mp)); /* LOG_DEBUG("-----------------------------------------"); LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes); @@ -268,27 +268,33 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) fileToAppend.printf("??:??:??,"); // Time } - fileToAppend.printf("%d,", getFrom(&mp)); // From - fileToAppend.printf("%s,", n->user.long_name); // Long Name - fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat - fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long + fileToAppend.printf("%d,", getFrom(&mp)); // From + const char *senderName = (n && strlen(n->long_name) > 0) ? n->long_name : "?"; + fileToAppend.printf("%s,", senderName); // Long Name + double senderLat = (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION)) ? n->latitude_i * 1e-7 : 0.0; + double senderLon = (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION)) ? n->longitude_i * 1e-7 : 0.0; + fileToAppend.printf("%f,", senderLat); // Sender Lat + fileToAppend.printf("%f,", senderLon); // Sender Long if (gpsStatus->getIsConnected() || config.position.fixed_position) { fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude } else { // When the phone API is in use, the node info will be updated with position - meshtastic_NodeInfoLite *us = nodeDB->getMeshNode(nodeDB->getNodeNum()); - fileToAppend.printf("%f,", us->position.latitude_i * 1e-7); // RX Lat - fileToAppend.printf("%f,", us->position.longitude_i * 1e-7); // RX Long - fileToAppend.printf("%d,", us->position.altitude); // RX Altitude + meshtastic_NodeDetail *us = nodeDB->getMeshNode(nodeDB->getNodeNum()); + double rxLat = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->latitude_i * 1e-7 : 0.0; + double rxLon = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->longitude_i * 1e-7 : 0.0; + int32_t rxAlt = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->altitude : 0; + fileToAppend.printf("%f,", rxLat); // RX Lat + fileToAppend.printf("%f,", rxLon); // RX Long + fileToAppend.printf("%d,", rxAlt); // RX Altitude } fileToAppend.printf("%f,", mp.rx_snr); // RX SNR - if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) { - float distance = GeoCoord::latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7, - gpsStatus->getLatitude() * 1e-7, gpsStatus->getLongitude() * 1e-7); + if (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION) && gpsStatus->getLatitude() && gpsStatus->getLongitude()) { + float distance = GeoCoord::latLongToMeter(n->latitude_i * 1e-7, n->longitude_i * 1e-7, gpsStatus->getLatitude() * 1e-7, + gpsStatus->getLongitude() * 1e-7); fileToAppend.printf("%f,", distance); // Distance in meters } else { fileToAppend.printf("0,"); diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 05173983c..26d99c3bc 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -17,8 +17,10 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) { if (!maybePKI) return false; - if ((nodeDB->getMeshNode(mp.from) == NULL || !nodeDB->getMeshNode(mp.from)->has_user) && - (nodeDB->getMeshNode(mp.to) == NULL || !nodeDB->getMeshNode(mp.to)->has_user)) + const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(mp.from); + const meshtastic_NodeDetail *toDetail = nodeDB->getMeshNode(mp.to); + if ((!fromDetail || !detailHasFlag(*fromDetail, NODEDETAIL_FLAG_HAS_USER)) && + (!toDetail || !detailHasFlag(*toDetail, NODEDETAIL_FLAG_HAS_USER))) return false; } else if (owner.is_licensed && nodeDB->getLicenseStatus(mp.from) == UserLicenseStatus::NotLicensed) { // Don't let licensed users to rebroadcast packets from unlicensed users diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 575e9fa96..0ccec0838 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -84,6 +84,42 @@ SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("Seria static Print *serialPrint = &Serial2; #endif +namespace +{ +const char *resolveNodeName(const meshtastic_NodeDetail *node, char *buffer, size_t bufferSize, bool preferLongName) +{ + if (!node) { + return "???"; + } + + if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) { + if (preferLongName) { + if (node->long_name[0]) { + return node->long_name; + } + if (node->short_name[0]) { + return node->short_name; + } + } else { + if (node->short_name[0]) { + return node->short_name; + } + if (node->long_name[0]) { + return node->long_name; + } + } + } + + if (buffer && bufferSize > 0) { + snprintf(buffer, bufferSize, "(%04X)", static_cast(node->num & 0xFFFF)); + buffer[bufferSize - 1] = '\0'; + return buffer; + } + + return "???"; +} +} // namespace + char serialBytes[512]; size_t serialPayloadSize; @@ -249,13 +285,15 @@ int32_t SerialModule::runOnce() if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) { lastNmeaTime = millis(); uint32_t readIndex = 0; - const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); - while (tempNodeInfo != NULL) { - if (tempNodeInfo->has_user && nodeDB->hasValidPosition(tempNodeInfo)) { - printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true); + const meshtastic_NodeDetail *tempNode = nodeDB->readNextMeshNode(readIndex); + while (tempNode != NULL) { + if (detailHasFlag(*tempNode, NODEDETAIL_FLAG_HAS_USER) && nodeDB->hasValidPosition(tempNode)) { + char nameBuffer[12] = {0}; + const char *name = resolveNodeName(tempNode, nameBuffer, sizeof(nameBuffer), true); + printWPL(outbuf, sizeof(outbuf), detailToPositionLite(*tempNode), name, true); serialPrint->printf("%s", outbuf); } - tempNodeInfo = nodeDB->readNextMeshNode(readIndex); + tempNode = nodeDB->readNextMeshNode(readIndex); } } } @@ -402,8 +440,9 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) { serialPrint->write(p.payload.bytes, p.payload.size); } else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp)); - const char *sender = (node && node->has_user) ? node->user.short_name : "???"; + meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp)); + char senderBuffer[12] = {0}; + const char *sender = resolveNodeName(node, senderBuffer, sizeof(senderBuffer), false); serialPrint->println(); serialPrint->printf("%s: %s", sender, p.payload.bytes); serialPrint->println(); @@ -419,7 +458,10 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp decoded = &scratch; } // send position packet as WPL to the serial port - printWPL(outbuf, sizeof(outbuf), *decoded, nodeDB->getMeshNode(getFrom(&mp))->user.long_name, + meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp)); + char nameBuffer[12] = {0}; + const char *name = resolveNodeName(node, nameBuffer, sizeof(nameBuffer), true); + printWPL(outbuf, sizeof(outbuf), *decoded, name, moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO); serialPrint->printf("%s", outbuf); } diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index 87a2f1bd2..5bb47ba18 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -317,7 +317,7 @@ void TraceRouteModule::maybeSetNextHop(NodeNum target, uint8_t nextHopByte) if (target == NODENUM_BROADCAST) return; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(target); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(target); if (node && node->next_hop != nextHopByte) { LOG_INFO("Updating next-hop for 0x%08x to 0x%02x based on traceroute", target, nextHopByte); node->next_hop = nextHopByte; @@ -490,13 +490,13 @@ TraceRouteModule::TraceRouteModule() const char *TraceRouteModule::getNodeName(NodeNum node) { - meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(node); - if (info && info->has_user) { - if (strlen(info->user.short_name) > 0) { - return info->user.short_name; + const meshtastic_NodeDetail *info = nodeDB->getMeshNode(node); + if (info && detailHasFlag(*info, NODEDETAIL_FLAG_HAS_USER)) { + if (strlen(info->short_name) > 0) { + return info->short_name; } - if (strlen(info->user.long_name) > 0) { - return info->user.long_name; + if (strlen(info->long_name) > 0) { + return info->long_name; } } diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index 4b05d5fa1..5d13c4056 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -115,7 +115,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, static char distStr[20]; // Get our node, to use our own position - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); // Text fields to draw (left of compass) // Last element must be NULL. This signals the end of the char*[] to drawColumns @@ -135,7 +135,11 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, // If our node has a position: if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { - const meshtastic_PositionLite &op = ourNode->position; + meshtastic_PositionLite op = meshtastic_PositionLite_init_default; + op.latitude_i = ourNode->latitude_i; + op.longitude_i = ourNode->longitude_i; + op.altitude = ourNode->altitude; + op.location_source = ourNode->position_source; float myHeading; if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) { myHeading = 0; diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index ad35e152a..34bcda3e3 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -131,11 +131,12 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length) // PKI messages get accepted even if we can't decrypt if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && strcmp(e.channel_id, "PKI") == 0) { - const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p.get())); - const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); + meshtastic_NodeDetail *tx = nodeDB->getMeshNode(getFrom(p.get())); + meshtastic_NodeDetail *rx = nodeDB->getMeshNode(p->to); // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's // likely they discovered each other via a channel we have downlink enabled for - if (isToUs(p.get()) || (tx && tx->has_user && rx && rx->has_user)) + if (isToUs(p.get()) || + (tx && detailHasFlag(*tx, NODEDETAIL_FLAG_HAS_USER) && rx && detailHasFlag(*rx, NODEDETAIL_FLAG_HAS_USER))) router->enqueueReceivedMessage(p.release()); } else if (router && perhapsDecode(p.get()) == DecodeState::DECODE_SUCCESS) // ignore messages if we don't have the channel key diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index b31d2dc2e..de2e6e764 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -313,10 +313,11 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, // Lambda function for adding a long name to the route auto addToRoute = [](JSONArray *route, NodeNum num) { char long_name[40] = "Unknown"; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num); - bool name_known = node ? node->has_user : false; - if (name_known) - memcpy(long_name, node->user.long_name, sizeof(long_name)); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(num); + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) { + strncpy(long_name, node->long_name, sizeof(long_name) - 1); + long_name[sizeof(long_name) - 1] = '\0'; + } route->push_back(new JSONValue(long_name)); }; addToRoute(&route, mp->to); // Started at the original transmitter (destination of response) diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp index 353c710a1..7cd46bfcf 100644 --- a/src/serialization/MeshPacketSerializer_nRF52.cpp +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -282,10 +282,11 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, auto addToRoute = [](JsonArray *route, NodeNum num) { char long_name[40] = "Unknown"; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num); - bool name_known = node ? node->has_user : false; - if (name_known) - memcpy(long_name, node->user.long_name, sizeof(long_name)); + meshtastic_NodeDetail *node = nodeDB->getMeshNode(num); + if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) { + strncpy(long_name, node->long_name, sizeof(long_name) - 1); + long_name[sizeof(long_name) - 1] = '\0'; + } route->add(long_name); };