Skip to content
mathisono edited this page Sep 8, 2026 · 26 revisions

MeshCore Bridge

You should only enable the MeshCore bridge if you have an appropriate MeshCore device available to your Crow node, either over the network or attached by USB.

MeshCore Companion API

For current Crow builds, the recommended MeshCore integration is the MeshCore Companion binary API. Crow can use the same Companion message backend over:

  • TCP to a network-reachable MeshCore endpoint, normally port 4403.
  • USB serial through /dev/ttyACM0 or /dev/ttyUSB0.

This is a binary framed protocol, not a line-oriented command console. Crow sends the Companion startup frame, receives device self-info, drains queued messages after 0x83 message-waiting notifications, and sends text through the same backend.

KISS is not the MeshCore Companion protocol. Crow's kiss_tcp mode is APRS/AX.25 support and should not be used for MeshCore Companion traffic.

Configure Crow

Add or edit the top-level configuration in /etc/crow.conf.override. Restart Crow after changing configuration.

TCP Companion API

Use TCP when the Companion API is reachable over the network:

{
  "meshcore": { "backend": "tcp" },
  "meshcore_tcp_api": {
    "enabled": true,
    "host": "192.168.4.1",
    "port": 4403,
    "max_pending_rx": 4,
    "channel_discovery": true,
    "channel_refresh_seconds": 600
  }
}

Use the MeshCore device or Companion API bridge IP for host, not the Crow node IP. The default Companion TCP port is 4403; do not use Crow's WebSocket/UI port for this setting.

USB serial Companion API

Use serial when the MeshCore node is physically attached to the Crow host:

{
  "meshcore": { "backend": "serial" },
  "meshcore_serial_api": {
    "enabled": true,
    "device": "/dev/ttyACM0",
    "baud": 115200,
    "app_start_profile": "meshcore_cli",
    "max_pending_rx": 4,
    "channel_discovery": false,
    "channel_slots": {
      "0": "MeshCore izOH6cXN6mrJ5e26oRXNcg=="
    }
  }
}

/dev/ttyUSB0 is also supported. meshcore_usb_api is accepted as an alias for meshcore_serial_api.

TCP is preferred when TCP and serial are both enabled without an explicit selector. Set meshcore.backend to serial to force USB.

Backend selector values

Accepted explicit selectors are:

TCP:   api, tcp, tcp-api, companion-api
USB:   serial, serial-api, usb, usb-api
UDP:   udp

A channel entry by itself does not enable a MeshCore backend; one of the transport configurations above is also required.

Public channel and custom channels

The built-in MeshCore public channel is:

MeshCore izOH6cXN6mrJ5e26oRXNcg==

It uses MeshCore channel slot 0 and is created automatically by Crow. To use a custom MeshCore channel, configure the same name/key in Crow and MeshCore. TCP can use channel_discovery to inspect slots on the radio. USB serial uses explicit slot mapping.

If a custom slot cannot be resolved from discovery, provide an optional map:

{
  "meshcore_tcp_api": {
    "enabled": true,
    "host": "192.168.4.1",
    "channel_slots": {
      "2": "MeshTalk AQ=="
    }
  }
}

The map is slot number to exact Crow channel namekey. For USB serial, place the same mapping under meshcore_serial_api. Serial uses explicit mapping and does not load the optional TCP discovery scanner.

Transmit and receive

The TCP and USB serial paths share the same framed Companion parser and group TX/RX implementation.

Crow receives:

  • direct messages delivered to the connected MeshCore node;
  • public and custom channel messages from mapped MeshCore slots;
  • queued messages drained after 0x83 message-waiting notifications.

Crow sends:

  • channel text with Companion command 0x03;
  • direct text with Companion command 0x02 on TCP, when the recipient public-key prefix is known.

TCP direct replies require Crow to learn the target MeshCore public-key prefix from a prior inbound direct message. If the prefix is not known, Crow fails the direct send safely. Native USB serial direct send is intentionally disabled; group text remains bidirectional.

Useful status counters include:

pending_rx
message_waiting
sync_requests
no_more_messages
channel_sends_ok
channel_sends_failed
direct_sends_ok
direct_sends_failed
outbound_group_sent
outbound_group_rejected
outbound_confirmed
radio_errors

Verify setup

Restart Crow:

/etc/init.d/crow restart

For TCP, confirm the MeshCore endpoint is reachable:

nc -zv 192.168.4.1 4403

Check startup logs:

logread | grep -E 'meshcore_backend|meshcore_tcp_api|meshcore_serial_api'

Expected startup output includes selected tcp backend or selected serial backend, followed by a successful Companion handshake.

Send a unique message from another MeshCore node on the public channel. The message should appear in Crow. Then send a message from Crow on the public channel and verify that a second MeshCore node receives it.

Hardware validation status

Crow has been field-tested on AREDN Hub5 and BaseBox5 nodes. MeshCore USB testing used a RAKwireless WisCore RAK4631 attached at /dev/ttyACM0; TCP Companion testing used a network-reachable MeshCore endpoint.

The v0.0.2-r21140793 APK was upgraded live on Hub5 and BB5MC on 2026-09-02. Both nodes preserved their configuration checksums and recovered Crow, the watchdog, HTTP UI, and APRS/Xastir connections. Their Companion endpoints connected and then closed without returning frames, so that deployment is not a current-release handshake or RF-delivery proof.

The last successful USB protocol handshake was the following historical test on 2026-08-25 on KJ6DZB-WSB-hub5 (AREDN 4.26.7.0):

  • Source: main, commit 4eea321.
  • The attached RAK USB Companion radio enumerates as a RAKwireless WisCore RAK4631 (239a:8029) at /dev/ttyACM0.
  • Hub5 required the matching USB host/CDC drivers (kmod-usb2, kmod-usb-acm, kmod-usb-dwc3, kmod-usb-dwc3-qcom, and kmod-usb3).
  • AREDN's minimal image omits stty, so Crow's packaged static crow-rawtty helper applies persistent 115200/raw termios settings. This was verified independently before Crow was restarted.
  • Crow selects the direct serial backend, opens /dev/ttyACM0, sends CMD_APP_START, and receives RESP_SELF_INFO from the radio, identified as KJ6DZB-Crow.
  • At that commit, CMD_GET_CHANNEL discovery was verified on slots 0–7. The radio reports only the Public group in slot 0; no matching configured Crow group channel was found or imported automatically.
  • Host-side protocol tests passed: serial Companion tests (57), TCP Companion tests (51), backend-selection checks, and router/gatekeeper matrix checks.
  • That main integration repeated the USB Companion handshake successfully before its publication. Current serial releases instead use explicit slot mappings and do not load the TCP discovery scanner.

Group receive, group transmit, and over-air delivery are still pending. Before enabling transmit, explicitly configure the same group name/key in both Crow and the radio, then bind Crow's tx_channel_index to the discovered radio slot. Crow deliberately does not persist a key discovered from the radio or select a transmit channel automatically. Direct messages and GPS were not tested in this validation.

For developer-level protocol tests, see the main repository docs and run:

node tests/run_meshcore_tcp_api_tests.js

Channel discovery

When TCP channel_discovery is enabled, Crow queries MeshCore channel slots with CMD_GET_CHANNEL (0x1F) after startup and on the refresh timer.

Discovery is runtime-only. It helps Crow resolve MeshCore slots and inspect the radio's channel state, but it does not persist channel secrets or overwrite operator-managed configuration. USB serial uses the explicit slot mapping described above instead.

Once a channel is available, join it with:

/join #TacNet key=your-passphrase

See Commands for command details and Configuring Channels for channel setup.

Legacy MeshCore2Net setup

The older MeshCore2Net/UDP path remains available for deployments that already use it. New installs should prefer the Companion TCP or USB serial backend above.

Firmware

Building an AREDN to MeshCore hardware bridge is more involved than the Meshtastic bridge. By default, MeshCore does not provide Ethernet access, but the official firmware can send and receive packets through the serial port on a MeshCore node. See https://github.com/meshcore-dev/MeshCore.

Hardware

These instructions use the WisBlock system: the WisBlock Base (RAK19007) and the WisBlock Core (RAK4631). This device has a serial port available and is powered and programmed over USB. A Raspberry Pi can support remote programming and configuration over USB, plus serial packet forwarding.

MeshCoreBridge

The picture above shows the Raspberry Pi connected to the WisBlock boards via a USB cable and 3-wire ribbon serial cable. The PoE HAT allows powering and communicating with the board over a single Ethernet cable.

MeshCore configuration

The MeshCore node must be configured first. The Python tools are available at https://github.com/meshcore-dev/meshcore-cli.

After configuration, retrieve the node's public key:

meshcli -s /dev/ttyACM0 -r get public.key

MeshCore2Net

To copy MeshCore packets between the MeshCore node and the local network, the Raspberry Pi must run bridge software. The historical bridge is https://github.com/kn6plv/MeshCore2Net.

Install NodeJS using the official instructions at https://github.com/nvm-sh/nvm#installing-and-updating, then install the bridge:

cd /root
git clone https://github.com/kn6plv/MeshCore2Net.git
cd MeshCore2Net
./install.sh

Assuming everything goes well, the bridge will run as a service after reboot.

References

Clone this wiki locally