Skip to content

Getting started ​

Prerequisites ​

On the Brain:

  • A CAN interface wired to the NMEA 2000 backbone and up at 250 kbit/s. The unit ships pointing at can1. NMEA 2000 runs at 250 kbit/s and nothing else: the daemon reads the interface's configured bitrate at startup and exits if it is not 250000. The one exception is an interface whose name begins with vcan, where the check is skipped.
  • An MQTT broker on 127.0.0.1:1883. The unit is ordered After=mosquitto.service. MQTT failing is not fatal — the daemon runs on and keeps retrying — but nothing appears on the screens until the broker is there.
  • muxen-systemd, a hard dependency of the package. It provides the muxen user and group the daemon runs as, and muxen.target.
  • can-utils and mosquitto-clients to actually watch what is happening. Neither is required by the package.

Termination, backbone length and drop lengths are NMEA 2000 installation matters and are outside this manual. A bus that is not correctly terminated produces error counters, not silence — see Troubleshooting.

Install ​

sh
sudo apt install muxen-nmea2000

One package installs the daemon, the four tools, four systemd units, bash completion for every binary, an nginx snippet, and the name tables under /usr/share/muxen-nmea2000/.

Only muxen-nmea2000.service is enabled at install time. The templated unit, the simulator and the from-simulator unit are installed but left disabled and not started.

Start it and check it claimed an address ​

muxen-nmea2000.service is WantedBy=muxen.target and PartOf=muxen.target, so it comes up with the rest of the MUXEN stack. It also answers to the alias muxen-nmea.service.

sh
sudo systemctl start muxen-nmea2000
systemctl status muxen-nmea2000
journalctl -u muxen-nmea2000 -n 40

The first line worth reading in the journal is the configuration summary:

Config: can1 NAME=0x11223344FF467788 addr=0x80 (100-200) MQTT Alarms DateTime

It names the interface, the J1939 NAME, the preferred address, the dynamic address range, and every feature that is on. If the only features listed are MQTT Alarms DateTime, no report module is enabled yet — that is the default, and Configuration is the next chapter to read.

Then, with --verbose-control or in the MQTT output, four addresses get claimed rather than one. The daemon presents itself as four virtual devices on the bus — navigation, engine, AIS and electrical — because a single NMEA 2000 device may only advertise a bounded list of PGNs, and this gateway handles far more than one device's worth. Each virtual device claims its own address, so a healthy daemon occupies four consecutive addresses from 128 upwards.

Nothing else is published until all four have claimed.

Confirm data is flowing ​

The fastest check is the bus inventory:

sh
$ muxen-nmea2000-inventory

It subscribes to nmea/devices, waits for the retained message and prints one row per device seen on the bus, with its address, manufacturer and function. An empty list after the timeout means the daemon is not seeing the bus at all, not that the bus is empty — go to Troubleshooting.

Then look at the raw topics:

sh
mosquitto_sub -h 127.0.0.1 -t 'nmea/#' -v          # everything
mosquitto_sub -h 127.0.0.1 -t 'nmea/interface' -v  # CAN link health, every 10 s
mosquitto_sub -h 127.0.0.1 -t 'nmea/devices' -v    # the device list
mosquitto_sub -h 127.0.0.1 -t 'nmea/status' -v     # module health, every 30 s

nmea/interface is the one to read when in doubt: it carries the link state (ERROR-ACTIVE is the healthy one), the configured bitrate, and the running frame and error counters straight from the kernel.

Turn on the reports the boat needs ​

By default the daemon decodes the bus but publishes no report. On a commissioned boat the report modules are turned on by /etc/muxen/deploy.json, written at commissioning time, and the daemon reads it at startup.

To see the effect immediately without touching the commissioned file, stop the service and run the daemon by hand:

sh
sudo systemctl stop muxen-nmea2000
sudo -u muxen /usr/bin/muxen-nmea2000 \
    --interface=can1 \
    --name=0x11223344FF467788 \
    --report-all \
    --verbose-control

--report-all enables navigation, AIS, motor, route and thruster in one go, and overrides whatever deploy.json says. Within a few seconds:

sh
mosquitto_sub -h 127.0.0.1 -t 'nmea/navigation' -v

Run by hand like this the daemon uses /var/lib/muxen-nmea2000 and /run/muxen-nmea2000 — the same paths systemd gives it — so only one copy can run at a time. Stop the manual run before restarting the service.

For a permanent change, put the parameters in deploy.json, or override the unit's command line with a drop-in:

sh
sudo systemctl edit muxen-nmea2000
ini
[Service]
Environment="MUXEN_NMEA_INTERFACE=can1"
Environment="MUXEN_NMEA_NAME=0x11223344FF467788"

Those two are the only variables muxen-nmea2000.service interpolates. See Configuration.

A real templated instance ​

muxen-nmea2000@.service is the unit installers get wrong, so this is the worked example.

It is a template: the text after the @ is the instance name, and the instance name is the CAN interface. There is no muxen-nmea2000@.service to start — only concrete instances of it.

sh
$ systemctl start muxen-nmea2000@can0
$ systemctl status muxen-nmea2000@can0
● muxen-nmea2000@can0.service - MUXEN NMEA 2000 CAN Bus Application (instance can0)
     Loaded: loaded (/usr/lib/systemd/system/muxen-nmea2000@.service; static)
     Active: active (running)

The unit refuses to start if /sys/class/net/can0 does not exist (ConditionPathExists), so a typo in the instance name is a skipped start, not a crash loop.

Everything that would collide with the commissioned daemon is derived from the instance name:

ResourcePrimaryInstance can0
MQTT topic prefixnmeanmea-can0
MQTT client idauto-generatedmuxen-nmea2000-can0
IPC socket/run/muxen-nmea2000/sources.sock/run/muxen-nmea2000-can0/sources.sock
Runtime directory/run/muxen-nmea2000/run/muxen-nmea2000-can0
State directory/var/lib/muxen-nmea2000/var/lib/muxen-nmea2000-can0

So the instance's data is here, not under nmea/:

sh
mosquitto_sub -h 127.0.0.1 -t 'nmea-can0/#' -v
muxen-nmea2000-inventory -I can0
muxen-nmea2000-config    -I can0

Three things make an instance safe to start on a bus that already carries the commissioned daemon:

  1. It ignores /etc/muxen/deploy.json. The unit passes --no-deploy-config, so an instance is configured only from its command line and /etc/muxen/env.can0. It cannot pick up — or disturb — the commissioned configuration.
  2. It carries a different J1939 NAME, 0x91223344FF467799 against the primary's 0x11223344FF467788. Two identical NAMEs on one bus are illegal.
  3. Its NAME is numerically higher and arbitrary-address-capable. On an address collision the numerically lower NAME wins, so the primary always keeps its address and the instance is the one that relocates. A tool instance can never dislodge the boat's gateway.

Instances have no [Install] section and are not part of muxen.target, so they never come up at boot. Stop one the obvious way:

sh
sudo systemctl stop muxen-nmea2000@can0

Full detail, including per-instance environment files: More than one bus.

A bus with no boat attached ​

For bench work there is a synthetic bus. muxen-nmea2000-simulator transmits realistic engine, navigation, AIS, route and thruster PGNs on a virtual CAN interface:

sh
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

muxen-nmea2000-simulator --all vcan0

In another shell, point a daemon at the same interface. vcan skips the 250 kbit/s check, so this works with no CAN hardware at all:

sh
muxen-nmea2000 --interface=vcan0 --name=0x11223344FF467788 --report-all

The packaged shortcut for the same thing is muxen-nmea2000-from-simulator.service, which creates vcan0 itself and starts a daemon on it with --report-all. It declares Conflicts=muxen-nmea2000.service, so enabling it takes the real daemon down — never enable it on a boat.

Where to go next ​

Integration of multiplexed solutions
MUXEN and the MUXEN logo are trademarks of MUXEN SAS.