2.8: NodeDB shrink, decoupling, and restructuring (#10413)

* 2.8: NodeDB refactor to decouple satellite entries and decrease size

* Regen

* Refactor node mute handling to use dedicated functions for clarity and consistency

* Develop ref

* Fix NodeDB review follow-ups

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/6b1d6cf6-ed6b-43b6-95cb-8e141757664e

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Address review validation nits

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/6b1d6cf6-ed6b-43b6-95cb-8e141757664e

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Trunk

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Extract legacy NodeDatabase migration

* Fix remaining NodeDB review issues

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/c76b9a5a-7244-4fbc-9ef0-98091d8caaea

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fixes

* Trunk

* Fix latest review compile follow-ups

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/5198da01-ec4c-4c16-8a09-68b8e6d5d410

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fix cppcheck style warnings

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/e60287ba-4ece-46e0-83d8-a6d89664c0bb

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Change pointer type for mesh node in set_favorite function

* Change pointer types for mesh node references to const in multiple applets

* Add NodeDB layout v25 documentation and migration guidelines

* Remove tests for uninitialized PacketHistory state due to undefined behavior

* Fix code block formatting in copilot instructions

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Ben Meadors
2026-05-09 15:12:10 -05:00
committed by GitHub
co-authored by thebentern Copilot Autofix powered by AI copilot-swe-agent[bot]
parent 1bcabb893b
commit 94bb21ecc7
58 changed files with 2605 additions and 643 deletions
+15 -14
View File
@@ -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 && !nodeInfoLiteIsFavorite(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;
nodeInfoLiteSetBit(remoteNode, NODEINFO_BITFIELD_IS_FAVORITE_MASK, true);
}
}
} else {
@@ -393,7 +393,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
LOG_INFO("Client received set_favorite_node command");
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node);
if (node != NULL) {
node->is_favorite = true;
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_FAVORITE_MASK, true);
saveChanges(SEGMENT_NODEDATABASE, false);
if (screen)
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
@@ -404,7 +404,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
LOG_INFO("Client received remove_favorite_node command");
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node);
if (node != NULL) {
node->is_favorite = false;
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_FAVORITE_MASK, false);
saveChanges(SEGMENT_NODEDATABASE, false);
if (screen)
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
@@ -415,11 +415,10 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
LOG_INFO("Client received set_ignored_node command");
meshtastic_NodeInfoLite *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;
memset(node->user.public_key.bytes, 0, sizeof(node->user.public_key.bytes));
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_IGNORED_MASK, true);
nodeDB->eraseNodeSatellites(node->num);
node->public_key.size = 0;
memset(node->public_key.bytes, 0, sizeof(node->public_key.bytes));
saveChanges(SEGMENT_NODEDATABASE, false);
}
break;
@@ -428,7 +427,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
LOG_INFO("Client received remove_ignored_node command");
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_ignored_node);
if (node != NULL) {
node->is_ignored = false;
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_IGNORED_MASK, false);
saveChanges(SEGMENT_NODEDATABASE, false);
}
break;
@@ -437,7 +436,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
LOG_INFO("Client received toggle_muted_node command");
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->toggle_muted_node);
if (node != NULL) {
node->bitfield ^= (1 << NODEINFO_BITFIELD_IS_MUTED_SHIFT);
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_MUTED_MASK, !nodeInfoLiteIsMuted(node));
saveChanges(SEGMENT_NODEDATABASE, false);
}
break;
@@ -445,9 +444,11 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
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);
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
// Route the fixed position through updatePosition so it lands in the
// satellite map (or, on builds with PositionDB excluded, just sets
// localPosition for the local broadcast path).
nodeDB->updatePosition(node->num, r->set_fixed_position, RX_SRC_LOCAL);
nodeDB->setLocalPosition(r->set_fixed_position);
config.position.fixed_position = true;
saveChanges(SEGMENT_NODEDATABASE | SEGMENT_CONFIG, false);
+12 -12
View File
@@ -141,7 +141,7 @@ void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t
static bool returnToCannedList = false;
bool hasKeyForNode(const meshtastic_NodeInfoLite *node)
{
return node && node->has_user && node->user.public_key.size > 0;
return nodeInfoLiteHasUser(node) && node->public_key.size > 0;
}
/**
* @brief Items in array this->messages will be set to be pointing on the right
@@ -262,10 +262,10 @@ void CannedMessageModule::updateDestinationSelectionList()
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)
if (!node || node->num == myNodeNum || !nodeInfoLiteHasUser(node) || 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)});
@@ -1054,7 +1054,7 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
}
NodeNum myNodeNum = nodeDB->getNodeNum();
if (node && node->num != myNodeNum && node->has_user && node->user.public_key.size == 32) {
if (node && node->num != myNodeNum && nodeInfoLiteHasUser(node) && node->public_key.size == 32) {
p->pki_encrypted = true;
p->channel = 0; // force PKI
}
@@ -1415,8 +1415,8 @@ const char *CannedMessageModule::getNodeName(NodeNum node)
return "Broadcast";
meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(node);
if (info && info->has_user && strlen(info->user.long_name) > 0) {
return info->user.long_name;
if (nodeInfoLiteHasUser(info) && strlen(info->long_name) > 0) {
return info->long_name;
}
static char fallback[12];
@@ -1723,17 +1723,17 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
meshtastic_NodeInfoLite *node = this->filteredNodes[nodeIndex].node;
if (node) {
if (display->getWidth() <= 64) {
entryText = node->user.short_name;
} else if (node->user.long_name[0]) {
entryText = node->user.long_name;
entryText = node->short_name;
} else if (node->long_name[0]) {
entryText = node->long_name;
} else {
entryText = node->user.short_name;
entryText = node->short_name;
}
}
int availWidth = display->getWidth() -
((graphics::currentResolution == graphics::ScreenResolution::High) ? 40 : 20) -
((node && node->is_favorite) ? 10 : 0);
((nodeInfoLiteIsFavorite(node)) ? 10 : 0);
if (availWidth < 0)
availWidth = 0;
char truncatedEntry[96];
@@ -1742,7 +1742,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
entryText = truncatedEntry;
// Prepend "* " if this is a favorite
if (node && node->is_favorite) {
if (nodeInfoLiteIsFavorite(node)) {
entryText = "* " + entryText;
}
graphics::UIRenderer::truncateStringWithEmotes(display, entryText.c_str(), truncatedEntry, sizeof(truncatedEntry),
+1 -1
View File
@@ -372,7 +372,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
// If we receive a direct message and the receipent is us, apply DM mute setting
// Else we just handle it as not muted.
const bool isDmToUs = !isBroadcast(mp.to) && isToUs(&mp);
bool is_muted = isDmToUs ? (sender && ((sender->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0))
bool is_muted = isDmToUs ? nodeInfoLiteIsMuted(sender)
: (ch.settings.has_module_settings && ch.settings.module_settings.is_muted);
const bool buzzerModeIsDirectOnly =
+31 -17
View File
@@ -10,6 +10,18 @@
KeyVerificationModule *keyVerificationModule;
namespace
{
void copyNodeLongNameOrUnknown(char *dest, size_t destSize, const meshtastic_NodeInfoLite *node)
{
if (!dest || destSize == 0)
return;
const char *name = (node && nodeInfoLiteHasUser(node) && node->long_name[0]) ? node->long_name : "Unknown";
strncpy(dest, name, destSize - 1);
dest[destSize - 1] = '\0';
}
} // namespace
KeyVerificationModule::KeyVerificationModule()
: ProtobufModule("KeyVerification", meshtastic_PortNum_KEY_VERIFICATION_APP, &meshtastic_KeyVerification_msg)
{
@@ -37,7 +49,8 @@ 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 (remoteNodePtr)
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
resetToIdle();
} else if (request->key_verification.message_type == meshtastic_KeyVerificationAdmin_MessageType_DO_NOT_VERIFY) {
resetToIdle();
@@ -72,9 +85,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,
sizeof(cn->payload_variant.key_verification_number_request.remote_longname));
copyNodeLongNameOrUnknown(cn->payload_variant.key_verification_number_request.remote_longname,
sizeof(cn->payload_variant.key_verification_number_request.remote_longname),
nodeDB->getMeshNode(currentRemoteNode));
service->sendClientNotification(cn);
LOG_INFO("Received hash2");
currentState = KEY_VERIFICATION_SENDER_AWAITING_NUMBER;
@@ -95,7 +108,8 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
[=](int selected) {
if (selected == 1) {
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
if (remoteNodePtr)
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
}
};
screen->showOverlayBanner(options);)
@@ -104,9 +118,9 @@ 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,
sizeof(cn->payload_variant.key_verification_final.remote_longname));
copyNodeLongNameOrUnknown(cn->payload_variant.key_verification_final.remote_longname,
sizeof(cn->payload_variant.key_verification_final.remote_longname),
nodeDB->getMeshNode(currentRemoteNode));
cn->payload_variant.key_verification_final.isSender = false;
service->sendClientNotification(cn);
@@ -202,9 +216,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,
sizeof(cn->payload_variant.key_verification_number_inform.remote_longname));
copyNodeLongNameOrUnknown(cn->payload_variant.key_verification_number_inform.remote_longname,
sizeof(cn->payload_variant.key_verification_number_inform.remote_longname),
nodeDB->getMeshNode(currentRemoteNode));
cn->payload_variant.key_verification_number_inform.security_number = currentSecurityNumber;
service->sendClientNotification(cn);
LOG_WARN("Security Number %04u, nonce %llu", currentSecurityNumber, currentNonce);
@@ -219,7 +233,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
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) {
if (!remoteNodePtr || !nodeInfoLiteHasUser(remoteNodePtr) || remoteNodePtr->public_key.size != 32) {
currentState = KEY_VERIFICATION_IDLE;
return; // should we throw an error here?
}
@@ -232,7 +246,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
hash.update(&currentRemoteNode, 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,9 +279,9 @@ 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,
sizeof(cn->payload_variant.key_verification_final.remote_longname));
copyNodeLongNameOrUnknown(cn->payload_variant.key_verification_final.remote_longname,
sizeof(cn->payload_variant.key_verification_final.remote_longname),
nodeDB->getMeshNode(currentRemoteNode));
cn->payload_variant.key_verification_final.isSender = true;
service->sendClientNotification(cn);
LOG_INFO(message);
@@ -310,4 +324,4 @@ void KeyVerificationModule::generateVerificationCode(char *readableCode)
readableCode[i] = (hash1[i] >> 2) + 48; // not a standardized base64, but workable and avoids having a dictionary.
}
}
#endif
#endif
+23 -13
View File
@@ -183,8 +183,7 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
return nullptr;
}
meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position
assert(node->has_position);
const meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position
// configuration of POSITION packet
// consider making this a function argument?
@@ -194,7 +193,9 @@ 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));
meshtastic_PositionLite cachedSelf;
if (nodeDB->copyNodePosition(node->num, cachedSelf))
nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(cachedSelf));
}
localPosition.seq_number++;
@@ -420,7 +421,7 @@ int32_t PositionModule::runOnce()
doDeepSleep(nightyNightMs, false, false);
}
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
if (node == nullptr)
return RUNONCE_INTERVAL;
@@ -445,8 +446,11 @@ int32_t PositionModule::runOnce()
} else if (nodeDB->hasValidPosition(node)) {
lastGpsSend = now;
lastGpsLatitude = node->position.latitude_i;
lastGpsLongitude = node->position.longitude_i;
meshtastic_PositionLite selfPos;
if (nodeDB->copyNodePosition(node->num, selfPos)) {
lastGpsLatitude = selfPos.latitude_i;
lastGpsLongitude = selfPos.longitude_i;
}
if (transmitHistory)
transmitHistory->setLastSentToMesh(meshtastic_PortNum_POSITION_APP);
@@ -461,7 +465,10 @@ int32_t PositionModule::runOnce()
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);
meshtastic_PositionLite selfPos;
if (!nodeDB->copyNodePosition(node->num, selfPos))
return RUNONCE_INTERVAL; // Defensive: hasValidPosition should imply this is non-null
auto smartPosition = getDistanceTraveledSinceLastSend(selfPos);
msSinceLastSend = now - lastGpsSend;
if (smartPosition.hasTraveledOverThreshold &&
@@ -479,8 +486,8 @@ 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;
lastGpsLatitude = selfPos.latitude_i;
lastGpsLongitude = selfPos.longitude_i;
}
}
}
@@ -563,11 +570,14 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic
void PositionModule::handleNewPosition()
{
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
const meshtastic_NodeInfoLite *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);
meshtastic_PositionLite selfPos;
if (!nodeDB->copyNodePosition(node->num, selfPos))
return;
auto smartPosition = getDistanceTraveledSinceLastSend(selfPos);
uint32_t msSinceLastSend = millis() - lastGpsSend;
if (smartPosition.hasTraveledOverThreshold &&
Throttle::execute(
@@ -583,8 +593,8 @@ 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;
lastGpsLatitude = selfPos.latitude_i;
lastGpsLongitude = selfPos.longitude_i;
}
}
}
+21 -11
View File
@@ -268,26 +268,36 @@ 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
fileToAppend.printf("%s,", n ? n->long_name : ""); // Long Name
meshtastic_PositionLite senderPos;
const bool haveSenderPos = nodeDB->copyNodePosition(getFrom(&mp), senderPos);
if (haveSenderPos) {
fileToAppend.printf("%f,", senderPos.latitude_i * 1e-7); // Sender Lat
fileToAppend.printf("%f,", senderPos.longitude_i * 1e-7); // Sender Long
} else {
fileToAppend.printf("0.0,0.0,");
}
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_PositionLite usPos;
if (nodeDB->copyNodePosition(nodeDB->getNodeNum(), usPos)) {
fileToAppend.printf("%f,", usPos.latitude_i * 1e-7); // RX Lat
fileToAppend.printf("%f,", usPos.longitude_i * 1e-7); // RX Long
fileToAppend.printf("%d,", usPos.altitude); // RX Altitude
} else {
fileToAppend.printf("0.0,0.0,0,");
}
}
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,
if (haveSenderPos && senderPos.latitude_i && senderPos.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
float distance = GeoCoord::latLongToMeter(senderPos.latitude_i * 1e-7, senderPos.longitude_i * 1e-7,
gpsStatus->getLatitude() * 1e-7, gpsStatus->getLongitude() * 1e-7);
fileToAppend.printf("%f,", distance); // Distance in meters
} else {
@@ -340,4 +350,4 @@ bool RangeTestModuleRadio::removeFile()
return 0;
#endif
}
}
+1 -2
View File
@@ -17,8 +17,7 @@ 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))
if (!nodeInfoLiteHasUser(nodeDB->getMeshNode(mp.from)) && !nodeInfoLiteHasUser(nodeDB->getMeshNode(mp.to)))
return false;
} else if (owner.is_licensed && ((nodeDB->getLicenseStatus(mp.from) == UserLicenseStatus::NotLicensed) ||
(nodeDB->getLicenseStatus(mp.to) == UserLicenseStatus::NotLicensed))) {
+15 -8
View File
@@ -250,9 +250,12 @@ int32_t SerialModule::runOnce()
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);
serialPrint->printf("%s", outbuf);
if (nodeInfoLiteHasUser(tempNodeInfo) && nodeDB->hasValidPosition(tempNodeInfo)) {
meshtastic_PositionLite pos;
if (nodeDB->copyNodePosition(tempNodeInfo->num, pos)) {
printWPL(outbuf, sizeof(outbuf), pos, tempNodeInfo->long_name, true);
serialPrint->printf("%s", outbuf);
}
}
tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
}
@@ -401,7 +404,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
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 : "???";
const char *sender = nodeInfoLiteHasUser(node) ? node->short_name : "???";
serialPrint->println();
serialPrint->printf("%s: %s", sender, p.payload.bytes);
serialPrint->println();
@@ -417,9 +420,13 @@ 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,
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO);
serialPrint->printf("%s", outbuf);
{
meshtastic_NodeInfoLite *senderNode = nodeDB->getMeshNode(getFrom(&mp));
const char *senderName = senderNode ? senderNode->long_name : "";
printWPL(outbuf, sizeof(outbuf), *decoded, senderName,
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO);
serialPrint->printf("%s", outbuf);
}
}
}
}
@@ -714,4 +721,4 @@ void SerialModule::processWXSerial()
#endif
return;
}
#endif
#endif
+3 -10
View File
@@ -2,6 +2,7 @@
#include "StatusMessageModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "ProtobufModule.h"
StatusMessageModule *statusMessageModule;
@@ -36,16 +37,8 @@ ProcessMessage StatusMessageModule::handleReceived(const meshtastic_MeshPacket &
LOG_INFO("Received a NodeStatus message %s", incomingMessage.status);
RecentStatus entry;
entry.fromNodeId = mp.from;
entry.statusText = incomingMessage.status;
recentReceived.push_back(std::move(entry));
// Keep only last MAX_RECENT_STATUSMESSAGES
if (recentReceived.size() > MAX_RECENT_STATUSMESSAGES) {
recentReceived.erase(recentReceived.begin()); // drop oldest
}
if (nodeDB)
nodeDB->setNodeStatus(mp.from, incomingMessage);
}
}
return ProcessMessage::CONTINUE;
+3 -18
View File
@@ -2,8 +2,6 @@
#if !MESHTASTIC_EXCLUDE_STATUS
#include "SinglePortModule.h"
#include "configuration.h"
#include <string>
#include <vector>
class StatusMessageModule : public SinglePortModule, private concurrency::OSThread
{
@@ -20,29 +18,16 @@ class StatusMessageModule : public SinglePortModule, private concurrency::OSThre
this->setInterval(1000 * 12 * 60 * 60);
}
// TODO: If we have a string, set the initial delay (15 minutes maybe)
// Keep vector from reallocating as we fill up to MAX_RECENT_STATUSMESSAGES
recentReceived.reserve(MAX_RECENT_STATUSMESSAGES);
}
virtual int32_t runOnce() override;
struct RecentStatus {
uint32_t fromNodeId; // mp.from
std::string statusText; // incomingMessage.status
};
const std::vector<RecentStatus> &getRecentReceived() const { return recentReceived; }
protected:
/** Called to handle a particular incoming message
/** Called to handle a particular incoming message. The cached most-recent
* status for each node lives on NodeDB; use nodeDB->copyNodeStatus(num, ...).
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
private:
static constexpr size_t MAX_RECENT_STATUSMESSAGES = 5;
std::vector<RecentStatus> recentReceived;
};
extern StatusMessageModule *statusMessageModule;
#endif
#endif
@@ -515,6 +515,10 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(mp);
// Cache the latest env metrics per node on NodeDB so the phone can
// pull last-known values across reboots and replays.
nodeDB->updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO);
}
return false; // Let others look at this message also if they want
+5 -5
View File
@@ -492,12 +492,12 @@ 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;
if (nodeInfoLiteHasUser(info)) {
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;
}
}
+2 -2
View File
@@ -1226,9 +1226,9 @@ bool TrafficManagementModule::shouldRespondToNodeInfo(const meshtastic_MeshPacke
// Fallback only when PSRAM cache is unavailable on this target.
// In this mode we use the node-wide table maintained by NodeInfoModule.
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
if (!node || !node->has_user)
if (!nodeInfoLiteHasUser(node))
return false;
cachedUser = TypeConversions::ConvertToUser(node->num, node->user);
cachedUser = TypeConversions::ConvertToUser(node);
}
if (!sendResponse)
+5 -3
View File
@@ -100,7 +100,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
char distStr[20] = "";
// Get our node, to use our own position
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
const meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
// Match compass sizing/placement to favorite node screen logic.
const int w = display->getWidth();
@@ -147,8 +147,10 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
const char *statusLine2 = nullptr;
// Distance only needs our own position fix; compass/bearing additionally needs heading.
if (hasOwnPositionFix) {
const meshtastic_PositionLite &op = ourNode->position;
meshtastic_PositionLite ownPos;
const bool haveOwnPos = ourNode && nodeDB->copyNodePosition(ourNode->num, ownPos);
if (hasOwnPositionFix && haveOwnPos) {
const meshtastic_PositionLite &op = ownPos;
const float d =
GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));