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.

question regarding power consumption calculation:

which values are you getting reported out of pfc voltage? i do have a ric70t (tripple phase) and the value is 290V which is quite strange. which is in my case power = compressor current 5A * 290V = 1450 Watt which is way too low under full load.

or do i have to calculate compressor current * pfc voltage *3 to get power consumption in total wirh 3 phase models?

Hi,
can you turn on and off ,set temperature and mode your fairland inver-x heatpump with this set (m5stack atom lite / m5stack tail485) or just get data from sensors?

Thanks
Gábor

i implemented this with m5 atom + rs485 exactly like it is described here. everything works, controlling and getting sensor data - the only drawback is to find the best location where to place the m5 to have a proper WiFi signal

Fairland VBIV 20/3F H8 (PC1004 board) via Elfin-EW11A — All Modbus function codes return “Illegal Function”

Hi everyone,

I’m trying to integrate my Fairland/VBIV 20/3F H8 pool heat pump (with PC1004 controller board) into Home Assistant via Modbus. I’ve connected an Elfin-EW11A RS485-to-WiFi gateway to the CN13 port on the PC1004 board (pinout: 12V, 485_A3, 485_B3, GND).

What works

  • EW11A connects to WiFi and is reachable at 192.168.178.254:8899
  • Home Assistant opens the Modbus connection successfully (“modbus warmtepomp communication open”)
  • The device at slave ID 1 responds to requests (no timeouts)
  • CRC checks pass — framing is correct

The problem

Every function code returns Exception Code 0x01 (Illegal Function):

# FC03 (Read Holding Registers) at address 3:
send: 0x01 0x03 0x00 0x03 0x00 0x01
recv: 0x01 0x83 0x01  → Illegal Function

# FC04 (Read Input Registers) at address 3:
send: 0x01 0x04 0x00 0x03 0x00 0x01
recv: 0x01 0x84 0x01  → Illegal Function

# FC01, FC02, FC03, FC04 at address 0: all return Illegal Function

I tested both:

  • EW11A Protocol = “Modbus” + HA type: tcp
  • EW11A Protocol = “None” + HA type: rtuovertcp

Same result in both modes.

EW11A settings

  • Serial: 9600, 8, 1, None
  • TCP Server on port 8899
  • Before my tests, the EW11A status page showed 6971 frames sent/received with 0 failures (so the serial link was working at some point)

My setup

  • Heat pump: VBIV 20/3F H8 (Fairland OEM)
  • Controller board: PC1004
  • Connected to: CN13 port (pins labeled 12V, 485_A3, 485_B3, GND)
  • Gateway: Elfin-EW11A (firmware 1.44.1)
  • Home Assistant on 192.168.178.83

My questions

  1. Is CN13 the correct Modbus port? Most community posts reference a connector labeled “B A G +12V”. The CN13 labels say “485_A3, 485_B3” — could this be a different RS485 bus with a proprietary protocol?
  2. Does the PC1004 board use standard Modbus? Most documented setups use MWH298 or similar boards. Has anyone successfully connected a PC1004?
  3. Could the slave ID be different from 1? Is there a way to determine the correct slave ID for this board?
  4. Does anyone have a register map or documentation for the PC1004?

Any help would be greatly appreciated! I’ve been following this thread and the spdr870 GitHub repo, but those are for different controller boards.

Thanks!



Thank you. I bought the same stuff and it works perfectly. You made my day!

Fixed it. Everything works now

... but you still have to open the cover to reach the connector, don't you? I mean, the Wifi-Connector doesn't seem to be useable for our purpose.

Thank you for your contibuation I was stuck and tried a lot of thing except this ! Once I have connected on D5 & D6 it has instantanely worked !

Thanks a lot for sharing this ESPHome / Modbus configuration. It helped me a lot to get my BWT pool heat pump working in Home Assistant.

In my case, I used an AZDelivery NodeMCU ESP8266 board and connected it through D5 / D6 instead of the original RX/TX setup, which was not working for me.

I also don’t have the ESPHome add-on available in my Home Assistant setup. ESPHome Builder was crashing on Home Assistant because my Raspberry Pi 3 is already close to its usage limits, so I had to do it another way using the terminal.

One important thing asked in some replies: I did not change the DIP switch pin settings.

After that, I was able to integrate the entities into my Home Assistant dashboard. Thanks again for the work and for sharing it! Now lots of fun making cards and automations :slightly_smiling_face:

Thank you so much! Awesome work.

I finally made my own integration with github-repos from contributors here and other documents found around.
It is configured to use RS485-protocol towards the Aquaforte MWH367-PBC-board on it's Wifi-port.
phendrickx/Aquaforte-PoolHeater: RS485 integration through ESPHome on Aquaforte Heatpump

A question regarding the switch from Tuya to ESPhome control, are you able to set timers and do they operate on the unit same as when set by Tuya app?

When set from the app, the heat pump will start/stop to sample water temperature and decide whether to stay running.