mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-01 07:30:33 +00:00
Merge branch 'develop' of https://github.com/meshtastic/firmware into 7943-mute-target
This commit is contained in:
@@ -423,6 +423,32 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
||||
}
|
||||
}
|
||||
|
||||
bool Channels::setDefaultPresetCryptoForHash(ChannelHash channelHash)
|
||||
{
|
||||
// Iterate all known presets
|
||||
for (int preset = _meshtastic_Config_LoRaConfig_ModemPreset_MIN; preset <= _meshtastic_Config_LoRaConfig_ModemPreset_MAX;
|
||||
++preset) {
|
||||
const char *name = DisplayFormatters::getModemPresetDisplayName((meshtastic_Config_LoRaConfig_ModemPreset)preset, false);
|
||||
if (!name)
|
||||
continue;
|
||||
if (strcmp(name, "Invalid") == 0)
|
||||
continue; // skip invalid placeholder
|
||||
uint8_t h = xorHash((const uint8_t *)name, strlen(name));
|
||||
// Expand default PSK alias 1 to actual bytes and xor into hash
|
||||
uint8_t tmp = h ^ xorHash(defaultpsk, sizeof(defaultpsk));
|
||||
if (tmp == channelHash) {
|
||||
// Set crypto to defaultpsk and report success
|
||||
CryptoKey k;
|
||||
memcpy(k.bytes, defaultpsk, sizeof(defaultpsk));
|
||||
k.length = sizeof(defaultpsk);
|
||||
crypto->setKey(k);
|
||||
LOG_INFO("Matched default preset '%s' for hash 0x%x; set default PSK", name, channelHash);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
|
||||
*
|
||||
* This method is called before encoding outbound packets
|
||||
|
||||
@@ -94,6 +94,8 @@ class Channels
|
||||
|
||||
bool ensureLicensedOperation();
|
||||
|
||||
bool setDefaultPresetCryptoForHash(ChannelHash channelHash);
|
||||
|
||||
private:
|
||||
/** Given a channel index, change to use the crypto key specified by that index
|
||||
*
|
||||
|
||||
@@ -43,12 +43,30 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
return Router::shouldFilterReceived(p);
|
||||
}
|
||||
|
||||
bool FloodingRouter::roleAllowsCancelingDupe(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) {
|
||||
// ROUTER, REPEATER, ROUTER_LATE should never cancel relaying a packet (i.e. we should always rebroadcast),
|
||||
// even if we've heard another station rebroadcast it already.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
||||
// CLIENT_BASE: if the packet is from or to a favorited node,
|
||||
// we should act like a ROUTER and should never cancel a rebroadcast (i.e. we should always rebroadcast),
|
||||
// even if we've heard another station rebroadcast it already.
|
||||
return !nodeDB->isFromOrToFavoritedNode(*p);
|
||||
}
|
||||
|
||||
// All other roles (such as CLIENT) should cancel a rebroadcast if they hear another station's rebroadcast.
|
||||
return true;
|
||||
}
|
||||
|
||||
void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE &&
|
||||
p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
|
||||
if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA && roleAllowsCancelingDupe(p)) {
|
||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||
// But only LoRa packets should be able to trigger this.
|
||||
if (Router::cancelSending(p->from, p->id))
|
||||
|
||||
@@ -59,6 +59,10 @@ class FloodingRouter : public Router
|
||||
*/
|
||||
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
||||
|
||||
// Return false for roles like ROUTER or REPEATER which should always rebroadcast even when we've heard another rebroadcast of
|
||||
// the same packet
|
||||
bool roleAllowsCancelingDupe(const meshtastic_MeshPacket *p);
|
||||
|
||||
/* Call when receiving a duplicate packet to check whether we should cancel a packet in the Tx queue */
|
||||
void perhapsCancelDupe(const meshtastic_MeshPacket *p);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "PointerQueue.h"
|
||||
#include "configuration.h" // For LOG_WARN, LOG_DEBUG, LOG_HEAP
|
||||
|
||||
template <class T> class Allocator
|
||||
{
|
||||
@@ -14,13 +15,14 @@ template <class T> class Allocator
|
||||
Allocator() : deleter([this](T *p) { this->release(p); }) {}
|
||||
virtual ~Allocator() {}
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
|
||||
/// Return a queable object which has been prefilled with zeros. Return nullptr if no buffer is available
|
||||
/// Note: this method is safe to call from regular OR ISR code
|
||||
T *allocZeroed()
|
||||
{
|
||||
T *p = allocZeroed(0);
|
||||
|
||||
assert(p); // FIXME panic instead
|
||||
if (!p) {
|
||||
LOG_WARN("Failed to allocate zeroed memory");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -39,10 +41,12 @@ template <class T> class Allocator
|
||||
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
T *p = alloc(maxWait);
|
||||
assert(p);
|
||||
if (!p) {
|
||||
LOG_WARN("Failed to allocate memory for copy");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (p)
|
||||
*p = src;
|
||||
*p = src;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -83,7 +87,9 @@ template <class T> class MemoryDynamic : public Allocator<T>
|
||||
/// Return a buffer for use by others
|
||||
virtual void release(T *p) override
|
||||
{
|
||||
assert(p);
|
||||
if (p == nullptr)
|
||||
return;
|
||||
|
||||
LOG_HEAP("Freeing 0x%x", p);
|
||||
|
||||
free(p);
|
||||
@@ -98,3 +104,58 @@ template <class T> class MemoryDynamic : public Allocator<T>
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A static memory pool that uses a fixed buffer instead of heap allocation
|
||||
*/
|
||||
template <class T, int MaxSize> class MemoryPool : public Allocator<T>
|
||||
{
|
||||
private:
|
||||
T pool[MaxSize];
|
||||
bool used[MaxSize];
|
||||
|
||||
public:
|
||||
MemoryPool() : pool{}, used{}
|
||||
{
|
||||
// Arrays are now zero-initialized by member initializer list
|
||||
// pool array: all elements are default-constructed (zero for POD types)
|
||||
// used array: all elements are false (zero-initialized)
|
||||
}
|
||||
|
||||
/// Return a buffer for use by others
|
||||
virtual void release(T *p) override
|
||||
{
|
||||
if (!p) {
|
||||
LOG_DEBUG("Failed to release memory, pointer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the index of this pointer in our pool
|
||||
int index = p - pool;
|
||||
if (index >= 0 && index < MaxSize) {
|
||||
assert(used[index]); // Should be marked as used
|
||||
used[index] = false;
|
||||
LOG_HEAP("Released static pool item %d at 0x%x", index, p);
|
||||
} else {
|
||||
LOG_WARN("Pointer 0x%x not from our pool!", p);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Alloc some storage from our static pool
|
||||
virtual T *alloc(TickType_t maxWait) override
|
||||
{
|
||||
// Find first free slot
|
||||
for (int i = 0; i < MaxSize; i++) {
|
||||
if (!used[i]) {
|
||||
used[i] = true;
|
||||
LOG_HEAP("Allocated static pool item %d at 0x%x", i, &pool[i]);
|
||||
return &pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
// No free slots available - return nullptr instead of asserting
|
||||
LOG_WARN("No free slots available in static memory pool!");
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,7 +100,6 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
||||
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||
auto ourNodeNum = nodeDB->getNodeNum();
|
||||
bool toUs = isBroadcast(mp.to) || isToUs(&mp);
|
||||
bool fromUs = mp.from == ourNodeNum;
|
||||
|
||||
for (auto i = modules->begin(); i != modules->end(); ++i) {
|
||||
auto &pi = **i;
|
||||
|
||||
@@ -46,11 +46,14 @@ the new node can build its node db)
|
||||
|
||||
MeshService *service;
|
||||
|
||||
static MemoryDynamic<meshtastic_MqttClientProxyMessage> staticMqttClientProxyMessagePool;
|
||||
#define MAX_MQTT_PROXY_MESSAGES 16
|
||||
static MemoryPool<meshtastic_MqttClientProxyMessage, MAX_MQTT_PROXY_MESSAGES> staticMqttClientProxyMessagePool;
|
||||
|
||||
static MemoryDynamic<meshtastic_QueueStatus> staticQueueStatusPool;
|
||||
#define MAX_QUEUE_STATUS 4
|
||||
static MemoryPool<meshtastic_QueueStatus, MAX_QUEUE_STATUS> staticQueueStatusPool;
|
||||
|
||||
static MemoryDynamic<meshtastic_ClientNotification> staticClientNotificationPool;
|
||||
#define MAX_CLIENT_NOTIFICATIONS 4
|
||||
static MemoryPool<meshtastic_ClientNotification, MAX_CLIENT_NOTIFICATIONS> staticClientNotificationPool;
|
||||
|
||||
Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool;
|
||||
|
||||
|
||||
@@ -161,6 +161,15 @@ bool NextHopRouter::stopRetransmission(NodeNum from, PacketId id)
|
||||
return stopRetransmission(key);
|
||||
}
|
||||
|
||||
bool NextHopRouter::roleAllowsCancelingFromTxQueue(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
// Return true if we're allowed to cancel a packet in the txQueue (so we may never transmit it even once)
|
||||
|
||||
// Return false for roles like ROUTER, REPEATER, ROUTER_LATE which should always transmit the packet at least once.
|
||||
|
||||
return roleAllowsCancelingDupe(p); // same logic as FloodingRouter::roleAllowsCancelingDupe
|
||||
}
|
||||
|
||||
bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
||||
{
|
||||
auto old = findPendingPacket(key);
|
||||
@@ -170,17 +179,21 @@ bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
||||
to avoid canceling a transmission if it was ACKed super fast via MQTT */
|
||||
if (old->numRetransmissions < NUM_RELIABLE_RETX - 1) {
|
||||
// We only cancel it if we are the original sender or if we're not a router(_late)/repeater
|
||||
if (isFromUs(p) || (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) {
|
||||
if (isFromUs(p) || roleAllowsCancelingFromTxQueue(p)) {
|
||||
// remove the 'original' (identified by originator and packet->id) from the txqueue and free it
|
||||
cancelSending(getFrom(p), p->id);
|
||||
// now free the pooled copy for retransmission too
|
||||
packetPool.release(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of whether or not we canceled this packet from the txQueue, remove it from our pending list so it doesn't
|
||||
// get scheduled again. (This is the core of stopRetransmission.)
|
||||
auto numErased = pending.erase(key);
|
||||
assert(numErased == 1);
|
||||
|
||||
// When we remove an entry from pending, always be sure to release the copy of the packet that was allocated in the call
|
||||
// to startRetransmission.
|
||||
packetPool.release(p);
|
||||
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
|
||||
@@ -121,6 +121,9 @@ class NextHopRouter : public FloodingRouter
|
||||
*/
|
||||
PendingPacket *startRetransmission(meshtastic_MeshPacket *p, uint8_t numReTx = NUM_INTERMEDIATE_RETX);
|
||||
|
||||
// Return true if we're allowed to cancel a packet in the txQueue (so we may never transmit it even once)
|
||||
bool roleAllowsCancelingFromTxQueue(const meshtastic_MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
||||
*
|
||||
|
||||
@@ -1770,6 +1770,65 @@ void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId)
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeDB::isFavorite(uint32_t nodeId)
|
||||
{
|
||||
// returns true if nodeId is_favorite; false if not or not found
|
||||
|
||||
// NODENUM_BROADCAST will never be in the DB
|
||||
if (nodeId == NODENUM_BROADCAST)
|
||||
return false;
|
||||
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||
|
||||
if (lite) {
|
||||
return lite->is_favorite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
|
||||
{
|
||||
// This method is logically equivalent to:
|
||||
// return isFavorite(p.from) || isFavorite(p.to);
|
||||
// but is more efficient by:
|
||||
// 1. doing only one pass through the database, instead of two
|
||||
// 2. exiting early when a favorite is found, or if both from and to have been seen
|
||||
|
||||
if (p.to == NODENUM_BROADCAST)
|
||||
return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from
|
||||
|
||||
meshtastic_NodeInfoLite *lite = NULL;
|
||||
|
||||
bool seenFrom = false;
|
||||
bool seenTo = false;
|
||||
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
lite = &meshNodes->at(i);
|
||||
|
||||
if (lite->num == p.from) {
|
||||
if (lite->is_favorite)
|
||||
return true;
|
||||
|
||||
seenFrom = true;
|
||||
}
|
||||
|
||||
if (lite->num == p.to) {
|
||||
if (lite->is_favorite)
|
||||
return true;
|
||||
|
||||
seenTo = true;
|
||||
}
|
||||
|
||||
if (seenFrom && seenTo)
|
||||
return false; // we've seen both, and neither is a favorite, so we can stop searching early
|
||||
|
||||
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching
|
||||
// all favorited nodes first.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeDB::pause_sort(bool paused)
|
||||
{
|
||||
sortingIsPaused = paused;
|
||||
|
||||
@@ -185,6 +185,16 @@ class NodeDB
|
||||
*/
|
||||
void set_favorite(bool is_favorite, uint32_t nodeId);
|
||||
|
||||
/*
|
||||
* Returns true if the node is in the NodeDB and marked as favorite
|
||||
*/
|
||||
bool isFavorite(uint32_t nodeId);
|
||||
|
||||
/*
|
||||
* Returns true if p->from or p->to is a favorited node
|
||||
*/
|
||||
bool isFromOrToFavoritedNode(const meshtastic_MeshPacket &p);
|
||||
|
||||
/**
|
||||
* Other functions like the node picker can request a pause in the node sorting
|
||||
*/
|
||||
|
||||
@@ -314,16 +314,33 @@ uint32_t RadioInterface::getTxDelayMsecWeightedWorst(float snr)
|
||||
return (2 * CWmax * slotTimeMsec) + pow_of_2(CWsize) * slotTimeMsec;
|
||||
}
|
||||
|
||||
/** Returns true if we should rebroadcast early like a ROUTER */
|
||||
bool RadioInterface::shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p)
|
||||
{
|
||||
// If we are a ROUTER or REPEATER, we always rebroadcast early
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a CLIENT_BASE and the packet is from or to a favorited node, we should rebroadcast early
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
||||
return nodeDB->isFromOrToFavoritedNode(*p);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** The delay to use when we want to flood a message */
|
||||
uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
|
||||
uint32_t RadioInterface::getTxDelayMsecWeighted(meshtastic_MeshPacket *p)
|
||||
{
|
||||
// high SNR = large CW size (Long Delay)
|
||||
// low SNR = small CW size (Short Delay)
|
||||
float snr = p->rx_snr;
|
||||
uint32_t delay = 0;
|
||||
uint8_t CWsize = getCWsize(snr);
|
||||
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize);
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
if (shouldRebroadcastEarlyLikeRouter(p)) {
|
||||
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
||||
LOG_DEBUG("rx_snr found in packet. Router: setting tx delay:%d", delay);
|
||||
} else {
|
||||
|
||||
@@ -180,8 +180,11 @@ class RadioInterface
|
||||
/** The worst-case SNR_based packet delay */
|
||||
uint32_t getTxDelayMsecWeightedWorst(float snr);
|
||||
|
||||
/** Returns true if we should rebroadcast early like a ROUTER */
|
||||
bool shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p);
|
||||
|
||||
/** The delay to use when we want to flood a message. Use a weighted scale based on SNR */
|
||||
uint32_t getTxDelayMsecWeighted(float snr);
|
||||
uint32_t getTxDelayMsecWeighted(meshtastic_MeshPacket *p);
|
||||
|
||||
/** If the packet is not already in the late rebroadcast window, move it there */
|
||||
virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; }
|
||||
|
||||
@@ -310,7 +310,7 @@ void RadioLibInterface::setTransmitDelay()
|
||||
// So we want to make sure the other side has had a chance to reconfigure its radio.
|
||||
|
||||
if (p->tx_after) {
|
||||
unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec();
|
||||
unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p) : getTxDelayMsec();
|
||||
unsigned long now = millis();
|
||||
p->tx_after = min(max(p->tx_after + add_delay, now + add_delay), now + 2 * getTxDelayMsecWeightedWorst(p->rx_snr));
|
||||
notifyLater(p->tx_after - now, TRANSMIT_DELAY_COMPLETED, false);
|
||||
@@ -323,7 +323,7 @@ void RadioLibInterface::setTransmitDelay()
|
||||
} else {
|
||||
// If there is a SNR, start a timer scaled based on that SNR.
|
||||
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
|
||||
startTransmitTimerSNR(p->rx_snr);
|
||||
startTransmitTimerRebroadcast(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,11 +336,11 @@ void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||
}
|
||||
}
|
||||
|
||||
void RadioLibInterface::startTransmitTimerSNR(float snr)
|
||||
void RadioLibInterface::startTransmitTimerRebroadcast(meshtastic_MeshPacket *p)
|
||||
{
|
||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||
if (!txQueue.empty()) {
|
||||
uint32_t delay = getTxDelayMsecWeighted(snr);
|
||||
uint32_t delay = getTxDelayMsecWeighted(p);
|
||||
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
* timer scaled to SNR of to be flooded packet
|
||||
* @return Timestamp after which the packet may be sent
|
||||
*/
|
||||
void startTransmitTimerSNR(float snr);
|
||||
void startTransmitTimerRebroadcast(meshtastic_MeshPacket *p);
|
||||
|
||||
void handleTransmitInterrupt();
|
||||
void handleReceiveInterrupt();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
|
||||
#include "configuration.h"
|
||||
#include "detect/LoRaRadioType.h"
|
||||
#include "main.h"
|
||||
@@ -27,14 +28,24 @@
|
||||
|
||||
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
|
||||
// And every TX packet might have a retransmission packet or an ack alive at any moment
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
// Portduino (native) targets can use dynamic memory pools with runtime-configurable sizes
|
||||
#define MAX_PACKETS \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||
|
||||
// static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
|
||||
static MemoryDynamic<meshtastic_MeshPacket> staticPool;
|
||||
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
||||
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
||||
#else
|
||||
// Embedded targets use static memory pools with compile-time constants
|
||||
#define MAX_PACKETS_STATIC \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||
|
||||
static MemoryPool<meshtastic_MeshPacket, MAX_PACKETS_STATIC> staticPool;
|
||||
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
|
||||
#endif
|
||||
|
||||
static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
|
||||
|
||||
@@ -419,6 +430,36 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_UDP_MULTICAST
|
||||
// Fallback: for UDP multicast, try default preset names with default PSK if normal channel match failed
|
||||
if (!decrypted && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP) {
|
||||
if (channels.setDefaultPresetCryptoForHash(p->channel)) {
|
||||
memcpy(bytes, p->encrypted.bytes, rawSize);
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
meshtastic_Data decodedtmp;
|
||||
memset(&decodedtmp, 0, sizeof(decodedtmp));
|
||||
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) &&
|
||||
decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||
p->decoded = decodedtmp;
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
// Map to our local default channel index (name+PSK default), not necessarily primary
|
||||
ChannelIndex defaultIndex = channels.getPrimaryIndex();
|
||||
for (ChannelIndex i = 0; i < channels.getNumChannels(); ++i) {
|
||||
if (channels.isDefaultChannel(i)) {
|
||||
defaultIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chIndex = defaultIndex;
|
||||
decrypted = true;
|
||||
} else {
|
||||
LOG_WARN("UDP fallback decode attempted but failed for hash 0x%x", p->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (decrypted) {
|
||||
// parsing was successful
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
@@ -662,7 +703,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
|
||||
// call modules here
|
||||
// If this could be a spoofed packet, don't let the modules see it.
|
||||
if (!skipHandle && p->from != nodeDB->getNodeNum()) {
|
||||
if (!skipHandle) {
|
||||
MeshModule::callModules(*p, src);
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||
@@ -676,8 +717,6 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
!isFromUs(p) && mqtt)
|
||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||
#endif
|
||||
} else if (p->from == nodeDB->getNodeNum() && !skipHandle) {
|
||||
MeshModule::callModules(*p, src);
|
||||
}
|
||||
|
||||
packetPool.release(p_encrypted); // Release the encrypted packet
|
||||
|
||||
@@ -66,6 +66,8 @@ typedef enum _meshtastic_Language {
|
||||
meshtastic_Language_UKRAINIAN = 16,
|
||||
/* Bulgarian */
|
||||
meshtastic_Language_BULGARIAN = 17,
|
||||
/* Czech */
|
||||
meshtastic_Language_CZECH = 18,
|
||||
/* Simplified Chinese (experimental) */
|
||||
meshtastic_Language_SIMPLIFIED_CHINESE = 30,
|
||||
/* Traditional Chinese (experimental) */
|
||||
|
||||
@@ -292,11 +292,14 @@ JSONArray htmlListDir(const char *dirname, uint8_t levels)
|
||||
JSONObject thisFileMap;
|
||||
thisFileMap["size"] = new JSONValue((int)file.size());
|
||||
#ifdef ARCH_ESP32
|
||||
thisFileMap["name"] = new JSONValue(String(file.path()).substring(1).c_str());
|
||||
String fileName = String(file.path()).substring(1);
|
||||
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||
#else
|
||||
thisFileMap["name"] = new JSONValue(String(file.name()).substring(1).c_str());
|
||||
String fileName = String(file.name()).substring(1);
|
||||
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||
#endif
|
||||
if (String(file.name()).substring(1).endsWith(".gz")) {
|
||||
String tempName = String(file.name()).substring(1);
|
||||
if (tempName.endsWith(".gz")) {
|
||||
#ifdef ARCH_ESP32
|
||||
String modifiedFile = String(file.path()).substring(1);
|
||||
#else
|
||||
@@ -339,7 +342,8 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
|
||||
delete value;
|
||||
|
||||
@@ -367,7 +371,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
return;
|
||||
} else {
|
||||
@@ -376,7 +381,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["status"] = new JSONValue("Error");
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
return;
|
||||
}
|
||||
@@ -622,10 +628,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
||||
tempArray.push_back(new JSONValue((int)logArray[i]));
|
||||
}
|
||||
JSONValue *result = new JSONValue(tempArray);
|
||||
// Clean up original array to prevent memory leak
|
||||
for (auto *val : tempArray) {
|
||||
delete val;
|
||||
}
|
||||
// Note: Don't delete tempArray elements here - JSONValue now owns them
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -656,7 +659,9 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
||||
// data->wifi
|
||||
JSONObject jsonObjWifi;
|
||||
jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI());
|
||||
jsonObjWifi["ip"] = new JSONValue(WiFi.localIP().toString().c_str());
|
||||
String wifiIPString = WiFi.localIP().toString();
|
||||
std::string wifiIP = wifiIPString.c_str();
|
||||
jsonObjWifi["ip"] = new JSONValue(wifiIP.c_str());
|
||||
|
||||
// data->memory
|
||||
JSONObject jsonObjMemory;
|
||||
@@ -702,7 +707,8 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
}
|
||||
|
||||
@@ -773,7 +779,8 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
|
||||
// Clean up the nodesArray to prevent memory leak
|
||||
@@ -926,7 +933,8 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
}
|
||||
|
||||
@@ -968,7 +976,8 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
res->print(value->Stringify().c_str());
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
|
||||
// Clean up the networkObjs to prevent memory leak
|
||||
|
||||
@@ -15,22 +15,26 @@
|
||||
// FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in
|
||||
// RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
|
||||
#ifndef MAX_RX_TOPHONE
|
||||
#if defined(ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||
#define MAX_RX_TOPHONE 8
|
||||
#else
|
||||
#define MAX_RX_TOPHONE 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// max number of QueueStatus packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_QUEUESTATUS_TOPHONE
|
||||
#define MAX_RX_QUEUESTATUS_TOPHONE 4
|
||||
#define MAX_RX_QUEUESTATUS_TOPHONE 2
|
||||
#endif
|
||||
|
||||
/// max number of MqttClientProxyMessage packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_MQTTPROXY_TOPHONE
|
||||
#define MAX_RX_MQTTPROXY_TOPHONE 32
|
||||
#define MAX_RX_MQTTPROXY_TOPHONE 8
|
||||
#endif
|
||||
|
||||
/// max number of ClientNotification packets which can be waiting for delivery to phone
|
||||
#ifndef MAX_RX_NOTIFICATION_TOPHONE
|
||||
#define MAX_RX_NOTIFICATION_TOPHONE 4
|
||||
#define MAX_RX_NOTIFICATION_TOPHONE 2
|
||||
#endif
|
||||
|
||||
/// Verify baseline assumption of node size. If it increases, we need to reevaluate
|
||||
|
||||
@@ -50,10 +50,10 @@ class UdpMulticastHandler final
|
||||
LOG_DEBUG("UDP broadcast from: %s, len=%u", packet.remoteIP().toString().c_str(), packetLength);
|
||||
#endif
|
||||
meshtastic_MeshPacket mp;
|
||||
mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP;
|
||||
LOG_DEBUG("Decoding MeshPacket from UDP len=%u", packetLength);
|
||||
bool isPacketDecoded = pb_decode_from_bytes(packet.data(), packetLength, &meshtastic_MeshPacket_msg, &mp);
|
||||
if (isPacketDecoded && router && mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) {
|
||||
mp.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP;
|
||||
mp.pki_encrypted = false;
|
||||
mp.public_key.size = 0;
|
||||
memset(mp.public_key.bytes, 0, sizeof(mp.public_key.bytes));
|
||||
|
||||
Reference in New Issue
Block a user