Controlling a Fairland Pool Heatpump eliminating Tuya

Glad it worked for you. No beer needed. Seeing that my little documentation helped others is more than enough.

I could also need some help unfortunately.
Im using a ESP32 and a RS485 adapter

and I have used your yaml code from GitHub @rstcologne
ESP32 boots up correctly, web interface works - but I don’t get any data from the Fairland pump and I can’t communicate with it.

I have tried to first flip the RX/TX cables between the RS485 and the ESP32 but I got a massive amount of modbus errors that then disappeared when I moved them back to the original RX/TX ports.

I have also tried to change position of the A and B cabled from the RS485 to the fairland pump but that doesn’t make any difference… I still get the same loggs in the web interface.
This is how it looks like. I don’t get any good readings of temperature as an example. I have tried to flip power switch on and off and I can see in the log that the ESP32 is trying to send the command OFF/ON - but nothing is happening.
Any suggestions on how I should move on with troubleshooting?

ok so in my attempts to get this working I suspected that maybe the fairland pump don’t use the modbus address 0001 so I took some help with ChatGPT and built another ESP32 to a modbus scanner and scanned the pump on all modbus addresses.
I got no respons on any channel, which is very strange. So I plugged in the original wifi unit and that works - so the pump works as it should.

could anyone be kind to test the modbus scanner config so I can make sure that it works?

esphome:
  name: modbus-scanner
  friendly_name: Modbus Scanner

  on_boot:
    priority: -10
    then:
      - script.execute: scan_addresses

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  encryption:
    key: "***********"

ota:
  - platform: esphome
    password: "*********"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Modbus-Scanner Fallback Hotspot"
    password: "**************"

captive_portal:

uart:
  id: mod_bus
  tx_pin: TX
  rx_pin: RX
  baud_rate: 9600
  stop_bits: 1
  parity: NONE

modbus:
  id: modbus1
  #flow_control_pin: GPIO4  # Change to your wiring

modbus_controller:
  - id: scanner_device
    address: 1
    modbus_id: modbus1
    update_interval: never  # we'll trigger updates manually

# A simple test register to read. Change to an input register if needed.
sensor:
  - platform: modbus_controller
    modbus_controller_id: scanner_device
    name: "Scanner Test Register"
    id: test_reg
    register_type: holding     # try 'input' if your device exposes only input regs
    address: 0x0000            # change to a known-good register if you have one
    value_type: U_WORD
    accuracy_decimals: 0
    on_value:
      then:
        - lambda: |-
            ESP_LOGI("scanner", "✅ Found responding device at address %d", id(current_addr));

globals:
  - id: scan_index
    type: int
    restore_value: no
    initial_value: '1'          # start at address 1
  - id: current_addr
    type: int
    restore_value: no
    initial_value: '1'

script:
  - id: scan_addresses
    then:
      - logger.log: "🔍 Starting Modbus address scan (1..247)..."
      - while:
          condition:
            lambda: 'return id(scan_index) <= 247;'
          then:
            - lambda: |-
                id(current_addr) = id(scan_index);              // remember for logging
                id(scanner_device).set_address(id(current_addr));
            - component.update: scanner_device                  # poll controller (reads test_reg)
            - delay: 300ms
            - lambda: 'id(scan_index) += 1;'
      - logger.log: "✅ Scan finished."

I guess that next step is to try a different 485 board to see if that solves it…
anyone know if there is a way to test and verify it?
thanks

Hi Everyone,

I am a solar installer and Sigenergy Inverters can switch on SG Ready heat pumps using their DO port as described here: https://drive.google.com/file/d/1PLkny0YojKDxslZx-K8HUSDfTHJV6z7v/view?usp=sharing. Can anyone advise which port on the heat pump control board the DO1 and DO2 wires should connect to? I am guessing the BAG +12V port and specifically the DO1 would go to A and the DO2 would go to B but I wonder if anyone has any experience with this? Or should they be going to a different port altogether - perhaps what is labelled as remote controller on this wiring diagram

Hallo Alex ,
ich heiße Roman, bin auch aus Deutschland und habe auch die selbe Wärmepumpe wie du. Ich heize auch den Haus mit der Pumpe nur die Wärmepumpe hat zu viel Leistung und schaltet nach zu oft aus , meine Idee ist Leistung von der Pumpe zu regeln anhand Kompressor Frequenz . Hast du vielleicht eine Idee wie ich das realisieren kann?

Hallo, das Problem habe ich auch regel das zurzeit über den Modbus mit Super Silence (max 28% )oder Silence (Max 80%).
Läuft gut aber schöner wäre das direkt die Kompressorfrequenz zu steuern.

Ich war eine Zeitlang bei den China Anbietern unterwegs um informationen zu bekommen.

Bin aber aus familiären zeitlichen Gründen noch nicht weiter gekommen.

Ich habe noch ein paar Modbus Register bekommen direkt für den DC Inverter. Bringt aber nicht viel den Kompressor zu regeln wenn das EEV von der Hauptplatine bei mir MWH 298 geregelt wird.

Aber sobald ich mehr zeit habe, werde ich mich hier auch wieder melden.

LG

Ich habe zwar für ein Leistungstärkeres Board diese Modbus Informationen bekommen.
Aber es sollten die gleichen sein.
So könnte man ja den Kompressor direkt ansteuern wenn man sich zwischen Hauptplatine und DC board klemmt. Den Modbussteckplatz sieht man ja in der PDF.

I did receive these Modbus details for a more powerful board,

but they should be identical.

This would allow the compressor to be controlled directly if you tap in between the main board and the DC board.

The Modbus connector can be seen in the PDF.

DC Inverterboard IPM Modul Modbus Register

Thanks for the info in this topic!

I have a Fairland Inver-X Vertical heatpump and added a m5stack atom lite + tail485. Imo this is a really easy solution as you only need 1 cable to connect everything.
This is what I used:

I followed this guide to configure the atom using home assistant ESPHome Builder: https://docs.m5stack.com/en/homeassistant/light/atom_lite_light

Then I installed this Yaml on the esp32. This one is giving all relevant sensor data. Temperatures are in Celsius. Edit the HIDDEN strings to correspond with your data.

esphome:
  name: warmtepomp-fairland
  friendly_name: Fairland Inver-X

esp32:
  board: m5stack-atom
  flash_size: 4MB
  framework:
    type: esp-idf

logger:
  baud_rate: 0  # Disabled — UART needed for Modbus

api:
  encryption:
    key: "HIDDEN"

ota:
  - platform: esphome
    password: "*****************"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Fairland Fallback Hotspot"
    password: "HIDDEN"

captive_portal:

# --- UART for RS485 (Tail485 pins) ---
uart:
  id: mod_bus
  tx_pin: GPIO26
  rx_pin: GPIO32
  baud_rate: 9600
  stop_bits: 1
  parity: NONE
  data_bits: 8

modbus:
  id: modbus1
  uart_id: mod_bus

modbus_controller:
  - id: fairland
    address: 0x01
    modbus_id: modbus1
    update_interval: 10s
    command_throttle: 100ms   # 100ms between requests, well above 60ms minimum

# ============================================================
# BINARY SENSORS
# ============================================================
binary_sensor:

  # --- Atom Lite Button ---
  - platform: gpio
    pin:
      number: GPIO39
      mode: INPUT
      inverted: true
    name: "Atom Button"
    id: atom_button
    filters:
      - delayed_on: 50ms
      - delayed_off: 50ms
    on_multi_click:
      - timing:
          - ON for at most 0.8s
          - OFF for at most 0.5s
          - ON for at most 0.8s
          - OFF for at least 0.2s
        then:
          - logger.log: "Double Clicked"
          - light.turn_on:
              id: atom_light
              red: 100%
              blue: 50%
              green: 20%
              brightness: 50%
      - timing:
          - ON for at least 0.8s
        then:
          - logger.log: "Single Long Clicked"
          - light.turn_on:
              id: atom_light
              green: 100%
              blue: 50%
              red: 30%
              brightness: 100%

  # --- Warmtepomp status ---
  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Status ON/OFF"
    register_type: discrete_input
    address: 0
    force_new_range: true

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Ontdooien Actief"
    register_type: discrete_input
    address: 1
    force_new_range: true

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Storing / Beveiliging"
    register_type: discrete_input
    address: 16
    force_new_range: true

# ============================================================
# LED
# ============================================================
light:
  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    pin: GPIO27
    num_leds: 1
    chipset: SK6812
    name: "Atom RGB Light"
    id: atom_light
    restore_mode: RESTORE_DEFAULT_OFF
    effects:
      - random:
          name: "Random"
          transition_length: 1s
          update_interval: 1s

# ============================================================
# SENSORS (Input Registers FC04)
# Temperature type 1: °C = (raw - 60) / 2 → filters: multiply 0.5, offset -30
# Temperature type 2: °C = raw / 2         → filters: multiply 0.5 only
# All values arrive in HA already converted to °C
# ============================================================
sensor:
  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Compressor Snelheid"
    register_type: read
    address: 0
    force_new_range: true
    unit_of_measurement: "%"
    state_class: measurement
    value_type: U_WORD

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "PFC Spanning"
    register_type: read
    address: 2
    force_new_range: true
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
    value_type: U_WORD

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Inlaatwater Temperatuur"
    id: pwp_inlet_water_temperature
    register_type: read
    address: 3
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Uitlaatwater Temperatuur"
    register_type: read
    address: 4
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Buitentemperatuur"
    register_type: read
    address: 5
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Gas Uitlaat Temperatuur"
    register_type: read
    address: 6
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5  # Type 2 — no offset

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Buitenspoel Temperatuur"
    register_type: read
    address: 7
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Gas Retour Temperatuur"
    register_type: read
    address: 8
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Binnenspoel Temperatuur"
    register_type: read
    address: 9
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5
      - offset: -30

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Compressor Frequentie"
    register_type: read
    address: 10
    force_new_range: true
    unit_of_measurement: "Hz"
    state_class: measurement
    value_type: U_WORD

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Compressor Stroom"
    register_type: read
    address: 11
    force_new_range: true
    unit_of_measurement: "A"
    device_class: current
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Koelplaat Temperatuur"
    register_type: read
    address: 12
    force_new_range: true
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    value_type: U_WORD
    filters:
      - multiply: 0.5  # Type 2 — no offset

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "EEV Opening"
    register_type: read
    address: 13
    force_new_range: true
    state_class: measurement
    value_type: U_WORD

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "DC Ventilator Snelheid"
    register_type: read
    address: 14
    force_new_range: true
    unit_of_measurement: "RPM"
    state_class: measurement
    value_type: U_WORD

# ============================================================
# SWITCH
# ============================================================
switch:
  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Warmtepomp Aan/Uit"
    register_type: coil
    address: 0

# ============================================================
# SELECT (mode control)
# ============================================================
select:
  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Functie Selectie"
    address: 0
    value_type: U_WORD
    optionsmap:
      "Auto": 0
      "Verwarmen": 1
      "Koelen": 2

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Werkingsmodus"
    address: 1
    value_type: U_WORD
    optionsmap:
      "Smart": 0
      "Stil": 1
      "Turbo": 3

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Waterpomp Modus"
    address: 5
    value_type: U_WORD
    optionsmap:
      "Continu": 0
      "Watertemperatuur Gestuurd": 1
      "Tijd + Watertemperatuur": 2

# ============================================================
# NUMBERS
# Raw registers hidden, template entities show clean °C
# Conversion: °C = (raw - 60) / 2  |  raw = (°C × 2) + 60
# ============================================================
number:

  # --- Hidden raw Modbus registers ---
  - platform: modbus_controller
    modbus_controller_id: fairland
    id: fairland_verwarmen_raw
    internal: true
    address: 3
    register_type: holding
    value_type: U_WORD
    min_value: 96
    max_value: 140
    step: 2

  - platform: modbus_controller
    modbus_controller_id: fairland
    id: fairland_koelen_raw
    internal: true
    address: 4
    register_type: holding
    value_type: U_WORD
    min_value: 84
    max_value: 120
    step: 2

  - platform: modbus_controller
    modbus_controller_id: fairland
    name: "Waterpomp Werktijd"
    address: 6
    register_type: holding
    value_type: U_WORD
    min_value: 10
    max_value: 120
    step: 5
    unit_of_measurement: "min"

  # --- Human-readable °C template entities ---
  - platform: template
    name: "Verwarmen Doeltemperatuur"
    unit_of_measurement: "°C"
    device_class: temperature
    min_value: 18
    max_value: 40
    step: 0.5
    lambda: |-
      return (id(fairland_verwarmen_raw).state - 60.0) / 2.0;
    set_action:
      - number.set:
          id: fairland_verwarmen_raw
          value: !lambda return (x * 2.0) + 60.0;

  - platform: template
    name: "Koelen Doeltemperatuur"
    unit_of_measurement: "°C"
    device_class: temperature
    min_value: 12
    max_value: 30
    step: 0.5
    lambda: |-
      return (id(fairland_koelen_raw).state - 60.0) / 2.0;
    set_action:
      - number.set:
          id: fairland_koelen_raw
          value: !lambda return (x * 2.0) + 60.0;

To open the Inver-X Vertical: first unscrew 3 screws on the top back of the heatpump to remove the top cover. Then unscrew all screws securing the front plate (multiple on the bottom, multiple on the top hidden under the top cover, also one or 2 on the right side behind the cover where the power cable enters the heatpump). After all screws of the front plate are unscrewed, you can lift the front cover, being carefull as the control screen is connected with a cable, just move it to the side a bit.

Then you can easily connect the atom:

I just left the atom in the enclosure and it had enough WiFi range to connect to my access point which is a couple of meters away.

I didn’t change anything on the dip switches:

In case some else is looking for a suitable 4 pin plug to use instead of opening up the heatpump. This one was a perfect match and is identical to the wifi module plug (4Pin, 0.3mm2). https://nl.aliexpress.com/item/1005011641408936.html Do double check the size references on your own unit.