Press "Enter" to skip to content

Hacking the LEDA LUC2 Fireplace: Reading CAN Bus Data with ESP32 and ESPHome

Ever since I installed a LEDA LUC2 fireplace, I wanted deeper insights into how it operates — particularly pressure difference, exhaust temperature, and ventilation state. Unfortunately, the vendor doesn’t provide any integration options, and the only external interface is a mysterious RJ12 port on the controller.

So I decided to reverse engineer it myself.

In this post, I’ll walk you through how I decoded the CAN bus communication of the LEDA LUC2, used an ESP32 with an MCP2515 module to read the values, and made them available in Home Assistant using ESPHome. Even better: the method works for any CAN-based system.

Step 1 – Sniffing the CAN Bus

I started by simply listening to the bus without sending anything. The goal was to see what kind of messages the LUC2 sends when it’s running.

The RJ12 port on the controller exposes a CAN High / CAN Low line, so I used an MCP2515 CAN transceiver paired with an ESP32 to tap into it passively.

Required Hardware

Step 2 – Wiring it Up

Here’s how I wired the ESP32 to the MCP2515 over SPI:

MCP2515 PinESP32 Pin
VCC3.3V
GNDGND
CSGPIO16
SCKGPIO22
MOSIGPIO21
MISOGPIO17
INTNot used

Then, I connected the CAN_H and CAN_L from the MCP2515 to the RJ12 port using a breakout cable. On the LEDA LUC2, the pinout of the RJ12 is:

RJ12 PinSignal
Pin 112V (used to power the ESP32 via buck converter)
Pin 2unused
Pin 3unused
Pin 4CAN-L
Pin 5CAN-H
Pin 6GND

⚠️ Note: Several users (including me, in early tests) accidentally swapped CAN-H and CAN-L, which results in no data being received. Make sure to get the polarity right.

Step 3 – Logging Raw CAN Frames

Once everything was connected, I flashed this minimal ESPHome YAML just to see what the fireplace was sending:

spi:
  clk_pin: GPIO22
  miso_pin: GPIO17
  mosi_pin: GPIO21

canbus:
  - platform: mcp2515
    cs_pin: GPIO16
    bit_rate: 125KBPS
    on_frame:
      - can_id: 0x28A
        then:
          - logger.log:
              format: "Raw frame: %s"
              args: [x]

When I powered up the fireplace, I immediately started seeing messages on CAN ID 0x28A. From here, I started dumping frames and inspecting the payloads over time.

Step 4 – Decoding the Messages

After collecting enough frames, I started to spot patterns:

  • Messages always 8 bytes
  • Byte 0 = message type
  • Message Type 0x00 = sensor values
  • Message Type 0x01 = status info

Example frame (Type 0x00):

00 03 E1 32 00 00 00 00
  • Byte 0: 00 → Type 0
  • Byte 1: 03 → Pressure value
  • Byte 2: E1 → Modifier / condition byte
  • Byte 3: 32 → Temperature (in °C)

I decoded pressure using this logic:

float pressure = x[1] * 0.1f;
if (x[2] == 0x81) {
  pressure += 25.5f;
}

The result? Actual pressure difference values like 26.5 Pa or 29.1 Pa. Same with exhaust temperature — a direct value in °C from Byte 3.

Step 5 – Integrating Into Home Assistant

I extended the ESPHome config to publish proper sensors:

sensor:
  - platform: template
    name: "Pressure Difference"
    id: pressure_difference_sensor
    unit_of_measurement: "Pa"
  - platform: template
    name: "Exhaust Temperature"
    id: exhaust_temperature_sensor
    unit_of_measurement: "°C"

binary_sensor:
  - platform: template
    name: "Ventilation Active"
    id: ventilation_status_sensor

And the full on_frame decoding logic:

on_frame:
  - can_id: 0x28A
    then:
      - lambda: |-
          if (x.size() == 8) {
            uint8_t type = x[0];
            if (type == 0x00) {
              float pressure = x[1]*0.1f + (x[2]==0x81 ? 25.5f : 0);
              float temp = x[3];
              id(pressure_difference_sensor).publish_state(pressure);
              id(exhaust_temperature_sensor).publish_state(temp);
            } else if (type == 0x01) {
              bool vent = (x[5] == 0x01);
              id(ventilation_status_sensor).publish_state(vent);
            }
          }

As soon as ESPHome connects to Home Assistant, you’ll see these as regular sensors — ready for dashboards or automations.

Step 6 – Automation Ideas

Here are a few things I did once the data was available in Home Assistant:

  • Low exhaust temperature alert: Push notification when fire needs reloading
  • Auto-ventilation: Trigger fans based on pressure + ventilation status
  • Trends: Long-term statistics and graphs of fireplace performance

Step 7 – Applying This to Other CAN Devices

This process works for any CAN device — not just LEDA:

  1. Sniff traffic using ESPHome and log raw frames
  2. Decode byte structure manually (you’ll get better with practice)
  3. Create sensors for useful values

Whether you’re working on solar inverters, EV chargers, or even cars, the approach is the same.

How Can You Discover Relevant CAN IDs on Other Devices

When applying this to other CAN systems, here’s a step-by-step process:

1. Log All CAN Traffic

Start with a generic logger in ESPHome or Arduino:

on_frame:
  - then:
      - logger.log:
          format: "ID: 0x%03X -> %s"
          args: [id, x]

Let it run for a few minutes and save the output.

2. Identify Repeating IDs

Look for:

  • Periodic messages (same ID every second)
  • Changing payloads (values that vary with time, temperature, input, etc.)
  1. Matching value changes to real-world actions
  2. Decoding payload bytes manually
  3. Iterating and testing

Final Thoughts

Reverse-engineering the LEDA LUC2 turned into a fun little weekend project — and now I have full visibility into my fireplace from anywhere. No cloud, no vendor lock-in, no guessing when to reload wood.

Everything I used is published here:

🧠 ESPHome config: 👉 https://gist.github.com/JavanXD/696d026ef202a7d6455ed4745df63e39

Enjoy the fire. 🔥

Leave a Reply

Your email address will not be published. Required fields are marked *