Appearance
The state file
An NMEA 2000 device negotiates its address at every power-up, so in principle it could land somewhere different each time. Chartplotters notice: a gateway that appears at a new address after every start looks like a new device, and some displays accumulate a list of ghosts.
muxen-nmea2000 avoids that by remembering the addresses it claimed last time and asking for the same ones again. That memory is a single small binary file.
Nothing in this chapter needs attention on a working boat. The file manages itself, and if it is ever wrong the daemon deletes it and carries on. It is documented because --show-state is a useful diagnostic when a device's address is in question.
Where it lives
| Path | |
|---|---|
/var/lib/muxen-nmea2000/state | the primary daemon |
/var/lib/muxen-nmea2000-<instance>/state | a templated instance |
<state-dir>/state.tmp | the temporary file used while writing |
--state-dir DIR overrides the directory. Under systemd it comes from StateDirectory=, which is why an instance's state lands beside its own runtime directory rather than in the primary's.
The directory is created with mode 0750 if it does not exist; the file is written with mode 0600.
What it stores
The four addresses claimed by the four virtual devices, each with the NAME it claimed them under.
Storing the NAME alongside the address is what makes the file safe. If the daemon is restarted with a different NAME — a different boat, a changed identity, a templated instance sharing a directory by mistake — the saved addresses do not match and are simply not used. A stale file can never make the daemon claim an address that belongs to somebody else's identity.
Format
Version 3, little-endian, fixed layout.
Header:
| Field | Type | Value |
|---|---|---|
magic | uint32 | 0x4A313933, ASCII J193 |
version | uint32 | 3 |
timestamp | uint64 | Unix seconds when written |
checksum | uint32 | CRC-32 over header and payload, with the checksum field zeroed |
reserved | uint32 |
Payload:
| Field | Type | |
|---|---|---|
vdev_count | uint8 | number of entries in use, at most 8 |
reserved | uint8[7] | |
vdevs | 8 entries | fixed-size array |
Each entry:
| Field | Type | |
|---|---|---|
name | uint64 | the J1939 NAME this virtual device claimed under |
address | uint8 | the address it claimed |
vdev_id | uint8 | 0 navigation, 1 engine, 2 AIS, 3 electrical |
function_code | uint8 | the J1939 function code |
reserved | uint8[5] |
The array is 8 entries although only 4 are used, so the format does not have to change if more virtual devices are added.
Earlier versions
| Version | Notes |
|---|---|
| 1 | original format, no checksum. Not read |
| 2 | added the CRC-32, one device only. Read once and migrated |
| 3 | current: multiple virtual devices |
A version 2 file is migrated automatically on first read:
State file: Migrating v2 format to v3...
State file: Migrated v2 NAME=0x…A migration that fails deletes the file, as any other corruption does.
Writing
Writes are atomic. The daemon writes state.tmp, flushes it, fsyncs it, renames it over state and then sets the mode to 0600. A power cut mid-write leaves either the old file or the new one, never a half-written one.
Validation, and what happens when it fails
Every field is checked on load, and a file that fails any check is deleted rather than partially trusted:
| Check | Failure message |
|---|---|
magic is 0x4A313933 | Invalid magic number 0x… (expected 0x4A313933) |
| version is 3 (or 2, to migrate) | Unexpected version N (expected 3) |
| CRC-32 matches | Checksum mismatch (got 0x…, expected 0x…) |
| entry count ≤ 8 | Invalid vdev count N (max 8) |
| NAME is neither 0 nor all-ones | Invalid NAME 0x… for vdev N |
| address is 0–253, or 254 (null) | Invalid address 0x… for vdev N |
| the file is long enough | Incomplete read |
followed by:
State file: Validation failed, deleting corrupted fileDeleting is the right response because the file is a cache of something the daemon can rediscover in two seconds. Keeping a suspect one risks claiming an address that belongs to another device.
A NAME mismatch is not corruption. The file is valid, it just belongs to a different identity, so it is ignored and left in place — reverting the identity should bring the addresses back.
A missing file is the normal first run:
State file: No saved state found (first run)None of these prevent startup. The daemon falls back to its preferred address and claims normally.
Inspecting and clearing it
Both commands act and exit; neither starts the daemon, and both are safe to run while it is running.
sh
$ muxen-nmea2000 --show-state
State: File path: /var/lib/muxen-nmea2000/state
State: --------------------------------------------------
State: Status: Valid state file (4 virtual devices)
State: --------------------------------------------------
State: VDEV[0]:
State: J1939 NAME: 0x11223344FF467788
State: Address: 0x80 (128)
State: Function Code: 130
State: NAME breakdown:
State: …The NAME breakdown decodes every field — identity number, manufacturer code and name, function code and name, industry group, arbitrary-address-capable — which makes --show-state the quickest way to check that a configured NAME is what you think it is.
With no valid file:
State: Status: No valid state file found
State: Possible reasons:
State: - File does not exist (first run)
State: - File is corrupted (invalid magic, checksum, etc.)
State: - File has invalid data (NAME or address out of range)To forget the saved addresses:
sh
$ muxen-nmea2000 --clear-state
State: Clearing file: /var/lib/muxen-nmea2000/state
State: File deleted successfully--clear-state exits non-zero if the file could not be deleted.
Add --state-dir to either command to work on an instance's file:
sh
muxen-nmea2000 --show-state --state-dir /var/lib/muxen-nmea2000-can0When clearing it is the right move
Rarely, and always for one of these reasons:
- The gateway keeps claiming an address you have reassigned to another device. Clearing makes it renegotiate from its preferred address.
- The boat's NAME has been changed deliberately and you want a clean start rather than a mismatch that is ignored.
- You are moving a Brain between boats.
It is never a fix for "no data on the screens". The state file has no influence on anything except which address is asked for first.
Related chapters
- How the address is negotiated — Address claiming
- The state machine in detail — The address claim state machine
- Per-instance state directories — More than one bus
