Follow-up to Atlantic Aurea 5 Hybrid in Home-assistant
Last post i said: rip out the second MCU, buy a DIYless slave board for the thermostat. The board arrived and became my test rig, but it never went in.
Turned out the control box already had everything — two OpenTherm interfaces, three relays, an 18 V rail. Only the firmware was missing. So there are now two chips in there: an ESP32 in IC1 doing 666 baud to the outdoor unit, and my own ATmega328P in IC2 sitting between the thermostat and the gas boiler.
What that second chip was
Not a front panel controller. A two-sided OpenTherm bridge — slave to the thermostat, master to the boiler — that rewrites a handful of Data-IDs on the way through. That’s where Atlantic decides whether your gas boiler gets a real flow temperature or a token 10 °C meaning “stay off”.
Full disassembly of both chips: here. Best find: a hard 5 °C lockout in the other chip, hardcoded, EEPROM empty. That’s the exact temperature mine kept handing over to gas at. No service menu was ever going to fix that.
The build
- ATmega328P-PU, DIP-28. Same pinout as the original ATmega8, drops right in.
- No crystal needed — there’s already an 8 MHz one under that socket.
- No ISP. Burn it in a TL866, push it in. Fuses
FF/D9/FD. - No wires. The traces between the two sockets are already there, crossed, RX to TX. Whoever laid that board out expected these two to talk.
- Prebuilt hex is in the repo, so no toolchain required.
Four things the disassembly didn’t tell me
I had the original firmware instruction by instruction and still got it wrong four times:
- The timer reload isn’t portable.
0xFC3Ais 966 cycles, hand-tuned to their ISR overhead. Copy it into a longer ISR and you get complete frames with broken parity — which reads exactly like a wiring fault. Use CTC withOCR1A = 999. - Measure edge intervals, don’t sample. Sampling mid-bit looks equivalent and isn’t; you lose the self-sync that makes Manchester robust.
- The two sides aren’t built the same. Boiler side is an opto (drive high). Thermostat side is a discrete high-side driver on 18 V (drive low), read through the analog comparator. That’s why the original reads one with
SBISand the other withSBIC. Normalise them and one side goes dead. - Never stay silent. The thermostat is master and wants an answer to everything. The original has an Unknown-DataId fallback for exactly this. And if you don’t know a value, send Data-Invalid, not 0 — a modulating thermostat reads 0 °C boiler water as ice cold and asks for full blast.
The evening that wasn’t a firmware problem
Boiler side decoding perfectly, thermostat side nothing. I rewrote the receiver twice before checking the obvious.
Thermostat and boiler were on each other’s terminals. And the DIP switches were off — position 2 goes to pin 25, and high there shuts down the entire boiler side in the original logic. Nothing on the enclosure tells you this.
Set it to ON. Learn from me.
Bonus find: the emergency bridge
Two of the three relays are driven together and swing the thermostat’s whole wire pair straight across to the boiler. That’s Atlantic’s own “what if the controller dies” answer, and it’s in my firmware now: ESP32 goes quiet, the AVR pulls those relays itself. Works when Home Assistant is down, the network is down, or i brick the ESP32 mid-OTA. House stays warm, i find out later.
(All three coils share one 100 Ω dropper, so only one relay may pull at a time. That interlock is in there too.)
Then i made it a dumb pipe
First version sent the ESP32 a summary: eight values the AVR unpacked itself. Everything else it read, used, and threw away. Every Data-ID i wanted later would have cost another burn.
So now every frame goes through raw, in both directions, plus what the bridge makes of them. Adding a sensor is one line of YAML:
lambda: 'return id(otlink)->get_ot(28);' # boiler return temperature
And the other way round too, because one-way isn’t a bridge: the ESP32 can tell the AVR to send any frame to the boiler, or to answer the thermostat with any value it likes.
The line i drew: the AVR keeps what has to work when the ESP32 is gone. Timing, passing frames through, never staying silent, the emergency bridge, the five-minute anti-cycling. Substitution stays down there too — it happens mid pass-through and a round trip over the link would blow the OpenTherm timing. But what gets substituted comes from above. Everything else is policy, and policy belongs where you can change it without a programmer.
The bug that only bites once
The bridge notices the boiler is gone when a reply doesn’t come, and starts answering the thermostat itself. But a reply only comes if you ask, and the asking happened in the branch that no longer ran. So once the boiler dropped out while a thermostat was talking it could never come back — not even when it had been sitting there answering for hours.
Every “is X still there” flag needs a path that can switch it back on, and that path must not live behind the flag itself.
What it actually does now
The thermostat computes a flow temperature and asks for it over ID 1. That’s the same unit the heat pump takes, so no translation and no heating curve — it already did that work. What i added is the hybrid decision, in three stages:
| boiler gets | when | |
|---|---|---|
| idle | 10 °C | heat pump is coping |
| assist | 35 °C, settable | pump has fallen short for N minutes |
| free | whatever the thermostat asks | room keeps dropping anyway |
Falling short is measured, not guessed: the pump is at max stage and more than 2 °C under its own setpoint. That timer only runs while that’s actually true, and resets the moment it isn’t.
And gas is locked out above a settable outside temperature — Atlantic’s 5 °C lockout turned upside down. They forced gas below 5 °C; here it’s forbidden above a line you draw yourself.
That lockout leans on the unit’s own outside sensor, which raised the question whether ID 2 is ambient air or an evaporator probe. It’s ambient: over 350 hours it reads ~2.5 °C above a second thermostat, and that gap doesn’t move when the compressor runs. An evaporator would drop away under load.
What i get out of it
- The brake — hand the boiler a 10 °C setpoint so it sits still, while the thermostat carries on normally. Hot water, faults and diagnostics keep working.
- Standalone answering when the boiler isn’t talking, heat pump as source.
- What the thermostat actually asked for, before the brake touches it. That’s the number that says whether the heat pump could’ve done it alone.
Everything’s on the esp32-volledige-controlbox branch: config, both components, AVR source, hex, wiring, fuses.
One practical note: don’t overwrite the original chips. Keep them and the box goes back to factory in two minutes, which matters if you still have warranty, need a service visit, or sell the house. That’s the real reason for a fresh 328P — more than the extra flash.
Still open: the fault code table, and a heating season. It’s 28 °C outside, so none of that policy has run under actual demand yet.
Usual disclaimer: mains-adjacent hardware, possibly still under warranty, reverse-engineered protocol. Your call.


