Appearance
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 can0must succeed. The unit checks for/sys/class/net/<interface>before it starts anything. /usr/bin/gzip. The package depends ongzip (>= 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-toolsOne 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.serviceA 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.serviceThe decoding recorder is a different template, instantiated the same way:
sh
sudo systemctl enable --now muxen-csv-datalogger@can0.serviceRead 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 %iIt 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:
- No CSV files. Both units ran the same program, and that program writes BUSMASTER captures.
- 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. - The CSV unit could not write.
ProtectSystem=strictwith noReadWritePaths=and noStateDirectory=left/var/libread-only. The recorder failed its startup write test, printedconfig: Check logFolder permissions: /var/lib/muxen/dataloggerand exited — with status 0, sosystemctl statusnever saidfailed. 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 can0Check 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 20config: 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 1Those 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.logThe 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.log07: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 00Time 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 | headThe 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.serviceSmaller 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 --cleanThat 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 instanceStop recording
sh
sudo systemctl disable --now muxen-can-datalogger@can0.serviceA 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
- Naming, rotation and compression in detail — Log files
- How much disk this really costs — Disk budget
- Replaying a capture onto a bench bus — Replay
- When something does not work — Troubleshooting
