Skip to content

Getting started ​

This chapter installs the daemon, declares one sensor, and confirms that the value it publishes is the one the boat should see.

Prerequisites ​

On the Brain:

  • An MQTT broker on 127.0.0.1:1883. The daemon connects at startup and exits if the broker refuses; systemd restarts it 30 seconds later. The unit orders itself After=mosquitto.service.
  • A CAN interface carrying the MUXEN bus, normally can0, unless the daemon is going to be run with --mqtt.
  • The boat's deployment file, /etc/muxen/deploy.json, readable by the muxen user. The daemon runs as that user and reads only the sensors array out of the file.

For --mqtt mode, a service publishing the device/… topics is required. The package carries Recommends: muxen-boat (>= 5.0.0) for that reason.

Install ​

sh
sudo apt install muxen-sensors

The package installs the daemon /usr/bin/muxen-sensors and the unit /usr/lib/systemd/system/muxen-sensors.service. It depends on muxen-systemd, which provides the muxen-deploy.target the unit attaches to.

Nothing needs enabling by hand — the package's generated maintainer scripts enable and start the unit. Confirm it:

sh
systemctl is-enabled muxen-sensors
systemctl status muxen-sensors

The unit is PartOf=muxen-deploy.target and WantedBy the same target, so a MUXEN deployment restarts it along with everything else that depends on the configuration file.

What the unit runs ​

ExecStart=/usr/bin/muxen-sensors --interface can0 --sensors ${MUXEN_DEPLOY}

with these defaults, overridable with a drop-in (systemctl edit muxen-sensors) — except the CAN interface, which is literal in ExecStart and in the unit's device dependency:

VariableDefault
MUXEN_DEPLOY/etc/muxen/deploy.json

Both arguments are mandatory to the daemon: it refuses to start without --sensors, and without --interface unless --mqtt is given. An empty override in a drop-in is therefore a startup failure, not a default.

--mqtt is not reachable through an environment variable. Running the daemon from MQTT rather than CAN needs a drop-in that replaces ExecStart — see Reference.

Declare a first sensor ​

Sensors live in the sensors array of /etc/muxen/deploy.json. The minimum entry names the variable, says where to read it, and says what the result is:

json
{
  "sensors": [
    {
      "name": "FreshWaterPortside",
      "input": {
        "deviceType": 1,
        "deviceInstance": 1,
        "deviceChannel": "analogInput0",
        "min": 0.0,
        "max": 5.0
      },
      "output": {
        "unit": "obix:units/liter",
        "min": 0,
        "max": 250
      },
      "scaleType": "polynomial",
      "polynomial": [0, 50]
    }
  ]
}

Read from the top:

  • name becomes the topic app/sensor/FreshWaterPortside. It may not contain /, #, + or a space. The app/sensor part is --prefix; an install migrating from the older variable/ prefix sets MUXEN_TOPIC_PREFIX in a drop-in and reads variable/FreshWaterPortside instead.
  • input points at analogue channel 0 of the Bloc 8 (function 1) at instance 1, and says the sender never produces less than 0 or more than 5. Readings outside that are clamped to it.
  • scaleType + polynomial convert the reading: 0 + 50 × input, so 5 gives 250.
  • output labels the result and bounds it to 0…250 litres.

The daemon reads this file once, at startup, so a change needs a restart:

sh
sudo systemctl restart muxen-sensors

In normal operation that happens by itself. muxen-systemd — a dependency of this package — watches /etc/muxen/deploy.json and restarts muxen-deploy.target when it is modified, and this unit is PartOf that target.

Verify ​

1. The daemon loaded the file.

sh
journalctl -u muxen-sensors -n 40

A healthy start prints its configuration, then the list of variables, then the dispatcher table:

config: verbose = 0
config: sensors = /etc/muxen/deploy.json
config: data source = can
config: can interface = can0
sensors: 1 variables
sensors: FreshWaterPortside
can: dispatcher table size: 1
can: canId   	canMask 	canDlc  	name
can: 0x002041	0xFFFFFF	7       	FreshWaterPortside
main: mqtt connected
can: can0 is UP (state = ERROR_ACTIVE, attempt = 0)

sensors: N variables is the count that survived parsing. If it is lower than the number of entries in the file, a line above it names each one that failed.

2. The value is being published.

sh
mosquitto_sub -h 127.0.0.1 -t 'app/sensor/#' -v

One message per variable per second — on variable/# instead, if this install still pins the old prefix. config: topic prefix = … in the startup log says which one. The payload arrives compact, on one line; it is shown indented here, as --json-pretty would publish it:

json
{
  "name": "FreshWaterPortside",
  "data": {
    "value": 143.5,
    "unit": "obix:units/liter",
    "min": 0.0,
    "max": 250.0
  },
  "input": {
    "functionCode": 1,
    "instance": 1,
    "channel": 6,
    "value": 2.87,
    "min": 0.0,
    "max": 5.0
  },
  "metadata": {
    "rxdate": "2026-08-16T09:14:22.310Z",
    "rxTimestamp": 1786000462,
    "expireAfterSec": 30
  }
}

input.value is what the device reported; data.value is what the calibration made of it. Having both on the same message is what makes a wrong gauge diagnosable without a bus analyser.

Nothing is published until the first frame arrives. A sensor that has never received anything stays silent — it does not publish a zero.

3. The reading is the right one. Compare input.value against the device's own topic, if muxen-boat is running:

sh
mosquitto_sub -h 127.0.0.1 -t 'device/1/1/io' -v

The two must agree. If they do and data.value is still wrong, the calibration is the problem, not the wiring — see Calibration and filtering.

Run it by hand at commissioning ​

The verbose mode prints the full interpretation of every entry, which is the fastest way to check that a file says what its author meant:

sh
sudo -u muxen /usr/bin/muxen-sensors -v -i can0 -s /etc/muxen/deploy.json
sensors: 1 variables
sensors: FreshWaterPortside
sensors: ├╴Input
sensors: │ ├╴DeviceType = 1
sensors: │ ├╴DeviceInstance = 1
sensors: │ ├╴DeviceChannel = 6
sensors: │ ├╴Min = 0.000000
sensors: │ └╴Max = 5.000000
sensors: ├╴Output
sensors: │ ├╴Unit = obix:units/liter
sensors: │ ├╴Min = 0.000000
sensors: │ └╴Max = 250.000000
sensors: └╴Scale = polynomial
sensors:   ├╴Order 0 = 0.000000
sensors:   ├╴Order 1 = 50.000000
sensors:   ├╴Order 2 = 0.000000
sensors:   ├╴Order 3 = 0.000000
sensors:   └╴Order 4 = 0.000000

Stop the service first, or the two instances compete for the same MQTT client identifier. -vv adds a trace of every frame and every MQTT message, which on a live bus is a firehose.

Where to go next ​

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