Skip to content

Getting started ​

Recording the bus takes one command. Making sure the recording cannot take the boat down takes about ten minutes more, and this chapter walks both.

Prerequisites ​

On the Brain:

  • A SocketCAN interface that is up. ip link show can0 must succeed. The unit checks for /sys/class/net/<interface> before it starts anything.
  • /usr/bin/gzip. The package depends on gzip (>= 1.9) and the recorders exec it directly by absolute path after every rotation. It is not optional — see gzip is not optional below.
  • Room on the partition holding /var/lib, and a decision about how much of it this package may have. Read Disk budget before you leave the boat.

Nothing else. No broker, no configuration file, no other muxen-* service, no network.

Install ​

sh
sudo apt install muxen-datalogger-tools

One package carries both recorders, both systemd template units, both bash-completion files, and it creates /var/lib/muxen/datalogger.

Installing starts nothing. Neither template is enabled by the package, and a template unit cannot run without an instance anyway.

Enable a recorder, per interface ​

Both units are templates. The text between @ and .service is the name of the CAN interface to record — nothing else. It is substituted into two places in the unit: the --interface argument, and the ConditionPathIsDirectory=/sys/class/net/… guard.

The raw recorder, on can0:

sh
sudo systemctl enable --now muxen-can-datalogger@can0.service

A second bus is a second instance, and the two are completely independent — separate processes, separate file series:

sh
sudo systemctl enable --now muxen-can-datalogger@can1.service

The decoding recorder is a different template, instantiated the same way:

sh
sudo systemctl enable --now muxen-csv-datalogger@can0.service

Read the next section before you run that last command.

The two units are not interchangeable — and one of them is wrong ​

Up to and including 2.3.3, muxen-csv-datalogger@.service had this ExecStart:

ini
ExecStart=/usr/bin/muxen-can-datalogger --interface %i

It started the CAN recorder, not the CSV one, and its hardening blocked its own log folder. Both are fixed, but if you are running 2.3.3 or older the symptoms are:

  1. No CSV files. Both units ran the same program, and that program writes BUSMASTER captures.
  2. Enabling both units on one interface ran two raw recorders in the same folder. Both computed the same next file index and opened the same path with fopen(…, "w+"), interleaving and overwriting each other. The result is one corrupt capture per rotation, not two good ones.
  3. The CSV unit could not write. ProtectSystem=strict with no ReadWritePaths= and no StateDirectory= left /var/lib read-only. The recorder failed its startup write test, printed config: Check logFolder permissions: /var/lib/muxen/datalogger and exited — with status 0, so systemctl status never said failed. It showed a unit restarting every 30 seconds, quietly.

On an older package, run the decoded recorder by hand instead of enabling its unit:

sh
sudo /usr/bin/muxen-csv-datalogger --interface can0

Check the CHANGELOG.md of the version you actually installed.

Verify ​

A healthy recorder says everything it knows at startup and then goes silent.

sh
systemctl status muxen-can-datalogger@can0.service
journalctl -u muxen-can-datalogger@can0.service -n 20
config: verbose = 0
config: interface = can0
config: replay = (null)
config: logFolder = /var/lib/muxen/datalogger
config: maxRecordTime = 10800
config: maxFileSizeBeforeCompression = 10485760
busmaster: next log file index is 1

Those seven lines are the whole startup report. next log file index is the useful one: it is the highest index already present in the folder plus one, so on a restart it proves the recorder found the existing captures and will not overwrite them.

Then check that the file is growing:

sh
ls -l /var/lib/muxen/datalogger/
wc -l /var/lib/muxen/datalogger/can0.1.log
sleep 10
wc -l /var/lib/muxen/datalogger/can0.1.log

The line count must increase. Every frame is flushed to the kernel as it is written, so the file is readable while it is being recorded — no need to stop the service to look at it.

If the count does not move, the bus is silent as far as this Brain is concerned. Check with candump can0; the recorder and candump see exactly the same thing.

Read a first capture ​

The current file is plain text:

sh
tail -5 /var/lib/muxen/datalogger/can0.1.log
07:59:56:0998 Rx 1 0x1042 x 5 0B 55 05 06 00
07:59:56:1193 Rx 1 0x40C1 x 8 00 00 00 00 16 00 18 00
07:59:56:1687 Rx 1 0x1043 x 5 0C 55 54 34 00

Time is UTC, to 100 microseconds. Rx 1 is a constant: every line says received, on channel 1. Then the identifier, x for an extended frame, the data length, and the bytes in hex.

Rotated files are compressed and read the same way through zcat:

sh
zcat /var/lib/muxen/datalogger/can0.1.log.gz | head -20
zgrep ' 0x1040 ' /var/lib/muxen/datalogger/can0.*.log.gz | head

The full grammar of both formats is in File formats.

Set a disk budget before you leave ​

The recorders create files and never remove them. There is no retention setting to configure, so the budget is set in two places: how big each file is allowed to get, and what removes the old ones.

Size the files. The units pass only --interface, so every other option keeps its built-in default: rotate after 3 hours or 10 MiB, whichever comes first. To change that, add a systemd drop-in — the empty ExecStart= line is required, it clears the one inherited from the template:

sh
sudo mkdir -p /etc/systemd/system/muxen-can-datalogger@can0.service.d
sudo tee /etc/systemd/system/muxen-can-datalogger@can0.service.d/override.conf >/dev/null <<'EOF'
[Service]
ExecStart=
ExecStart=/usr/bin/muxen-can-datalogger --interface %i \
    --maxRecordTime 1800 \
    --maxFileSizeBeforeCompression 4194304
EOF
sudo systemctl daemon-reload
sudo systemctl restart muxen-can-datalogger@can0.service

Smaller files mean more of them, each compressed sooner. That does not reduce the total, it only shortens the window of uncompressed data at risk when the Brain loses power.

Arrange for deletion. Nothing in this package does it. A systemd-tmpfiles rule is the least surprising way, and it is one file:

sh
sudo tee /etc/tmpfiles.d/muxen-datalogger.conf >/dev/null <<'EOF'
# path                          mode uid  gid  age
d /var/lib/muxen/datalogger     0755 root root 30d
EOF
sudo systemd-tmpfiles --clean

That removes captures older than 30 days on the daily systemd-tmpfiles-clean.timer. Pick the age from the measured daily volume and the free space you are willing to give up — Disk budget does that arithmetic.

Do this even on a boat where recording is meant to be temporary. "Temporary" recording that nobody disabled is the normal way this package fills a disk.

gzip is not optional ​

After closing a file both recorders fork() and execve()/usr/bin/gzip with --best. The child does not check whether the exec succeeded and does not exit if it fails — it returns into the recorder's own code and carries on as a second copy of the recorder, writing to the same folder. One more copy per rotation.

So on a Brain where /usr/bin/gzip is missing or not executable, the symptom is not "files are not compressed". It is a growing number of recorder processes and a folder filling several times faster than expected. The package dependency on gzip (>= 1.9) is what normally prevents this; if you build or deploy outside the .deb, check it yourself.

sh
ls -l /usr/bin/gzip
pgrep -a muxen-can-datalogger | wc -l    # one per enabled instance

Stop recording ​

sh
sudo systemctl disable --now muxen-can-datalogger@can0.service

A clean stop is not just a kill: on SIGTERM the recorder writes the closing lines of the current file, closes it and compresses it. The capture is therefore complete and compressed after a normal stop, and left open and uncompressed after a power cut or a SIGKILL.

Disabling the unit does not remove any file.

Where to go next ​

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