Source:
src/Packet.h,src/MeshCore.h, docs.meshcore.io/packet_format/, docs.meshcore.io/payloads/
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:22All multi-byte integers use little-endian byte order (except CayenneLPP payloads, which are big-endian).
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; } // :77Source:
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 || ... } // :67Source:
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/payloadsSource:
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 — FUTURESource:
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; } // :92The 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 defaultPresent 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
}Source: docs.meshcore.io/payloads/
Node identity advertisement. Contains: public key (32 bytes), Unix timestamp (4 bytes), Ed25519 signature (64 bytes), optional GPS coordinates, feature flags, and node name.
Simple acknowledgement. Contains a CRC checksum of the acknowledged packet.
Returns the route a packet took back to the originator. Contains: path length, sequence of node hashes, optional additional payload.
Application-specific request. Contains: destination hash, source hash, 2-byte MAC, Unix timestamp, application data (e.g., get stats, keepalive).
Reply to REQ or ANON_REQ. Contains opaque application data.
Direct encrypted message. Contains: Unix timestamp (4 bytes), message type byte, UTF-8 text (encrypted with ECDH shared secret).
Request with ephemeral sender key. Contains: ephemeral public key of sender, encrypted payload.
Group/channel broadcast. Contains: channel hash (routing identifier), encrypted content in TXT_MSG format.
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 bytesUnencrypted control packets. Subtypes include DISCOVER_REQ and DISCOVER_RESP for neighbor discovery.
Used when data exceeds a single LoRa frame.
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)
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 detectionminimal— basic deduplicationmoderate— balanced detectionstrict— aggressive deduplication
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);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);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; } // :170Configure the duty cycle limit via CLI: get/set dutycycle <1–100> (default: 50%).
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 |
|---|---|
0x0000–0x00FF |
Reserved for internal use |
0x0100–0xFEFF |
Custom applications (submit PR to reserve) |
0xFF00–0xFFFF |
Reserved for testing/development |
To reserve a range outside 0xFF00–0xFFFF, demonstrate a working application and submit a pull request.