Skip to content

Maximum payload lengths

This page is the single reference for how large a single frame and a logical multi-fragment message may be. Related: Packet layout, Fragmentation, Text encoding.

SX1262 (and SX1261): maximum LoRa packet payload

For LoRa® with explicit header mode, Semtech SX1261 / SX1262 document SetPacketParams → LoRa PayloadLength (PacketParam4) with a range of 1 to 255 bytes. That value is the length of the payload written to the chip’s buffer for that packet — i.e. the maximum single LoRa PHY payload is 255 bytes for the modem payload field (CRC handling is separate in the modem; do not double-count against the 255 when following Semtech driver usage).

Reference: Semtech datasheet SX1261/SX1262, LoRa PacketParam4 — PayloadLength (see current revision of DS.SX1261-2 on semtech.com).

LowMesh implication: Treat 255 bytes as the hard ceiling for one over-the-air Mesh PDU (everything you pass as the LoRa packet payload: dynamic headers + prefix blocks + encrypted application tail), unless you use a different radio or implicit header with a smaller fixed size.

No payloadLen field

The mesh framing does not carry a single payloadLen byte. Implementations derive the application tail length from the modem rxSize (or TX builder cursor) minus the sum of:

  • Base header size (16 or 20 bytes depending on HAS_DST_ID),
  • optional Spatial Region block (5 bytes if HAS_REGION),
  • optional Fragment block (2 bytes if HAS_FRAGMENT),
  • optional Routing block (1 + pathCount × hashSize bytes if hopControl bit 7 / wire ttlRaw & 0x80, with hashSize = ((verFlags >> 9) & 3) + 1).

[ \textbf{prefixBytes} = \textbf{base + region + frag + route} ] [ \textbf{appTailLen} = \textbf{rxPhyLen} - \textbf{prefixBytes} ] [ \textbf{cipherLen} = \textbf{appTailLen} - \textbf{micLen} ]

TX stacks MUST ensure the entire Mesh PDU ≤ 255 bytes. Floods MUST additionally respect hop/hash-size policies so relays do not grow frames past the PHY cap — see Routing.

Single over-the-air frame (one Mesh PDU)

channelHashMIC inside application tail?
0x0000 (default community)YesAES-256-CCM-4 with defaultCommunityPsk32
non-zero (named / DM)Yes4-byte AES-256-CCM-4 MIC after ciphertext

Normative: All channelHash values, including 0x0000, use AES-256-CCM-4. The default community domain uses the fixed ROM defaultCommunityPsk32 unless a product profile overrides it — encrypted/authenticated on-air, but not private against other LowMesh devices (globally shared key). There is no plaintext default-channel inner payload in the current specification.

Logical messages (fragmented)

Fragmentation is not limited by one frame’s application tail alone:

  • Each fragment is still one Mesh PDU with its own fragment block when HAS_FRAGMENT is set.
  • The reassembled blob (file, long text, voice) can be much larger.

Documented informal range (see Text encoding):

UseTypical logical size
Fragmented chat / file64 KiB – 1 MiB (policy; enforce RAM caps in firmware)

Upper bound is implementation (RAM, flash, airtime).

USB / BLE hex API

The line-oriented hex API is not the same as RF:

  • Long frames are split across multiple hex lines or use binary mode; see USB & BLE hex API.
  • BLE ATT MTU (often 23–247) limits how many bytes fit in one GATT write unless you chunk — this affects transport, not on-air Mesh PDU layout.

Summary table

LayerLimit
SX1262 LoRa PHY payload255 bytes max (PayloadLength 1–255)
Mesh PDU≤ 255 bytes total (dynamic headers + tail)
Application tailappTailLen = rxPhyLen − prefixBytes; cipherLen = appTailLen − micLen; on-air **`cipherLen
Reassembled multi-fragment message64 KiB–1 MiB typical policy; hard cap = implementation

LowMeshOS — always open-source mesh protocol documentation