Version Information
Meshtastic
This analysis is based on Meshtastic firmware v2.6.4 (as of May 2025)
MeshCore
This analysis is based on MeshCore v1.6.0 (as of May 2025)
Core Routing Approaches
Meshtastic Routing Architecture
Meshtastic utilizes a hybrid routing approach consisting of three main components:
- Flooding Router: The base routing layer that implements simple flood-based message propagation.
- Next Hop Router: Extends the flooding router with packet acknowledgment and retransmission.
- Reliable Router: Adds additional reliability mechanisms for messages that require acknowledgments.
Flooding Mechanism
When a node receives a packet:
- It checks if it has seen this packet before (using a duplicate detection table)
- If new, it decrements the hop limit and, if not zero, rebroadcasts the packet
- All nodes in a channel rebroadcast messages regardless of the destination
FloodingRouter Implementation
void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
{
// Never rebroadcast from the phone
if (!iface) {
LOG_DEBUG("No interface - dropping rebroadcast\n");
return;
}
// Note: if the packet originates from the local node, do not rebroadcast as the Routing layer has already
// done that if it needs to. (previously this was causing duplicate rebroadcasts and interference)
if (shouldFilterReceived(p) || p->from == getNodeNum()) {
LOG_DEBUG("Dropping rebroadcast of received, from=0x%x, to=0x%x\n", p->from, p->to);
return;
}
if (iface->findInTxQueue(p->from, p->id)) {
LOG_DEBUG("This same packet is still in the TX queue - we will relayer anyway\n");
}
// Is this message destined for or from us? Then don't rebroadcast
NodeNum ourNode = getNodeNum();
if (p->to == ourNode || p->from == ourNode) {
LOG_DEBUG("Dropping rebroadcast of our message, from=0x%x, to=0x%x\n", p->from, p->to);
return;
}
// If the message is a broadcast or destined for our node, rebroadcast
// broadcasts and messages directed to the phone always have max_hop_limit
if (loopbackBroadcastsToPhone(config.lora.loopback_broadcasts_to_phone) ? isBroadcast(p->to) : p->to == NODENUM_BROADCAST) {
// Treat messages that arrive without a hop_limit included as having a default of 3
auto hopLimit = Default::getHopLimit(p);
if (hopLimit > 0) {
// rebroadcasting a broadcast packet
LOG_DEBUG("Rebroadcasting broadcast message, from=0x%x, id=0x%x, hopLimit=%d\n", p->from, p->id, hopLimit);
auto newP = packetPool.allocCopy(*p);
newP->hop_limit = hopLimit - 1; // bump down the hop count
iface->sendTo(newP);
} else {
LOG_DEBUG("Not rebroadcasting broadcast message, from=0x%x, id=0x%x, hopLimit=0\n", p->from, p->id);
}
}
}
Packet Retransmission Logic
Meshtastic implements retransmission for reliability:
- Intermediate nodes perform up to 2 retransmissions
- Original senders perform up to 3 retransmissions for reliable messages
- Packets are stored in a pending retransmission queue until acknowledgment or timeout
NextHopRouter Constants
// The number of retransmissions intermediate nodes will do (actually 1 less than this)
constexpr static uint8_t NUM_INTERMEDIATE_RETX = 2;
// The number of retransmissions the original sender will do
constexpr static uint8_t NUM_RELIABLE_RETX = 3;
Congestion Control
Meshtastic scales intervals based on network congestion:
- Router/repeater nodes don't have their intervals scaled
- Tracker/sensor nodes get priority for position and telemetry
- Regular nodes scale back transmission as the network grows
Congestion Scaling
uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes)
{
// If we are a router, we don't scale the value. It's already significantly higher.
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER)
return getConfiguredOrDefaultMs(configured, defaultValue);
// Additionally if we're a tracker or sensor, we want priority to send position and telemetry
if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_TRACKER))
return getConfiguredOrDefaultMs(configured, defaultValue);
return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes);
}
MeshCore Routing Architecture
MeshCore employs a more selective routing approach with distinct routing modes:
- Flood Routing: Similar to Meshtastic but with priority-based retransmission
- Direct Routing: Path-specific routing with selective forwarding
- Zero-Hop Routing: For local broadcast without retransmission
Selective Packet Forwarding
MeshCore only forwards packets if they match specific criteria:
- Only forwards direct-routed packets if this node is the next hop
- Does not retransmit packets it has seen before
- Removes its hash from the path before rebroadcasting direct packets
Packet Routing Logic
DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) {
if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) {
if (_tables->hasSeen(pkt)) return ACTION_RELEASE; // don't retransmit!
// remove our hash from 'path', then re-broadcast
pkt->path_len -= PATH_HASH_SIZE;
memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len);
uint32_t d = getDirectRetransmitDelay(pkt);
return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority
}
return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard.
}
// Additional routing logic...
}
Priority-Based Transmission
MeshCore uses a priority system for transmissions:
- Direct messages get highest priority (0)
- Regular messages get medium priority (1)
- Path discovery packets get lower priority (2)
- Advertisements get lowest priority (3)
Flood Routing with Priority
void Mesh::sendFlood(Packet* packet, uint32_t delay_millis) {
packet->header &= ~PH_ROUTE_MASK;
packet->header |= ROUTE_TYPE_FLOOD;
packet->path_len = 0;
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
uint8_t pri;
if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) {
pri = 2;
} else if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT) {
pri = 3; // de-prioritie these
} else {
pri = 1;
}
sendPacket(packet, pri, delay_millis);
}
Congestion Mitigation
MeshCore employs several congestion mitigation techniques:
- Airtime Budget: Nodes limit to approximately 33% airtime utilization
- Randomized Delays: Prevents collision storms after regional events
- Path-Based Priority: Messages closer to source get higher priority
Airtime Budget
float Dispatcher::getAirtimeBudgetFactor() const {
return 2.0; // default, 33.3% (1/3rd)
}
Randomized Delay Implementation
// Multiple nodes around sender could all try to rebroadcast at once, so apply a random delay
uint32_t rand_delay = _rng->nextInt(ANNOUNCE_DELAY_MIN, ANNOUNCE_DELAY_MAX);
Network Capacity Analysis for Large-Scale Disasters
This analysis examines how each platform would perform in a large-scale disaster scenario with approximately 50,000 people trying to communicate across a mesh of 1,000 nodes in 300 km².
Meshtastic Network Capacity
Broadcast Storm Risk
With 50,000 active users, Meshtastic's flood-based routing would create significant congestion:
- Each message potentially propagates to every node within hop limits
- With default settings, congestion scaling would help but might not be sufficient
Resource Utilization
Packet Pool Definition
#define MAX_RX_FROMRADIO 4 // max number of packets destined to our queue
#define MAX_PACKETS (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + 2) // max number of packets in flight
- Packet pool size is limited and could be quickly exhausted
- Under heavy load, oldest packets would be dropped from queues
Congestion Control
- Meshtastic implements hop limits (typically 3-7 hops)
- Role-based prioritization helps critical nodes like ROUTER maintain function
- Less effective at discarding unnecessary retransmissions
MeshCore Network Capacity
Selective Forwarding
Seen Packet Tracking
bool hasSeen(const mesh::Packet* packet) override {
if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) {
uint32_t ack;
memcpy(&ack, packet->payload, 4);
for (int i = 0; i < MAX_PACKET_ACKS; i++) {
if (ack == _acks[i]) {
if (packet->isRouteDirect()) {
_direct_dups++; // keep some stats
} else {
_flood_dups++;
}
return true;
}
}
// Add to seen table...
}
// More implementation...
}
Only forwards packets that are specifically destined via that node:
- Significantly reduces network-wide traffic compared to flooding
- Well-suited for dense node deployments
Prioritization System
- Assigns higher priority to direct messages (pri=0) over flood messages (pri=1-3)
- Assigns decreasing priority as messages propagate further from source
Airtime Management
- Limits each node to approximately 33% airtime utilization
- Implements randomized retransmission delays to prevent collisions
Performance Predictions for 300km² with 1K Nodes
Meshtastic Performance
Message Propagation
- With configured hop limits, messages would reach approximately 7-10km radius
- Nodes would need to be positioned within 1-2km of each other for reliable coverage
- Would require strategic node placement for full coverage
Broadcast Efficiency
- High redundancy ensures messages reach destinations if any path exists
- At 50K users sending messages, network would likely experience significant congestion
- Congestion scaling would increase intervals between broadcast messages
Scalability Concerns
- Duplicate detection tables are limited in size
- As traffic increases, ability to track duplicates decreases
- Likely to experience significant packet loss at peak usage
MeshCore Performance
Message Propagation
- Direct routing requires established paths
- Initial network formation would rely on announce packets
- With 1K strategically placed nodes, path discovery may take time but would be more efficient
Traffic Management
- Selective forwarding means significantly less network congestion
- Room servers could act as regional message collection points
- Airtime limiting prevents any single node from overwhelming the network
Scalability Strengths
- Priority system gives preference to direct routed packets
- Random delays prevent collision storms after regional events
- Decreasing priority with distance prevents distant nodes from consuming bandwidth
Optimizations for Disaster Scenarios
Optimizing Meshtastic
- Increase Router Density: Deploy more router/repeater nodes with strategic placement
- Reduce Default Hop Limit: Consider reducing from default 3-7 to 2-3 hops
- Strengthen Congestion Scaling: More aggressive scaling at high node counts
- Implement Priority-Based Queuing: Critical messages should have transmission priority
Optimizing MeshCore
- Pre-established Room Servers: Deploy room servers at strategic locations
- Optimize Announce Intervals: Reduce announce frequency in high-density areas
- Increase Path Discovery Proactivity: More aggressive path discovery for critical routes
- Adjust Airtime Budget: Consider reducing below 33% in high-density areas
Technical Conclusion
In a large-scale disaster scenario with 50,000 users across 1,000 nodes in 300 km²:
Meshtastic would provide more immediate message propagation through its aggressive flooding mechanism, but would likely suffer from significant congestion and packet loss. It would perform best in the immediate aftermath when node count is lower and redundancy is critical.
MeshCore would offer more sustainable communication over time through its selective routing and prioritization schemes. While initial path discovery might take longer, the network would maintain higher throughput once paths are established, especially with strategic room server placement.
For maximum resilience, a hybrid deployment could leverage Meshtastic's flooding for critical emergency messages and MeshCore's selective routing for sustained communication channels.