This project provides an ESPHome-based RS485 integration for Tylö / Tylo and Helo sauna heaters (Pure, Combi, Elite). It allows Home Assistant to read temperatures, states, and sensor data, and send basic commands, while running in parallel to the original control panel.
This interface was developed for and validated with the Tylö Sense Pure (Pure panel). Basic functions also worked in our tests with Combi and Elite setups, but full compatibility and feature coverage are not guaranteed.
Unofficial project. See Disclaimer & Trademarks.
Last tested with:
- ESPHome: 2025.12.4
- Framework: ESP-IDF 5.5.x
- Target: ESP32-S3
Other ESPHome versions may work, but are not guaranteed to be fully compatible. Breaking changes in ESPHome or ESP-IDF may require adjustments to this project.
This project is actively maintained. Breaking changes will be addressed as they arise. Please open an issue if you encounter problems.
This project utilizes an ESP32-S3 (ESP32-S3-WROOM-1) paired with an external RS485-to-UART interface based on the MAX485.
The door sensor used in this project is an ABUS FU7350W.
The external switch with LED feedback used in this project can be found here.
![]() |
|---|
| Lolin S3 / ESP32-S3-DevKitC-1 (no termination) | AtomS3 Lite + Tail485 (termination 68 Ω) | Touch Display (JC3248W535EN) |
|---|---|---|
|
|
|
| Pin | Signal | |
|---|---|---|
| 1 | A | ![]() |
| 2 | B | |
| 3 | 12V DC | |
| 4 | GND |
| Pos | Unit | Pins used | Comment | Pin 1 | Pin 2 | Pin 3 | Pin 4 |
|---|---|---|---|---|---|---|---|
| 1 - NTC | Temp. sensor in the room | 2, 3 | 10kΩ. May also be connected at Pos 4 - SEC/NTC. | - | 10kΩ | 10kΩ | - |
| 2 - EXT SWITCH | External switch | 3, 4 | Start/Stop. Momentary or latching switch | - | - | Switch | Switch |
| External switch with LED indication | 2, 3, 4 | Start/stop operation. 12VDC max. 40mA | - | LED GND | Switch | Switch / LED 12V | |
| 3 - DOOR SWITCH | Door contact (NO) | 3, 4 | - | - | Switch | Switch | |
| Door contact (NO) with LED indication | 2, 3, 4 | 12VDC max. 40mA. | - | LED GND | Switch | Switch / LED 12V | |
| 4 - SEC/NTC | Combined temperature sensor/cut-out in the room | 2, 3 | Only used for certain products. | Sec | 10kΩ | 10kΩ | Sec |
| 5-8 - RS485 | Control panels | 1, 2, 3, 4 | Tylö Pure control panel, ESP32 (RS485) | A | B | 12V | GND |
| Baud | Bits | Parity | Stop bit |
|---|---|---|---|
| 19200 | 8 | Even | 1 |
These details have been reverse-engineered by analyzing intercepted communication between a heater and its control panel. As such, the list may not be complete or fully accurate. Further testing and validation may be required to confirm all behaviors and formats.
| Start of Frame (SOF) | Address ID | Message Type / Origin | Data | CRC | End of Frame (EOF) |
|---|---|---|---|---|---|
0x98 |
0x40 |
0x06 |
0 Bytes | 2 Bytes | 0x9C |
0x07 |
|||||
Explanation:0x06: Sent by the heater as a Keep-Alive request.0x07: Sent by the control panel as a Keep-Alive acknowledgment.
|
|||||
| Start of Frame (SOF) | Address ID | Message Type / Origin | Code | Data | CRC | End of Frame (EOF) |
|---|---|---|---|---|---|---|
0x98 |
0x40 |
0x06 (Heater Request) |
2 Bytes | 0–4 Bytes | 2 Bytes | 0x9C |
0x07 (Panel Command) |
2 Bytes | |||||
0x08 (Heater Data) |
2 Bytes | |||||
0x09 (Panel Data / ACK?) |
2 Bytes |
| Code | Direction | Description |
|---|---|---|
0x3400 |
Heater → Panel | Light and Heater Status |
0x3801 |
Heater → Panel | Elite/Combi sensor frame (temp/RH data) |
0x4002 |
Both | Bath Time and Maximum Temperature |
0x4003 |
Both | Overheating PCB Limit |
0x4200 |
Both | Date/Time |
0x5200 |
Both | Aux relay |
0x5201 |
Both | Aux relay |
0x5202 |
Both | Aux relay |
0x6000 |
Both | Temperature Data: Current and target sauna temperature |
0x6001 |
Both | Humidity control |
0x7000 |
Panel → Heater | Request Heater Status Changes |
0x7180 |
Heater → Panel | Relay/output bitmap (physical outputs X3–X18) |
0x7280 |
Heater → Panel | Tank level |
0x9000 |
Both | Not-allowed start window |
0x9400 |
Heater → Panel | Total Uptime |
0x9401 |
Heater → Panel | Remaining Bath Time |
0xB000 |
Heater → Panel | Error Codes |
0xB600 |
Heater → Panel | Sensor Errors |
On each bus cycle the heater emits a heartbeat. Under normal conditions the panel follows ≈600 µs later. If the panel needs to issue a command, it skips that heartbeat and sends its command ≈1.6 ms after the heater heartbeat.
We transmit only after a panel EOF. To avoid collisions we wait at least one byte-time at 19 200 baud (8E1 ≈ 0.57 ms) plus a small guard on the PURE model, then start our frame (typically ~615 µs after the panel EOF).
COMBI / ELITE behave slightly differently (likely due to the optional RS-485 humidity/temperature sensor). For these models we transmit ≈7 100 µs after the panel EOF.
Measured timing to send 0x07 (Command) to heater via ESP32.
substitutions:
# — Device / Model -
device_name: "tylo"
model_name: "pure" # pure | combi | elite | combi_elite
branch_ref: "main" # main | dev
# — Area & Logging —
area_name: "Sauna"
log_level: "INFO"
# — Board / Flash (configurable) —
board_type: "esp32-s3-devkitc-1" # "esp32-s3-devkitc-1", "lolin_s3"
flash_size: "16MB" # "4MB" | "8MB" | "16MB"
rgb_led_pin: GPIO38 # devkitc-1 = GPIO38 | Atom S3 lite = GPIO35
led_brightness_connected: "30" # 0..100; 0 = OFF
# — UART pins (if wired differently) —
uart_tx_pin: "GPIO41"
uart_rx_pin: "GPIO42"
# — Defaults for bath/temperature —
default_bath_temperature: "92"
default_bath_time: "240"
default_max_bath_temperature: "110"
# — Defaults for Humidity settings —
default_humidity_step: "0" # 0..10 - COMBI
default_humidity_percent: "0" # 0..100 - COMBI_ELITE
# — Temperature limits —
min_temp_c: "40"
max_temp_c: "110"
# — Secrets from secrets.yaml —
api_encryption_key: !secret api_key_sauna
ota_password: !secret ota_pass_sauna
wifi_ssid_sub: !secret wifi_ssid
wifi_password_sub: !secret wifi_password
esphome:
name: ${device_name}
friendly_name: ${device_name}
comment: ${device_name} sauna controller
esp32:
board: ${board_type}
flash_size: ${flash_size}
framework:
type: esp-idf
external_components:
- source:
type: git
url: https://github.com/f-io/esphome-tylo
ref: ${branch_ref}
refresh: 1s
components: [ sauna360 ]
packages:
base:
url: https://github.com/f-io/esphome-tylo
ref: main
refresh: 1s
files: ["packages/base.yaml"]
led:
url: https://github.com/f-io/esphome-tylo
ref: main
refresh: 1s
files: ["packages/led.yaml"]
model:
url: https://github.com/f-io/esphome-tylo
ref: main
refresh: 1s
files: ["models/pure.yaml"] # pure | combi | elite | combi_elite# Wi-Fi
wifi_ssid: "YOUR_WIFI_SSID"
wifi_password: "YOUR_WIFI_PASSWORD"
# API & OTA for Sauna
api_key_sauna: "REPLACE_WITH_BASE64_KEY"
ota_pass_sauna: "REPLACE_WITH_STRONG_PASSWORD"This project is independent of Sauna360, Tylö and Helo and is not affiliated with, endorsed by, or approved by them. All product names, logos, and brands are the property of their respective owners. Brand names are used solely to describe interoperability/compatibility (nominative use).
Reverse engineering: Protocol details were derived from observing legitimate communication between lawfully acquired devices for the purpose of interoperability. No proprietary firmware, manuals, or copyrighted materials are distributed.
Safety/Liability: Working with heaters and mains-powered equipment is hazardous. Use at your own risk. Usage may affect compliance and may void warranty. The author and contributors assume no liability for any damages or regulatory issues. Follow local electrical and fire-safety regulations. Regulatory requirements vary by country; verify compliance locally.







