-
Notifications
You must be signed in to change notification settings - Fork 49
System Setup Discovery Protocol
When the thermostat enters System Setup mode (triggered by the user at the thermostat), it temporarily switches its bus address from 0x20 to 0x1F ("SystemInit") and drives a multi-phase discovery and commissioning sequence across the RS-485 bus.
The physical layer is the standard ABCD bus: RS-485 half-duplex at 38400,8,N,1. See Infinity Protocol Hardware and Infinity Framing Protocol for background.
| Hex Addr | Device | Present | Notes |
|---|---|---|---|
0x1F |
SystemInit | Active during setup | Thermostat assumes this address during commissioning |
0x20 |
Thermostat | Active | Resumes after SystemInit phase |
0x21–0x28
|
Thermostat1–8 | Scanned, no reply | Additional thermostat / Smart Sensor slots |
0x40 |
IndoorUnit | Active | Furnace / Air Handler |
0x41 |
IndoorUnit1 | Scanned, no reply | Probed by IndoorUnit itself |
0x42 |
IndoorUnit2 | Scanned, no reply | Probed by SystemInit — "select indoor evaporator" |
0x50 |
OutdoorUnit | Scanned, no reply | Outdoor slot 0 |
0x51 |
OutdoorUnit1 | Scanned, no reply | |
0x52 |
OutdoorUnit2 | Active | Heat pump in this system |
0x54 |
OutdoorUnit4 | Scanned, no reply | |
0x60–0x61
|
ZoneControl | Scanned, no reply | |
0x80 |
NIM | Scanned, no reply | |
0x0E |
Device 14 | Scanned, no reply | Unknown class — receives 10 attempts per register (vs 2 for others) |
0x92 |
SAM | Active | See SAM ASCII Protocol |
See Infinity - known devices for the full address scheme with community-verified models.
All frames use the standard CarBus format documented in Infinity Framing Protocol:
[DST] [DST_ID] [SRC] [SRC_ID] [LEN] [00 00] [FUNC] [PAYLOAD...] [CRC16]
1B 1B 1B 1B 1B 2B 1B LEN-3B 2B
| Hex | Name | Description |
|---|---|---|
0x0b |
READ | Request to read a register |
0x06 |
REPLY | Response with register data (also used as write-ACK) |
0x0c |
WRITE | Write data to a register |
0x15 |
EXCEPTION | Error/NAK — payload 0x04 means "no such register" or device not responding |
Request: 40 01 20 01 03 00 00 0b 00 01 04 6d 7e
dst=0x40 src=0x20 len=3 func=READ reg=0x0104 CRC
Reply: 20 01 40 01 7b 00 00 06 00 01 04 [120 bytes data] 50 3a
dst=0x20 src=0x40 len=0x7b func=REPLY reg=0x0104 CRC
Replies swap the address pairs. Broadcast uses [F1 F1] as the destination pair.
| Register | Name | Purpose |
|---|---|---|
0x0104 |
device_info |
Device identification — 120-byte payload (model, serial, software version) |
0x0202 |
time |
Time broadcast (weekday, hour, minute) |
0x0203 |
date |
Date broadcast (month, day, year) |
0x0302 |
status | Indoor/outdoor unit operating status |
0x0303 |
status2 | Additional outdoor status |
0x0304 |
config | Unit configuration parameters |
0x0305 |
demand | Heating/cooling demand (written to indoor unit) |
0x0306 |
capabilities | Unit capability flags |
0x0307 |
control | Control register (written to indoor unit) |
0x030a |
config2 | Extended configuration |
0x030d |
sam_status |
SAM status — 7 data bytes: 3 sensor values + 4 reserved |
0x030e |
table_list | List of supported register table IDs |
0x0310 |
sensors1 | Sensor readings group 1 |
0x0311 |
sensors2 | Sensor readings group 2 |
0x031c |
setup_complete | Appears at end of airflow test — setup-complete / airflow-calibration register |
0x031e |
param | Parameter register |
0x0316 |
indoor_ext | Extended indoor unit data |
0x0402 |
airflow_data | Airflow calibration/measurement data |
0x0403 |
mode | Operating mode control |
0x0404 |
equipment | Equipment configuration |
0x0409 |
aux_status | Auxiliary equipment status |
0x040d |
fault | Fault/diagnostic register |
0x041e |
tstat_probe | Thermostat presence probe (scans for additional thermostats) |
0x0602 |
odu_status | Outdoor unit status |
0x0604 |
odu_config | Outdoor unit configuration |
0x0605 |
odu_demand | Outdoor unit demand (written) |
0x0608 |
odu_fault | Outdoor unit fault register |
0x060a |
odu_ident | Outdoor unit identification (compressor/coil model strings) |
0x060b |
odu_control | Outdoor unit control register |
0x060d |
odu_mode | Outdoor unit mode |
0x060e |
odu_tables | Outdoor unit performance tables |
0x0610 |
odu_setpoint | Outdoor unit setpoint |
0x0612 |
odu_param | Outdoor unit parameters |
0x061a |
odu_flag | Outdoor unit flag |
0x061e |
odu_config2 | Outdoor unit config 2 (written frequently) |
0x061f |
odu_perf | Outdoor unit performance data (float values) |
0x3005 |
sam_poll_1 | SAM bus polling register (phase 1) |
0x3003 |
sam_poll_2 | SAM bus polling register (phase 2) |
0x3405 |
nim_probe | NIM/zone controller presence probe |
0x3c0c |
sam_poll_3 | SAM bus polling register |
0x3c0d |
sam_poll_4 | SAM bus polling register |
0x3c14 |
sam_poll_5 | SAM bus polling register |
0x3e01 |
tabledef |
Table definition query (outdoor unit) |
0x3e02 |
sys_config | System configuration broadcast |
0x3e08 |
dev14_probe | Device 0x0E presence probe |
0x3e0a |
odu_probe2 | Secondary outdoor unit probe |
All timestamps are relative to T=0 = first SystemInit frame. The thermostat drives the entire discovery.
The thermostat switches to address 0x1F (SystemInit) and broadcasts initialization:
[T+0.0s] SystemInit -> Broadcast WRITE 0305 (demand=all zeros)
[T+0.1s] SystemInit -> Broadcast WRITE 3e02 (sys_config=0x00)
[T+0.1s] SystemInit -> Broadcast WRITE 0605 (odu_demand=all zeros)
This triplet repeats every ~1.3 seconds as a heartbeat throughout the SystemInit phase.
SystemInit probes IndoorUnit2 (0x42) — "Select indoor evaporator":
[T+0.5s] SystemInit -> 0x42 READ 030d — no reply (2 attempts)
[T+0.7s] SystemInit -> 0x42 READ 031b — no reply (2 attempts)
[T+0.8s] SystemInit -> 0x42 READ 0404 — no reply (2 attempts)
This probe cycle for 0x42 repeats ~4 times. Each register gets exactly 2 attempts before moving on. SystemInit then deep-probes 0x42 with registers 030a, 0403, 0407, 040b, 040c, 040f, 0410, 0411, 0413, 0414 — all no reply.
SystemInit reads known IndoorUnit (0x40):
[T+1.1s] SystemInit -> 0x40 READ 0104 → reply (device_info)
[T+1.3s] SystemInit -> 0x40 READ 0306 → reply
[T+1.5s] SystemInit -> 0x40 READ 030a → reply
[T+3.6s] SystemInit -> 0x40 READ 0402, 0403, 0409 → replies
The thermostat (back as 0x20) scans for all possible devices.
Outdoor unit slot scan:
[T+11s] Thermostat -> 0x50 READ 3e01 — 2x, no reply
[T+12s] Thermostat -> 0x51 READ 061f — 2x, no reply
[T+14s] Thermostat -> 0x50, 0x51, 0x54 READ 0104 — 2x each, no reply
Device 0x0E scan (receives 10 attempts per register — significantly more than other addresses):
[T+11s] Thermostat -> 0x0E READ 3e08 — 10 attempts, no reply
[T+13s] Thermostat -> 0x0E READ 3e01 — 10 attempts, no reply
[T+13s] Thermostat -> 0x0E READ 3e0a — 10 attempts, no reply
NIM scan:
[T+12s] Thermostat -> 0x80 READ 3405 — 2x, no reply (4 rounds)
Indoor unit sub-unit scan (initiated by IndoorUnit itself):
[T+21s] IndoorUnit(0x40) -> 0x41 READ 030a — no reply (6 attempts)
Zone controller scan — "Zoning: No zones found":
[T+31s] Thermostat -> 0x60, 0x61 READ 3405 — 2x each, 4 rounds through T+40s
Additional thermostat scan (sequential):
[T+31s] Thermostat -> 0x21..0x28 READ 041e — 2x each, 2 rounds
The thermostat displays "Filter type", "Humidifier", "UV lights", "Summary" screens. Traffic is sparse — the thermostat waits for user input. Periodic polling of IndoorUnit and OutdoorUnit2 continues at reduced frequency. No new device scanning.
Register 0x031c first appears — this is a setup-complete / airflow-calibration register not present during earlier phases:
[T+294s] Thermostat -> IndoorUnit WRITE 031c payload=0c0000000000000000000000
[T+294s] Thermostat -> OutdoorUnit2 WRITE 031c payload=440000000000000000000000
"Done" appears on screen at ~T+300s.
Normal polling resumes with 0x031c now included in the regular write cycle.
| Device | Probe Register | Probe Address(es) | Attempts per Addr | Retry Rounds |
|---|---|---|---|---|
| IndoorUnit | 0x0104 |
0x40 |
1 (gets reply) | 1 |
| IndoorUnit1 | 0x030a |
0x41 |
6 | 2 |
| IndoorUnit2 |
0x030d,0x031b,0x0404,0x0104
|
0x42 |
2 each | ~4 cycles |
| OutdoorUnit |
0x3e01,0x3e0a,0x0104,0x030a
|
0x50 |
2 each | 2 |
| OutdoorUnit1 |
0x061f,0x0104,0x030a
|
0x51 |
2 each | 1 |
| OutdoorUnit2 | 0x0104 |
0x52 |
1 (gets reply) | 1 |
| OutdoorUnit4 |
0x0104,0x030a
|
0x54 |
2 each | 1 |
| SAM |
0x0104,0x030d
|
0x92 |
3 | continuous |
| ZoneControl | 0x3405 |
0x60,0x61
|
2 | 4 rounds |
| NIM | 0x3405 |
0x80 |
2 | 4 rounds |
| Device 0x0E |
0x3e08,0x3e01,0x3e0a
|
0x0E |
10 each | 2 |
| Thermostat1-8 | 0x041e |
0x21–0x28
|
2 each | 2 rounds |
- IndoorUnit2 (
0x42) — T+0.5s (SystemInit probes, no reply) - IndoorUnit (
0x40) — T+1s (gets reply) - OutdoorUnit (
0x50), OutdoorUnit1 (0x51), OutdoorUnit4 (0x54) — T+11-14s - Device
0x0E— T+11-13s - NIM (
0x80) — T+12-24s - IndoorUnit1 (
0x41) — T+21s (probed by IndoorUnit, not thermostat) - ZoneControl (
0x60,0x61) — T+31-40s - Thermostat1-8 (
0x21–0x28) — T+31-38s
IndoorUnit (0x40), OutdoorUnit2 (0x52), and SAM (0x92) are already known from pre-setup steady-state polling and are not re-discovered.
After setup, the thermostat repeats this cycle approximately every 4–6 seconds:
┌─ READ 0104 from IndoorUnit ──────────── device identification
├─ READ 0104 from OutdoorUnit2 ─────────── device identification
├─ READ 0306 from IndoorUnit ──────────── capabilities
├─ READ 030a from IndoorUnit ──────────── configuration
├─ READ 0104 from SAM (3x on NET bus) ── SAM identification
├─ READ 0302 from OutdoorUnit2 ────────── status
├─ READ 0303 from OutdoorUnit2 ────────── status2
├─ READ 0604 from OutdoorUnit2 ────────── ODU config
├─ READ 0608 from OutdoorUnit2 ────────── ODU fault
├─ READ 060e from OutdoorUnit2 ────────── ODU tables
├─ READ 061f from OutdoorUnit2 ────────── ODU performance
├─ WRITE 061e to OutdoorUnit2 ─────────── ODU config2
├─ WRITE 0610 to OutdoorUnit2 ─────────── ODU setpoint
├─ WRITE 0612 to OutdoorUnit2 ─────────── ODU param
├─ WRITE 0605 to OutdoorUnit2 ─────────── ODU demand
├─ WRITE 061a to OutdoorUnit2 ─────────── ODU flag
├─ WRITE 060d to OutdoorUnit2 ─────────── ODU mode
├─ WRITE 061e to OutdoorUnit2 ─────────── ODU config2 (repeat)
├─ WRITE 060b to OutdoorUnit2 ─────────── ODU control
├─ READ 0404 from IndoorUnit ──────────── equipment config
├─ READ 040d from IndoorUnit ──────────── fault register
├─ WRITE 0305 to IndoorUnit ───────────── demand
├─ WRITE 0403 to IndoorUnit ───────────── mode
├─ WRITE 0409 to IndoorUnit ───────────── aux status
├─ WRITE 0307 to IndoorUnit ───────────── control
├─ READ 030d from IndoorUnit ──────────── sam_status (returns zeros)
├─ READ 030d from OutdoorUnit2 ────────── sam_status (returns zeros)
├─ READ 030d from SAM ─────────────────── sam_status (returns sensor data)
├─ READ 0316 from IndoorUnit ──────────── extended data
├─ READ 0402 from IndoorUnit ──────────── airflow data
├─ READ 0409 from IndoorUnit ──────────── aux status
└─ [Exception burst to SAM on NET bus]
| Register | Payload (hex) | Notes |
|---|---|---|
0x061e |
0f 3f 40 00 00 06 00 00 00 |
Config — written twice per cycle |
0x0610 |
00 00 00 00 |
Setpoint = 0 (idle) |
0x0612 |
22 b8 00 00 00 00 00 00 00 00 |
Parameter |
0x0605 |
00 00 00 00 01 00 00 |
Demand with flag byte |
0x061a |
01 |
Single flag byte |
0x060d |
00 |
Mode = off |
0x060b |
01 04 63 00 00 00 00 |
Control register |
The SAM sits on the same RS-485 bus as all other devices (see Infinity Protocol Hardware). It continuously polls the thermostat and replies to reads addressed to 0x92 throughout the setup process.
The SAM cycles through these registers, polling the thermostat approximately every 65ms:
| Register | Approx. Duration | Notes |
|---|---|---|
0x3c14 |
2-10s blocks | Most frequent |
0x3c0d |
2-8s blocks | |
0x3c0c |
2-8s blocks | |
0x3005 |
2-5s blocks | |
0x3003 |
1-3s blocks |
Register 0x0104 (device_info):
Offset Len Field
0 3 Register header: 00 01 04
3 50 Device name (ASCII, null-padded)
53 16 Software version (ASCII)
69 20 Model number (ASCII, null-padded)
89 12 Serial number (ASCII)
101 24 Reference (ASCII, null-padded)
Total payload: 125 bytes (3 header + 122 data). Frame LEN = 0x7b (123).
Register 0x030d (sam_status):
Offset Len Field
0 3 Register header: 00 03 0d
3 1 val1 (sensor reading)
4 1 val2
5 1 val3
6 4 Reserved (always 0x00)
The thermostat sends exception frames (func=0x15, payload=0x04) addressed to the SAM when it has no data to report. These are not errors — they are "nothing to report" signals. The SAM should silently ignore them.
Pattern: bursts of 1-35 frames at ~67ms spacing, always identical:
92 01 20 01 01 00 00 15 04 66 2a