Skip to main content

Meshtastic Transport

Meshtastic LoRa provides 5–15km range with no infrastructure. Ara connects to Meshtastic devices via USB serial using the Meshtastic protobuf API (Apache 2.0).

Prerequisites

  • USB OTG cable (Android device → Meshtastic radio)
  • Meshtastic radio with standard firmware flashed
  • usb-serial-for-android library
// build.gradle
dependencies {
implementation("com.github.mik3y:usb-serial-for-android:3.6.0")
}

Finding the serial port

Discover available USB serial devices:

import com.hoho.android.usbserial.driver.UsbSerialProber

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)

// Filter for Meshtastic devices (VID/PID varies)
// Common: 0x1a86 (CH340), 0x10c4 (CP2102), 0x0403 (FTDI)
val meshtasticDriver = drivers.firstOrNull { driver ->
val device = driver.device
device.vendorId == 0x1a86 || device.vendorId == 0x10c4 || device.vendorId == 0x0403
}

if (meshtasticDriver == null) {
return // No Meshtastic device found
}

// Get device path
val devicePath = meshtasticDriver.device.deviceName

Android

node.addTransportMeshtastic(portPath = devicePath, channel = 0)
ParameterDescription
portPathUSB device path from discovery
channelMeshtastic channel index (0-7, default 0)

Go

node.AddTransportMeshtastic("/dev/ttyUSB0", 0)

Measured performance

Figures measured on NZ 915 MHz hardware (two physical Meshtastic devices, SF10, 1% duty cycle).

ScenarioMeasured latencyThroughput
Single small changeset (1 row, 1 non-PK col)~10–20s end-to-end
Burst (5 rows, 10 changes, ~1.9 KB wire)~25s end-to-end~4,600 bytes/min
Sustained (1 row/min, steady state)~70–80s per delivery~110 bytes/min

Burst throughput is ~40× the sustained budget because Meshtastic firmware allows short transmit bursts before duty-cycle throttling kicks in. Repeated bursts at this rate trigger MAX_RETRANSMIT / DUTY_CYCLE_LIMIT errors and convergence degrades.

Constraints

The figures below are for 915 MHz at 1% duty cycle (SF10), a common LoRa configuration. Actual throughput depends on your regional band, spreading factor, and duty cycle limit.

ParameterValue
Meshtastic packet payload220 bytes
Ara chunk payload188 bytes (192 byte budget − 4 byte chunk header)
Duty cycle1% (region-dependent)
Sustained throughput budget~110 bytes/minute per node
Burst throughput (observed)~4,000–5,000 bytes/minute

The ~110 bytes/min figure is a sustained average, not a hard burst cap. A single ~1.9 KB burst completes in ~25 s; sending at that rate continuously would exhaust the budget and trigger firmware throttling. For steady-state operation, size changesets to stay within ~110 bytes/minute averaged over time.

Capacity planning

LoRa capacity is shared across all nodes on the same channel. Use this table to estimate channel utilisation as your node count grows:

NodesEstimated utilisation
6~46%
12~92%
20+>100% — segment across channels

When utilisation exceeds 100%, changesets queue and convergence time degrades. Split large meshes across multiple LoRa channels with MQTT bridging between segments.

Chunking

Changesets exceeding 188 bytes are split into chunks with sequence numbers. Chunking is handled in the Meshtastic transport, not the sync engine.

Chunk header (4 bytes): [chunk_id:1][seq:1][total:1][reserved:1]
Payload per chunk: up to 188 bytes

CRDT change count

The CRDT engine tracks one change per non-PK column per row, not one change per row. A table with 2 non-PK columns produces 2 changes per insert. Factor this into changeset size estimates: a 5-row insert into a 2-column table produces ~10 changes.

Attachment delivery

ContentTransport
Thumbnail (≤ 2 KB)LoRa — ~11 packets, ~20 min
Full resolutionMQTT / WiFi only