MeshDash Docs
R2.0
/
Home Getting Started Radio Connection

Radio Connection

Getting Started serial usb ttyACM0 tcp wifi bluetooth ble port connection usbipd wsl dialout
Connecting your Meshtastic radio via Serial, TCP, or BLE — configuration and troubleshooting.

MeshDash supports three physical connection modes. The connection.py module manages the full lifecycle: initial connect, packet streaming, reconnection on loss, and graceful shutdown.

Serial (USB) — Recommended

The most reliable and lowest-latency method. Plug your radio directly into the device running MeshDash over USB.

Finding Your Port

# List all serial devices
ls /dev/tty*

# More targeted — show only ACM and USB serial
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

# Watch for the device appearing when you plug in
dmesg | tail -20
Linux (USB CDC)
/dev/ttyACM0 — T-Beam, Heltec, most modern Meshtastic devices
Linux (CH340/CP210x)
/dev/ttyUSB0 — older adapter chips
Windows CMD (native)
COM3 etc.
WSL2 (after usbipd)
/dev/ttyACM0 or /dev/ttyS3

Permissions

# Add your user to the dialout group (logout/login required after)
sudo usermod -aG dialout $USER

# Verify membership
groups | grep dialout

WSL2 USB Passthrough (usbipd)

WSL2 does not automatically share USB devices. Install usbipd on Windows then attach your radio:

# In Windows PowerShell (as Administrator)
winget install usbipd

# List all USB devices
usbipd list

# Bind and attach the radio (replace 1-3 with your BUSID)
usbipd bind --busid 1-3
usbipd attach --wsl --busid 1-3

# In WSL, verify
ls /dev/ttyACM*

Config

MESHTASTIC_CONNECTION_TYPE=SERIAL
MESHTASTIC_SERIAL_PORT=/dev/ttyACM0

TCP (WiFi / LAN)

Connects over the local network to a radio with WiFi enabled (e.g. T-Beam in AP or STA mode).

Finding the Radio's IP

  • Check your router's DHCP client table
  • Use the Meshtastic mobile app — it shows the IP in device settings
  • Scan: nmap -sn 192.168.1.0/24
Assign a static IP or DHCP reservation to your radio. If the IP changes, MeshDash cannot reconnect until the config is updated and the service restarted.

Config

MESHTASTIC_CONNECTION_TYPE=TCP
MESHTASTIC_HOST=192.168.1.50
MESHTASTIC_PORT=4403

Bluetooth (BLE)

Uses the Meshtastic Python SDK's BLE interface. Requires compatible Bluetooth hardware on the MeshDash host.

BLE is less stable than Serial or TCP for a persistent long-running connection. Use it only when the other modes are not available.

Finding the MAC Address

sudo bluetoothctl
scan on
# Wait ~10 seconds
devices
# Note the MAC: e.g. AA:BB:CC:DD:EE:FF
quit

Config

MESHTASTIC_CONNECTION_TYPE=BLE
MESHTASTIC_BLE_MAC=AA:BB:CC:DD:EE:FF

MQTT — Network Observer Mode

Connects to the Meshtastic MQTT servers (mqtt.meshtastic.org) or a self-hosted broker. No physical radio required — observe the wider mesh, read messages, and view telemetry from any node broadcasting to MQTT. Useful for regional monitoring without radio hardware.

How MQTT Integration Works

The MQTT manager uses paho-mqtt for transport and meshtastic protobufs to decode/encode ServiceEnvelope payloads. Incoming messages are decoded from protobuf, translated to MeshDash-compatible packet dicts, and placed on the slot's packet queue — the existing packet-processing worker consumes them identically to Serial/TCP packets.

  • Subscribe topics: msh/{REGION}/2/e/{CHANNEL}/#
  • Publish topics: msh/{REGION}/2/e/{CHANNEL}/{GATEWAY_ID}
  • Region # falls back to EU_868 (public broker rejects fully-wildcard subscriptions)
  • Echo suppression: Packets from the manager's own node (matched by MQTT_NODE_ID) are silently dropped to prevent echo loops

MQTT Presets

meshtastic_public
Broker: mqtt.meshtastic.org, Port: 1883, TLS: No
meshtastic_public_tls
Broker: mqtt.meshtastic.org, Port: 8883, TLS: Yes

Credentials for the public broker: meshdev / large4cats.

MQTT Encryption (AES-CTR)

Inbound: Encrypted packets on the 2/e/ topic are decrypted with the default Meshtastic PSK (single byte 0x01 padded to 16 bytes). If the default key fails and user-supplied PSKs are available (via set_channel_psk()), those are tried next.

Outbound: All messages published to 2/e/ are encrypted using AES-CTR with the same nonce scheme the Meshtastic firmware uses: nonce[0..7] = packet_id as little-endian uint64, nonce[8..15] = from_node_num as little-endian uint64. Channel PSK resolved: caller-supplied → stored PSKs → default PSK.

Firehose Filters

Config flags to drop noisy packet types:

MQTT_DROP_TELEMETRY
Drop telemetry packets
MQTT_DROP_POSITION
Drop position packets
MQTT_DROP_NODEINFO
Drop node info packets
MQTT_DROP_NEIGHBOR
Drop neighbor packets
MQTT_DROP_ROUTING
Drop routing packets
MQTT_DROP_ENCRYPTED
Drop encrypted packets

Observer Mode vs Identity Mode

MQTT provides no myInfo burst. The manager synthesises a virtual local node from MQTT_NODE_ID (optional). If set, NodeInfo packets from that node are promoted to local and you can send to the mesh. If unset, the slot operates in observer mode — receive only.

Config

MESHTASTIC_CONNECTION_TYPE=MQTT
MQTT_BROKER=mqtt.meshtastic.org
MQTT_PORT=1883
MQTT_USERNAME=meshdev
MQTT_PASSWORD=large4cats
MQTT_TLS=false
MQTT_REGION=EU_868
MQTT_CHANNEL=#          # # = all channels
MQTT_NODE_ID=!aabbccdd  # optional — observer mode if unset

MeshCore — Alternative Protocol Support

Connects to MeshCore companion radios (serial, TCP, or BLE) using the meshcore Python library (≥2.3). MeshCore is a different protocol than Meshtastic — it uses a binary companion protocol with its own framing, event types, and commands. MeshDash translates MeshCore events into the standard packet pipeline.

Event Translation

MeshCore events are translated to MeshDash packet types:

MeshCore EventTypeMeshDash Packet TypeNotes
SELF_INFONode Info + PositionOwn node identity on appstart
ADVERTISEMENTNode Info + PositionAnother node discovered
NEW_CONTACTNode Info + PositionSame as advertisement
CONTACT_MSG_RECVMessage (DM)toId = local node
CHANNEL_MSG_RECVMessage (broadcast)toId = ^all
ACKAck (ROUTING_APP)DM delivery confirmation
TELEMETRY_RESPONSE / STATUS_RESPONSETelemetryBattery, voltage, uptime, noise floor
BATTERYTelemetryLightweight battery event
PATH_UPDATETracerouteRouting path discovery

Node ID Convention

MeshCore identifies nodes by a 6-byte public key prefix (12 hex chars), e.g. a1b2c3d4e5f6. MeshDash node IDs use the same !hexstring convention: !a1b2c3d4e5f6. This is longer than Meshtastic's 8-char IDs, but MeshDash treats node IDs as opaque strings throughout.

RX_LOG Correlation

MeshCore fires RX_LOG_DATA events separately from message events. The manager implements a 500ms correlation window:

  1. CHANNEL_MSG_RECV arrives → sleep 500ms
  2. Check _pending_rx_logs for entries matching {channel_idx}:{timestamp}:{text[:40]}
  3. Attach best SNR/RSSI to the message packet

ACK Tracking

When sendText() sends a DM, the meshcore library returns an expected_ack code stored in _pending_acks (capped at 500, 5-minute TTL). When an ACK event fires, the matched packet_id is emitted as a ROUTING_APP packet so the messages DB can update status to DELIVERED.

Session Startup Sequence

  1. Create MeshCore instance via transport factory
  2. Subscribe all event handlers
  3. start_auto_message_fetching()
  4. send_appstart() → triggers SELF_INFO
  5. _refresh_contacts() → populate contacts cache
  6. get_channel(0..7) → populate channel info (stops after 2 consecutive errors)
  7. get_self_telemetry() → battery/uptime appear immediately
  8. get_device_info() → firmware version, radio params
  9. send_advert(flood=False) → announce presence on mesh

Config

MESHTASTIC_CONNECTION_TYPE=MESHCORE
MESHCORE_TRANSPORT=serial                    # serial, tcp, or ble
MESHCORE_SERIAL_PORT=/dev/ttyUSB0
MESHCORE_BAUD=115200
MESHCORE_HOST=192.168.1.100                  # if transport=tcp
MESHCORE_PORT=4000                           # if transport=tcp
MESHCORE_BLE_MAC=                            # if transport=ble
MESHCORE_BLE_PIN=                            # if transport=ble
MESHCORE_LABEL=My MeshCore Node

Web Serial (Browser-Direct USB)

A fourth connection mode — WEBSERIAL — lets the browser hold the serial port directly, bypassing the server's serial connection entirely. This is useful when MeshDash runs on a remote server but you want to connect a radio physically attached to your local PC.

Set MESHTASTIC_CONNECTION_TYPE=WEBSERIAL in the config (or select it in Settings). After restart, the server will not open any serial port. A WEB SERIAL button appears in the topbar — click it to connect the browser to the radio via the Web Serial API.

Requires Chrome 89+ or Edge 89+ on HTTPS or localhost. Does not work in Firefox or Safari.

See the full Web Serial Connection guide for the complete setup walkthrough, baud rate selection, wakeup frame, and disconnect procedure.

Connection State Machine & Reconnection

All six connection types share the same state machine and reconnect logic. See Connection Manager for the full state transition diagram (IDLE → CONNECTING → DEGRADED → CONNECTED with RECONNECTING and DISCONNECTED branches), two-layer health checks, 3-strike system, exponential backoff with jitter, and is_ready event.

Which Connection Type Should I Use?

Are you connecting a radio physically attached to this server?
  │
  ├─ YES ── What radio type?
  │          ├─ Meshtastic radio (T-Beam, Heltec, etc.) → SERIAL
  │          │   Most reliable. USB data cable required. Use dialout group.
  │          │   Auto-detects port. Config: MESHTASTIC_CONNECTION_TYPE=SERIAL
  │          │
  │          └─ MeshCore radio → MESHCORE (serial)
  │              Alternative protocol. Config: MESHCORE_TRANSPORT=serial
  │
  ├─ Is radio on the local network with WiFi?
  │  └─ TCP → connects to radio's IP:4403
  │     Assign static IP or DHCP reservation.
  │     TCP keepalive probes detect dead radios.
  │     Config: MESHTASTIC_CONNECTION_TYPE=TCP MESHTASTIC_HOST=192.168.1.50
  │
  ├─ Bluetooth radio nearby? (least stable, experimental)
  │  ├─ Meshtastic BLE → BLE
  │  │   Requires server Bluetooth adapter. Pair from CLI.
  │  │   Config: MESHTASTIC_CONNECTION_TYPE=BLE MESHTASTIC_BLE_MAC=AA:BB:CC:DD:EE:FF
  │  └─ MeshCore BLE → MESHCORE (ble)
  │
  ├─ No physical radio at all? Just want to observe the mesh?
  │  └─ MQTT → connects to mqtt.meshtastic.org
  │     Observe without radio hardware. Receive only by default.
  │     Set MQTT_NODE_ID for send capability.
  │     Config: MESHTASTIC_CONNECTION_TYPE=MQTT
  │
  └─ Radio is near your browser, not the server?
     └─ WEBSERIAL → browser holds the USB connection
        Chrome/Edge only. HTTPS required. Server parks idle.
        Config: MESHTASTIC_CONNECTION_TYPE=WEBSERIAL

The /api/status endpoint always reflects the current connection state:

GET /api/status

{
  "api_status": "online",
  "connection_status": "Connected",
  "is_system_ready": true,
  "local_node_info": { ... },
  "last_error": null,
  "public_mode": false
}

is_system_ready is true only when the radio connection is fully established and the background sync is complete. Always check this before sending messages.

Connection History

Every connection state change (Connected, Disconnected, Reconnecting, etc.) is logged to the connection_log table and exposed via:

GET /api/system/connection_history?limit=60

Returns timestamped entries with a numeric value field (0.9 = connected, 0.5 = transitioning, 0.1 = disconnected) suitable for time-series charting.

Troubleshooting

SymptomLikely CauseFix
Permission denied /dev/ttyACM0Not in dialout groupsudo usermod -aG dialout $USER then re-login
Device or resource busyAnother process has the port openClose Meshtastic app / Python Flasher. fuser /dev/ttyACM0 to identify.
No /dev/ttyACM* visibleCharge-only cable, wrong driverUse a data USB cable. Check dmesg | tail when plugging in.
TCP connection refusedWrong IP or radio WiFi disabledVerify IP with nmap, confirm radio is in WiFi mode.
Keeps reconnecting every ~30 sSerial cable quality / Pi USB powerUse a powered USB hub, shorter cable, or TCP mode.
BLE not foundRadio out of range or BLE off in firmwareCheck radio Bluetooth settings, reduce distance to <2 m for pairing.