mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
merge: integrate upstream dev (refactored CLI into handleGetCmd/handleSetCmd)
Resolves conflict in src/helpers/CommonCLI.cpp where upstream extracted the get/set handlers into separate methods. Our region.autotag and region.autotag.max.hops commands are now placed in the new handleGetCmd and handleSetCmd methods.
This commit is contained in:
commit
4751380d4e
30 changed files with 1391 additions and 854 deletions
|
|
@ -230,7 +230,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
|
|||
file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
||||
file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
||||
file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
|
||||
file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
|
||||
file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
|
||||
file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90
|
||||
file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
|
@ -268,7 +270,9 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
|
|||
file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
|
||||
file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
|
||||
file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88
|
||||
file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
|
||||
file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89
|
||||
file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90
|
||||
file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
#define CMD_SEND_BINARY_REQ 50
|
||||
#define CMD_FACTORY_RESET 51
|
||||
#define CMD_SEND_PATH_DISCOVERY_REQ 52
|
||||
#define CMD_SET_FLOOD_SCOPE 54 // v8+
|
||||
#define CMD_SET_FLOOD_SCOPE_KEY 54 // v8+
|
||||
#define CMD_SEND_CONTROL_DATA 55 // v8+
|
||||
#define CMD_GET_STATS 56 // v8+, second byte is stats type
|
||||
#define CMD_SEND_ANON_REQ 57
|
||||
|
|
@ -59,6 +59,8 @@
|
|||
#define CMD_GET_ALLOWED_REPEAT_FREQ 60
|
||||
#define CMD_SET_PATH_HASH_MODE 61
|
||||
#define CMD_SEND_CHANNEL_DATA 62
|
||||
#define CMD_SET_DEFAULT_FLOOD_SCOPE 63
|
||||
#define CMD_GET_DEFAULT_FLOOD_SCOPE 64
|
||||
|
||||
// Stats sub-types for CMD_GET_STATS
|
||||
#define STATS_TYPE_CORE 0
|
||||
|
|
@ -93,6 +95,7 @@
|
|||
#define RESP_CODE_AUTOADD_CONFIG 25
|
||||
#define RESP_ALLOWED_REPEAT_FREQ 26
|
||||
#define RESP_CODE_CHANNEL_DATA_RECV 27
|
||||
#define RESP_CODE_DEFAULT_FLOOD_SCOPE 28
|
||||
|
||||
#define MAX_CHANNEL_DATA_LENGTH (MAX_FRAME_SIZE - 9)
|
||||
|
||||
|
|
@ -479,27 +482,32 @@ bool MyMesh::allowPacketForward(const mesh::Packet* packet) {
|
|||
return _prefs.client_repeat != 0;
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) {
|
||||
// TODO: dynamic send_scope, depending on recipient and current 'home' Region
|
||||
if (send_scope.isNull()) {
|
||||
void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis) {
|
||||
if (scope.isNull()) {
|
||||
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
} else {
|
||||
uint16_t codes[2];
|
||||
codes[0] = send_scope.calcTransportCode(pkt);
|
||||
codes[0] = scope.calcTransportCode(pkt);
|
||||
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
||||
sendFlood(pkt, codes, delay_millis, _prefs.path_hash_mode + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) {
|
||||
// TODO: dynamic send_scope, depending on recipient and current 'home' Region
|
||||
TransportKey default_scope;
|
||||
memcpy(&default_scope.key, _prefs.default_scope_key, sizeof(default_scope.key));
|
||||
|
||||
auto scope = send_scope.isNull() ? &default_scope : &send_scope;
|
||||
sendFloodScoped(*scope, pkt, delay_millis);
|
||||
}
|
||||
void MyMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis) {
|
||||
// TODO: have per-channel send_scope
|
||||
if (send_scope.isNull()) {
|
||||
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
} else {
|
||||
uint16_t codes[2];
|
||||
codes[0] = send_scope.calcTransportCode(pkt);
|
||||
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
||||
sendFlood(pkt, codes, delay_millis, _prefs.path_hash_mode + 1);
|
||||
}
|
||||
TransportKey default_scope;
|
||||
memcpy(&default_scope.key, _prefs.default_scope_key, sizeof(default_scope.key));
|
||||
|
||||
auto scope = send_scope.isNull() ? &default_scope : &send_scope;
|
||||
sendFloodScoped(*scope, pkt, delay_millis);
|
||||
}
|
||||
|
||||
void MyMesh::onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp,
|
||||
|
|
@ -893,6 +901,17 @@ void MyMesh::begin(bool has_display) {
|
|||
strcpy(_prefs.node_name, pub_key_hex);
|
||||
#endif
|
||||
|
||||
// if build provides default-scope, init with that
|
||||
#ifdef DEFAULT_FLOOD_SCOPE_NAME
|
||||
strcpy(_prefs.default_scope_name, DEFAULT_FLOOD_SCOPE_NAME);
|
||||
{
|
||||
TransportKeyStore temp;
|
||||
TransportKey key;
|
||||
temp.getAutoKeyFor(0, "#" DEFAULT_FLOOD_SCOPE_NAME, key);
|
||||
memcpy(_prefs.default_scope_key, key.key, sizeof(key.key));
|
||||
}
|
||||
#endif
|
||||
|
||||
// load persisted prefs
|
||||
_store->loadPrefs(_prefs, sensors.node_lat, sensors.node_lon);
|
||||
|
||||
|
|
@ -1210,7 +1229,9 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
if (pkt) {
|
||||
if (len >= 2 && cmd_frame[1] == 1) { // optional param (1 = flood, 0 = zero hop)
|
||||
unsigned long delay_millis = 0;
|
||||
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
TransportKey default_scope;
|
||||
memcpy(&default_scope.key, _prefs.default_scope_key, sizeof(default_scope.key));
|
||||
sendFloodScoped(default_scope, pkt, delay_millis);
|
||||
} else {
|
||||
sendZeroHop(pkt);
|
||||
}
|
||||
|
|
@ -1862,13 +1883,39 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||
} else {
|
||||
writeErrFrame(ERR_CODE_FILE_IO_ERROR);
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SET_FLOOD_SCOPE && len >= 2 && cmd_frame[1] == 0) {
|
||||
} else if (cmd_frame[0] == CMD_SET_FLOOD_SCOPE_KEY && len >= 2 && cmd_frame[1] == 0) {
|
||||
if (len >= 2 + 16) {
|
||||
memcpy(send_scope.key, &cmd_frame[2], sizeof(send_scope.key)); // set curr scope TransportKey
|
||||
} else {
|
||||
memset(send_scope.key, 0, sizeof(send_scope.key)); // set scope to null
|
||||
}
|
||||
writeOKFrame();
|
||||
} else if (cmd_frame[0] == CMD_SET_DEFAULT_FLOOD_SCOPE && len >= 1) {
|
||||
if (len >= 1+31+16) {
|
||||
int n = strlen((char *) &cmd_frame[1]);
|
||||
if (n > 0 && n < 31) {
|
||||
strcpy(_prefs.default_scope_name, (char *) &cmd_frame[1]);
|
||||
memcpy(_prefs.default_scope_key, &cmd_frame[1+31], 16);
|
||||
savePrefs();
|
||||
writeOKFrame();
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
|
||||
}
|
||||
} else {
|
||||
memset(_prefs.default_scope_name, 0, sizeof(_prefs.default_scope_name)); // set default scope to null
|
||||
memset(_prefs.default_scope_key, 0, sizeof(_prefs.default_scope_key));
|
||||
savePrefs();
|
||||
writeOKFrame();
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_GET_DEFAULT_FLOOD_SCOPE) {
|
||||
out_frame[0] = RESP_CODE_DEFAULT_FLOOD_SCOPE;
|
||||
if (strlen(_prefs.default_scope_name) > 0) {
|
||||
memcpy(&out_frame[1], _prefs.default_scope_name, 31);
|
||||
memcpy(&out_frame[1+31], _prefs.default_scope_key, 16);
|
||||
_serial->writeFrame(out_frame, 1+31+16);
|
||||
} else {
|
||||
_serial->writeFrame(out_frame, 1); // no name or key means null
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_CONTROL_DATA && len >= 2 && (cmd_frame[1] & 0x80) != 0) {
|
||||
auto resp = createControlData(&cmd_frame[1], len - 1);
|
||||
if (resp) {
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
#include "AbstractUITask.h"
|
||||
|
||||
/*------------ Frame Protocol --------------*/
|
||||
#define FIRMWARE_VER_CODE 10
|
||||
#define FIRMWARE_VER_CODE 11
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "20 Mar 2026"
|
||||
#define FIRMWARE_BUILD_DATE "19 Apr 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.14.1"
|
||||
#define FIRMWARE_VERSION "v1.15.0"
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
|
|
@ -112,6 +112,7 @@ protected:
|
|||
bool filterRecvFloodPacket(mesh::Packet* packet) override;
|
||||
bool allowPacketForward(const mesh::Packet* packet) override;
|
||||
|
||||
void sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis);
|
||||
void sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis=0) override;
|
||||
void sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis=0) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,4 +32,6 @@ struct NodePrefs { // persisted to file
|
|||
uint8_t client_repeat;
|
||||
uint8_t path_hash_mode; // which path mode to use when sending
|
||||
uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
|
||||
char default_scope_name[31];
|
||||
uint8_t default_scope_key[16];
|
||||
};
|
||||
|
|
@ -413,6 +413,19 @@ bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[])
|
|||
return n >= max_counters[hash_size];
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) {
|
||||
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope
|
||||
TransportKey scope;
|
||||
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) {
|
||||
sendFloodScoped(scope, packet, delay_millis, path_hash_size);
|
||||
} else {
|
||||
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
||||
}
|
||||
} else {
|
||||
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
||||
}
|
||||
}
|
||||
|
||||
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
||||
if (_prefs.disable_fwd) return false;
|
||||
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
|
||||
|
|
@ -609,10 +622,10 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
|||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
||||
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
|
||||
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else if (reply_path_len < 0) {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
|
||||
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
if (reply) sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
|
||||
uint8_t path_len = ((reply_path_hash_size - 1) << 6) | (reply_path_len & 63);
|
||||
|
|
@ -685,7 +698,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
||||
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
|
||||
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
mesh::Packet *reply =
|
||||
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
|
||||
|
|
@ -693,7 +706,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
|
||||
} else {
|
||||
sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -724,7 +737,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
mesh::Packet *ack = createAck(ack_hash);
|
||||
if (ack) {
|
||||
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFlood(ack, TXT_ACK_DELAY, packet->getPathHashSize());
|
||||
sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY);
|
||||
}
|
||||
|
|
@ -752,7 +765,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFlood(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize());
|
||||
sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize());
|
||||
} else {
|
||||
sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS);
|
||||
}
|
||||
|
|
@ -862,7 +875,9 @@ void MyMesh::sendNodeDiscoverReq() {
|
|||
MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng,
|
||||
mesh::RTCClock &rtc, mesh::MeshTables &tables)
|
||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
||||
_cli(board, rtc, sensors, acl, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store),
|
||||
region_map(key_store), temp_map(key_store),
|
||||
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
||||
telemetry(MAX_PACKET_PAYLOAD - 4),
|
||||
discover_limiter(4, 120), // max 4 every 2 minutes
|
||||
anon_limiter(4, 180) // max 4 every 3 minutes
|
||||
#if defined(WITH_RS232_BRIDGE)
|
||||
|
|
@ -932,6 +947,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||
|
||||
pending_discover_tag = 0;
|
||||
pending_discover_until = 0;
|
||||
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
|
||||
void MyMesh::begin(FILESYSTEM *fs) {
|
||||
|
|
@ -943,6 +960,26 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|||
// TODO: key_store.begin();
|
||||
region_map.load(_fs);
|
||||
|
||||
// establish default-scope
|
||||
{
|
||||
RegionEntry* r = region_map.getDefaultRegion();
|
||||
if (r) {
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
} else {
|
||||
#ifdef DEFAULT_FLOOD_SCOPE_NAME
|
||||
r = region_map.findByName(DEFAULT_FLOOD_SCOPE_NAME);
|
||||
if (r == NULL) {
|
||||
r = region_map.putRegion(DEFAULT_FLOOD_SCOPE_NAME, 0); // auto-create the default scope region
|
||||
if (r) { r->flags = 0; } // Allow-flood
|
||||
}
|
||||
if (r) {
|
||||
region_map.setDefaultRegion(r);
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_BRIDGE)
|
||||
if (_prefs.bridge_enabled) {
|
||||
bridge.begin();
|
||||
|
|
@ -966,6 +1003,17 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size) {
|
||||
if (scope.isNull()) {
|
||||
sendFlood(pkt, delay_millis, path_hash_size);
|
||||
} else {
|
||||
uint16_t codes[2];
|
||||
codes[0] = scope.calcTransportCode(pkt);
|
||||
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
||||
sendFlood(pkt, codes, delay_millis, path_hash_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {
|
||||
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
||||
pending_freq = freq;
|
||||
|
|
@ -993,7 +1041,7 @@ void MyMesh::sendSelfAdvertisement(int delay_millis, bool flood) {
|
|||
mesh::Packet *pkt = createSelfAdvert();
|
||||
if (pkt) {
|
||||
if (flood) {
|
||||
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
} else {
|
||||
sendZeroHop(pkt, delay_millis);
|
||||
}
|
||||
|
|
@ -1099,6 +1147,25 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void MyMesh::startRegionsLoad() {
|
||||
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
||||
memset(load_stack, 0, sizeof(load_stack));
|
||||
load_stack[0] = &temp_map.getWildcard();
|
||||
region_load_active = true;
|
||||
}
|
||||
|
||||
bool MyMesh::saveRegions() {
|
||||
return region_map.save(_fs);
|
||||
}
|
||||
|
||||
void MyMesh::onDefaultRegionChanged(const RegionEntry* r) {
|
||||
if (r) {
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
} else {
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::formatStatsReply(char *reply) {
|
||||
StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr);
|
||||
}
|
||||
|
|
@ -1208,107 +1275,6 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
|||
Serial.printf("\n");
|
||||
}
|
||||
reply[0] = 0;
|
||||
} else if (memcmp(command, "region", 6) == 0) {
|
||||
reply[0] = 0;
|
||||
|
||||
const char* parts[4];
|
||||
int n = mesh::Utils::parseTextParts(command, parts, 4, ' ');
|
||||
if (n == 1) {
|
||||
region_map.exportTo(reply, 160);
|
||||
} else if (n >= 2 && strcmp(parts[1], "load") == 0) {
|
||||
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
||||
memset(load_stack, 0, sizeof(load_stack));
|
||||
load_stack[0] = &temp_map.getWildcard();
|
||||
region_load_active = true;
|
||||
} else if (n >= 2 && strcmp(parts[1], "save") == 0) {
|
||||
_prefs.discovery_mod_timestamp = rtc_clock.getCurrentTime(); // this node is now 'modified' (for discovery info)
|
||||
savePrefs();
|
||||
bool success = region_map.save(_fs);
|
||||
strcpy(reply, success ? "OK" : "Err - save failed");
|
||||
} else if (n >= 3 && strcmp(parts[1], "allowf") == 0) {
|
||||
auto region = region_map.findByNamePrefix(parts[2]);
|
||||
if (region) {
|
||||
region->flags &= ~REGION_DENY_FLOOD;
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Err - unknown region");
|
||||
}
|
||||
} else if (n >= 3 && strcmp(parts[1], "denyf") == 0) {
|
||||
auto region = region_map.findByNamePrefix(parts[2]);
|
||||
if (region) {
|
||||
region->flags |= REGION_DENY_FLOOD;
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Err - unknown region");
|
||||
}
|
||||
} else if (n >= 3 && strcmp(parts[1], "get") == 0) {
|
||||
auto region = region_map.findByNamePrefix(parts[2]);
|
||||
if (region) {
|
||||
auto parent = region_map.findById(region->parent);
|
||||
if (parent && parent->id != 0) {
|
||||
sprintf(reply, " %s (%s) %s", region->name, parent->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F");
|
||||
} else {
|
||||
sprintf(reply, " %s %s", region->name, (region->flags & REGION_DENY_FLOOD) ? "" : "F");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Err - unknown region");
|
||||
}
|
||||
} else if (n >= 3 && strcmp(parts[1], "home") == 0) {
|
||||
auto home = region_map.findByNamePrefix(parts[2]);
|
||||
if (home) {
|
||||
region_map.setHomeRegion(home);
|
||||
sprintf(reply, " home is now %s", home->name);
|
||||
} else {
|
||||
strcpy(reply, "Err - unknown region");
|
||||
}
|
||||
} else if (n == 2 && strcmp(parts[1], "home") == 0) {
|
||||
auto home = region_map.getHomeRegion();
|
||||
sprintf(reply, " home is %s", home ? home->name : "*");
|
||||
} else if (n >= 3 && strcmp(parts[1], "put") == 0) {
|
||||
auto parent = n >= 4 ? region_map.findByNamePrefix(parts[3]) : ®ion_map.getWildcard();
|
||||
if (parent == NULL) {
|
||||
strcpy(reply, "Err - unknown parent");
|
||||
} else {
|
||||
auto region = region_map.putRegion(parts[2], parent->id);
|
||||
if (region == NULL) {
|
||||
strcpy(reply, "Err - unable to put");
|
||||
} else {
|
||||
strcpy(reply, "OK");
|
||||
}
|
||||
}
|
||||
} else if (n >= 3 && strcmp(parts[1], "remove") == 0) {
|
||||
auto region = region_map.findByName(parts[2]);
|
||||
if (region) {
|
||||
if (region_map.removeRegion(*region)) {
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Err - not empty");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Err - not found");
|
||||
}
|
||||
} else if (n >= 3 && strcmp(parts[1], "list") == 0) {
|
||||
uint8_t mask = 0;
|
||||
bool invert = false;
|
||||
|
||||
if (strcmp(parts[2], "allowed") == 0) {
|
||||
mask = REGION_DENY_FLOOD;
|
||||
invert = false; // list regions that DON'T have DENY flag
|
||||
} else if (strcmp(parts[2], "denied") == 0) {
|
||||
mask = REGION_DENY_FLOOD;
|
||||
invert = true; // list regions that DO have DENY flag
|
||||
} else {
|
||||
strcpy(reply, "Err - use 'allowed' or 'denied'");
|
||||
return;
|
||||
}
|
||||
|
||||
int len = region_map.exportNamesTo(reply, 160, mask, invert);
|
||||
if (len == 0) {
|
||||
strcpy(reply, "-none-");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Err - ??");
|
||||
}
|
||||
} else if (memcmp(command, "discover.neighbors", 18) == 0) {
|
||||
const char* sub = command + 18;
|
||||
while (*sub == ' ') sub++;
|
||||
|
|
@ -1333,7 +1299,7 @@ void MyMesh::loop() {
|
|||
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
|
||||
mesh::Packet *pkt = createSelfAdvert();
|
||||
uint32_t delay_millis = 0;
|
||||
if (pkt) sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
if (pkt) sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
|
||||
updateFloodAdvertTimer(); // schedule next flood advert
|
||||
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ struct NeighbourInfo {
|
|||
};
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "20 Mar 2026"
|
||||
#define FIRMWARE_BUILD_DATE "19 Apr 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.14.1"
|
||||
#define FIRMWARE_VERSION "v1.15.0"
|
||||
#endif
|
||||
|
||||
#define FIRMWARE_ROLE "repeater"
|
||||
|
|
@ -97,6 +97,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
|||
RegionMap region_map, temp_map;
|
||||
RegionEntry* load_stack[8];
|
||||
RegionEntry* recv_pkt_region;
|
||||
TransportKey default_scope;
|
||||
RateLimiter discover_limiter, anon_limiter;
|
||||
uint32_t pending_discover_tag;
|
||||
unsigned long pending_discover_until;
|
||||
|
|
@ -172,6 +173,8 @@ protected:
|
|||
bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
|
||||
void onControlDataRecv(mesh::Packet* packet) override;
|
||||
|
||||
void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size);
|
||||
|
||||
public:
|
||||
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables);
|
||||
|
||||
|
|
@ -189,6 +192,9 @@ public:
|
|||
_cli.savePrefs(_fs);
|
||||
}
|
||||
|
||||
void sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size);
|
||||
|
||||
// CommonCLICallbacks
|
||||
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
|
||||
bool formatFileSystem() override;
|
||||
void sendSelfAdvertisement(int delay_millis, bool flood) override;
|
||||
|
|
@ -208,11 +214,15 @@ public:
|
|||
void formatStatsReply(char *reply) override;
|
||||
void formatRadioStatsReply(char *reply) override;
|
||||
void formatPacketStatsReply(char *reply) override;
|
||||
void startRegionsLoad() override;
|
||||
bool saveRegions() override;
|
||||
void onDefaultRegionChanged(const RegionEntry* r) override;
|
||||
|
||||
mesh::LocalIdentity& getSelfId() override { return self_id; }
|
||||
|
||||
void saveIdentity(const mesh::LocalIdentity& new_id) override;
|
||||
void clearStats() override;
|
||||
|
||||
void handleCommand(uint32_t sender_timestamp, char* command, char* reply);
|
||||
void loop();
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) {
|
|||
if (reply) {
|
||||
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
unsigned long delay_millis = 0;
|
||||
sendFlood(reply, delay_millis, _prefs.path_hash_mode + 1);
|
||||
sendFloodScoped(default_scope, reply, delay_millis, _prefs.path_hash_mode + 1); // REVISIT
|
||||
client->extra.room.ack_timeout = futureMillis(PUSH_ACK_TIMEOUT_FLOOD);
|
||||
} else {
|
||||
sendDirect(reply, client->out_path, client->out_path_len);
|
||||
|
|
@ -286,6 +286,23 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
||||
// just try to determine region for packet (apply later in allowPacketForward())
|
||||
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
||||
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
||||
if (region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
||||
recv_pkt_region = NULL;
|
||||
} else {
|
||||
recv_pkt_region = ®ion_map.getWildcard();
|
||||
}
|
||||
} else {
|
||||
recv_pkt_region = NULL;
|
||||
}
|
||||
// do normal processing
|
||||
return false;
|
||||
}
|
||||
|
||||
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
|
||||
uint8_t *data, size_t len) {
|
||||
if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin
|
||||
|
|
@ -361,14 +378,14 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
|||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
||||
mesh::Packet *path = createPathReturn(sender, client->shared_secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_RESPONSE, reply_data, 13);
|
||||
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13);
|
||||
if (reply) {
|
||||
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
|
||||
} else {
|
||||
sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -458,7 +475,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
if (send_ack) {
|
||||
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
mesh::Packet *ack = createAck(ack_hash);
|
||||
if (ack) sendFlood(ack, TXT_ACK_DELAY, packet->getPathHashSize());
|
||||
if (ack) sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize());
|
||||
delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS;
|
||||
} else {
|
||||
uint32_t d = TXT_ACK_DELAY;
|
||||
|
|
@ -491,7 +508,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
||||
sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
sendFloodReply(reply, delay_millis + SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
sendDirect(reply, client->out_path, client->out_path_len, delay_millis + SERVER_RESPONSE_DELAY);
|
||||
}
|
||||
|
|
@ -546,14 +563,14 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
||||
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
|
||||
if (path) sendFlood(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
} else {
|
||||
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
|
||||
if (reply) {
|
||||
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
||||
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
|
||||
} else {
|
||||
sendFlood(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -595,12 +612,16 @@ void MyMesh::onAckRecv(mesh::Packet *packet, uint32_t ack_crc) {
|
|||
MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng,
|
||||
mesh::RTCClock &rtc, mesh::MeshTables &tables)
|
||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
||||
_cli(board, rtc, sensors, acl, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) {
|
||||
region_map(key_store), temp_map(key_store),
|
||||
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
||||
telemetry(MAX_PACKET_PAYLOAD - 4)
|
||||
{
|
||||
last_millis = 0;
|
||||
uptime_millis = 0;
|
||||
next_local_advert = next_flood_advert = 0;
|
||||
dirty_contacts_expiry = 0;
|
||||
_logging = false;
|
||||
region_load_active = false;
|
||||
set_radio_at = revert_radio_at = 0;
|
||||
|
||||
// defaults
|
||||
|
|
@ -637,6 +658,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||
next_push = 0;
|
||||
memset(posts, 0, sizeof(posts));
|
||||
_num_posted = _num_post_pushes = 0;
|
||||
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
|
||||
void MyMesh::begin(FILESYSTEM *fs) {
|
||||
|
|
@ -646,6 +669,27 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|||
_cli.loadPrefs(_fs);
|
||||
|
||||
acl.load(_fs, self_id);
|
||||
region_map.load(_fs);
|
||||
|
||||
// establish default-scope
|
||||
{
|
||||
RegionEntry* r = region_map.getDefaultRegion();
|
||||
if (r) {
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
} else {
|
||||
#ifdef DEFAULT_FLOOD_SCOPE_NAME
|
||||
r = region_map.findByName(DEFAULT_FLOOD_SCOPE_NAME);
|
||||
if (r == NULL) {
|
||||
r = region_map.putRegion(DEFAULT_FLOOD_SCOPE_NAME, 0); // auto-create the default scope region
|
||||
if (r) { r->flags = 0; } // Allow-flood
|
||||
}
|
||||
if (r) {
|
||||
region_map.setDefaultRegion(r);
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_set_tx_power(_prefs.tx_power_dbm);
|
||||
|
|
@ -660,6 +704,30 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size) {
|
||||
if (scope.isNull()) {
|
||||
sendFlood(pkt, delay_millis, path_hash_size);
|
||||
} else {
|
||||
uint16_t codes[2];
|
||||
codes[0] = scope.calcTransportCode(pkt);
|
||||
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
||||
sendFlood(pkt, codes, delay_millis, path_hash_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) {
|
||||
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope
|
||||
TransportKey scope;
|
||||
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) {
|
||||
sendFloodScoped(scope, packet, delay_millis, path_hash_size);
|
||||
} else {
|
||||
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
||||
}
|
||||
} else {
|
||||
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {
|
||||
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
||||
pending_freq = freq;
|
||||
|
|
@ -687,7 +755,7 @@ void MyMesh::sendSelfAdvertisement(int delay_millis, bool flood) {
|
|||
mesh::Packet *pkt = createSelfAdvert();
|
||||
if (pkt) {
|
||||
if (flood) {
|
||||
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
} else {
|
||||
sendZeroHop(pkt, delay_millis);
|
||||
}
|
||||
|
|
@ -744,6 +812,25 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
|
|||
store.save("_main", new_id);
|
||||
}
|
||||
|
||||
void MyMesh::startRegionsLoad() {
|
||||
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
||||
memset(load_stack, 0, sizeof(load_stack));
|
||||
load_stack[0] = &temp_map.getWildcard();
|
||||
region_load_active = true;
|
||||
}
|
||||
|
||||
bool MyMesh::saveRegions() {
|
||||
return region_map.save(_fs);
|
||||
}
|
||||
|
||||
void MyMesh::onDefaultRegionChanged(const RegionEntry* r) {
|
||||
if (r) {
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
} else {
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
}
|
||||
|
||||
void MyMesh::clearStats() {
|
||||
radio_driver.resetStats();
|
||||
resetStats();
|
||||
|
|
@ -764,6 +851,40 @@ void MyMesh::formatPacketStatsReply(char *reply) {
|
|||
}
|
||||
|
||||
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
|
||||
if (region_load_active) {
|
||||
if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation
|
||||
region_map = temp_map; // copy over the temp instance as new current map
|
||||
region_load_active = false;
|
||||
|
||||
sprintf(reply, "OK - loaded %d regions", region_map.getCount());
|
||||
} else {
|
||||
char *np = command;
|
||||
while (*np == ' ') np++; // skip indent
|
||||
int indent = np - command;
|
||||
|
||||
char *ep = np;
|
||||
while (RegionMap::is_name_char(*ep)) ep++;
|
||||
if (*ep) { *ep++ = 0; } // set null terminator for end of name
|
||||
|
||||
while (*ep && *ep != 'F') ep++; // look for (optional) flags
|
||||
|
||||
if (indent > 0 && indent < 8 && strlen(np) > 0) {
|
||||
auto parent = load_stack[indent - 1];
|
||||
if (parent) {
|
||||
auto old = region_map.findByName(np);
|
||||
auto nw = temp_map.putRegion(np, parent->id, old ? old->id : 0); // carry-over the current ID (if name already exists)
|
||||
if (nw) {
|
||||
nw->flags = old ? old->flags : (*ep == 'F' ? 0 : REGION_DENY_FLOOD); // carry-over flags from curr
|
||||
|
||||
load_stack[indent] = nw; // keep pointers to parent regions, to resolve parent_id's
|
||||
}
|
||||
}
|
||||
}
|
||||
reply[0] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (*command == ' ')
|
||||
command++; // skip leading spaces
|
||||
|
||||
|
|
@ -865,7 +986,7 @@ void MyMesh::loop() {
|
|||
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
|
||||
mesh::Packet *pkt = createSelfAdvert();
|
||||
uint32_t delay_millis = 0;
|
||||
if (pkt) sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
if (pkt) sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
||||
|
||||
updateFloodAdvertTimer(); // schedule next flood advert
|
||||
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@
|
|||
#include <helpers/CommonCLI.h>
|
||||
#include <helpers/StatsFormatHelper.h>
|
||||
#include <helpers/ClientACL.h>
|
||||
#include <helpers/RegionMap.h>
|
||||
#include <RTClib.h>
|
||||
#include <target.h>
|
||||
|
||||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "20 Mar 2026"
|
||||
#define FIRMWARE_BUILD_DATE "19 Apr 2026"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.14.1"
|
||||
#define FIRMWARE_VERSION "v1.15.0"
|
||||
#endif
|
||||
|
||||
#ifndef LORA_FREQ
|
||||
|
|
@ -93,7 +94,10 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
|||
uint64_t uptime_millis;
|
||||
unsigned long next_local_advert, next_flood_advert;
|
||||
bool _logging;
|
||||
bool region_load_active;
|
||||
NodePrefs _prefs;
|
||||
TransportKeyStore key_store;
|
||||
RegionMap region_map, temp_map;
|
||||
ClientACL acl;
|
||||
CommonCLI _cli;
|
||||
unsigned long dirty_contacts_expiry;
|
||||
|
|
@ -104,6 +108,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
|||
int next_post_idx;
|
||||
PostInfo posts[MAX_UNSYNCED_POSTS]; // cyclic queue
|
||||
CayenneLPP telemetry;
|
||||
RegionEntry* load_stack[8];
|
||||
RegionEntry* recv_pkt_region;
|
||||
TransportKey default_scope;
|
||||
unsigned long set_radio_at, revert_radio_at;
|
||||
float pending_freq;
|
||||
float pending_bw;
|
||||
|
|
@ -144,6 +151,8 @@ protected:
|
|||
return _prefs.multi_acks;
|
||||
}
|
||||
|
||||
bool filterRecvFloodPacket(mesh::Packet* pkt) override;
|
||||
|
||||
bool allowPacketForward(const mesh::Packet* packet) override;
|
||||
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
|
||||
int searchPeersByHash(const uint8_t* hash) override ;
|
||||
|
|
@ -158,6 +167,8 @@ protected:
|
|||
}
|
||||
#endif
|
||||
|
||||
void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size);
|
||||
|
||||
public:
|
||||
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables);
|
||||
|
||||
|
|
@ -175,6 +186,9 @@ public:
|
|||
_cli.savePrefs(_fs);
|
||||
}
|
||||
|
||||
void sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size);
|
||||
|
||||
// CommonCLICallbacks
|
||||
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
|
||||
bool formatFileSystem() override;
|
||||
void sendSelfAdvertisement(int delay_millis, bool flood) override;
|
||||
|
|
@ -196,6 +210,9 @@ public:
|
|||
void formatStatsReply(char *reply) override;
|
||||
void formatRadioStatsReply(char *reply) override;
|
||||
void formatPacketStatsReply(char *reply) override;
|
||||
void startRegionsLoad() override;
|
||||
bool saveRegions() override;
|
||||
void onDefaultRegionChanged(const RegionEntry* r) override;
|
||||
|
||||
mesh::LocalIdentity& getSelfId() override { return self_id; }
|
||||
|
||||
|
|
|
|||
|
|
@ -696,7 +696,9 @@ void SensorMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) {
|
|||
|
||||
SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
|
||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
||||
_cli(board, rtc, sensors, acl, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4)
|
||||
region_map(key_store),
|
||||
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
||||
telemetry(MAX_PACKET_PAYLOAD - 4)
|
||||
{
|
||||
next_local_advert = next_flood_advert = 0;
|
||||
dirty_contacts_expiry = 0;
|
||||
|
|
@ -729,6 +731,8 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||
_prefs.gps_enabled = 0;
|
||||
_prefs.gps_interval = 0;
|
||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
||||
|
||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||
}
|
||||
|
||||
void SensorMesh::begin(FILESYSTEM* fs) {
|
||||
|
|
@ -738,6 +742,27 @@ void SensorMesh::begin(FILESYSTEM* fs) {
|
|||
_cli.loadPrefs(_fs);
|
||||
|
||||
acl.load(_fs, self_id);
|
||||
region_map.load(_fs);
|
||||
|
||||
// establish default-scope
|
||||
{
|
||||
RegionEntry* r = region_map.getDefaultRegion();
|
||||
if (r) {
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
} else {
|
||||
#ifdef DEFAULT_FLOOD_SCOPE_NAME
|
||||
r = region_map.findByName(DEFAULT_FLOOD_SCOPE_NAME);
|
||||
if (r == NULL) {
|
||||
r = region_map.putRegion(DEFAULT_FLOOD_SCOPE_NAME, 0); // auto-create the default scope region
|
||||
if (r) { r->flags = 0; } // Allow-flood
|
||||
}
|
||||
if (r) {
|
||||
region_map.setDefaultRegion(r);
|
||||
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_set_tx_power(_prefs.tx_power_dbm);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <helpers/CommonCLI.h>
|
||||
#include <helpers/StatsFormatHelper.h>
|
||||
#include <helpers/ClientACL.h>
|
||||
#include <helpers/RegionMap.h>
|
||||
#include <RTClib.h>
|
||||
#include <target.h>
|
||||
|
||||
|
|
@ -138,6 +139,9 @@ private:
|
|||
uint8_t reply_data[MAX_PACKET_PAYLOAD];
|
||||
unsigned long dirty_contacts_expiry;
|
||||
CayenneLPP telemetry;
|
||||
TransportKeyStore key_store;
|
||||
RegionMap region_map;
|
||||
TransportKey default_scope;
|
||||
uint32_t last_read_time;
|
||||
int matching_peer_indexes[MAX_SEARCH_RESULTS];
|
||||
int num_alert_tasks;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue