Half duplex communication and DE on Modbus RTU serial Server over RS485

1,728 views
Skip to first unread message

anana

unread,
Jul 6, 2012, 10:00:32 AM7/6/12
to pymo...@googlegroups.com
I am using a MAX1486 and need to set DE = 1 pin for transmitting and DE = 0 for receiving.

Did anybody do this before?
It works the first time the client polls the data. But then the server seems to hear its own answer (server/sync.py handles it on line 107) and after that the server will not answer any more.
Please find code at the end.

Thank you in advance.


scadad-server - DEBUG - scada_server.py:316 - SCADA Modbus Table update daemon started.
scadad-server - INFO - scada_server.py:701 - Start ModbusSerialServer with server address '1'
scadad-server - INFO - scada_server.py:702 - Baudrate: 38400, 8 N 1
scadad-server - DEBUG - scada_server.py:192 - Started thread to serve client
pymodbus.server.sync - DEBUG - sync.py:40 - Client Connected [/dev/ttyS4:/dev/ttyS4]
pymodbus.server.sync - DEBUG - sync.py:107 - 0x1 0x4 0x9c 0x3f 0x0 0x5 0x2e 0x55
pymodbus.server.sync - DEBUG - sync.py:122 - send: 01040a000007dc00070006000e5953
pymodbus.server.sync - DEBUG - sync.py:107 - 0x1 0x4 0xa 0x0 0x0 0x7 0xdc 0x0 0x7 0x0 0x6 0x0 0xe 0x59 0x53
pymodbus.server.sync - DEBUG - sync.py:107 - 0x1 0x4 0x9c 0x3f 0x0 0x5 0x2e 0x55
pymodbus.server.sync - DEBUG - sync.py:107 - 0x1 0x4 0x9c 0x3f 0x0 0x5 0x2e 0x55



class MyModbusSingleRequestHandler(ModbusSingleRequestHandler):
    def send(self, message):
        ''' Send a request (string) to the network

        :param message: The unencoded modbus response
        '''
        # HALF-Duplex, set DE=1 to transmit
        os.system("echo '1' > /sys/class/gpio/gpio88/value")
        to_return = ModbusSingleRequestHandler.send(self, message)
        # HALF-Duplex, set DE=0 to receive again
        os.system("echo '0' > /sys/class/gpio/gpio88/value")
        return to_return


class MyModbusSerialServer(ModbusSerialServer):
    def __init__(self, context, framer=None, identity=None, **kwargs):
        #call constructor from parent class
        ModbusSerialServer.__init__(self, context, framer=ModbusRtuFramer, identity=identity, **kwargs)

    def My_build_handler(self):
        request = self.socket
        request.send = request.write
        request.recv = request.read
        handler = MyModbusSingleRequestHandler(request,
            (self.device, self.device), self)
        return handler

    def serve_forever(self):
        log.debug("Started thread to serve client")
        handler = self.My_build_handler()
        while True: handler.handle()


def mani(debug):
            s = MyModbusSerialServer(context, bytesize=bytesize, parity=parity,
                                      stopbits=stopbits, baudrate=baudrate, port="/dev/ttyS4",
                                      timeout=0.1)
            try:
                log.info("Start ModbusSerialServer with server address '%i'" % ServerUnitId)
                log.info("Baudrate: %s, %s %s %s", baudrate, bytesize, parity, stopbits)
                s.serve_forever()
            except KeyboardInterrupt:
                #stop modbus server and thread
                s.server_close()
                t.running = False

Galen Collins

unread,
Jul 6, 2012, 11:55:38 AM7/6/12
to pymo...@googlegroups.com

Is there any chance you can make sure you have latest from github?

anana

unread,
Jul 9, 2012, 4:40:56 AM7/9/12
to pymo...@googlegroups.com
I think I do, is that right?

python-pymodbus:
  Installed: 0.9.0+r175-2
  Candidate: 0.9.0+r175-2

anana

unread,
Jul 10, 2012, 6:49:34 AM7/10/12
to pymo...@googlegroups.com
i tried the latest git version now - without success.

Maybe the problem is the package handling. First time the asnwer of the server is correct. then the server get's the last byte of his own answer and this byte is handled by the function processIncomingPacket(). self.__buffer has then the length 1. Now i send more requests to the server but the server did not answer anymore. but the length of self.__buffer increases with every request. so after the first request, the length of self.__buffer is 9. after the seconds request, the length is 17, and so on... But the length should be 8 every time. So here's our output (with some more prints in transaction.py):

# scadad-run -d

pymodbus.server.sync - DEBUG - sync.py:40 - Client Connected [/dev/ttyS4:/dev/ttyS4]
pymodbus.server.sync - DEBUG - sync.py:97 - 0x1 0x4 0x9c 0x3f 0x0 0xa 0x6e 0x51
is frame ready?:  True
len(self.__buffer) is: 8
frame checked: result is: ReadRegisterRequest (39999,10)
pymodbus.server.sync - DEBUG - sync.py:112 - send: 010414000007dc0007000a000c002300360000000000000764
pymodbus.server.sync - DEBUG - sync.py:97 - 0x64
is frame ready?:  False
len(self.__buffer) is: 1
pymodbus.server.sync - DEBUG - sync.py:97 - 0x1 0x4 0x9c 0x3f 0x0 0xa 0x6e 0x51
is frame ready?:  True
len(self.__buffer) is: 9
check frame failed
pymodbus.server.sync - DEBUG - sync.py:97 - 0x1 0x4 0x9c 0x3f 0x0 0xa 0x6e 0x51
is frame ready?:  True
len(self.__buffer) is: 17
check frame failed
pymodbus.server.sync - DEBUG - sync.py:97 - 0x1 0x4 0x9c 0x3f 0x0 0xa 0x6e 0x51
is frame ready?:  True
len(self.__buffer) is: 25


Any ideas how to solve the problem?

TIA Ana



El viernes, 6 de julio de 2012 17:55:38 UTC+2, Bashwork escribió:

anana

unread,
Jul 12, 2012, 9:12:49 AM7/12/12
to pymo...@googlegroups.com
We solved the problem applying kernel patch.
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=57c3686842114de3b0c00633591e9605c46fb769

But still think this is a bug in pymodbus.

Why do the broken data stay in the buffer? This brings the server to an unrecoverable state, where the input buffer increases with every new request and the server will not answer to any more requests.

According to 'Specification and Implementation Guide for MODBUS over serial line V1.02', chapter 2.5.1.1: "Partial messages must be detected and errors must be set as a result."

In case of Modbus RTU, After 3.5 delay the frame is considered to be over. That means, no new data should complete it and, it should be discarded.

Do you think this should be fixed? Or is there a reason why it is like that?

Bashwork

unread,
Jul 12, 2012, 6:41:49 PM7/12/12
to pymo...@googlegroups.com
Anna,

Great job finding your fix (in kernel no less)!  You are correct, we should be discarding the bad frames after the message is invalid, however we can't really rely on the timing of the characters using python (or really on any non-RTOS) because we can only assure milliseconds responses when we need microseconds!  As such we are relying on the framing of the serial message streams to effectively handle this.  The hard part about determining what to skip is that we really only have a stream of data without any sentinel markers, so our only recourse is to dump the entire buffer if we are not able to parse the message after a few attempts.  This in turn _can_ lead to the case of chasing the next frame start.

I will start working on a mechanism to address this problem, but please let me know if you have any better ideas than what I have suggested.

Galen
I am using a MAX1486 and need to set DE = 1 pin for transmitting and DE = 0 for receiving.

anana

unread,
Mar 4, 2013, 1:11:50 PM3/4/13
to pymo...@googlegroups.com
Dear Gallen,

did you make any progress on this topic?
It is very problematic because, if there is a little communication broblem and the server gets some invalid data, it will never recover until it is restarted.

Thank you,

Ana
Reply all
Reply to author
Forward
0 new messages