Appearance
The device catalogue
Everything the boat says arrives on one family of MQTT topics, and every topic names the piece of equipment it came from. This chapter explains how that naming works and what a payload looks like; the pages it links to, one per device model, list every field of every frame.
For the owner and the crew, the useful idea is small: each box on board has a kind and a number. The kind is a function code — 5 means a battery, 8 means a lighting module, 10 means a solar charger. The number is the instance: the first battery is 0, the second is 1. Together they make the address that appears in every topic and on every screen that reports a fault.
For an integrator this chapter plus the pages below are the whole interface. There is nothing else to read: the daemon has no API beyond these topics.
Connection
| Parameter | Value |
|---|---|
| Host | 127.0.0.1 — the daemon connects to the local broker |
| Port | 1883 (MQTT over TCP) |
| WebSocket port | 1884, proxied by nginx at /ws/boat for web clients |
| TLS | none |
| Client ID | muxen-boat |
| Keepalive | 5 s |
Those values come from src/init.c (the daemon's MQTT context) and config/mosquitto-boat.conf (the listeners the package installs).
sh
# Watch everything on the broker
mosquitto_sub -h 127.0.0.1 -p 1883 -t '#' -v
# Watch all device traffic
mosquitto_sub -h 127.0.0.1 -t 'device/#' -vTopic format
device/<function>/<instance>/<subtopic>| Element | Meaning |
|---|---|
<function> | the CAN function code, taken from the source address of the frame — the table below |
<instance> | which device of that kind, 0-based |
<subtopic> | the frame name (life, state, …), or command for an inbound control message |
Most devices publish a life frame — the periodic "here is what I am doing" message — plus one or more state frames carrying the slower or less central values. The subtopic names are per device model and are listed on each page.
Four topics do not follow the plain form:
| Topic | Direction | Purpose |
|---|---|---|
device/<function>/<instance>/error/<errorCode> | published | Diagnostic Trouble Code, see common |
device/<function>/<instance>/reset | command | generic reset, any function |
device/<function>/<instance>/rtr/<frameId> | command | generic Remote Transmission Request, any function |
device/interface, system/time, app/boat/info | published | the daemon's own topics, see system |
Payload envelope
Every payload decoded from a CAN frame is wrapped the same way:
json
{ "data": { /* frame fields */ }, "metadata": { "rxdate": "...", "rxTimestamp": 0, "expireAfterSec": 0 } }| Metadata key | Type | Notes |
|---|---|---|
rxdate | string | reception time, ISO-8601 UTC with milliseconds: YYYY-MM-DDTHH:MM:SS.mmmZ. Lowercase rxdate on all CAN-derived topics |
rxTimestamp | int | reception time, Unix epoch seconds |
expireAfterSec | int | how long the value stays meaningful; past that age a consumer must treat it as stale |
counter | int | some life frames carry the CAN rolling counter here; noted per frame on each page |
expireAfterSec is the field that matters operationally. It is per frame, not global — 5 seconds for wind data, 30 for most equipment, a year for identity frames like uid — and it is how a screen tells "the battery is at 12.8 V" from "the battery was at 12.8 V before it stopped answering". The daemon does not delete a topic when a device goes quiet; the consumer is expected to compare rxdate against system/time.
The
device/interfacetopic predates this convention and uses a capitalrxDate(see system). The TypeScript client normalises both ontorxdate.
Numbers decoded from CAN carry a fixed precision — 0.1 V, 0.1 A, 4 decimals for an angle in radians — and are serialised rounded to that many decimals. Each page states the precision per field.
Enumerated fields are emitted twice: a human-readable string under the field name, and the raw integer under <name>Code. A consumer should switch on the Code and display the string, because the string is the part that may be reworded.
The service runs with --json-plain, so payloads on a live Brain are compact single-line JSON. The examples in the catalogue are shown formatted for reading.
Generic commands
Two commands work on every function code. Both take an empty payload — the topic carries everything:
sh
# Reset the device at function 3, instance 2
mosquitto_pub -h 127.0.0.1 -t 'device/3/2/reset' -m ''
# Ask the device at function 3, instance 5 to send frame id 2 now
mosquitto_pub -h 127.0.0.1 -t 'device/3/5/rtr/2' -m ''reset sends a diagnostic reset request to that device. rtr sends a Remote Transmission Request for one frame id, which is how a client gets a value immediately instead of waiting for the next periodic broadcast — useful for the slow frames, and for filling a screen at load time.
Per-device commands go to device/<function>/<instance>/command with a JSON body, and are documented on each device's page. Sixteen function codes accept them; the rest are read-only. There is no reply topic and no acknowledgement: a command either produces a CAN frame or is dropped, and the confirmation is the device's next published frame.
A command that the daemon cannot encode is discarded silently at the default log level. Running the daemon with -v prints the reason — an out-of-range channel, a conflicting pair of flags, a payload with no actionable field. Troubleshooting lists the messages.
Malformed input is safe: an empty payload, a truncated payload or invalid JSON is rejected by the parser and ignored.
The catalogue
One page per device model. <i> is the instance in every topic below.
| Code | Device | Commands | Page |
|---|---|---|---|
| any | Common broadcast frames | — | common — the frames any device can send whatever its function: Diagnostic Trouble Codes, device UID, boot flag, communication shutdown |
| 0 | Button / control panel | ✓ | button — physical button panel: which of its 8 buttons are pressed, the colour and blink state of its 8 LEDs, and a command that simulates a press |
| 1 | Bloc8 power outputs | ✓ | bloc8 — 8-channel power distribution block with per-channel current sensing, short-circuit and not-connected detection, dimming and bus voltage |
| 3 | Power source interconnection | ✓ | interco — battery interconnection and parallel-bus manager: per-input state with over-current and over-voltage flags, and the current through the interconnection |
| 4 | Power source genset | ✓ | group — generator/group manager: run state, oil and water temperature alarms, the no-start and no-cut-off interlocks, RPM and output power, with start / stop commands |
| 5 | Battery | ✓ | battery — battery monitor: voltage, current, state of charge, temperature, charge request, charge and discharge limits, cycle count |
| 6 | Converter (inverter/charger) | ✓ | converter — inverter/charger: charger and inverter state on both the output and the input side, AC input selection and per-input current limits |
| 7 | Motor | ✓ | motor — propulsion motor controller: state, voltage, current, temperature, signed RPM, and drive / regeneration / regulator commands with a setpoint |
| 8 | Lighting | ✓ | lighting — 6-channel lighting controller: per-channel on/off and dimming level |
| 10 | Solar MPPT | ✓ | solar — solar charge controller: charger state and off-reason, panel voltage/current, regulator temperature, and the full charger parameter set |
| 11 | Wind turbine (EOL) | ✓ | wind-turbine — wind generator: state, output voltage and current, temperature, rotation speed, and an on / off / automatic command |
| 12 | Navigation instruments | — | navigation — wind (relative and true), heading, speed, distance and time to destination, and instrument time |
| 16 | IMOCA keel (team access) | — | keel-imoca — canting-keel system: sensor presence and lockouts, keel angle and ram pressures, capacitor bank, wing angles |
| 17 | Generic I/O | ✓ | generic-io — general-purpose I/O module: two relays, two digital inputs, two analogue inputs, and a per-channel relay command |
| 20 | Watermaker | ✓ | watermaker — desalinator: production state, salinity, flow, filter and high-pressure readings, pump and valve states, and mode commands |
| 21 | HVAC | ✓ | hvac — air conditioning unit: measured temperature and setpoint, fan speed, heating/cooling/dehumidify mode, and a two-field command |
| 22 / 23 | SFSP receiver / interface | — | sfsp — wireless switch system: the receiver reports how many switches it emulates, the interface reports its 8 button states and its signal strength |
| 24 | Magic trim | — | magictrim — trim actuator: motor presence and rotation, RPM, current, voltage, and port/starboard pressures |
| 25 | Remote battery switch | ✓ | remote-switch — remote main switch: contactor and injection state, connected battery count, injected current, and force/auto commands |
| 26 | CGS (generator set) | — | cgs — generator set controller: contactor, drive, safe-stop and fault flags, motor and controller temperatures, torque, load and electrical power |
| 27 | Alternator | ✓ | alternator — alternator regulator: charge state, working mode, voltage, current, RPM, temperature and load, with on / auto / boost commands |
| 28 | Thermal engine | — | thermal-engine — combustion engine interface: RPM, load and torque, hour meter, coolant, oil and fuel readings, transmission gear |
| 30 | Battery concentrator | — | battery-concentrator — bank-level battery reporting: state, charge request, voltage, current, state of charge, temperature, battery type, and charge/discharge limits |
| 32 | Bow thruster | ✓ | bow-thruster — bow thruster: running direction, thrust percentage, voltage, current, hour meter, temperature, azimuth angle |
| — | Daemon topics | — | system — system/time and device/interface: the Brain's clock and the CAN link's own health |
The function code is also what makes a device's numeric id: a MUXEN device id is function × 64 + instance, which is the form the diagnostic tooling uses. The MQTT topics use the two parts separately.
Which frames reach the UDP output
When the daemon is started with --udp, a subset of the decoded frames is also emitted as NMEA-0183-style sentences over UDP, in addition to — never instead of — the MQTT publication. The subset is fixed in src/dispatcher-can.c and covers bloc8, motor, battery, solar, converter, navigation, interconnection, button, group, lighting, IMOCA keel (except the wing frame), generic I/O, wind turbine and SFSP.
Everything else — HVAC, watermaker, magic trim, CGS, alternator, thermal engine, bow thruster, remote switch, battery concentrator, the common broadcast frames and the daemon's own topics — is MQTT only. See Reference.
