Modbus and RS485 to ETH Adapter and DDSD285 meter cannot get data from device

Looking for a little help after reading what seems like hundreds of snippets and articles.

I have the device connected and a sensor configured. I get no data from the device. I can see in the logs that it is sending the following string.

Here is my config for the sensor

      - name: Power1
        unique_id: OfficePower 
        unit_of_measurement: KW
        input_type: input
        slave: 1
        address: 0x0090
        data_type: float16

What gets sent out by HA is the following:

send: 0x1 0x3 0x0 0x90 0x0 0x1 0x84 0x27

This seems to suggest HA is sending a function code 3 out. I need it to send a function code 4 out (read a register)

The Autometers Modbus RTU and Modbus ASCII protocols uses a subset of the standard
Modbus function codes to provide access to measurement and information registers. These
standard function codes provide basic support for IEEE 32-bit floating point numbers, 16 bit
integer and 8 bit char values.
Function Code
Name
Usage
0x04Read Input RegistersUsed for reading floating point and 16 bit
integer measurements
0x06Write Single RegisterUsed for writing floating point and 16 bit
integer values to a single register
0x10Write Multiple RegistersUsed for writing floating point and 16 bit
integer values to multiple registers
0x11Report Device IDUsed for reading device information including
device ID, description, software version etc
Read Device IdentificationUsed for reading device identification and
additional information

Does anyone have any idea how I can commiticate and send out a function code 4 message (read) a register type float16 from register 0x0090

Totally stumped by this.
Any help appreciated.

Cheers

Modbus is such fun.

The ‘input_type’ defaults to holding if not specified, and the unit of measurement I believe should be ‘kW’, so perhaps the ‘KW’ is causing an issue and the next line is not being read correctly.

Hey thanks for responding. I made the change you suggested but alas nothing received.

Message now being sent is:

 send: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x4 0x0 0x90 0x0 0x1

Decoding the end of that line then it seems to be saying send to ID 1 mode 4 address 0x0090 which is correct. That should read a value from register 0x0090 and return it.

I have no idea what to do next with it.

Cheers

Did I not say Modbus is fun?

You could post your entire YAML with the Modbus config, as well as details of the meter device, and your interface/adaptor.

I am guessing that your have an ethernet to RS485 adapter, but you seem to be using rtuovertcp as HA seems to be sending the checksum. If your adapter is just forwarding then this is OK, but if your adapter is Modbus aware and acts as a protocol converter, then you need tcp only.

Starting from the top:
The DDSD285 meter appears to have RS485 and RS232 and ethernet.

  • are you sure it supports Modbus?
  • do you have a correct register number?
  • how is Modbus presented - over RS485 or ethernet?
  • if you are using RS485, what physical port, and have you got the right A-B connections?
  • what is the baud rate, stop bit and parity setting?
  • are your wires OK, not broken, good connections, twisted pair?

Adapter - serial to ethernet

  • have you got the wires as A-A and B-B?
  • have you tried the wires the other way around (some devices have the AB inverted)?
  • how is the adapter connected to power and the LAN?
  • is your HA on the same LAN as the adapter?
  • is the adapter set up correctly? Serial RS485, baud, stop, parity?
  • what port does the adapter expose for connection? default is 502, but some adapters use other ports by default?
  • is the adapter set as a server for TCP?
  • does the adapter have Modbus Protocol conversion or is it just pass-through?
  • are you sure the adapter is working, is there a loop-back test you can use, do you have manufacturer test software?

Home Assistant

  • have you tried using a PC to connect to the adapter on its own?
  • can you ping the adapter?
  • have you configured HA Modbus hub settings correctly? Generally worth using only the required settings (ignore those that default) and set up just one sensor with the basics only.

Yes, there is a lot that needs to be exactly right otherwise it will not work.

Geoff many thanks.
The DDSD285 meter appears to have RS485 and RS232 and ethernet.

  • are you sure it supports Modbus? Yes
  • do you have a correct register number? Yes
  • how is Modbus presented - over RS485 or ethernet? RS485 from the meter to the RS485 to ETH adapter.
  • if you are using RS485, what physical port, and have you got the right A-B connections? Pins 23 TX+ and 24 TX- as labelled on the device. Yes to correct A-B.
  • what is the baud rate, stop bit and parity setting? 9600,1, EVEN
  • are your wires OK, not broken, good connections, twisted pair? All good. twisted pair G/GW

Adapter - serial to ethernet

  • have you got the wires as A-A and B-B? WIred as per device and Meter instructions.
  • have you tried the wires the other way around (some devices have the AB inverted)? Yes tried.
  • how is the adapter connected to power and the LAN? External power, switch to device.
  • is your HA on the same LAN as the adapter? Yes
  • is the adapter set up correctly? Serial RS485, baud, stop, parity? Yes
  • what port does the adapter expose for connection? default is 502, but some adapters use other ports by default? 4196
  • is the adapter set as a server for TCP? Yes
  • does the adapter have Modbus Protocol conversion or is it just pass-through? No idea I believe it is transparent bridge to the RS485 side.
  • are you sure the adapter is working, is there a loop-back test you can use, do you have manufacturer test software? Seems to have all the right lights on it. No test software. I can talk to the device setup page on its built in webserver. All config looks ok.

Home Assistant

  • have you tried using a PC to connect to the adapter on its own? Yes it is connected to the network fine.
  • can you ping the adapter? Yes
  • have you configured HA Modbus hub settings correctly? Generally worth using only the required settings (ignore those that default) and set up just one sensor with the basics only.

Here is the config. This is a new HA server and this is the first integration.

Example configuration.yaml entry for a TCP connection

modbus:

  • name: modbus_hub
    type: rtuovertcp
    host: 192.168.0.130
    port: 4196
    delay: 0
    message_wait_milliseconds: 30
    timeout: 5
    sensors:
    • name: Power1
      unique_id: OfficePower
      unit_of_measurement: kW
      input_type: input
      slave: 1
      address: 0x0090
      data_type: float16

Looking up an online manual for the meter does show the RS485 as TX+/- for A/B.
Modbus RS485 is most commonly 9600 baud, 8 data bits, 1 stop bit, but no parity. Are you sure it is EVEN parity, and does the device have Modbus Slave ID set to 1?

I also assume that you do not have anything else connect to this RS485 port, just your adapter…

As you have not shared the exact model of your adapter, I have to assume from a Google search that you have a Waveshare device. At least, these appear to use port 4196 (which is again non standard).

Waveshare devices can support Modbus gateway TCP to RTU translation. If you have a device acting as a Modbus Gateway, then you use Modbus TCP at the HA end, and the port would normally be 502 by default. Modbus TCP is the better choice if you have the option.

If you can get into the adapter configuration page that is a good start. We just need to know exactly what make / model / settings you are working with!

Waveshare do have test software that can be run from a windows PC.

Again, when starting and when having problems, stripping out the unnecessary HA settings is helpful as it reduces the opportunity for error. I use Node-RED myself, but I think a most basic YAML would be

modbus:
  - name: my_modbus_hub
    type: tcp
    host: 192.168.0.130
    port: 502
    sensors:
      - name: Test1
        slave: 1
        address: 144

Everything else defaults, this should just read slave 1, register 144 (decimal) and a plain int16, using Modbus TCP at ip port 502. The type tcp may need to be rtuovertcp, and other settings may need changing, but this should work.

You may need to add ‘input_type: input’ if you are reading an input rather than holding register too…

The config I sent is essentially a very simple sensor to read 1 register. I have tried every permutation of using tcp at the HA end as well as rtuovertctp. Previously the meter was talked to via a usb to RS485 adaptor and the settings that worked were 9600,8,1,EVEN. ID 1

But have tried 9600,8,1,NONE also.

Cheers

Well, if you have previously had this working via a usb-RS485 adapter, then clearly the bit that is now not working is the Ethernet adapter.

Again, without knowing anything about the adapter you are using, there is little further of value I can add.

Sorry it is a waveshare RS485 to ETH (B) device. Will check all connections again and report back.

Many thanks for looking into this.

Cheers

I now have this working after a bit of permutations with the configs.

I had to set the protocol field in the device settings page to none then set rtuovertcp in the modbus entries.

The test now works as a base to go from.

Config now has this:

Office Electricity Meter

modbus:
name: modbus_hub
type: rtuovertcp
host: 192.168.0.130
port: 4196
delay: 0
message_wait_milliseconds: 30
timeout: 5
sensors:
- name: Current Office Consumption
unique_id: CurrentOfficeConsumption
unit_of_measurement: kW
input_type: input
slave: 1
address: 0x0090
data_type: float32
- name: Office Voltage
unique_id: OfficeVoltage
unit_of_measurement: V
input_type: input
slave: 1
address: 0x0010
data_type: float32
- name: Office Total Amps
unique_id: OfficeTotalAmps
unit_of_measurement: A
input_type: input
slave: 1
address: 0x0058
data_type: float32
- name: Office Meter Reading
unique_id: OfficeMeterReading
unit_of_measurement: Kwh
input_type: input
slave: 1
address: 0x07D0
data_type: float32

Thanks for responding on this thread.

Cheers

I suspect that you have a Waveshare with Modbus Gateway capability, and it was set by default to translate Modbus TCP to RTU. Using RTU over TCP caused the extra and unexpected CRC at the end of the package to be treated as part of a new message.

Modbus over TCP does not have CRC checksum, and this is added by a Modbus Gateway when changing from TCP to serial, or removed in the reverse direction.

Either use a forwarding setting or adapter without protocol translation and rtuovertcp in HA, or use protocol translation (Modbus Gateway) with tcp in HA.

Here is a test programin python I cobbled together to aid in troubleshooting this in case it helps anyone else :slight_smile:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import struct, time, subprocess, inspect, logging
from pymodbus.client import ModbusTcpClient
from pymodbus.framer import FramerType  # RTU over TCP framing

# ---- CONFIG ----
HOST, PORT = "YOUR IP ADDRESS", YOUR PORT
UNIT_ID    = 1            # <-- set to your actual meter address
TIMEOUT    = 3.0
RETRIES    = 3
DELAY      = 0.02

# Float word/byte order (flip WORDORDER to "little" if numbers look wrong)
WORDORDER  = "big"        # "big"=[hi,lo], "little"=[lo,hi]
BYTEORDER  = ">"          # Modbus bytes are typically big-endian

# OPTIONAL: enable wire-level debug if needed
# logging.basicConfig(level=logging.DEBUG)

# ---------- Helpers ----------
def regs_to_float32(regs):
    if len(regs) != 2:
        raise ValueError(f"need 2 regs, got {regs}")
    hi, lo = regs if WORDORDER == "big" else regs[::-1]
    return struct.unpack(BYTEORDER + "f", struct.pack(BYTEORDER + "HH", hi, lo))[0]

def read_input_registers_kw(client, address, count, unit):
    """
    Call read_input_registers(address, ...) using the correct keyword for your pymodbus:
      prefer device_id (3.x), else unit, else slave.
    """
    fn = client.read_input_registers
    params = inspect.signature(fn).parameters
    kwargs = {}
    if "count" in params: kwargs["count"] = count
    if   "device_id" in params: kwargs["device_id"] = unit
    elif "unit"      in params: kwargs["unit"]      = unit
    elif "slave"     in params: kwargs["slave"]     = unit
    return fn(address, **kwargs)

def read_float_input(client, reg_3xxxx):
    """
    3xxxx Input Register float (FC=4). Zero-based address = reg - 30001.
    """
    addr = reg_3xxxx - 30001
    rr = read_input_registers_kw(client, addr, 2, UNIT_ID)
    if rr is None or (hasattr(rr, "isError") and rr.isError()):
        raise RuntimeError(f"Modbus error @3xxxx={reg_3xxxx}: {rr}")
    return regs_to_float32(rr.registers)

def main():
    client = ModbusTcpClient(
        HOST, port=PORT,
        framer=FramerType.RTU,   # <-- RTU-over-TCP (raw RTU frames with CRC over TCP)
        timeout=TIMEOUT, retries=RETRIES
    )
    if not client.connect():
        raise RuntimeError(f"Could not connect to {HOST}:{PORT}")

    try:
        # --- Smoke test: RTU read of Input Register #200 ---
        # This proves framing + unit id are correct before we parse floats.
        probe_addr = 200  # known good zero-based address for testing
        probe = read_input_registers_kw(client, probe_addr, 1, UNIT_ID)
        if probe is None or (hasattr(probe, "isError") and probe.isError()):
            raise RuntimeError(f"Probe read failed (addr=200, unit={UNIT_ID}): {probe}")
        print(f"Probe OK: addr={probe_addr} -> registers={probe.registers}")

        # --- Your actual readings (floats, FC=4, Input Registers) ---
        TOTKWH    = round(read_float_input(client, 32001), 4); time.sleep(DELAY)
        VOLTAGE   = round(read_float_input(client, 30017), 4); time.sleep(DELAY)
        FREQUENCY = round(read_float_input(client, 30079), 4); time.sleep(DELAY)
        AMPS      = round(read_float_input(client, 30089), 4); time.sleep(DELAY)
        POWER     = round(read_float_input(client, 30145), 4) * 1000; time.sleep(DELAY)  # kW -> W

    finally:
        client.close()

    print("Volts  Amps  Frequency_Hz  Power_W  Total_KWHs")
    print(f"{VOLTAGE}  {AMPS}  {FREQUENCY}  {POWER}  {TOTKWH}\n")

    out_path = "/some folder somewhere/meter_data.txt"
    with open(out_path, "a") as f:
        f.write(time.strftime("%Y%m%d %X ") + f"{VOLTAGE} {AMPS} {FREQUENCY} {POWER} {TOTKWH}\n")

if __name__ == "__main__":
    main()