mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-24 02:10:46 +00:00

Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (rp2350) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / version (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32s3 (push) Blocked by required conditions
CI / build-esp32c3 (push) Blocked by required conditions
CI / build-esp32c6 (push) Blocked by required conditions
CI / build-nrf52840 (push) Blocked by required conditions
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
* Merge pull request #7777 from meshtastic/create-pull-request/bump-version Bump release version * Only send Neighbours if we have some to send. (#7493) * Only send Neighbours if we have some to send. The original intent of NeighborInfo was that when a NeighbourInfo was sent all of the nodes that saw it would reply with NeighbourInfo. So, NeighbourInfo was sent even if there were no hop-zero nodes in the NodeDB. Since 2023, when this was implemented, our understanding of running city-wide meshes has improved substantially. We have taken steps to reduce the impact of NeighborInfo over LoRa. This change aligns with those ideas: we will now only send NeighborInfo if we have some neighbors to contribute. The impact of this change is that a node must first see another directly connected node in another packet type before NeighborInfo is sent. This means that a node with no neighbors is no longer able to trigger other nodes to broadcast NeighborInfo. It will, however, receive the regular periodic broadcast of NeighborInfo, and will be able to send NeighborInfo if it has at least 1 neighbor. * Include all the things * AvOid memleak * We don't gotTime if time is 2019. (#7772) There are certain GPS chips that have a hard-coded time in firmware that they will return before lock. We set our own hard-coded time, BUILD_EPOCH, that should be newer and use the comparison to not set a bad time. In https://github.com/meshtastic/firmware/pull/7261 we introduced the RTCSetResult and improved it in https://github.com/meshtastic/firmware/pull/7375 . However, the original try-fix left logic in GPS.cpp that could still result in broadcasting the bad time. Further, as part of our fix we cleared the GPS buffer if we didn't get a good time. The mesh was hurting at the time, so this was a reasonable approach. However, given time tends to come in when we're trying to get early lock, this had the potential side effect of throwing away valuable information to get position lock. This change reverses the clearBuffer and changes the logic so if time is not set it will not be broadcast. Fixes https://github.com/meshtastic/firmware/issues/7771 Fixes https://github.com/meshtastic/firmware/issues/7750 --------- Co-authored-by: Tom Fifield <tom@tomfifield.net>
219 lines
8.7 KiB
C++
219 lines
8.7 KiB
C++
#include "NeighborInfoModule.h"
|
|
#include "Default.h"
|
|
#include "MeshService.h"
|
|
#include "NodeDB.h"
|
|
#include "RTC.h"
|
|
#include <Throttle.h>
|
|
|
|
NeighborInfoModule *neighborInfoModule;
|
|
|
|
/*
|
|
Prints a single neighbor info packet and associated neighbors
|
|
Uses LOG_DEBUG, which equates to Console.log
|
|
NOTE: For debugging only
|
|
*/
|
|
void NeighborInfoModule::printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np)
|
|
{
|
|
LOG_DEBUG("%s NEIGHBORINFO PACKET from Node 0x%x to Node 0x%x (last sent by 0x%x)", header, np->node_id, nodeDB->getNodeNum(),
|
|
np->last_sent_by_id);
|
|
LOG_DEBUG("Packet contains %d neighbors", np->neighbors_count);
|
|
for (int i = 0; i < np->neighbors_count; i++) {
|
|
LOG_DEBUG("Neighbor %d: node_id=0x%x, snr=%.2f", i, np->neighbors[i].node_id, np->neighbors[i].snr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Prints the nodeDB neighbors
|
|
NOTE: for debugging only
|
|
*/
|
|
void NeighborInfoModule::printNodeDBNeighbors()
|
|
{
|
|
LOG_DEBUG("Our NodeDB contains %d neighbors", neighbors.size());
|
|
for (size_t i = 0; i < neighbors.size(); i++) {
|
|
LOG_DEBUG("Node %d: node_id=0x%x, snr=%.2f", i, neighbors[i].node_id, neighbors[i].snr);
|
|
}
|
|
}
|
|
|
|
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
|
|
NeighborInfoModule::NeighborInfoModule()
|
|
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
|
|
concurrency::OSThread("NeighborInfo")
|
|
{
|
|
ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP;
|
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
|
|
|
if (moduleConfig.neighbor_info.enabled) {
|
|
isPromiscuous = true; // Update neighbors from all packets
|
|
setIntervalFromNow(Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval,
|
|
default_telemetry_broadcast_interval_secs));
|
|
} else {
|
|
LOG_DEBUG("NeighborInfoModule is disabled");
|
|
disable();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
|
|
Assumes that the neighborInfo packet has been allocated
|
|
@returns the number of entries collected
|
|
*/
|
|
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
|
|
{
|
|
NodeNum my_node_id = nodeDB->getNodeNum();
|
|
neighborInfo->node_id = my_node_id;
|
|
neighborInfo->last_sent_by_id = my_node_id;
|
|
neighborInfo->node_broadcast_interval_secs =
|
|
Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_telemetry_broadcast_interval_secs);
|
|
|
|
cleanUpNeighbors();
|
|
|
|
for (auto nbr : neighbors) {
|
|
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) {
|
|
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id;
|
|
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr;
|
|
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
|
|
// the mesh
|
|
neighborInfo->neighbors_count++;
|
|
}
|
|
}
|
|
printNodeDBNeighbors();
|
|
return neighborInfo->neighbors_count;
|
|
}
|
|
|
|
/*
|
|
Remove neighbors from the database that we haven't heard from in a while
|
|
*/
|
|
void NeighborInfoModule::cleanUpNeighbors()
|
|
{
|
|
uint32_t now = getTime();
|
|
NodeNum my_node_id = nodeDB->getNodeNum();
|
|
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
|
|
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
|
|
// cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970
|
|
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
|
|
LOG_DEBUG("Remove neighbor with node ID 0x%x", it->node_id);
|
|
it = std::vector<meshtastic_Neighbor>::reverse_iterator(
|
|
neighbors.erase(std::next(it).base())); // Erase the element and update the iterator
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Send neighbor info to the mesh */
|
|
void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies)
|
|
{
|
|
meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
|
|
collectNeighborInfo(&neighborInfo);
|
|
// only send neighbours if we have some to send
|
|
if (neighborInfo.neighbors_count > 0) {
|
|
meshtastic_MeshPacket *p = allocDataProtobuf(neighborInfo);
|
|
p->to = dest;
|
|
p->decoded.want_response = wantReplies;
|
|
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
|
printNeighborInfo("SENDING", &neighborInfo);
|
|
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Encompasses the full construction and sending packet to mesh
|
|
Will be used for broadcast.
|
|
*/
|
|
int32_t NeighborInfoModule::runOnce()
|
|
{
|
|
if (moduleConfig.neighbor_info.transmit_over_lora &&
|
|
(!channels.isDefaultChannel(channels.getPrimaryIndex()) || !RadioInterface::uses_default_frequency_slot) &&
|
|
airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) {
|
|
sendNeighborInfo(NODENUM_BROADCAST, false);
|
|
} else {
|
|
sendNeighborInfo(NODENUM_BROADCAST_NO_LORA, false);
|
|
}
|
|
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
|
|
}
|
|
|
|
/*
|
|
Collect a received neighbor info packet from another node
|
|
Pass it to an upper client; do not persist this data on the mesh
|
|
*/
|
|
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
|
|
{
|
|
if (np) {
|
|
printNeighborInfo("RECEIVED", np);
|
|
updateNeighbors(mp, np);
|
|
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
|
|
// If the hopLimit is the same as hopStart, then it is a neighbor
|
|
getOrCreateNeighbor(mp.from, mp.from, 0, mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
|
|
}
|
|
// Allow others to handle this packet
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Copy the content of a current NeighborInfo packet into a new one and update the last_sent_by_id to our NodeNum
|
|
*/
|
|
void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n)
|
|
{
|
|
n->last_sent_by_id = nodeDB->getNodeNum();
|
|
|
|
// Set updated last_sent_by_id to the payload of the to be flooded packet
|
|
p.decoded.payload.size =
|
|
pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_NeighborInfo_msg, n);
|
|
}
|
|
|
|
void NeighborInfoModule::resetNeighbors()
|
|
{
|
|
neighbors.clear();
|
|
}
|
|
|
|
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
|
|
{
|
|
// The last sent ID will be 0 if the packet is from the phone, which we don't count as
|
|
// an edge. So we assume that if it's zero, then this packet is from our node.
|
|
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
|
getOrCreateNeighbor(mp.from, np->last_sent_by_id, np->node_broadcast_interval_secs, mp.rx_snr);
|
|
}
|
|
}
|
|
|
|
meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSender, NodeNum n,
|
|
uint32_t node_broadcast_interval_secs, float snr)
|
|
{
|
|
// our node and the phone are the same node (not neighbors)
|
|
if (n == 0) {
|
|
n = nodeDB->getNodeNum();
|
|
}
|
|
// look for one in the existing list
|
|
for (size_t i = 0; i < neighbors.size(); i++) {
|
|
if (neighbors[i].node_id == n) {
|
|
// if found, update it
|
|
neighbors[i].snr = snr;
|
|
neighbors[i].last_rx_time = getTime();
|
|
// Only if this is the original sender, the broadcast interval corresponds to it
|
|
if (originalSender == n && node_broadcast_interval_secs != 0)
|
|
neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs;
|
|
return &neighbors[i];
|
|
}
|
|
}
|
|
// otherwise, allocate one and assign data to it
|
|
|
|
meshtastic_Neighbor new_nbr = meshtastic_Neighbor_init_zero;
|
|
new_nbr.node_id = n;
|
|
new_nbr.snr = snr;
|
|
new_nbr.last_rx_time = getTime();
|
|
// Only if this is the original sender, the broadcast interval corresponds to it
|
|
if (originalSender == n && node_broadcast_interval_secs != 0)
|
|
new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs;
|
|
else // Assume the same broadcast interval as us for the neighbor if we don't know it
|
|
new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
|
|
|
|
if (neighbors.size() < MAX_NUM_NEIGHBORS) {
|
|
neighbors.push_back(new_nbr);
|
|
} else {
|
|
// If we have too many neighbors, replace the oldest one
|
|
LOG_WARN("Neighbor DB is full, replace oldest neighbor");
|
|
neighbors.erase(neighbors.begin());
|
|
neighbors.push_back(new_nbr);
|
|
}
|
|
return &neighbors.back();
|
|
}
|