While cleaning up my Grafana dashboards I noticed that my Viessmann Vitoladens 300-C cycles way too often: 17-52 burner starts per day, averaging just 9 minutes per run. That's way too short for an oil condensing boiler. So I dug deeper and found a massive bug in my vcontrold config in the process. Since 2022 I'd been reading and writing heating circuit 1 instead of heating circuit 2. All my heating curve changes went nowhere, all the logged values were phantom data.
I've been logging the heating system via vcontrold since 2019, and ever since the data has been flowing into InfluxDB so I can see in Grafana what the system is doing. Always worked, or so I thought.
How I found the bug
I changed the heating curve slope from 1.4 to 1.6 on the heating display as a test. vcontrold kept reading 1.2. That can't be right - either vcontrold is reading wrong or the display is showing garbage. So I checked the vito.xml: all the heating circuit commands were using 2xxx addresses (heating circuit 1). But my system only has heating circuit 2 with mixer M2. That one runs on 3xxx addresses.
My tests with a temporary config confirmed that the HK2 addresses deliver exactly the display values. The HK1 addresses showed frozen defaults. Since 2022 I'd been logging ghost data.
Viessmann Optolink address scheme
Heating circuits have different address ranges:
| Heating circuit | Address range | Curve | Operation | Timer |
|---|---|---|---|---|
| HK1 (no mixer) | 2xxx | 27D3/27D4 | 2323 | 2000-2030 |
| HK2 (with mixer) | 3xxx | 37D3/37D4 | 3323 | 3000-3030 |
| HK3 (mixer M3) | 4xxx | same pattern | same pattern | same pattern |
Global addresses (boiler, hot water, solar, burner) stay at 0xxx, 5xxx, 6xxx, 7xxx, 8xxx.
The big address correction
Moved all heating circuit commands from HK1 to HK2:
| Command | Old (HK1) | New (HK2) |
|---|---|---|
| KennlinieNeigung | 27D3 | 37D3 |
| KennlinieNiveau | 27D4 | 37D4 |
| Betriebsart | 2323 | 3323 |
| RaumNorSoll | 2306 | 3306 |
| RaumRedSoll | 2307 | 3307 |
| VLSoll | 2544 | 3544 |
| Timer Mo-So | 2000-2030 | 3000-3030 |
On top of that I checked the OpenV reference XML and found even more broken addresses:
| Sensor | Old | New | Problem |
|---|---|---|---|
| Speicherladepumpe | 6513 | 6513 | Stays correct (0845 was wrong, reverted) |
| Zirkulationspumpe | 6515 | 6515 | Stays correct (0846 was wrong, reverted) |
| Brennerstatus | 55DE | 0842 | Never showed ON |
| DrehzahlPumpeIntern | 7663 (CO) | 7663 (PC) | Unit correction |
Sensors that constantly returned 20°C or ? I removed (WWAuslauf, RaumHK, Umschaltventil, BrennerStufe). The system doesn't have those sensors.
The cycling problem
With the correct addresses I could finally see real data. Analysis of the last few months:
| Outside temp | Avg starts/day | Avg runtime/day | Avg min/start |
|---|---|---|---|
| -5° to 0° | 52 | 9.9h | 11.5 min |
| 0° to 5° | 45 | 7.7h | 10.2 min |
| 5° to 10° | 34 | 5.1h | 9.1 min |
| 10° to 15° | 17 | 2.5h | 9.2 min |
9 minutes per start is too short. Target for condensing operation: 20+ minutes. The live logs showed that during the shoulder season the cycling mainly comes from hot water heating, not from the heating circuit. The burner heats the boiler (55L volume) from ~48°C to 65-70°C for hot water, quickly reaches the cut-off hysteresis, and switches off. Then the next cycle starts.
Coding address 9F: the game changer
Research in Viessmann community threads turned up: coding address 9F controls the differential temperature between the boiler and the heating circuit with mixer. The factory value is 8K, so the boiler heats 8°C above the flow setpoint. At 9F=8 the mixer never opens all the way. The heat stays in the 55L boiler instead of flowing into the radiators.
So I set 9F to 0. That way the mixer is fully open at burner start, full flow runs through the radiators. The entire radiator and wall mass acts as a heat sink and the burner runs longer.
Experiences from the forum showed that this drops the starts by 22% (31->24/day) and increases the runtime by 21% (5.7->6.9 min).
Accessing coding level 2
Coding level 2 on the Vitotronic 200 display:
- Menu + OK simultaneously for ~2 sec. -> coding level 1
- Menu + OK simultaneously again -> coding level 2 (service)
- Use up/down to pick the coding address -> OK -> change value -> OK
Groups: 00=scheme, 02-33=boiler, 55-75=hot water, 7F-95=general, A0-FF=heating circuit (9F lives here).
All changes from April 16, 2026
| Setting | Before | After | Effect |
|---|---|---|---|
| 9F (cod.2) | 8 | 0 | ~20% fewer starts |
| Curve slope | 1.4 | 1.0 | Lower flow temp |
| vcontrold addresses | HK1 (2xxx) | HK2 (3xxx) | Real values |
| Flame address | 55DE | 0842 | Burner status visible |
| Sampling | 3 min | 1 min | Capture cycling better |
On top of that I lowered the heating curve slope from 1.4 to 1.0. Lower flow temperatures mean less oil and less aggressive cycling.
Outlook
Now I'm collecting 2-3 days of data with 9F=0 and slope 1.0. If the burner starts drop below 25/day and the runtimes climb above 12 minutes, it worked. In winter I'll then also test coding addresses C5, C6 and 06 to further limit the flow and boiler temperature.
The big lesson: always cross-check the display values against the logged values. Otherwise you spend 4 years optimizing ghost data.
