Autoterm Heater Protocol
Findings consolidated from the following community projects:
- Grimoire314 — Autoterm/Planar reverse engineering
- schroeder-robert/autoterm-air-2d-serial-control
- prclm/AutotermHeaterController (and the kalutep fork)
- Pekaway/VAN_PI (Node-RED flow)
- csreades/AutothermDieselRepeater
Autoterm heaters expose a simple binary request/response protocol over a 5V UART link. The display polls the heater periodically with a status request and a panel-temperature heartbeat, and sends control frames (start, stop, settings, ventilation) on demand.
Link Layer
| Parameter | Value |
|---|---|
| Signal level | 5V, 8N1, no flow control |
| Baud rate | 9600 or 19200 (model/firmware dependent) |
To auto-detect the baud rate, send a Status Request at each candidate rate; the one that yields a parseable reply is the configured one.
Frame Format
All frames share the same envelope:
Example: AA 03 06 00 01 01 ... 05 CC CC
│ │ │ │ │ └───────┘ └───┘
│ │ │ │ │ │ │
│ │ │ │ │ │ └── CRC-16/Modbus (big-endian)
│ │ │ │ │ └────────── Payload (length = byte 2)
│ │ │ │ └───────────────── Command ID
│ │ │ └──────────────────── Reserved (always 0x00)
│ │ └─────────────────────── Payload length
│ └────────────────────────── Direction
└───────────────────────────── Preamble (always 0xAA)
| Byte | Field | Values |
|---|---|---|
| 0 | Preamble | 0xAA |
| 1 | Direction | 0x03 = Display → Heater, 0x04 = Heater → Display |
| 2 | Payload length (N) | Bytes of payload (excludes header and CRC) |
| 3 | Reserved | Always 0x00 |
| 4 | Command ID | See Commands |
| 5 .. 4+N | Payload | Command-specific |
| 5+N .. 6+N | CRC-16/Modbus | Computed over bytes 0 .. 4+N, high byte first |
CRC-16/Modbus parameters: poly 0xA001, init 0xFFFF, no reflection of the result.
The CRC is transmitted big-endian — the high byte goes on the wire first, which is
the opposite of the on-bus byte order used by most Modbus implementations.
Responses
Every Display → Heater command receives a reply with direction = 0x04 and the
same command ID echoed in byte 4. The reply payload follows one of three
patterns: structured data (0x0F status, 0x02 read), a payload echo
of the request (0x01, 0x02 write, 0x11, partially 0x23), or empty
(0x03). See each command for the exact bytes.
Reply payload contents other than 0x0F status and 0x02 read are taken from
community reverse-engineering captures and have not been independently verified.
Commands
| ID | Name | Dir | Reply payload |
|---|---|---|---|
0x01 | Start | D → H | 6-byte settings echo |
0x02 | Settings | D → H | 6-byte settings (write: echo, read: current) |
0x03 | Stop | D → H | empty |
0x0F | Status | both | 19-byte status |
0x11 | Panel Temperature / Heartbeat | both | 1-byte echo |
0x23 | Ventilation | D → H | 4-byte partial echo |
0x01 — Start
Starts a new heating cycle from standby. The 6-byte payload is shared with Settings (0x02); see that section for the field layout.
TX AA 03 06 00 01 01 00 04 00 00 05 <CRC> ; example: power mode, level 5
RX AA 04 06 00 01 01 00 04 00 00 05 <CRC> ; heater echoes the accepted settings
State then transitions Starting → Heating on subsequent status polls.
0x02 — Settings
Two forms share command 0x02:
- Write (6-byte payload) — update parameters of a running heater without restarting it.
- Read (empty payload) — query the heater’s active configuration. The heater replies with a 6-byte payload using the same field layout.
AA 03 06 00 02 01 00 02 14 01 05 CC CC ; write: useWT=1, src=panel, target=20°C, wait=on, level=5
AA 03 00 00 02 9D BD ; read request
AA 04 06 00 02 01 00 02 14 01 05 <CRC> ; read reply (same layout)
| Payload byte | Field | Notes |
|---|---|---|
| 0 | useWT | 0x01 disables the work-timer (heater runs indefinitely) |
| 1 | Work time | Minutes of programmed run-time; only meaningful when useWT = 0x00 |
| 2 | Temperature source | Operating mode — see Temperature source |
| 3 | Target temperature | Setpoint in °C (unsigned). Ignored when tempSource = 0x04 |
| 4 | Wait mode | 0x00 = shut down at setpoint, 0x01 = idle-ventilate at setpoint and re-ignite on drop |
| 5 | Power level | 0x01 – 0x09 (1 = lowest). Used directly in power mode, and as the start power in thermostatic modes |
Polling the read form on startup recovers the active mode, setpoint, and power level after a controller restart; polling periodically detects settings changes made by another controller on the bus. OEM panels poll it every few seconds.
0x03 — Stop
Stops the heater. State transitions Stopping → Cooling → Standby over the
next polls.
TX AA 03 00 00 03 5D 7C
RX AA 04 00 00 03 29 7D ; empty-payload ack
No request payload; the reply also carries no payload.
0x0F — Status
Polls the heater for its current state and sensor readings.
Request (no payload):
AA 03 00 00 0F 58 7C
Response (19-byte payload):
AA 04 13 00 0F 03 00 00 0A 7F 00 84 01 B2 04 00 37 37 00 6D 00 6D 00 64 C3 0A
│ │ │ │ └──┴── heat exchanger temp (K)
│ │ │ └────────────────── external temp sensor
│ │ └───────────────────── internal temp
│ └─────────────────────────── state minor
└────────────────────────────── state major
| Payload byte | Field | Encoding |
|---|---|---|
| 0 | State major | See Heater state |
| 1 | State minor | See Heater state |
| 2 | Unknown | Seen 0x00 |
| 3 | Internal temperature | Signed 8-bit, °C |
| 4 | External temperature sensor | Signed 8-bit, °C. 0x7F = sensor disconnected |
| 5 | Unknown | Seen 0x00 |
| 6 | Unknown | Seen 0x84 |
| 7–8 | Heat exchanger temperature | u16 big-endian, Kelvin (°C = value − 273) |
| 9 | Unknown | Seen 0x04 |
| 10 | Unknown | Seen 0x00 |
| 11 | Unknown | Seen 0x37 |
| 12 | Unknown | Seen 0x37 |
| 13 | Unknown | Seen 0x00 |
| 14 | Fan tachometer | RPM ÷ 60 (revolutions per second) |
| 15 | Unknown | Seen 0x00 |
| 16 | Pump frequency | Hz × 100 (value 0x6D = 1.09 Hz) |
| 17 | Unknown | Seen 0x00 |
| 18 | Unknown | Seen 0x64 |
Decoding example
For the response frame above:
State: major=0x03, minor=0x00 → Heating
Internal: 0x0A = 10 → 10°C
External: 0x7F → sensor disconnected
Heat exch.: 0x01B2 = 434 → 434 − 273 = 161°C
Fan: 0x6D = 109 → 109 × 60 = 6540 RPM
Pump: 0x6D = 109 → 109 × 0.01 = 1.09 Hz
0x11 — Panel Temperature / Heartbeat
Pushes the panel-side temperature to the heater and doubles as the mandatory heartbeat. The display must send this once per second regardless of the configured Temperature source; the heater flags “no panel” after roughly three seconds of silence. The heater echoes the same frame back as an acknowledgement.
AA 03 01 00 11 1A 76 D0 ; request: panel temp = 26°C
AA 04 01 00 11 1A B6 65 ; echo
| Byte | Field | Encoding |
|---|---|---|
| 5 | Panel temperature | Signed 8-bit, °C. 0x7F signals “no panel sensor” |
When tempSource = 0x02 (panel), the byte is the value the heater regulates against.
In all other modes the byte is still required but informational — 0x7F is the
conventional choice and what OEM panels send when no panel sensor is fitted.
0x23 — Ventilation
Runs the fan only, without ignition.
TX AA 03 04 00 23 FF FF 08 FF E1 0B ; ventilation level 8
RX AA 04 04 00 23 FF FF 08 43 B6 4B ; bytes 0–2 mirror the request, byte 3 differs
| Payload byte | Field | Values |
|---|---|---|
| 0 | Unknown | Always 0xFF in the request |
| 1 | Unknown | Always 0xFF in the request |
| 2 | Ventilation level | 0x00 – 0x09 |
| 3 | Unknown | 0xFF in the request; the heater replies with a different value (purpose unknown) |
Field Reference
Temperature source
| Value | Mode | Behaviour |
|---|---|---|
0x01 | Internal sensor thermostatic | Regulates against the heater’s intake-air sensor. Inaccurate because that probe sits in the heater’s cabin air inlet, not the living space |
0x02 | Panel sensor thermostatic | Regulates against the temperature the display pushes via 0x11 |
0x03 | External sensor thermostatic | Regulates against a separate probe plugged directly into the heater harness |
0x04 | Power mode | No thermostat — heater runs at a fixed power level (1 – 9). Bytes 3 and 4 of the Settings payload are ignored |
Heater state
Encoded as a (major, minor) pair in the Status response. The major byte identifies the overall phase; the minor byte gives a sub-step.
| Major | Minor | State | Description |
|---|---|---|---|
0x00 | 0x01 | Standby | Idle, waiting for commands |
0x01 | 0x00 | Cooling | Post-shutdown cool-down (fan running, no flame) |
0x01 | 0x01 | Ventilation | Fan-only mode (after ventilation command) |
0x02 | any | Starting | Ignition sequence in progress |
0x03 | any | Heating | Stable heating |
0x04 | 0x00 | Stopping | Shutdown sequence in progress |
Timing
| Parameter | Value |
|---|---|
| Poll interval (status + heartbeat) | 1 s |
| Heater silence tolerance before “no panel” fault | ~3 s |
| Response timeout | 600 ms |
| Consecutive missed responses tolerated before link is considered down | 2 |
Operating Procedures
Startup — recover heater state
After the controller connects (or reconnects) it does not know whether the heater is in standby, mid-burn, or shutting down. To sync up:
- Send 0x11 — establishes the link and proves the baud rate.
- Send 0x0F — reveals the current operating state.
- Send 0x02 read — reveals the active mode, setpoint, and power level.
From there the controller enters the periodic-poll cadence below.
Periodic poll
Every second:
- Display → Heater: 0x11 with current panel temperature (or
0x7Fif no panel sensor) - Heater → Display: heartbeat echo
- Display → Heater: 0x0F
- Heater → Display: Status response
The 0x02 read can be re-issued every few seconds to pick up settings changes made by other controllers on the bus.
Start heating at power level 5
- Display → Heater: 0x01 with payload
01 00 04 00 00 05 - Heater → Display:
0x01reply echoing the 6-byte settings payload - Subsequent polls show state transitioning
Starting→Heating.
Change power level while running
- Display → Heater: 0x02 write with new power level in payload byte 5
- Heater → Display:
0x02reply echoing the 6-byte settings payload
Stop
- Display → Heater: 0x03
- Heater → Display:
0x03reply with empty payload - State transitions
Stopping→Cooling→Standbyover the next polls.
