mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-26 18:09:04 +00:00
Merge branch 'master' into picomputer-s3
This commit is contained in:
commit
e3260c1d19
@ -1,6 +1,6 @@
|
||||
version: 0.1
|
||||
cli:
|
||||
version: 1.9.1
|
||||
version: 1.10.0
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
@ -10,7 +10,7 @@ lint:
|
||||
enabled:
|
||||
- taplo@0.7.0
|
||||
- ruff@0.0.265
|
||||
- yamllint@1.31.0
|
||||
- yamllint@1.32.0
|
||||
- isort@5.12.0
|
||||
- markdownlint@0.34.0
|
||||
- oxipng@8.0.0
|
||||
|
@ -49,10 +49,13 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
||||
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
|
||||
// If it is a traceRoute request, update the route that it went via me
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && traceRouteModule &&
|
||||
traceRouteModule->wantPacket(p)) {
|
||||
traceRouteModule->updateRoute(tosend);
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
// If it is a traceRoute request, update the route that it went via me
|
||||
if (traceRouteModule && traceRouteModule->wantPacket(p))
|
||||
traceRouteModule->updateRoute(tosend);
|
||||
// If it is a neighborInfo packet, update last_sent_by_id
|
||||
if (neighborInfoModule && neighborInfoModule->wantPacket(p))
|
||||
neighborInfoModule->updateLastSentById(tosend);
|
||||
}
|
||||
|
||||
LOG_INFO("Rebroadcasting received floodmsg to neighbors\n");
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "PacketHistory.h"
|
||||
#include "Router.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include "modules/TraceRouteModule.h"
|
||||
|
||||
/**
|
||||
@ -57,4 +58,4 @@ class FloodingRouter : public Router, protected PacketHistory
|
||||
* Look for broadcasts we need to rebroadcast
|
||||
*/
|
||||
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
||||
};
|
||||
};
|
@ -15,6 +15,7 @@
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
@ -63,11 +64,7 @@ uint32_t error_address = 0;
|
||||
|
||||
static uint8_t ourMacAddr[6];
|
||||
|
||||
NodeDB::NodeDB()
|
||||
: nodes(devicestate.node_db), numNodes(&devicestate.node_db_count), meshNodes(devicestate.node_db_lite),
|
||||
numMeshNodes(&devicestate.node_db_lite_count)
|
||||
{
|
||||
}
|
||||
NodeDB::NodeDB() : meshNodes(devicestate.node_db_lite), numMeshNodes(&devicestate.node_db_lite_count) {}
|
||||
|
||||
/**
|
||||
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
||||
@ -248,6 +245,9 @@ void NodeDB::installDefaultModuleConfig()
|
||||
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
|
||||
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
|
||||
|
||||
moduleConfig.has_neighbor_info = true;
|
||||
moduleConfig.neighbor_info.enabled = false;
|
||||
|
||||
initModuleConfigIntervals();
|
||||
}
|
||||
|
||||
@ -271,6 +271,7 @@ void NodeDB::initModuleConfigIntervals()
|
||||
moduleConfig.telemetry.device_update_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.telemetry.environment_update_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.telemetry.air_quality_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.neighbor_info.update_interval = default_broadcast_interval_secs;
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultChannels()
|
||||
@ -282,12 +283,11 @@ void NodeDB::installDefaultChannels()
|
||||
|
||||
void NodeDB::resetNodes()
|
||||
{
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
|
||||
devicestate.node_db_lite_count = 0;
|
||||
memset(devicestate.node_db_lite, 0, sizeof(devicestate.node_db_lite));
|
||||
saveDeviceStateToDisk();
|
||||
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
||||
neighborInfoModule->resetNeighbors();
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultDeviceState()
|
||||
@ -295,12 +295,11 @@ void NodeDB::installDefaultDeviceState()
|
||||
LOG_INFO("Installing default DeviceState\n");
|
||||
memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
|
||||
|
||||
*numNodes = 0;
|
||||
*numMeshNodes = 0;
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_owner = true;
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.node_db_lite_count = 0;
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
@ -328,11 +327,9 @@ void NodeDB::init()
|
||||
int saveWhat = 0;
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
|
||||
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
pickNewNodeNum();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
@ -343,19 +340,6 @@ void NodeDB::init()
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
if (*numNodes > 0) {
|
||||
LOG_DEBUG("Legacy NodeDB detected... Migrating to NodeDBLite\n");
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfo *oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
while (oldNodeInfo != NULL) {
|
||||
migrateToNodeInfoLite(oldNodeInfo);
|
||||
oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
}
|
||||
LOG_DEBUG("Migration complete! Clearing out legacy NodeDB...\n");
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
Preferences preferences;
|
||||
preferences.begin("meshtastic", false);
|
||||
@ -365,7 +349,7 @@ void NodeDB::init()
|
||||
#endif
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, *numNodes);
|
||||
LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, *numMeshNodes);
|
||||
|
||||
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
|
||||
saveWhat |= SEGMENT_DEVICESTATE;
|
||||
@ -611,14 +595,6 @@ void NodeDB::saveToDisk(int saveWhat)
|
||||
}
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfo *NodeDB::readNextNodeInfo(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numNodes)
|
||||
return &nodes[readIndex++];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numMeshNodes)
|
||||
@ -796,17 +772,6 @@ uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
return info->channel;
|
||||
}
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfo *NodeDB::getNodeInfo(NodeNum n)
|
||||
{
|
||||
for (int i = 0; i < *numNodes; i++)
|
||||
if (nodes[i].num == n)
|
||||
return &nodes[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
@ -852,57 +817,6 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
return lite;
|
||||
}
|
||||
|
||||
void NodeDB::migrateToNodeInfoLite(const meshtastic_NodeInfo *node)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(node->num);
|
||||
|
||||
if (!lite) {
|
||||
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
// look for oldest node and erase it
|
||||
uint32_t oldest = UINT32_MAX;
|
||||
int oldestIndex = -1;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].last_heard < oldest) {
|
||||
oldest = meshNodes[i].last_heard;
|
||||
oldestIndex = i;
|
||||
}
|
||||
}
|
||||
// Shove the remaining nodes down the chain
|
||||
for (int i = oldestIndex; i < *numMeshNodes - 1; i++) {
|
||||
meshNodes[i] = meshNodes[i + 1];
|
||||
}
|
||||
(*numMeshNodes)--;
|
||||
}
|
||||
// add the node at the end
|
||||
lite = &meshNodes[(*numMeshNodes)++];
|
||||
|
||||
// everything is missing except the nodenum
|
||||
memset(lite, 0, sizeof(*lite));
|
||||
lite->num = node->num;
|
||||
lite->snr = node->snr;
|
||||
lite->last_heard = node->last_heard;
|
||||
lite->channel = node->channel;
|
||||
|
||||
if (node->has_position) {
|
||||
lite->has_position = true;
|
||||
lite->position.latitude_i = node->position.latitude_i;
|
||||
lite->position.longitude_i = node->position.longitude_i;
|
||||
lite->position.altitude = node->position.altitude;
|
||||
lite->position.location_source = node->position.location_source;
|
||||
lite->position.time = node->position.time;
|
||||
}
|
||||
if (node->has_user) {
|
||||
lite->has_user = true;
|
||||
lite->user = node->user;
|
||||
}
|
||||
if (node->has_device_metrics) {
|
||||
lite->has_device_metrics = true;
|
||||
lite->device_metrics = node->device_metrics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ DeviceState versions used to be defined in the .proto file but really only this
|
||||
#define SEGMENT_DEVICESTATE 4
|
||||
#define SEGMENT_CHANNELS 8
|
||||
|
||||
#define DEVICESTATE_CUR_VER 20
|
||||
#define DEVICESTATE_CUR_VER 22
|
||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||
|
||||
extern meshtastic_DeviceState devicestate;
|
||||
@ -45,9 +45,6 @@ class NodeDB
|
||||
// Eventually use a smarter datastructure
|
||||
// HashMap<NodeNum, NodeInfo> nodes;
|
||||
// Note: these two references just point into our static array we serialize to/from disk
|
||||
meshtastic_NodeInfo *nodes;
|
||||
pb_size_t *numNodes;
|
||||
|
||||
meshtastic_NodeInfoLite *meshNodes;
|
||||
pb_size_t *numMeshNodes;
|
||||
|
||||
@ -137,18 +134,6 @@ class NodeDB
|
||||
private:
|
||||
/// Find a node in our DB, create an empty NodeInfoLite if missing
|
||||
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
|
||||
void migrateToNodeInfoLite(const meshtastic_NodeInfo *node);
|
||||
/// Find a node in our DB, return null for missing
|
||||
meshtastic_NodeInfo *getNodeInfo(NodeNum n);
|
||||
/// Allow the bluetooth layer to read our next nodeinfo record, or NULL if done reading
|
||||
const meshtastic_NodeInfo *readNextNodeInfo(uint32_t &readIndex);
|
||||
size_t getNumNodes() { return *numNodes; }
|
||||
|
||||
meshtastic_NodeInfo *getNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numNodes);
|
||||
return &nodes[x];
|
||||
}
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void notifyObservers(bool forceUpdate = false)
|
||||
@ -226,10 +211,6 @@ inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t d
|
||||
|
||||
/// Sometimes we will have Position objects that only have a time, so check for
|
||||
/// valid lat/lon
|
||||
static inline bool hasValidPosition(const meshtastic_NodeInfo *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
}
|
||||
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
|
@ -279,6 +279,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.remote_hardware = moduleConfig.remote_hardware;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_neighbor_info_tag:
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.neighbor_info = moduleConfig.neighbor_info;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown module config type %d\n", config_state);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define MAX_RX_TOPHONE 32
|
||||
|
||||
/// max number of nodes allowed in the mesh
|
||||
#define MAX_NUM_NODES (member_size(meshtastic_DeviceState, node_db) / member_size(meshtastic_DeviceState, node_db[0]))
|
||||
#define MAX_NUM_NODES (member_size(meshtastic_DeviceState, node_db_lite) / member_size(meshtastic_DeviceState, node_db_lite[0]))
|
||||
|
||||
/// Max number of channels allowed
|
||||
#define MAX_NUM_CHANNELS (member_size(meshtastic_ChannelFile, channels) / member_size(meshtastic_ChannelFile, channels[0]))
|
||||
|
@ -368,6 +368,11 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
|
||||
moduleConfig.has_remote_hardware = true;
|
||||
moduleConfig.remote_hardware = c.payload_variant.remote_hardware;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_neighbor_info_tag:
|
||||
LOG_INFO("Setting module config: Neighbor Info\n");
|
||||
moduleConfig.has_neighbor_info = true;
|
||||
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
|
||||
break;
|
||||
}
|
||||
|
||||
saveChanges(SEGMENT_MODULECONFIG);
|
||||
@ -503,6 +508,11 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
|
||||
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
|
||||
res.get_module_config_response.payload_variant.remote_hardware = moduleConfig.remote_hardware;
|
||||
break;
|
||||
case meshtastic_AdminMessage_ModuleConfigType_NEIGHBORINFO_CONFIG:
|
||||
LOG_INFO("Getting module config: Neighbor Info\n");
|
||||
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag;
|
||||
res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info;
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: The phone app needs to know the ls_secsvalue so it can properly expect sleep behavior.
|
||||
@ -663,4 +673,4 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP
|
||||
{
|
||||
// restrict to the admin channel for rx
|
||||
boundChannel = Channels::adminChannel;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include "input/kbMatrixImpl.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include "modules/NodeInfoModule.h"
|
||||
#include "modules/PositionModule.h"
|
||||
#include "modules/RemoteHardwareModule.h"
|
||||
@ -48,6 +49,7 @@ void setupModules()
|
||||
waypointModule = new WaypointModule();
|
||||
textMessageModule = new TextMessageModule();
|
||||
traceRouteModule = new TraceRouteModule();
|
||||
neighborInfoModule = new NeighborInfoModule();
|
||||
|
||||
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
||||
// to a global variable.
|
||||
|
319
src/modules/NeighborInfoModule.cpp
Normal file
319
src/modules/NeighborInfoModule.cpp
Normal file
@ -0,0 +1,319 @@
|
||||
#include "NeighborInfoModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
|
||||
#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options
|
||||
NeighborInfoModule *neighborInfoModule;
|
||||
|
||||
static const char *neighborInfoConfigFile = "/prefs/neighbors.proto";
|
||||
|
||||
/*
|
||||
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 %d to Node %d (last sent by %d)\n", header, np->node_id, nodeDB.getNodeNum(),
|
||||
np->last_sent_by_id);
|
||||
LOG_DEBUG("----------------\n");
|
||||
LOG_DEBUG("Packet contains %d neighbors\n", np->neighbors_count);
|
||||
for (int i = 0; i < np->neighbors_count; i++) {
|
||||
LOG_DEBUG("Neighbor %d: node_id=%d, snr=%.2f\n", i, np->neighbors[i].node_id, np->neighbors[i].snr);
|
||||
}
|
||||
LOG_DEBUG("----------------\n");
|
||||
}
|
||||
/*
|
||||
Prints the nodeDB nodes so we can see whose nodeInfo we have
|
||||
NOTE: for debugging only
|
||||
*/
|
||||
void NeighborInfoModule::printNodeDBNodes(const char *header)
|
||||
{
|
||||
int num_nodes = nodeDB.getNumMeshNodes();
|
||||
LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
|
||||
LOG_DEBUG("----------------\n");
|
||||
LOG_DEBUG("DB contains %d nodes\n", num_nodes);
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
meshtastic_NodeInfoLite *dbEntry = nodeDB.getMeshNodeByIndex(i);
|
||||
LOG_DEBUG(" Node %d: node_id=%d, snr=%.2f\n", i, dbEntry->num, dbEntry->snr);
|
||||
}
|
||||
LOG_DEBUG("----------------\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Prints the nodeDB neighbors
|
||||
NOTE: for debugging only
|
||||
*/
|
||||
void NeighborInfoModule::printNodeDBNeighbors(const char *header)
|
||||
{
|
||||
int num_neighbors = getNumNeighbors();
|
||||
LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
|
||||
LOG_DEBUG("----------------\n");
|
||||
LOG_DEBUG("DB contains %d neighbors\n", num_neighbors);
|
||||
for (int i = 0; i < num_neighbors; i++) {
|
||||
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
|
||||
LOG_DEBUG(" Node %d: node_id=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
|
||||
}
|
||||
LOG_DEBUG("----------------\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Prints the nodeDB with selectors for the neighbors we've chosen to send (inefficiently)
|
||||
Uses LOG_DEBUG, which equates to Console.log
|
||||
NOTE: For debugging only
|
||||
*/
|
||||
void NeighborInfoModule::printNodeDBSelection(const char *header, const meshtastic_NeighborInfo *np)
|
||||
{
|
||||
int num_neighbors = getNumNeighbors();
|
||||
LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
|
||||
LOG_DEBUG("----------------\n");
|
||||
LOG_DEBUG("Selected %d neighbors of %d DB neighbors\n", np->neighbors_count, num_neighbors);
|
||||
for (int i = 0; i < num_neighbors; i++) {
|
||||
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
|
||||
bool chosen = false;
|
||||
for (int j = 0; j < np->neighbors_count; j++) {
|
||||
if (np->neighbors[j].node_id == dbEntry->node_id) {
|
||||
chosen = true;
|
||||
}
|
||||
}
|
||||
if (!chosen) {
|
||||
LOG_DEBUG(" Node %d: neighbor=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
|
||||
} else {
|
||||
LOG_DEBUG("---> Node %d: neighbor=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("----------------\n");
|
||||
}
|
||||
|
||||
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
|
||||
NeighborInfoModule::NeighborInfoModule()
|
||||
: neighbors(neighborState.neighbors), numNeighbors(&neighborState.neighbors_count),
|
||||
ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg), concurrency::OSThread(
|
||||
"NeighborInfoModule")
|
||||
{
|
||||
ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP;
|
||||
|
||||
if (moduleConfig.neighbor_info.enabled) {
|
||||
this->loadProtoForModule();
|
||||
setIntervalFromNow(35 * 1000);
|
||||
} else {
|
||||
LOG_DEBUG("NeighborInfoModule is disabled\n");
|
||||
disable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate a zeroed neighbor info packet
|
||||
*/
|
||||
meshtastic_NeighborInfo *NeighborInfoModule::allocateNeighborInfoPacket()
|
||||
{
|
||||
meshtastic_NeighborInfo *neighborInfo = (meshtastic_NeighborInfo *)malloc(sizeof(meshtastic_NeighborInfo));
|
||||
memset(neighborInfo, 0, sizeof(meshtastic_NeighborInfo));
|
||||
return neighborInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
{
|
||||
int my_node_id = nodeDB.getNodeNum();
|
||||
neighborInfo->node_id = my_node_id;
|
||||
neighborInfo->last_sent_by_id = my_node_id;
|
||||
neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
|
||||
|
||||
int num_neighbors = cleanUpNeighbors();
|
||||
|
||||
for (int i = 0; i < num_neighbors; i++) {
|
||||
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
|
||||
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (dbEntry->node_id != my_node_id)) {
|
||||
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = dbEntry->node_id;
|
||||
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = dbEntry->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++;
|
||||
}
|
||||
}
|
||||
printNodeDBNodes("DBSTATE");
|
||||
printNodeDBNeighbors("NEIGHBORS");
|
||||
printNodeDBSelection("COLLECTED", neighborInfo);
|
||||
return neighborInfo->neighbors_count;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove neighbors from the database that we haven't heard from in a while
|
||||
@returns new number of neighbors
|
||||
*/
|
||||
size_t NeighborInfoModule::cleanUpNeighbors()
|
||||
{
|
||||
uint32_t now = getTime();
|
||||
int num_neighbors = getNumNeighbors();
|
||||
NodeNum my_node_id = nodeDB.getNodeNum();
|
||||
|
||||
// Find neighbors to remove
|
||||
std::vector<int> indices_to_remove;
|
||||
for (int i = 0; i < num_neighbors; i++) {
|
||||
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
|
||||
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
|
||||
if ((now - dbEntry->last_rx_time > dbEntry->node_broadcast_interval_secs * 2) && (dbEntry->node_id != my_node_id)) {
|
||||
indices_to_remove.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the neighbor list
|
||||
for (int i = 0; i < indices_to_remove.size(); i++) {
|
||||
int index = indices_to_remove[i];
|
||||
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", neighbors[index].node_id);
|
||||
for (int j = index; j < num_neighbors - 1; j++) {
|
||||
neighbors[j] = neighbors[j + 1];
|
||||
}
|
||||
(*numNeighbors)--;
|
||||
}
|
||||
|
||||
// Save the neighbor list if we removed any neighbors
|
||||
if (indices_to_remove.size() > 0) {
|
||||
saveProtoForModule();
|
||||
}
|
||||
|
||||
return *numNeighbors;
|
||||
}
|
||||
|
||||
/* Send neighbor info to the mesh */
|
||||
void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
meshtastic_NeighborInfo *neighborInfo = allocateNeighborInfoPacket();
|
||||
collectNeighborInfo(neighborInfo);
|
||||
meshtastic_MeshPacket *p = allocDataProtobuf(*neighborInfo);
|
||||
// send regardless of whether or not we have neighbors in our DB,
|
||||
// because we want to get neighbors for the next cycle
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
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()
|
||||
{
|
||||
bool requestReplies = false;
|
||||
sendNeighborInfo(NODENUM_BROADCAST, requestReplies);
|
||||
return getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs);
|
||||
}
|
||||
|
||||
/*
|
||||
Collect a recieved 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)
|
||||
{
|
||||
printNeighborInfo("RECEIVED", np);
|
||||
updateNeighbors(mp, np);
|
||||
// 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::updateLastSentById(meshtastic_MeshPacket *p)
|
||||
{
|
||||
auto &incoming = p->decoded;
|
||||
meshtastic_NeighborInfo scratch;
|
||||
meshtastic_NeighborInfo *updated = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, &meshtastic_NeighborInfo_msg, &scratch);
|
||||
updated = &scratch;
|
||||
|
||||
updated->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, updated);
|
||||
}
|
||||
|
||||
void NeighborInfoModule::resetNeighbors()
|
||||
{
|
||||
*numNeighbors = 0;
|
||||
neighborState.neighbors_count = 0;
|
||||
memset(neighborState.neighbors, 0, sizeof(neighborState.neighbors));
|
||||
saveProtoForModule();
|
||||
}
|
||||
|
||||
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, 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, int 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 (int i = 0; i < (*numNeighbors); i++) {
|
||||
meshtastic_Neighbor *nbr = &neighbors[i];
|
||||
if (nbr->node_id == n) {
|
||||
// if found, update it
|
||||
nbr->snr = snr;
|
||||
nbr->last_rx_time = getTime();
|
||||
// Only if this is the original sender, the broadcast interval corresponds to it
|
||||
if (originalSender == n)
|
||||
nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
|
||||
saveProtoForModule(); // Save the updated neighbor
|
||||
return nbr;
|
||||
}
|
||||
}
|
||||
// otherwise, allocate one and assign data to it
|
||||
// TODO: max memory for the database should take neighbors into account, but currently doesn't
|
||||
if (*numNeighbors < MAX_NUM_NODES) {
|
||||
(*numNeighbors)++;
|
||||
}
|
||||
meshtastic_Neighbor *new_nbr = &neighbors[((*numNeighbors) - 1)];
|
||||
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)
|
||||
new_nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
|
||||
saveProtoForModule(); // Save the new neighbor
|
||||
return new_nbr;
|
||||
}
|
||||
|
||||
void NeighborInfoModule::loadProtoForModule()
|
||||
{
|
||||
if (!nodeDB.loadProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, sizeof(meshtastic_NeighborInfo),
|
||||
&meshtastic_NeighborInfo_msg, &neighborState)) {
|
||||
neighborState = meshtastic_NeighborInfo_init_zero;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the module config to file.
|
||||
*
|
||||
* @return true On success.
|
||||
* @return false On error.
|
||||
*/
|
||||
bool NeighborInfoModule::saveProtoForModule()
|
||||
{
|
||||
bool okay = true;
|
||||
|
||||
#ifdef FS
|
||||
FS.mkdir("/prefs");
|
||||
#endif
|
||||
|
||||
okay &= nodeDB.saveProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, &meshtastic_NeighborInfo_msg, &neighborState);
|
||||
|
||||
return okay;
|
||||
}
|
84
src/modules/NeighborInfoModule.h
Normal file
84
src/modules/NeighborInfoModule.h
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "ProtobufModule.h"
|
||||
|
||||
/*
|
||||
* Neighborinfo module for sending info on each node's 0-hop neighbors to the mesh
|
||||
*/
|
||||
class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, private concurrency::OSThread
|
||||
{
|
||||
meshtastic_Neighbor *neighbors;
|
||||
pb_size_t *numNeighbors;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Expose the constructor
|
||||
*/
|
||||
NeighborInfoModule();
|
||||
|
||||
/* Reset neighbor info after clearing nodeDB*/
|
||||
void resetNeighbors();
|
||||
|
||||
bool saveProtoForModule();
|
||||
|
||||
// Let FloodingRouter call updateLastSentById upon rebroadcasting a NeighborInfo packet
|
||||
friend class FloodingRouter;
|
||||
|
||||
protected:
|
||||
// Note: this holds our local info.
|
||||
meshtastic_NeighborInfo neighborState;
|
||||
|
||||
/*
|
||||
* Called to handle a particular incoming message
|
||||
* @return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *nb) override;
|
||||
|
||||
/*
|
||||
* Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
|
||||
* @return the number of entries collected
|
||||
*/
|
||||
uint32_t collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo);
|
||||
|
||||
/*
|
||||
Remove neighbors from the database that we haven't heard from in a while
|
||||
@returns new number of neighbors
|
||||
*/
|
||||
size_t cleanUpNeighbors();
|
||||
|
||||
/* Allocate a new NeighborInfo packet */
|
||||
meshtastic_NeighborInfo *allocateNeighborInfoPacket();
|
||||
|
||||
// Find a neighbor in our DB, create an empty neighbor if missing
|
||||
meshtastic_Neighbor *getOrCreateNeighbor(NodeNum originalSender, NodeNum n, uint32_t node_broadcast_interval_secs, int snr);
|
||||
|
||||
/*
|
||||
* Send info on our node's neighbors into the mesh
|
||||
*/
|
||||
void sendNeighborInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
size_t getNumNeighbors() { return *numNeighbors; }
|
||||
|
||||
meshtastic_Neighbor *getNeighborByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numNeighbors);
|
||||
return &neighbors[x];
|
||||
}
|
||||
|
||||
/* update neighbors with subpacket sniffed from network */
|
||||
void updateNeighbors(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np);
|
||||
|
||||
/* update a NeighborInfo packet with our NodeNum as last_sent_by_id */
|
||||
void updateLastSentById(meshtastic_MeshPacket *p);
|
||||
|
||||
void loadProtoForModule();
|
||||
|
||||
/* Does our periodic broadcast */
|
||||
int32_t runOnce() override;
|
||||
|
||||
/* These are for debugging only */
|
||||
void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np);
|
||||
void printNodeDBNodes(const char *header);
|
||||
void printNodeDBNeighbors(const char *header);
|
||||
void printNodeDBSelection(const char *header, const meshtastic_NeighborInfo *np);
|
||||
};
|
||||
extern NeighborInfoModule *neighborInfoModule;
|
@ -363,6 +363,19 @@ JSONValue::JSONValue(int m_integer_value)
|
||||
number_value = (double)m_integer_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Number
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param uint m_integer_value The number to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(uint m_integer_value)
|
||||
{
|
||||
type = JSONType_Number;
|
||||
number_value = (double)m_integer_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Array
|
||||
*
|
||||
@ -874,4 +887,4 @@ std::string JSONValue::Indent(size_t depth)
|
||||
depth ? --depth : 0;
|
||||
std::string indentStr(depth * indent_step, ' ');
|
||||
return indentStr;
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ class JSONValue
|
||||
JSONValue(bool m_bool_value);
|
||||
JSONValue(double m_number_value);
|
||||
JSONValue(int m_integer_value);
|
||||
JSONValue(uint m_integer_value);
|
||||
JSONValue(const JSONArray &m_array_value);
|
||||
JSONValue(const JSONObject &m_object_value);
|
||||
JSONValue(const JSONValue &m_source);
|
||||
@ -91,4 +92,4 @@ class JSONValue
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
@ -541,7 +541,7 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
||||
msgPayload["battery_level"] = new JSONValue((int)decoded->variant.device_metrics.battery_level);
|
||||
msgPayload["battery_level"] = new JSONValue((uint)decoded->variant.device_metrics.battery_level);
|
||||
msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage);
|
||||
msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization);
|
||||
msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx);
|
||||
@ -588,10 +588,10 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if ((int)decoded->time) {
|
||||
msgPayload["time"] = new JSONValue((int)decoded->time);
|
||||
msgPayload["time"] = new JSONValue((uint)decoded->time);
|
||||
}
|
||||
if ((int)decoded->timestamp) {
|
||||
msgPayload["timestamp"] = new JSONValue((int)decoded->timestamp);
|
||||
msgPayload["timestamp"] = new JSONValue((uint)decoded->timestamp);
|
||||
}
|
||||
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
|
||||
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
|
||||
@ -599,13 +599,13 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
msgPayload["altitude"] = new JSONValue((int)decoded->altitude);
|
||||
}
|
||||
if ((int)decoded->ground_speed) {
|
||||
msgPayload["ground_speed"] = new JSONValue((int)decoded->ground_speed);
|
||||
msgPayload["ground_speed"] = new JSONValue((uint)decoded->ground_speed);
|
||||
}
|
||||
if (int(decoded->ground_track)) {
|
||||
msgPayload["ground_track"] = new JSONValue((int)decoded->ground_track);
|
||||
msgPayload["ground_track"] = new JSONValue((uint)decoded->ground_track);
|
||||
}
|
||||
if (int(decoded->sats_in_view)) {
|
||||
msgPayload["sats_in_view"] = new JSONValue((int)decoded->sats_in_view);
|
||||
msgPayload["sats_in_view"] = new JSONValue((uint)decoded->sats_in_view);
|
||||
}
|
||||
if ((int)decoded->PDOP) {
|
||||
msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP);
|
||||
@ -632,11 +632,11 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["id"] = new JSONValue((int)decoded->id);
|
||||
msgPayload["id"] = new JSONValue((uint)decoded->id);
|
||||
msgPayload["name"] = new JSONValue(decoded->name);
|
||||
msgPayload["description"] = new JSONValue(decoded->description);
|
||||
msgPayload["expire"] = new JSONValue((int)decoded->expire);
|
||||
msgPayload["locked_to"] = new JSONValue((int)decoded->locked_to);
|
||||
msgPayload["expire"] = new JSONValue((uint)decoded->expire);
|
||||
msgPayload["locked_to"] = new JSONValue((uint)decoded->locked_to);
|
||||
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
|
||||
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
@ -646,16 +646,34 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
};
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_NEIGHBORINFO_APP: {
|
||||
msgType = "neighborinfo";
|
||||
meshtastic_NeighborInfo scratch;
|
||||
meshtastic_NeighborInfo *decoded = NULL;
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["node_id"] = new JSONValue((uint)decoded->node_id);
|
||||
msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count);
|
||||
msgPayload["neighbors"] = new JSONValue(decoded->neighbors);
|
||||
} else {
|
||||
LOG_ERROR("Error decoding protobuf for neighborinfo message!\n");
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
// add more packet types here if needed
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
jsonObj["id"] = new JSONValue((int)mp->id);
|
||||
jsonObj["timestamp"] = new JSONValue((int)mp->rx_time);
|
||||
jsonObj["to"] = new JSONValue((int)mp->to);
|
||||
jsonObj["from"] = new JSONValue((int)mp->from);
|
||||
jsonObj["channel"] = new JSONValue((int)mp->channel);
|
||||
jsonObj["id"] = new JSONValue((uint)mp->id);
|
||||
jsonObj["timestamp"] = new JSONValue((uint)mp->rx_time);
|
||||
jsonObj["to"] = new JSONValue((uint)mp->to);
|
||||
jsonObj["from"] = new JSONValue((uint)mp->from);
|
||||
jsonObj["channel"] = new JSONValue((uint)mp->channel);
|
||||
jsonObj["type"] = new JSONValue(msgType.c_str());
|
||||
jsonObj["sender"] = new JSONValue(owner.id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user