Skip to main content

Overview

How it works

When you send a message on your Meshtastic companion app, it is relayed to the radio using Bluetooth, Wi-Fi/Ethernet or serial connection. That message is then broadcasted by the radio. If it hasn't received a confirmation from any other device after a certain timeout, it will retransmit the message up to three times.

When a receiving radio captures a packet, it checks to see if it has heard that message before. If it has it ignores the message. If it hasn't heard the message, it will rebroadcast it.

For each message a radio rebroadcasts, it marks the "hop limit" down by one. When a radio receives a packet with a hop limit of zero, it will not rebroadcast the message.

The radio will store a small amount of packets (around 30) in its memory for when it's not connected to a client app. If it's full, it will replace the oldest packets with newly incoming text messages only.

What is a mesh?

At the radio level a Meshtastic mesh is a set of nodes that share the same LoRa spreading factor, center frequency, and bandwidth. A node can only be in one radio mesh; it will not see or respond to messages from nodes using different values for these settings. For a mesh to form, nodes need to share the same values.

These values are grouped into "presets" that can be easily chosen in the LoRa configuration section. Presets make it easy for nodes to configure the same radio parameters.

Sitting on top of this radio mesh are Channels. A logical mesh is formed by a Channel with a particular name and encryption key. The default channel in a radio mesh is Channel 0 with a blank "name" and an encryption key of AQ==.

Nodes can belong to a maximum of 8 Channels in the radio mesh. A custom Channel can be created for use by a specific group. Only nodes configured with the same Channel name and encryption key will be able to read and display messages on that Channel. However, all nodes in the radio mesh will receive and may retransmit messages (depending on their Role) regardless of the Channel settings for the message.

Meshtastic Lora Chirp

Lora Chirp

At the physical layer, LoRa uses a modulation technique called Chirp Spread Spectrum (CSS). A "chirp" is a signal that sweeps in frequency over time, either upward (up-chirp) or downward (down-chirp) across the configured bandwidth.

Each LoRa symbol is encoded as a frequency-shifted chirp. The amount of shift represents the symbol value. The number of possible symbol values is determined by the Spreading Factor. For example:

  • SF7 → 2^7 = 128 possible symbols
  • SF12 → 2^12 = 4096 possible symbols

A complete Meshtastic LoRa packet on the air consists of:

  1. Preamble
    A series of 16 up-chirps used for synchronization. This allows receiving radios to detect the signal and align timing.

  2. Sync Word
    A specific chirp pattern that helps distinguish between different LoRa networks sharing the same frequency band. For Meshtastic, it is set to 0x2B.

  3. LoRa Header

    • Payload length
    • Coding rate
    • CRC presence flag
    • Header CRC
  4. LoRa Payload
    The LoRa payload contains a Meshtastic packet. This packet includes a Meshtastic routing header and an encrypted application payload. The payload is encoded using Forward Error Correction (FEC) according to the configured coding rate.

  5. Payload CRC
    A Cyclic Redundancy Check used to detect transmission errors.

All of these parts are transmitted as a continuous sequence of chirps. The spreading factor controls how long each chirp lasts, while the bandwidth controls how wide the frequency sweep is. Higher spreading factors increase range and reliability but reduce data rate.

Radios must use the same frequency, bandwidth and spreading factor to communicate but they can still receive packets sent with different coding rates.

A Meshtastic Packet Over the Air

A Meshtastic packet contains unencrypted routing metadata and the encrypted application message. Only the packet's application message is encrypted and encoded as a Protocol Buffer (protobuf). Except for Text Messages, message payloads are additionally encoded using their respective protobuf types.

The components of the Meshtastic specific portion of a LoRa chirp are:

  1. To (Destination Node Number) — 4 bytes
    The destination node number. This may be a specific node, or the broadcast address 4294967295.

  2. From (Sender Node Number) — 4 bytes
    The numeric ID of the originating node.

  3. Packet ID — 4 bytes
    A unique identifier used to detect duplicate packets and to reference this packet when sending replies.

  4. Packet Flags - 1 byte

    • Hop Limit - 3 bits
      The maximum number of times the packet may be rebroadcast. Each node that retransmits the packet decrements this value. When it reaches zero, the packet is no longer forwarded.

    • Want Ack — 1 bit
      Indicates if the sender is requesting an acknowledgement from the destination node.

    • Via MQTT — 1 bit
      Indicates the packet entered the mesh through an MQTT gateway rather than directly over LoRa.

    • Hop Start - 3 bits
      The original hop limit when the packet was first transmitted. This allows clients to determine how many hops the packet has already traveled.

  5. Channel Hash - 1 byte
    Indicates which configured Channel the packet belongs to and determines which encryption key is used. This channel number derived from a hash of the channel name and it's PSK and does not correlate with the client channel idexes. The default LongFast channel with default psk will hash to channel = 8

  6. Next Hop - 1 byte
    Used during routing to indicate the next intended relay node. When present, it helps guide the packet toward a specific node rather than being blindly rebroadcast.

  7. Relay Node - 1 byte
    The last byte of the node ID that most recently relayed the packet before it was received.

  8. Payload (Encrypted) - max 239 bytes
    Consists of:

    • Portnum - 1 byte
      Identifies the application port (for example, POSITION_APP, TEXT_MESSAGE_APP, etc.). This determines how the payload should be interpreted.

    • Want Response - 1 bit
      Determines if the message is requesting a response from the receipient

    • Reply ID. - 4 bytes
      The ID of the message this message is replying to

    • Emoji Flag - 1 bit
      If the payload should be treated as an emoji, for example "hearting" a message.

    • Application Payload. - up to ~200 bytes
      The actual application data. This may be:

      • A text message
      • Position information (GPS)
      • Telemetry data (battery, voltage, etc.)
      • Node info updates
      • Administrative and routing messages
    • Bitfield - 1 byte
      A bitmask of flags defined in the Meshtastic firmware. Each bit represents a boolean attribute about the packet.

      • Bit 0 (OK_TO_MQTT) – This packet is allowed to be forwarded to MQTT.
      • Bit 1 (WANT_RESPONSE) – The sender is requesting a response.

      The numeric value represents one or more of these flags combined using bitwise OR. For example:

      • bitfield = 1 → OK_TO_MQTT
      • bitfield = 2 → WANT_RESPONSE
      • bitfield = 3 → Both flags set

Before transmission, the Meshtastic payload is encrypted using the Channel’s shared key. Only nodes with the same Channel name and encryption key can decrypt and interpret the contents.

On reception, a node:

  • Verifies it has not seen the Packet ID before
  • Decrypts the payload (if it has the correct Channel key)
  • Delivers the decoded message to the connected client app
  • Rebroadcasts the packet if the hop limit allows and the node configuration permits it.

The MeshPacket

For convenience, Meshtastic packet data is wrapped in a MeshPacket protobuf for transport to clients, mqtt, and UPD. Over the air, part of this data is split out into a more space-efficient but less flexible header.

Below is a real-world example of a MeshPacket as seen by a client. Note that fields set to default are not included, i.e. emoji = false:

from: 2125894122
to: 4294967295
channel: 8
id: 3034765096
hop_limit: 2
hop_start: 3
priority: DEFAULT
relay_node: 121
rx_time: 1772514955
rx_snr: -1.5
rx_rssi: -105
decoded {
portnum: POSITION_APP
payload: "latitude_i: 377700280 longitude_i: -1224469570 altitude: 0 time: 1772514893 location_source: LOC_MANUAL precision_bits: 15"
bitfield: 1
}

In addition to the core routing fields described above, this example shows several additional metadata fields which are added to the MeshPacket by the reciving node:

  • Priority
    Indicates transmission priority. Higher-priority packets may be transmitted sooner than lower-priority ones.

  • RX Time
    The local timestamp (epoch time) when this node received the packet.

  • RX SNR
    Signal-to-noise ratio measured at reception. Higher values indicate a cleaner signal.

  • RX RSSI
    Received Signal Strength Indicator in dBm. Higher values indicate a stronger signal.

Together, these fields show both how the packet moved through the network and what the radio conditions were when it was received. This makes it easier to understand range, reliability, and how messages are flowing across the mesh.