Modbus issues

@marcelkhalaf For reference I post an example of my automation:

- id: FiltrateDelayTimerSP
  alias: Filtrate Delay Timer SP
  trigger:
    platform: state
    entity_id: input_number.slider8
  action:
    service: modbus.write_register
    data_template:
      hub: hub6
      unit: 6
      address: 3072
      value: "{{ states.input_number.slider8.state|int }}"

Your syntax is quite different in the automation so I would look at that.
And I draw your attention to the difference in your value template:

value: "{{ states.input_number.slider8.state|int }}" 
value: '{{ states(''input_number.box'') | int }}'

Pretty sure that’s where your problems lie?
Try setting yours up that way and post again any change.

I’ve been getting the same selection of errors/warnings with a modbus device on the serial port. Have come to the conclusion that it isn’t respecting the modbus specification. Ended up writing a custom sensor.py that forced the pymodbus rtu_framer to wait longer than the standard 3.5 character periods before sending a new register read.

The problem isn’t with the HA config nor pymodbus - I’m of the opinion that it is down to crappy firmware in the modbus connected device. A fix is possible, and I may be able to hack a few drivers for others to see if my suspicions are correct.

Just to see where you are coming from how about posting your config for us to get on the same page and maybe give some help?

configuration.yaml

modbus:
  name: hub1
  type: serial
  method: rtu
  port: /dev/ttyUSB0
  baudrate: 9600
  stopbits: 1
  bytesize: 8
  parity: N
  timeout: 10

sensor: !include sensors.yaml

sensors.yaml

- platform: modbus
  scan_interval: 15
  registers:
    - name: "Sensor_20"
      hub: hub1
      slave: 32
      register: 0
      register_type: holding
      unit_of_measurement: "°C"
      count: 1
      scale: 0.1
      offset: 0
      precision: 1
      data_type: int

- platform: sht20
  scan_interval: 15
  hub: hub1
  name: "Test Bench"
  slave: 18
  framer_delay: 0.30
  monitored_conditions:
    - temperature
    - humidity

The sht20 “driver” is a custom integration that I’ve hacked around to prove/disprove my hypothesis that a longer delay is needed between RTU frames sent by pymodbus - Hence the framer_delay parameter.

Some log samples with debug messages from pymodbus:

2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.transaction] RECV: 0x20 0x3 0x2 0x0 0xdc 0x5 0xda
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.framer.rtu_framer] Getting Frame - 0x3 0x2 0x0 0xdc
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.factory] Factory Response[ReadHoldingRegistersResponse: 3]
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.framer.rtu_framer] Frame advanced, resetting header!!
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.transaction] Adding transaction 2
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.transaction] Getting transaction 2
2019-11-24 13:23:15 DEBUG (SyncWorker_0) [pymodbus.transaction] Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.transaction] Current transaction state - TRANSCATION_COMPLETE
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.transaction] Running transaction 3
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.transaction] SEND: 0x12 0x4 0x0 0x1 0x0 0x2 0x22 0xa8
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.framer.rtu_framer] Changing state to IDLE - Last Frame End - 1574601795.022255, Current Time stamp - 1574601795.025107
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.framer.rtu_framer] Waiting for 3.5 char before next send - 4.01 ms
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.client.sync] New Transaction state 'SENDING'
2019-11-24 13:23:15 DEBUG (SyncWorker_9) [pymodbus.transaction] Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-11-24 13:23:25 DEBUG (SyncWorker_9) [pymodbus.transaction] Transaction failed. (Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)) 
2019-11-24 13:23:25 DEBUG (SyncWorker_9) [pymodbus.framer.rtu_framer] Frame - [b''] not ready
2019-11-24 13:23:25 DEBUG (SyncWorker_9) [pymodbus.transaction] Getting transaction 3
2019-11-24 13:23:25 DEBUG (SyncWorker_9) [pymodbus.transaction] Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
2019-11-24 13:23:25 WARNING (SyncWorker_9) [custom_components.sht20.sensor] No response from hub: hub1, slave: 0x12

Setting a longer delay between reads within pymodbus:

2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Current transaction state - TRANSCATION_COMPLETE
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Running transaction 8
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] SEND: 0x20 0x3 0x0 0x0 0x0 0x1 0x82 0xbb
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.framer.rtu_framer] Changing state to IDLE - Last Frame End - 1574601910.361777, Current Time stamp - 1574601926.004568
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.client.sync] New Transaction state 'SENDING'
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] RECV: 0x20 0x3 0x2 0x0 0xdc 0x5 0xda
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.framer.rtu_framer] Getting Frame - 0x3 0x2 0x0 0xdc
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.factory] Factory Response[ReadHoldingRegistersResponse: 3]
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.framer.rtu_framer] Frame advanced, resetting header!!
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Adding transaction 8
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Getting transaction 8
2019-11-24 13:25:26 DEBUG (SyncWorker_8) [pymodbus.transaction] Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Current transaction state - TRANSCATION_COMPLETE
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Running transaction 9
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] SEND: 0x12 0x4 0x0 0x1 0x0 0x2 0x22 0xa8
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.framer.rtu_framer] Changing state to IDLE - Last Frame End - 1574601926.023569, Current Time stamp - 1574601926.027157
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.framer.rtu_framer] Waiting for 3.5 char before next send - 300.0 ms
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.client.sync] New Transaction state 'SENDING'
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] RECV: 0x12 0x4 0x4 0x0 0xdc 0x2 0x4 0x18 0x1c
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.framer.rtu_framer] Getting Frame - 0x4 0x4 0x0 0xdc 0x2 0x4
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.factory] Factory Response[ReadInputRegistersResponse: 4]
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.framer.rtu_framer] Frame advanced, resetting header!!
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Adding transaction 9
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Getting transaction 9
2019-11-24 13:25:26 DEBUG (SyncWorker_17) [pymodbus.transaction] Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'

The modbus sensor config is the one with the problem I believe:

From the docs

PLEASE NOTE:

Are not supported WHILE

registers (map) IS (Required) for a modbus sensor

You need to consult your documentation for your device and figure out what registers to address for the information you need.

EDIT: OK I missed the

- platform: modbus
only noticed the 
- platform: sht20

part when I was answering sorry…what’s that you have setup?

https://www.aliexpress.com/item/32951321384.html - Have a couple of these sensors. The register map is hard coded in to my custom SHT20 driver. The framer_delay parameter is an addition of mine which seems to help avoid the “No response from hub” errors.

But its based on the modbus integration right?

Yes.

My sht20 code depends on the modbus layer to access the sensor registers.

Cool…now you have posted be good if you eventually post your progress and solution?

Have got it set up and running on a Raspberry Pi (a vintage Revision 2) and had just five “No response from hub” warnings overnight. A vast improvement on the 30+ per hour warnings I had been getting without the rtu_framer_delay tweaks,

Going to leave this little puppy running for the rest of the week while I work on a driver (integration) for a PZEM-016 power meter.

1 Like

OK. Been running a modified version of the modbus sensor integration. “No response from hub” warnings have fallen from one or more per hour down to around five per week… The attached diff should be applied to the modbus sensor integration, and a framer_delay added to the config.

modbus:
  name: hub1
  type: serial
  method: rtu
  port: /dev/ttyUSB0
  baudrate: 9600
  stopbits: 1
  bytesize: 8
  parity: N
  timeout: 10

- sensor:
  - platform: modbus
    scan_interval: 30
    registers:
      - name: "Water Tank"
        hub: hub1
        slave: 32
        framer_delay: 0.4
        register: 0
        register_type: holding

--- sensor.py	2019-05-16 12:02:50.000000000 +0100
+++ /tmp/modbus/sensor.py	2019-12-04 11:09:22.313533342 +0000
@@ -23,6 +23,7 @@
 CONF_REGISTERS = 'registers'
 CONF_REVERSE_ORDER = 'reverse_order'
 CONF_SCALE = 'scale'
+CONF_FRAMER_DELAY = "framer_delay"
 
 DATA_TYPE_CUSTOM = 'custom'
 DATA_TYPE_FLOAT = 'float'
@@ -50,6 +51,9 @@
         vol.Optional(CONF_SLAVE): cv.positive_int,
         vol.Optional(CONF_STRUCTURE): cv.string,
         vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
+        vol.Optional(CONF_FRAMER_DELAY, default=0): vol.All(
+            vol.Coerce(float), vol.Range(min=0, max=0.75)
+        )
     }]
 })
 
@@ -96,7 +100,7 @@
             register.get(CONF_UNIT_OF_MEASUREMENT), register.get(CONF_COUNT),
             register.get(CONF_REVERSE_ORDER), register.get(CONF_SCALE),
             register.get(CONF_OFFSET), structure,
-            register.get(CONF_PRECISION)))
+            register.get(CONF_PRECISION), register.get(CONF_FRAMER_DELAY)))
 
     if not sensors:
         return False
@@ -108,7 +112,7 @@
 
     def __init__(self, hub, name, slave, register, register_type,
                  unit_of_measurement, count, reverse_order, scale, offset,
-                 structure, precision):
+                 structure, precision, delay):
         """Initialize the modbus register sensor."""
         self._hub = hub
         self._name = name
@@ -124,6 +128,9 @@
         self._structure = structure
         self._value = None
 
+        if delay > self._hub._client.silent_interval:
+            self._hub._client.silent_interval = delay
+
     async def async_added_to_hass(self):
         """Handle entity which will be added."""
         state = await self.async_get_last_state()
1 Like

Hi Paul, I have the same problems with an eastron power meter, could you kindly prepare a “wiki” that explain how to add your custom driver? I didn’t understand the file location of modbus driver, is it possible to create a copy in order to keep also the original?
Thanks
Matteo

Hi Matteo

Reluctant to do a wiki - Information can quickly get out of date, and it needs commitment from someone to keep on top of any changes. As an example, the code base I generated the patch from isn’t quite the same as the code base I have on my desktop. Suffice to say, patching failed on several of the hunks. But the patch is trivial enough that the changes can be applied manually.

Make a copy of the modbus integration to your .homeassistant/custom_components directory, edit modbus/sensor.py to suit, and the new integration will over-ride the original.

@paul_c5x4 that modification looks like it should be put forward as a Pull Request and become a part of the core mate…are you up for that?

Don’t have a github account, and really don’t see much point in setting one up just for a single (trivial) change. Perhaps what I should have done is generated a patch from a local git clone and submitted via email…

I’ll try to do it today :+1:

I agree with @wellsy, maybe must be tested a little more, but should be consider it as core function.I read many post related this issue. Maybe modbus is not so common in home automation but there are few improvements that is possible to add in order to improve compatibility with modbus devices

1 Like

Good on you @Bard It would be more common if the modbus integration was more complete…there are potentially thousands of modbus devices out there with things that just dont quite fit atm I reckon.

Try the following patch generated with git-diff

It needs testing with a wider range of devices before it can be considered for a PR. Even then, it may not make it as it doesn’t use a public (modbus library) function to alter the framer delay.

diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py
index 10e11a9..078f1f1 100644
--- a/homeassistant/components/modbus/sensor.py
+++ b/homeassistant/components/modbus/sensor.py
@@ -23,6 +23,7 @@ CONF_REGISTER_TYPE = 'register_type'
 CONF_REGISTERS = 'registers'
 CONF_REVERSE_ORDER = 'reverse_order'
 CONF_SCALE = 'scale'
+CONF_FRAMER_DELAY = "framer_delay"
 
 DATA_TYPE_CUSTOM = 'custom'
 DATA_TYPE_FLOAT = 'float'
@@ -52,6 +53,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
         vol.Optional(CONF_SLAVE): cv.positive_int,
         vol.Optional(CONF_STRUCTURE): cv.string,
         vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
+        vol.Optional(CONF_FRAMER_DELAY, default=0): vol.All(
+            vol.Coerce(float), vol.Range(min=0, max=0.75))
     }]
 })
 
@@ -98,7 +101,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
             register.get(CONF_UNIT_OF_MEASUREMENT), register.get(CONF_COUNT),
             register.get(CONF_REVERSE_ORDER), register.get(CONF_SCALE),
             register.get(CONF_OFFSET), structure,
-            register.get(CONF_PRECISION)))
+            register.get(CONF_PRECISION), register.get(CONF_FRAMER_DELAY)))
 
     if not sensors:
         return False
@@ -110,7 +113,7 @@ class ModbusRegisterSensor(RestoreEntity):
 
     def __init__(self, hub, name, slave, register, register_type,
                  unit_of_measurement, count, reverse_order, scale, offset,
-                 structure, precision):
+                 structure, precision, delay):
         """Initialize the modbus register sensor."""
         self._hub = hub
         self._name = name
@@ -126,6 +129,9 @@ class ModbusRegisterSensor(RestoreEntity):
         self._structure = structure
         self._value = None
 
+        if delay > self._hub._client.silent_interval:
+            self._hub._client.silent_interval = delay
+
     async def async_added_to_hass(self):
         """Handle entity which will be added."""
         state = await self.async_get_last_state()

Hi @paul_c5x4 and @wellsy due to the unhappy “covid” period, I have more time for work on my home assistant configuration.
How can I load your patch? In the last days I found a possible solution to use the Addon appdaemon that create a python “sandbox” but i don’t know if is the best solution, could you kindly post some documentation reference?
thanks
Matteo