Appearance
Getting started
Prerequisites
On the Brain:
- An MQTT broker on
127.0.0.1:1883. The daemon connects to it at startup and exits if the connection fails — it does not wait for a broker that is not there yet. The unit orders itselfAfter=mosquitto.serviceand restarts every 30 s, so a broker that comes up late is tolerated, but a broker that never comes up leaves the daemon in a restart loop. can0up, unless you run the daemon in--mqttmode. The daemon binds a raw CAN socket to the interface named by--interfaceand fails to start if that interface does not exist.- A boat configuration file, by default
/etc/muxen/deploy.json. This package does not install one; it is the boat's deployment file, shared with the other MUXEN daemons. Without it the daemon exits at startup. mosquitto-clientsif you want to watch the output by hand.
Nothing else. The daemon holds no state of its own, writes no files, and opens no sockets other than the CAN socket and the MQTT connection.
Install
sh
sudo apt install muxen-energyThe package carries the muxen-energy daemon, its systemd unit and its bash completion. It depends on muxen-systemd, which provides the muxen.target / muxen-deploy.target pair the unit hangs off, and on muxen-sensors (>= 5.0.0), whose topic prefix it reads. muxen-boat is only recommended: a boat can be entirely CAN-fed and needs no daemon translating CAN frames onto device/….
Start it
The unit is WantedBy=muxen-deploy.target, so it comes up with the rest of the MUXEN stack and needs no enabling by hand after install:
sh
systemctl status muxen-energyThe unit runs:
/usr/bin/muxen-energy --interface can0 --energy ${MUXEN_DEPLOY}with MUXEN_DEPLOY=/etc/muxen/deploy.json as its built-in default, overridable with a drop-in (systemctl edit muxen-energy).
The interface is not an environment variable any more. It appears literally in ExecStart and in the unit's BindsTo=/WantedBy= device dependency, which systemd resolves before any environment exists. Changing it means a drop-in that updates both:
ini
[Unit]
BindsTo=
BindsTo=sys-subsystem-net-devices-can1.device
After=sys-subsystem-net-devices-can1.device
[Service]
ExecStart=
ExecStart=/usr/bin/muxen-energy --interface can1 --energy ${MUXEN_DEPLOY}The empty BindsTo= and ExecStart= are required: without them systemd appends rather than replaces.
The unit is also PartOf=muxen-deploy.target, so systemctl restart muxen-deploy.target restarts the daemon along with every other daemon that reads the deployment file. The muxen-systemd package watches /etc/muxen/deploy.json and restarts that target when the file changes — which is how a configuration change reaches the daemon. The configuration is read once, at startup, and never re-read.
Verify it is running
The daemon logs its whole startup to the journal, and that log is the main commissioning tool. Four things are worth reading, in order:
sh
journalctl -u muxen-energy -n 601. The configuration it actually used.
config: verbose = 0
config: sensors = /etc/muxen/deploy.json
config: data source = can
config: can interface = can0
config: filter period = 302. The zones it built. One line per zone, with the name it resolved:
zones: 4 zones
zones: 48V BD
zones: 48V TD
zones: 24V
zones: ACA zone shown as Zone 3 rather than a real name means no EnergyZoneName3 setting was found — see Energy zones.
3. What it subscribed to. The dispatcher tables are printed unconditionally, one row per subscription:
can: dispatcher table size: 20
can: canId canMask canDlc name
can: 0x001140 0xFFFFFF 8 battery
can: 0x001141 0xFFFFFF 8 battery
...
mqtt: dispatcher table size: 0The MQTT table holds one regex per configured sensor variable — none, on the boat above. In --mqtt mode the two tables swap: the devices move into the MQTT table as one regex each, and the CAN table is empty.
A table size of 0 on the side you expect means no device was matched — usually because nothing in the configuration carries a BindToParc, or because every device in it is of a type this daemon does not consume.
4. The broker connection.
mqtt: connectedSee the output
sh
mosquitto_sub -h 127.0.0.1 -t 'app/energy/#' -vOne retained message per zone, republished every second. The daemon sends it compact, on one line; it is shown indented here, as --json-pretty would publish it:
json
{
"id": 1,
"name": "48V BD",
"data": {
"capacity": 280.0,
"voltage": 54.2,
"temperature": 41.0,
"soc": 80.0,
"socValid": true,
"powerUsed": 6937.6,
"autonomy": 2.24,
"chargingTime": 0.0,
"charge": 28.0,
"discharge": 128.0,
"chargeBattery": 0.0,
"dischargeBattery": 100.0,
"chargeGroup": 0.0,
"chargeMotor": 0.0,
"dischargeMotor": 80.1,
"chargeSolar": 28.0,
"chargeWindTurbine": 0.0,
"chargeHydro": 0.0,
"chargeConverter": 0.0,
"dischargeConverter": 20.0,
"chargeOther": 0.0,
"dischargeOther": 27.9
},
"metadata": {
"rxdate": "2024-01-16T15:35:56.000Z",
"rxTimestamp": 1705419356,
"expireAfterSec": 5
}
}Currents are amperes, voltage volts, temperature degrees Celsius, soc percent, capacity amp-hours, powerUsed watts, autonomy and chargingTime hours. Every field is explained in The energy balance.
Messages are retained. The last payload of each zone stays on the broker after the daemon stops, so a screen showing plausible-looking numbers is not by itself proof that the daemon is alive. Compare metadata.rxdate against the clock, or watch the topic for a second.
The minimum configuration
The daemon reads three things out of the deployment file, and publishes nothing useful without them.
1. Bind each device to a zone. Every entry in devices[] whose parameters[] carry a BindToParc joins that zone; anything else falls into zone 1.
json
{
"function": 5,
"instance": 0,
"parameters": [
{ "name": "Capacity", "value": "280" },
{ "name": "BindToParc", "value": "1" }
]
}2. Give each battery a Capacity, in amp-hours. The zone capacity is the sum over its batteries, and without it autonomy and chargingTime stay at zero — the arithmetic divides a capacity that is zero.
3. Name the zones. A setting called EnergyZoneName<N> supplies the name published for zone N:
json
{ "name": "EnergyZoneName1", "value": "48V BD" }That is enough for a zone to publish. Converters that straddle two zones and currents that come from an analogue sensor need the rest of Energy zones.
Running it by hand
Useful while commissioning, on a Brain where the service is stopped:
sh
sudo systemctl stop muxen-energy
muxen-energy --interface can0 --energy /etc/muxen/deploy.json -v-v adds the zone tree — every zone with its variables and devices, each with its resolved function name and, for converters, which side of the converter this zone is on. -vv additionally dumps every CAN frame and every MQTT message the daemon receives, which is a lot of output on a live bus.
Without a CAN bus at all, take the data from MQTT instead:
sh
muxen-energy --mqtt --energy /etc/muxen/deploy.json -vIn that mode the daemon opens no CAN socket and subscribes to the device/<function>/<instance>/… topics instead.
Where to go next
- How zones are assembled, and every configuration key — Energy zones
- What each published number means — The energy balance
- When something does not work — Troubleshooting
- The exhaustive lookup surface — Reference
