Skip to content

Latest commit

 

History

History
292 lines (199 loc) · 11.4 KB

File metadata and controls

292 lines (199 loc) · 11.4 KB

Packet Format & Routing Protocol

Source: src/Packet.h, src/MeshCore.h, docs.meshcore.io/packet_format/, docs.meshcore.io/payloads/

Packet Structure

Every MeshCore packet follows this layout:

[header (1 byte)] [transport_codes (4 bytes, optional)] [path_length (1 byte)] [path (variable)] [payload (variable)]

Size limits from src/MeshCore.h:19–22:

#define MAX_PACKET_PAYLOAD  184   // src/MeshCore.h:19
#define MAX_PATH_SIZE        64   // src/MeshCore.h:21
#define MAX_TRANS_UNIT      255   // src/MeshCore.h:22

All multi-byte integers use little-endian byte order (except CayenneLPP payloads, which are big-endian).


Header Byte

The single-byte header field encodes three packed fields.

Bitmask definitions from src/Packet.h:8–12:

#define PH_ROUTE_MASK   0x03  // src/Packet.h:8  — bits 0–1
#define PH_TYPE_SHIFT      2  // src/Packet.h:9
#define PH_TYPE_MASK    0x0F  // src/Packet.h:10 — bits 2–5 (after shift)
#define PH_VER_SHIFT       6  // src/Packet.h:11
#define PH_VER_MASK     0x03  // src/Packet.h:12 — bits 6–7 (after shift)

Access methods on the Packet class (src/Packet.h:62–77):

uint8_t getRouteType()   const { return header & PH_ROUTE_MASK; }              // :62
uint8_t getPayloadType() const { return (header >> PH_TYPE_SHIFT) & PH_TYPE_MASK; } // :72
uint8_t getPayloadVer()  const { return (header >> PH_VER_SHIFT) & PH_VER_MASK; }   // :77

Route Types (Bits 0–1)

Source: src/Packet.h:14–17

#define ROUTE_TYPE_TRANSPORT_FLOOD   0x00  // :14 — flood mode + transport codes
#define ROUTE_TYPE_FLOOD             0x01  // :15 — flood mode, builds path (max 64 bytes)
#define ROUTE_TYPE_DIRECT            0x02  // :16 — direct route, path is supplied
#define ROUTE_TYPE_TRANSPORT_DIRECT  0x03  // :17 — direct route + transport codes
Value Name Description
0x00 ROUTE_TYPE_TRANSPORT_FLOOD Flood routing with 4-byte transport codes
0x01 ROUTE_TYPE_FLOOD Standard flood routing
0x02 ROUTE_TYPE_DIRECT Direct point-to-point routing with known path
0x03 ROUTE_TYPE_TRANSPORT_DIRECT Direct routing with transport codes

Helper methods (src/Packet.h:64–67):

bool isRouteFlood()  const { return getRouteType() == ROUTE_TYPE_FLOOD || ... }  // :64
bool isRouteDirect() const { return getRouteType() == ROUTE_TYPE_DIRECT || ... } // :65
bool hasTransportCodes() const { return getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD || ... } // :67

Payload Types (Bits 2–5)

Source: src/Packet.h:19–32

#define PAYLOAD_TYPE_REQ        0x00  // :19 — request (dest/src hashes, MAC, timestamp, blob)
#define PAYLOAD_TYPE_RESPONSE   0x01  // :20 — response to REQ or ANON_REQ
#define PAYLOAD_TYPE_TXT_MSG    0x02  // :21 — plain text message (dest/src hashes, MAC, timestamp, text)
#define PAYLOAD_TYPE_ACK        0x03  // :22 — simple ack
#define PAYLOAD_TYPE_ADVERT     0x04  // :23 — node advertising its Identity
#define PAYLOAD_TYPE_GRP_TXT    0x05  // :24 — (unverified) group text (channel hash, MAC, timestamp, "name: msg")
#define PAYLOAD_TYPE_GRP_DATA   0x06  // :25 — (unverified) group datagram (channel hash, MAC, data_type uint16, data_len, blob)
#define PAYLOAD_TYPE_ANON_REQ   0x07  // :26 — generic request (dest_hash, ephemeral pub_key, MAC)
#define PAYLOAD_TYPE_PATH       0x08  // :27 — returned path (dest/src hashes, MAC, path, extra)
#define PAYLOAD_TYPE_TRACE      0x09  // :28 — trace a path, collecting SNR for each hop
#define PAYLOAD_TYPE_MULTIPART  0x0A  // :29 — packet is one of a set of packets
#define PAYLOAD_TYPE_CONTROL    0x0B  // :30 — control/discovery packet
//...
#define PAYLOAD_TYPE_RAW_CUSTOM 0x0F  // :32 — custom bytes, for apps with custom encryption/payloads

Payload Versions (Bits 6–7)

Source: src/Packet.h:34–37

#define PAYLOAD_VER_1  0x00  // :34 — 1-byte src/dest hashes, 2-byte MAC (current)
#define PAYLOAD_VER_2  0x01  // :35 — FUTURE (eg. 2-byte hashes, 4-byte MAC ??)
#define PAYLOAD_VER_3  0x02  // :36 — FUTURE
#define PAYLOAD_VER_4  0x03  // :37 — FUTURE

Packet Class Fields

Source: src/Packet.h:42–51

// src/Packet.h:42–51
class Packet {
public:
  uint8_t  header;                       // :46 — encoded route/type/version
  uint16_t payload_len, path_len;        // :47
  uint16_t transport_codes[2];           // :48 — optional 4-byte transport data
  uint8_t  path[MAX_PATH_SIZE];          // :49 — up to 64 bytes
  uint8_t  payload[MAX_PACKET_PAYLOAD];  // :50 — up to 184 bytes
  int8_t   _snr;                         // :51 — raw SNR * 4
};

SNR access (src/Packet.h:92):

float getSNR() const { return ((float)_snr) / 4.0f; }  // :92

Path Length Encoding

The path_len field encodes both hop count and hash size in a single byte. From src/Packet.h:79–83:

uint8_t getPathHashSize()  const { return (path_len >> 6) + 1; }         // :79 — bits 6–7: 0→1byte, 1→2bytes, 2→3bytes
uint8_t getPathHashCount() const { return path_len & 63; }                // :80 — bits 0–5: hop count (0–63)
uint8_t getPathByteLen()   const { return getPathHashCount() * getPathHashSize(); } // :81
void setPathHashSizeAndCount(uint8_t sz, uint8_t n) {
  path_len = ((sz - 1) << 6) | (n & 63);  // :83
}

Multi-byte path hashes provide better network analysis and loop detection. Configurable via set path.hash.mode 0|1|2.

Default path hash size is 1 byte (src/MeshCore.h:17):

#define PATH_HASH_SIZE  1  // src/MeshCore.h:17 — V1 default

Transport Codes

Present only for ROUTE_TYPE_TRANSPORT_FLOOD (0x00) and ROUTE_TYPE_TRANSPORT_DIRECT (0x03). Always 4 bytes (2 × uint16_t). Check via src/Packet.h:67:

bool hasTransportCodes() const {
  return getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD
      || getRouteType() == ROUTE_TYPE_TRANSPORT_DIRECT; // :67
}

Payload Formats

Source: docs.meshcore.io/payloads/

PAYLOAD_TYPE_ADVERT (0x04)

Node identity advertisement. Contains: public key (32 bytes), Unix timestamp (4 bytes), Ed25519 signature (64 bytes), optional GPS coordinates, feature flags, and node name.

PAYLOAD_TYPE_ACK (0x03)

Simple acknowledgement. Contains a CRC checksum of the acknowledged packet.

PAYLOAD_TYPE_PATH (0x08)

Returns the route a packet took back to the originator. Contains: path length, sequence of node hashes, optional additional payload.

PAYLOAD_TYPE_REQ (0x00)

Application-specific request. Contains: destination hash, source hash, 2-byte MAC, Unix timestamp, application data (e.g., get stats, keepalive).

PAYLOAD_TYPE_RESPONSE (0x01)

Reply to REQ or ANON_REQ. Contains opaque application data.

PAYLOAD_TYPE_TXT_MSG (0x02)

Direct encrypted message. Contains: Unix timestamp (4 bytes), message type byte, UTF-8 text (encrypted with ECDH shared secret).

PAYLOAD_TYPE_ANON_REQ (0x07)

Request with ephemeral sender key. Contains: ephemeral public key of sender, encrypted payload.

PAYLOAD_TYPE_GRP_TXT (0x05)

Group/channel broadcast. Contains: channel hash (routing identifier), encrypted content in TXT_MSG format.

PAYLOAD_TYPE_GRP_DATA (0x06)

Binary group/channel broadcast. Contains: 16-bit data type identifier, length field, payload data. Max group data payload (src/MeshCore.h:20):

#define MAX_GROUP_DATA_LENGTH  (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 3)
// = 184 - 16 - 3 = 165 bytes

PAYLOAD_TYPE_CONTROL (0x0B)

Unencrypted control packets. Subtypes include DISCOVER_REQ and DISCOVER_RESP for neighbor discovery.

PAYLOAD_TYPE_MULTIPART (0x0A)

Used when data exceeds a single LoRa frame.

PAYLOAD_TYPE_RAW_CUSTOM (0x0F)

No defined format — raw bytes for applications with custom encryption. Comment from source: "custom packet as raw bytes, for applications with custom encryption, payloads, etc" (src/Packet.h:32)


Routing

Flood Routing

Default routing mode. A packet is forwarded by every repeater that receives it until the maximum hop count is reached or a duplicate is detected.

  • sendFlood() signature (src/Mesh.h:199):
    void sendFlood(Packet* packet, uint32_t delay_millis=0, uint8_t path_hash_size=1);

Loop detection modes (configurable via set loop.detect):

  • off — no loop detection
  • minimal — basic deduplication
  • moderate — balanced detection
  • strict — aggressive deduplication

Direct Routing

When a path to the destination is known, packets are sent along the specific route:

// src/Mesh.h:210
void sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uint32_t delay_millis=0);

Zero-Hop

Transmits only to direct LoRa neighbors. Used for advertisements to immediate neighbors and for discovery:

// src/Mesh.h:215
void sendZeroHop(Packet* packet, uint32_t delay_millis=0);

Duty Cycle

The Dispatcher enforces transmit duty cycle limits. The duty-cycle window defaults to 3600000 ms (1 hour) (src/Dispatcher.h:153 and src/Dispatcher.h:170):

duty_cycle_window_ms = 3600000;  // :153
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }  // :170

Configure the duty cycle limit via CLI: get/set dutycycle <1–100> (default: 50%).


Number Allocations for GRP_DATA

Source: docs.meshcore.io/number_allocations/

The 16-bit data type field in PAYLOAD_TYPE_GRP_DATA (see comment at src/Packet.h:25: "enc data: data_type(uint16), data_len, blob"):

Range Purpose
0x00000x00FF Reserved for internal use
0x01000xFEFF Custom applications (submit PR to reserve)
0xFF000xFFFF Reserved for testing/development

To reserve a range outside 0xFF000xFFFF, demonstrate a working application and submit a pull request.