ljean / modbus-tk

Create Modbus app easily with Python
Other
566 stars 212 forks source link

Delay required for receiving full serial messages reliably #112

Open nickbedbury opened 5 years ago

nickbedbury commented 5 years ago

I've been having trouble getting an RTU serial slave to receive requests fully. The slave seems to cut messages partway, causing CRC and framing errors.

Adding a small delay in modbus_rtu.py after the 1st blocking read seems to help:

# Read rest of the request
time.sleep(0.1)  # << added >>
while True:

In my scenario, the master/slave are using 9600 baud, so perhaps that's part of the issue. At faster baud rates, all the bytes would come in faster. I created the slave with code as follows:

    server = modbus_rtu.RtuServer(serial.Serial(port='/dev/ttyUSB0',
                                                baudrate=9600,
                                                bytesize=8,
                                                parity='N',
                                                stopbits=1,
                                                xonxoff=False),
                                  error_on_missing_slave=False)
ljean commented 5 years ago

modbus_tk should calculate a timeout for your baud rate thanks to utils.calculate_rtu_inter_char. For baud rate below 19200, we return a fixed value of 0.0005 sec. I wonder if, it shouldn't rounded up to 0.0006 sec. Can you please give a try and check if it solves the problem?

You may also try to change that behavior by setting custom values to the interframe_multiplier and the interchar_multiplier args of the RtuServer constructor. By default, we have 3.5 and 1.5, but you can give the value you want.

nickbedbury commented 5 years ago

I had tried with interchar_multiplier=2 and interframe_multiplier=5 but that didn't seem to be enough without the extra sleep I introduced.

wanfengshu commented 4 years ago

Glad to find the same topic, as I was learning raspberry this days, and try to build up a remote slave client. While master send read holding register was fine, but send write multiple register even only 2 bytes alway cause slave invalid CRC requrest, something like(1, cst.WRITE_MULTIPLE_REGISTERS, 0, output_value = [ 1,2]). I tried the method from Ijean set interframe_multiplier = 20 ; interchar_multiplier = 5, then it works under baudrate = 9600. I did not stuy the logic behind for the 2 variables, this just for your reference.

ghost commented 4 years ago

It would be interesting to hear what kind of hardware you are using. For a USB serial port adapter, the USB host would typically poll the adapter for new data at 1000 Hz as far as I understand. Sub-ms timeouts don't really make sense in that situation.

jmbott commented 4 years ago

I had this same problem. Increasing the interframe_multiplier worked for me. I have a Modbus RTU slave with a baudrate of 9600 and increased the frame timeout multiplier to 8.

With a baud of 9600 I get a _t0 of 0.001145833333 from utils.calculate_rtu_inter_char so a serial timeout of 0.009166666664 and a serial inter_byte_timeout of 0.001718749999.

Looking at this old stackoverflow question and then the pyserial library, at lines like,

vtime = int(self._inter_byte_timeout * 10)

and

vtime = int(self._timeout * 10)

I am wondering how much of that precision gets captured.

Also how the calculation in utils.calculate_rtu_inter_char was derived.

DmitrySidorow commented 3 years ago

At the speed of 115200, the library was very unstable. I picked up different delays for packets of different sizes. I don't know why it works :)

    if response:
        if self._serial.in_waiting > 0:
            # Most likely master timed out on this request and started a new one
            # for which we already received atleast 1 byte
            LOGGER.warning("Not sending response because there is new request pending")
        else:
            gpio.output(port.PG9, gpio.HIGH)
            self._serial.write(response)
            if len(response) > 20:
                time.sleep(0.0037) 
            else:
                time.sleep(0.0019)
            self._serial.flush()
            gpio.output(port.PG9, gpio.LOW)
            time.sleep(self.get_timeout())
jacksonmatheson commented 3 years ago

Hi all,

For what it's worth, I use this library in quite a few industrial applications and have had fantastic success even up to 2.5m baud (yes that said 2,500,000baud) using a wave share USB RS485 convertor and on some embedded devices we use.

If you can document this a bit further with easy to reproduce steps I'm happy to assign someone on our team the job to look into it further.

(we aren't affiliated with modbustk excluding 1 commit I've made but we have a major interest in this library working reliably and are keen to support.)

On Sun, Jun 13, 2021, 12:37 AM Dmitry @.***> wrote:

At the speed of 115200, the library was very unstable. I picked up different delays for packets of different sizes.

if response:
    if self._serial.in_waiting > 0:
        # Most likely master timed out on this request and started a new one
        # for which we already received atleast 1 byte
        LOGGER.warning("Not sending response because there is new request pending")
    else:
        gpio.output(port.PG9, gpio.HIGH)
        self._serial.write(response)
        if len(response) > 20:
            time.sleep(0.0037)
        else:
            time.sleep(0.0019)
        self._serial.flush()
        gpio.output(port.PG9, gpio.LOW)
        time.sleep(self.get_timeout())

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ljean/modbus-tk/issues/112#issuecomment-860062068, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHFD2JKPOTWZGEFCVVZHDHTTSNWKFANCNFSM4HMQ3NXA .

DmitrySidorow commented 3 years ago

Hi all, For what it's worth, I use this library in quite a few industrial applications and have had fantastic success even up to 2.5m baud (yes that said 2,500,000baud) using a wave share USB RS485 convertor and on some embedded devices we use. If you can document this a bit further with easy to reproduce steps I'm happy to assign someone on our team the job to look into it further. (we aren't affiliated with modbustk excluding 1 commit I've made but we have a major interest in this library working reliably and are keen to support.) On Sun, Jun 13, 2021, 12:37 AM Dmitry @.***> wrote: At the speed of 115200, the library was very unstable. I picked up different delays for packets of different sizes. if response: if self._serial.in_waiting > 0: # Most likely master timed out on this request and started a new one # for which we already received atleast 1 byte LOGGER.warning("Not sending response because there is new request pending") else: gpio.output(port.PG9, gpio.HIGH) self._serial.write(response) if len(response) > 20: time.sleep(0.0037) else: time.sleep(0.0019) self._serial.flush() gpio.output(port.PG9, gpio.LOW) time.sleep(self.get_timeout()) — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#112 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHFD2JKPOTWZGEFCVVZHDHTTSNWKFANCNFSM4HMQ3NXA .

I do not know if this can help others. I'm using an orange pi one with ADM1485ARZ. At the speed of 9600, everything worked well. At the speed of 115200, some packets were lost.