Skip to content

Alarms ​

When something on the boat goes wrong — a bilge float rises, a battery goes flat, an engine overheats — the MUXEN system raises an alarm and it appears on the MUXEN screens. This chapter is about the other screens.

NMEA 2000 has a standard way of carrying alerts, and every modern chartplotter knows how to display one and how to let you silence it. muxen-nmea2000 bridges the two: an alarm raised anywhere in the MUXEN system is put on the bus as a standard NMEA 2000 alert, in seven languages, and appears on the chartplotter. Press silence on the plotter and the request travels back into the MUXEN system.

For the crew, the practical consequence is: you can acknowledge a MUXEN alarm from the chartplotter at the helm, without going to a MUXEN screen.

This daemon does not decide what is an alarm. It never raises one by itself. It is a translator between the MUXEN alarm system and the NMEA 2000 bus, in both directions.

Turning it on ​

The subsystem is off by default, and the shipped muxen-nmea2000.service turns it on:

ExecStart=/usr/bin/muxen-nmea2000 … --enable-alarms

Without --enable-alarms the daemon subscribes to no alarm topic and silently ignores everything on them. A hand-run daemon that appears not to react to alarm messages is almost always missing this flag.

The two directions ​

    other muxen-* services                    chartplotter / MFD
             │                                        ▲
             │  nmea/alarm/active                     │  PGN 126983 Alert
             │  nmea/alarm/clear                      │  PGN 126985 Alert Text
             ▼                                        │
        muxen-nmea2000  ────────────────────────────► can1
             ▲                                        │
             │  nmea/alarm/mute/request               │  PGN 126984 Alert Response
             │  nmea/alarm/state                      │  (silence / acknowledge)
             │                                        ▼

Inbound: raising and clearing ​

Publish to nmea/alarm/active to raise an alarm:

sh
mosquitto_pub -h 127.0.0.1 -t nmea/alarm/active -m '{
  "alarmCode": 4001,
  "alarmDescription": "Overcurrent channel 1",
  "alarmDescription.FR": "Surintensité canal 1",
  "severity": "alarm",
  "deviceId": 40001,
  "functionCode": 210,
  "functionName": "Bloc 4",
  "instance": 1,
  "creationTime": "2026-08-16T09:00:00Z",
  "expireTime": "2026-08-16T10:00:00Z",
  "muted": false
}'
FieldMeaning
alarmCodethe alarm's identifier, unique per device
alarmDescriptionthe default (English) text
alarmDescription.FR, .DE, .EStranslations
severityalarm, warning or unknown
deviceId, instancewhich device and which instance raised it
functionCode, functionNamethe device's MUXEN function
creationTime, expireTimeISO 8601 timestamps
mutedwhether it is already silenced
filterByan existing mute filter, if any

An alarm is identified on the bus by the triple alarmCode + deviceId + instance. Republishing the same triple updates the existing alarm rather than creating a second one.

Clear it with the same triple:

sh
mosquitto_pub -h 127.0.0.1 -t nmea/alarm/clear -m \
  '{"alarmCode":4001,"deviceId":40001,"instance":1}'

Text longer than 256 characters is truncated. At most 64 alarms can be active at once.

Outbound: what goes on the bus ​

An active alarm is retransmitted continuously as PGN 126983 (Alert), at a rate set by its priority:

SeverityNMEA 2000 priorityRetransmit
—0 Emergency500 ms
alarm1 Alarm500 ms
warning2 Warning1000 ms
unknown3 Caution1000 ms
—4 Normal2000 ms

The text goes out as PGN 126985 (Alert Text) in seven languages — English, French, German, Spanish, Italian, Dutch and Portuguese — so a plotter set to any of them shows the alarm in that language.

The daemon's alert category is derived from the raising device's functionCode: 0–99 Engine, 100–199 Navigation, 200–299 Electrical, 300–399 Environmental, 400–499 Safety, 500–599 Communication, 600–699 Fuel, 700–799 Anchor, 800 and above General.

At most 8 alerts are transmitted per 500 ms tick. With more than eight simultaneous alarms, transmission rotates through them, so a high-priority alert on a saturated bus is never starved but every alert takes longer to repeat.

Inbound from the bus: silence and acknowledge ​

When an operator presses silence or acknowledge on a plotter, the plotter sends PGN 126984 (Alert Response). The daemon translates it into an MQTT request and waits for the MUXEN system to answer:

nmea/alarm/mute/request      →   {"requestId":…, "action":"silence"|"acknowledge",
                                  "duration":300, …}
nmea/alarm/mute/response     ←   {"requestId":…, "success":true, "filterBy":{…}}

Unmuting works the same way over nmea/alarm/unmute/request and nmea/alarm/unmute/response.

The daemon waits 5 seconds for a response, retries up to 3 times at 1 second intervals, and then publishes an error with code timeout on nmea/alarm/error. Up to 32 callbacks may be outstanding. The default mute duration it requests is 300 seconds.

The response must echo the requestId from the request. This is the one place where an integration has to do real work rather than just subscribe.

State changes ​

Every transition is announced on nmea/alarm/state:

json
{ "alarmCode": 4001, "deviceId": 40001, "instance": 1,
  "previousState": "active", "newState": "silenced",
  "trigger": "operator_action", "sourceAddress": 12,
  "timestamp": "2026-08-16T09:00:05Z" }

States are normal, active, silenced, acknowledged, escalated and disabled. Triggers are condition_detected, condition_cleared, operator_action, timeout, external_command and filter_expired.

Errors ​

nmea/alarm/error carries error, message, field, requestId and timestamp. The codes the daemon actually emits are:

CodeCause
invalid_jsonthe payload did not parse
missing_fielda required field was absent; field names it
alarm_not_founda clear or a response referenced an unknown alarm
timeoutno response to a mute or unmute request after 3 retries

Topic summary ​

TopicDirectionNotes
nmea/alarm/activeinraise or update an alarm
nmea/alarm/clearinclear an alarm
nmea/alarm/mute/responseinanswer to a mute request
nmea/alarm/unmute/responseinanswer to an unmute request
nmea/alarm/mute/requestouta plotter asked to silence
nmea/alarm/unmute/requestouta mute filter ended
nmea/alarm/stateoutevery state transition
nmea/alarm/erroroutrejected input, or a callback timeout

The daemon subscribes to exactly the four inbound topics. Anything published on the outbound ones by something else is ignored.

A ninth topic, nmea/alarm/list, exists in the code as a snapshot of every active alarm, but nothing currently publishes it. Do not build an integration on it. Track alarm state with nmea/alarm/state instead.

Decoding PGN 126983 on the wire ​

If you are decoding the alert off the CAN bus rather than off MQTT, note that the wire encoding and the MQTT vocabulary are not the same numbering:

Wire Alert StateValue
Disabled0
Normal1
Active2
Silenced3
Acknowledged4
Awaiting acknowledge5
Wire Alert TypeValue
Emergency Alarm1
Alarm2
Warning5
Caution8

Wire Alert Category is 0 Navigational or 1 Technical. Wire Trigger Condition is 0 Manual, 1 Auto, 2 Test, 3 Disabled. The message is 28 bytes, Fast Packet.

The category-from-functionCode mapping above is an internal classification and is a different field from the wire Alert Type. A decoder that confuses the two will misread every alert.

What is not implemented ​

Being explicit, because the NMEA 2000 alert family is larger than what this daemon does:

  • PGN 126986 (Alert Configuration) is defined but never transmitted or decoded. PGNs 126987 and 126988 are not handled at all.
  • Nothing escalates on its own. An alarm reaches the escalated state only if a device on the bus explicitly commands it. There is no unacknowledged-alarm timer.
  • alarmDescription.IT, .NL and .PT are transmitted but never read from MQTT. Only the default text and .FR, .DE, .ES are parsed, so the other three are always empty on the wire.
  • Nothing about the subsystem is runtime-configurable. Topics, timeouts, retry counts, the concurrent-alarm limit and the language set are compile-time constants.

Diagnostics ​

sh
mosquitto_sub -h 127.0.0.1 -t 'nmea/alarm/#' -v
journalctl -u muxen-nmea2000 -f | grep -i alarm
candump can1 | grep 19F007        # PGN 126983 leaving the Brain

The journal logs each alarm as it is added, each state change and each expiry:

Alarm added: code=4001, device=40001, instance=1, state=active
Alarm state change: …
Alarm removed: code=4001, device=40001, instance=1
Alarm expired: code=4001

The suspend and resume of alert transmission during CAN bus recovery is part of the general recovery behaviour — see Troubleshooting.

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