Tobias Reithmeier

Blog

LG Therma V and Home Assistant: Modbus via ESP32

Part 3 of the series "Smart Home in Practice". Part 2 told why the heat pump is moving from the cloud to the local hub. This one is the implementation; Part 4 covers the register hunt, Part 5 the diagnostics.

The good news first: the indoor unit of the LG Therma V has a port intended for external controllers, and behind it the unit speaks Modbus RTU. The bad news: the machine follows none of the published register maps, and three inconspicuous details decide whether a single byte flows at all. This part walks once from the empty terminal to live values in Home Assistant. The complete firmware covering everything described here is ready on GitHub.

One more clear word up front: this work happens inside the indoor unit, at your own risk, and can affect warranty and statutory guarantees. The unit is powered down for it, and the data terminals sit on a separate terminal block away from mains voltage. If you do not feel confident, hand it to a qualified installer.

The shopping list

  • Waveshare ESP32-S3-RS485-CAN for around 35 euros, an ESP32 board with an RS485 transceiver already soldered on. Any other ESP32 board with an RS485 transceiver works too.
  • A short wire pair and a USB power supply.
  • ESPHome on your computer, the open-source firmware that turns a YAML file into a finished Home Assistant device.

The wiring: terminals 21 and 22, not 28 and 29

The correct port sits on terminal block 2 of the indoor unit, labelled "3rd Party Controller, 5 V DC": terminal 21 is A, terminal 22 is B.

The first trap waits right next to it: terminals 28/29 are also labelled A/B, but they lead to the outdoor unit, and nothing answers there. If you see both labels in front of you, take the pair on the block with the 5 V label.

Two DIP switches matter: SW1-1 ON, SW1-2 OFF. This contradicts the common advice in forums. It follows the manual, which describes ON as the "unified open protocol". With SW1-2 ON, however, this unit goes completely silent: measured four times, across 247 addresses and six baud rates. The switches are only read at boot; after every change the indoor unit must be power-cycled. And SW1-3 has no function at all according to the manual, so leave it alone.

The three pitfalls

Three things together cost more time than all the rest, which is why they come before the configuration:

1. flow_control_pin is mandatory. The RS485 transceiver on the Waveshare board does not switch between sending and receiving by itself. Without the line, the board stays mutely in receive mode. It never sends a single byte, and no error points at it.

2. The slave address is 1. The control panel may show 33 as the central address in its menu. On this port, the unit only answers on address 1.

3. force_new_range: true on every register. ESPHome automatically merges neighbouring registers into block requests, and that grouping changes with every edit of the register list. On this unit, that shifted values between neighbouring registers and produced plausible but wrong readings for weeks. Part 4 tells that story; the lesson lives in every register definition.

The configuration

The skeleton of the ESPHome configuration is compact, with UART, Modbus and one controller:

uart:
  tx_pin: GPIO17
  rx_pin: GPIO18
  flow_control_pin: GPIO21   # without this line the board never transmits
  baud_rate: 9600
  stop_bits: 1

modbus:

modbus_controller:
  - id: therma_v
    address: 1               # not the 33 from the control panel menu
    update_interval: 30s

Every reading follows the same pattern. Here is the outdoor temperature from input register 12:

sensor:
  - platform: modbus_controller
    modbus_controller_id: therma_v
    name: "Outdoor temperature"
    register_type: read
    address: 12
    value_type: S_WORD
    filters:
      - multiply: 0.1
    force_new_range: true    # pitfall 3

The full configuration covers all 37 answering data points, a ready-made climate entity for the operating mode, the computed diagnostics from Part 5 and the mapping tools from Part 4. Flashing takes one command:

esphome run therma-v.yaml

Home Assistant then discovers the device on its own; a Lovelace dashboard with operation, refrigerant circuit and controls ships as YAML in the repository.

What the unit offers and what it does not

After the mapping, exactly 34 data points answer. The most important ones:

PointMeaningAccess
Flow, return, hot water, room, outdoortemperaturesread
Suction gas temperature, high and low pressurerefrigerant circuitread
Apparent powercompressor loadread
Operating mode (off / cool / heat / auto)HR26write
Heating circuit and hot water setpointsHR24 / HR29write
Silent modecoil 2write
Hot water enablecoil 6write

Not reachable through this port: heating circuit 2 in any form, water pressure and, particularly unfortunate, the flow rate. Without the volume flow there is no thermal power calculation and therefore no COP; Part 5 shows the substitute.

One quirk about bus load: the unit answers at a leisurely pace, around 625 milliseconds per request. 37 individual requests take about 23 seconds, so at a 30-second polling interval the bus is already busy three quarters of the time. If you add more registers, raise the interval with them.

The cliffhanger

Which leaves the question of how you know that register 12 is the outdoor temperature in the first place, when the unit follows no published map and the documentation is silent. The answer is a discipline of its own: systematically mapping unknown registers without ever writing blindly. That is Part 4.

Sources