NEWS
Wolf boilers on ebusd: solved — read and write
-
Wolf boilers on ebusd: the checksum is solved — read and write every register
TL;DR — The mystery byte in a Wolf read telegram is a CRC8 with polynomial
0x5cover the
low byte of the TelegramNr, XORed with the high byte. That was the last thing standing between
Wolf's own published parameter database and a complete ebusd config. You no longer need to
copy magic bytes out of somebody else's08.csv. There is a script below that generates a
config for any of 48 Wolf device templates. Writes work too, and the write format is
different from the read format in a way that explains why so many people'swrows do nothing.Tested on a Wolf FGB-K-28 (ebusd 25.1, eBUS shield, ioBroker). 22 of 22 generated IDs are
byte-identical to a hand-made config that has worked for years, plus 30 new registers that were
never readable before. Please try it on your CGB / CGB-2 / CGG-2 / COB / CHA / BM-2 and report
back — the derivation is device-independent but I can only prove it on the hardware I own.
1. Why this has been hard
Wolf publishes no eBUS documentation. Every Wolf config in the wild is hand-made, partial, and
propagated by copy-paste.--scanconfigcan never help: a Wolf boiler answers the scan with
MF=65; ID=" &", ebusd does not know manufacturer0x65, and the ID looks like line noise.Reads are
ZZ=08,PBSB=5022, three ID bytes:r,heating,dhw_temp,,,08,5022,cc0e00,... ^^^^^^ ?? 0e 00 0x000e = 14 = TelegramNrThe last two bytes are obviously the TelegramNr little-endian. The first byte is the problem.
It is different for every register, the forums call it "the CRC", and the accepted workaround has
been: if nobody has published the byte for the register you want, you cannot read that register.2. The checksum
It is linear over GF(2), which is what makes it recoverable. Take known-good (byte, TelegramNr)
pairs out of any working Wolf08.csv, treat each output bit as a linear equation over the 16
index bits plus a constant, and solve. On 22 verified pairs that is heavily over-determined and
gives a unique answer, whose bit-effects are exactly:function wolfCrc(telegramNr) { let c = telegramNr & 0xff; for (let i = 0; i < 8; i++) c = (c & 0x80) ? (((c << 1) & 0xff) ^ 0x5c) : ((c << 1) & 0xff); return (c ^ (telegramNr >> 8)) & 0xff; } const hx = n => n.toString(16).padStart(2, '0'); const readId = tn => hx(wolfCrc(tn)) + hx(tn & 0xff) + hx((tn >> 8) & 0xff);A CRC8 chain with polynomial
0x5c, init 0, over the LOW byte only — then the HIGH byte is
XORed in and never shifted. That last part is why it is not any textbook CRC8 and why
table-driven guesses fail.Checks that matter:
- It reproduces 22 of 22 IDs in a config that has worked for years.
- The linear solve leaves index bits 10–15 unconstrained (no training sample used them, since
all known registers were < 1024). The poly-0x5cform pins them as identity on the high byte.
That extrapolation is confirmed on real hardware: TelegramNr 10010 (high byte0x27, far
outside the training set) generates771a27and reads 1.80 bar of system pressure on my
boiler. Same for 10013, 10067, 10068, 65019. - TelegramNr 554 and 644 both produce
de— a genuine collision, because0x2aand0x84shift
to the same value. The high byte disambiguates them. If you have been confused by two registers
sharing a first byte, that is why.
Bonus: running this over an existing config is a good way to find typos. It flagged one in
mine —hg74hadd5where the algorithm says91. It had never worked and nobody knew why.3. Writes — and why yours probably do nothing
Writes use a different address and a different ID format:
Read Write Destination ZZ=08(slave)ZZ=03(master)PB SB 50225023First ID byte the checksum 00— writes are not checksummedPayload — value, then a 4-byte suffix 9d010000w,heating,dhw_target_set,Warmwassersolltemperatur eingestellt,,03,5023,001300,dhw_target_set,,SIN,10,°C,,suffix,,HEX:4,=9d010000,,Two things people get wrong:
- The destination is the master address
03, not08. I had this recorded as "the write
rows are mis-addressed" — that was wrong, and it had simply never been tested. Sending the same
write toZZ=08givesERR: read timeout. ebusctl writeprintingemptyis SUCCESS, not an error. A Wolf write is a master-master
telegram with no data response.emptyis what a successful one looks like.
Proof, on a deliberately harmless register (
hg07, heating pump run-on, valid range 0–30 min):$ ebusctl read -m 0 -c heating hg07 2 $ ebusctl write -c heating hg07 3 empty $ ebusctl read -m 0 -c heating hg07 3And the one that matters — the DHW setpoint, TelegramNr 19, write ID
001300:before: 45.0 empty after: 50.0 live: 50.0 <- TelegramNr 3, the derived setpoint, followedTest writes on something harmless first.
hg07orhg09are ideal: in-range values change
nothing you will notice, and they read straight back.4. Getting the register list for your Wolf device
You do not have to guess which TelegramNrs exist. Wolf's own SmartSet/ISM7 metadata is public
insidezivillian/ism7mqtt—parameter.xml,
converter.xmlanddevice.xmlundersrc/ism7mqtt/Resources/. Between them they give, for
every Wolf device: TelegramNr, data type, unit, min/max, and the value labels for enums.wolf-registers.js(attached) downloads them and does the join:$ node wolf-registers.js --list DTID WRSDeviceIds params Name 30000 0x18 80 BM 220000 0x20 139 BM-2 80000 0x6 45 CGB 180000 0x21 97 CGB-2 100000 0x1D 53 CGG-2 110000 0x1C 53 CGU-2 270000 0x29 178 CHA 90000 0x1A 49 COB 250000 0x26 64 FGB ...48 templatesWhich one are you? Look at your ebusd scan string. Mine says
MF=65; ID=" &". Those ID bytes
are0x20 0x26— and&is0x26, which is exactly theWRSDeviceIdsof theFGB
template. The "garbage" ID is not garbage; it contains your device id. Match it against the list.$ node wolf-registers.js --device FGB TelegramNr readID writeID Type Unit Range Name 13 280d00 000d00 SS10 °C Kesseltemperatur 14 cc0e00 000e00 SS10 °C Warmwassertemperatur 19 541300 001300 SS10 °C 30..80 Warmwassersolltemperatur eingestellt 270 cd0e01 000e01 SS100 0..30 Heizkurve 10010 771a27 001a27 SS100 bar Anlagendruck ... $ node wolf-registers.js --device FGB --csv --write > 08-generated.csvType mapping:
SS10→SINdivider 10,SS100→SINdivider 100,US10→UINdivider 10,
US→UIN,SS→SIN.5. Things that cost me hours
ebusctl read -honly works for messages ebusd ALREADY KNOWS. It is not a raw frame sender.
An unknown ID returnsERR: element not foundwithout touching the bus. So you cannot use it to
probe for new registers, and "a wrong checksum is rejected" proves nothing about your boiler — it
only proves ebusd's message table did not match.ebusctl hexandread -defneed--enablehex/--enabledefine. Off by default.Validate a config offline, as a normal user, before installing it:
ebusd --checkconfig --configpath=/home/you/ebustest/ --device=ens:127.0.0.1:59999Copy your config tree somewhere writable, edit, check. It prints
found messages: N (... M poll)
and exits 0 without touching the running daemon or the bus.Do not force-read many registers back to back. I ran 32 sequential
read -m 0calls and
roughly every other one came back empty, with the symbol rate spiking to 102. It looks exactly
like "this register is unsupported" and it is not. Read the poll cache instead —
ebusctl find -a -c heating— giving it a couple of minutes afterebusctl reloadto refill.Adding a write row whose name matches an existing read row does NOT increase
messages:.
ebusd merges them into one message with two directions. Check withebusctl find -w -c heating,
not the count. I briefly misread this as a rejected row.Polling more costs nothing. I went from 28 to 60 polled messages and the bus symbol rate
stayed at 12, exactly as before. Poll priority (r1…r9) controls how often each is polled,
not how much traffic there is in total.Values reading
32768or3276.8are the eBUS null sentinel — "this register does not exist
on this device". Six of mine had been dead for years. Their TelegramNrs (39, 321, 362, 365, 368)
simply are not in the FGB parameter list; they had been copied from a different Wolf model. No
config change will ever fix those, and now you can tell the difference between "wrong byte" and
"not present" by checking the generated list.6. ioBroker integration
MQTT. ebusd's own MQTT integration publishes reads fine:
--mqttint=/etc/ebusd/mqtt-integration.cfg --mqtthost=127.0.0.1 --mqttport 1883 ...Note it ships
filter-direction = r|u, i.e. read-only — it will not accept writes. To write
from a script, shell out:exec('ebusctl write -c heating dhw_target_set 50', () => { exec('ebusctl read -m 0 -c heating dhw_target_set', (e, out) => log('now ' + out)); });Always read back and verify. A write that silently does nothing is the failure mode here.
Poll priority gotcha. If you use the Home Assistant MQTT profile,
filter-seen = 5silently
promotes matched messages to poll priority 5. Remove it and everything goes tono data stored
because nothing is polled any more. Declare priority in the CSV instead (r5,heating,...).Two scripts are attached:
boiler_status_en.js— turns the raw enum numbers into readable states under
0_userdata.0.heating.boiler.*:status,mode,burner_status,firing,fault,alive,
plus a one-linesummarylikeFiring at 28% — Heating mode.faultis the one worth
alarming on: true whenburner_statusis 12 (Störung) or the safety limiter has tripped.
alivewatches staleness across several polled registers, because the boiler powers the eBUS —
so a dead bus means a dead boiler.boiler_aliases.js— a stablealias.0.boiler.*layer so renaming an ebusd message does not
break every widget and chart. It refuses to create an alias whose source does not exist and
tells you which ones to fix.
Useful enum decodings (from Wolf's own
KeyValueList, FGB — check yours with the tool):burner_status 0 Aus · 1 Ruhekontakt · 2 Vorspülen · 3 Zünden · 4 Stabilisierung · 5 Ein · 6 Ventilprüfung · 7 Nachspülen · 8 Softstart · 9 Taktsperre · 10 Betrieb ohne Brenner · 11 Abgasklappe · 12 Störung · 13 Gradienten Überwachung · 14 Gasdruck · 15 Spreizung hoch · 16 Spreizung KF operating_mode 0 Test · 1 Start · 2 Frost Heizkreis · 3 Frost Warmwasser · 4 Schornsteinfeger · 5 Kombibetrieb · 6 Parallelbetrieb · 7 Warmwasser · 8 Warmwassernachlauf · 9 Mindest-Kombizeit · 10 Heizbetrieb · 11 Nachlauf Heizkreispumpe · 12 Frostschutz · 13 Standby · 14 Kaskadenbetrieb · 15 GLT-Betrieb · 16-19 Kalibration · 20 WW Schnellstart · 21 Externe Deaktivierung program (10100) 1 Aus · 3 Winterbetrieb · 5 Sommerbetrieb TelegramNr 370 bit 3 = Brenner TelegramNr 371 bits 0,1,2,4,5 = Zündung, Ventil 1, Ventil 2, Kesselkreispumpe, 3-Wege-Ventil7. Safety
This is a gas appliance. A few things I would not do:
- Do not write to a register you have not looked up. The generated table gives the valid
range; stay inside it. Writing outside a documented range is not something I have tested and
not something I would. - Prove writes on a harmless parameter first (
hg07,hg09), read back, restore. - Do not try to make ebusd the heating controller. Emulating the controller broadcast means
repeating it forever, and if your automation dies in winter so does your frost protection. For
simple on/off demand a relay across the room-thermostat contact fails safe; this does not. - Back up
08.csvboiler_aliases.js boiler_status_en.js wolf-registers.js before every change and keepebusd --checkconfigin the loop. - Writes change settings in the boiler, persistently. They survive a reboot of everything
else. Write down what you changed.
8. What it got me
A Wolf FGB-K-28 that exposed 28 registers, six of them permanently dead, now exposes 60 — at
identical bus load. New and working:Anlagendruck (system pressure, bar) · Abgastemperatur (flue gas) · Modulationsgrad ·
Brennerstatus (17 states) · Betriebsart (20 modes) · Brenner, Zündung,
Ventil 1/2, Kesselkreispumpe, 3-Wege-Ventil (six real digital signals) ·
Drehzahl Kesselkreispumpe / ZHP WW · Heizkurve · Firmware · Anlagendruck ·
Auslauftemperatur Warmwasser · Sicherheitstemperaturbegrenzer · Anzahl Netz-Starts ·
and roughly fifteen more.Plus working writes, which turned into an automated weekly DHW disinfection cycle and a
vacation setback.Three errors found in a config that had been running for years: a wrong checksum on
hg74,
hg75typed as minutes when TelegramNr 364 is litres per minute (it is the DHW flow rate),
and six registers that do not exist on this device at all.
Files
wolf-registers.jsgenerates the register table / ebusd CSV for any Wolf device boiler_status_en.jsioBroker: decoded status states + fault/alive flags boiler_aliases.jsioBroker: stable alias.0.boiler.*layerCredit
The parameter database is zivillian/ism7mqtt's extraction
of Wolf's SmartSet metadata — without it none of this is possible. ebusd is
john30/ebusd. The forum threads that got me oriented are
ebusd discussion #779 and #721.Corrections welcome, especially from anyone who can confirm or refute the checksum on a non-FGB
Wolf device.
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden