pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.29k stars 937 forks source link

cannot enable/disable RTS for client and slave #33

Closed ghost closed 3 years ago

ghost commented 11 years ago

when i use serial RTU and RS-485 wires, how do i set set RTS high and low.

i know i gotta to do it in the source codes, like for read_holding_register request , i need to set it high to send the request and low to receive the response from slave.

and for the slave Linux box, i need to set RTS high to send a reponse back to master and set it low right after that.

where do i do that ?

i know i can setRTS using pyserial.

bashwork commented 11 years ago

No, you can overload classes in your own code. If you want to modify the source that is fine, bit it is harder for me to know how to help you when problems arise if I can't see your whole code base. If you can send me a single file with your subclassing it is much easier for me to help.

So just add those two blocks to the top of your library along with the necessary imports. On Oct 29, 2013 5:12 PM, "heming277" notifications@github.com wrote:

so if i understand correctly i should add this class into the sync.py in both master and slave sync.py?

thanks

Heming

On Tue, Oct 29, 2013 at 4:11 PM, Galen Collins notifications@github.comwrote:

Right, so in server/sync.py, there is the ModbusSerialServer. What you will have to do is something like::

class RtsModbusSerialServer(object):

def _build_handler(self): ''' A helper method to create and monkeypatch a serial handler. :returns: A patched handler '''

def rts_send(message): self.socket.setRTS(True) result = self.socket.write(message) self.socket.setRTS(False) return result

def rts_recv(count): self.socket.setRTS(False) result = self.socket.read(count) self.socket.setRTS(True) return result

request = self.socket request.send = rts_send request.recv = rts_recv handler = ModbusSingleRequestHandler(request, (self.device, self.device), self) return handler

— Reply to this email directly or view it on GitHub< https://github.com/bashwork/pymodbus/issues/33#issuecomment-27352576> .

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-27355550 .

ghost commented 11 years ago

ok so it still doesn't work. i have my codes below. would you kindly check if i did anything wrong? thanks very much i've noticed that when using sync client it can only work with async server? but async.py in the library code doesn't have ModbusSerialClient. does Sync server work with sync client?

here's my client code:

!/usr/bin/python2.4

from pymodbus.client.sync import ModbusTcpClient as ModbusClient

from pymodbus.client.sync import ModbusUdpClient as ModbusClient

from pymodbus.client.sync import ModbusSerialClient as ModbusClient from pymodbus.client.sync import ModbusSerialClient import serial,time

class RtsModbusSerialClient(ModbusSerialClient):

def _send(self, request):
    ''' Sends data on the underlying socket

    :param request: The encoded request to send
    :return: The number of bytes written
                '''
    if not self.socket:
        raise ConnectionException(self.__str__())
    if request:
        self.socket.setRTS(True)
        result = self.socket.write(request)
        time.sleep(0.0022)
        self.socket.setRTS(False)
        return result
    return 0

def _recv(self, size):
    ''' Reads data from the underlying descriptor

    :param size: The number of bytes to read
    :return: The bytes read
    '''
    if not self.socket:
        raise ConnectionException(self.__str__())
    self.socket.setRTS(False)
    result = self.socket.read(size)
    return result

---------------------------------------------------------------------------

configure the client logging

---------------------------------------------------------------------------

import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG)

client = ModbusClient('localhost', port=502)

client = ModbusClient(method='ascii', port='/dev/pts/2', timeout=1)

client = ModbusClient(method='rtu', retry_on_empty=True, retries=10, port='/dev\ /ttyS2', timeout=1, bytesize=8, baudrate=19200, stopbits=1, parity='E')

client.socket = serial.Serial(port='/dev/ttyS2')

client.socket.setRTS(True)

client.connect()

dw = client.write_coil(1, True)

dr = client.read_coils(1,1)

assert(dw.function_code < 0x80)

assert(dr.bits[0] == True)

rq = client.write_register(1, 10, unit = 1) rr = client.read_holding_registers(1, count = 1, unit = 1) print rr.registers[0] assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value

---------------------------------------------------------------------------

close the client

---------------------------------------------------------------------------

client.close()

And my Server code:

      #!/usr/bin/python2.4

'''

from pymodbus.server.async import StartTcpServer

from pymodbus.server.async import StartUdpServer

from pymodbus.server.sync import StartSerialServer from pymodbus.server.sync import ModbusSerialServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer import serial,time

class RtsModbusSerialServer(object):

def _build_handler(self):
    ''' A helper method to create and monkeypatch
        a serial handler.

    :returns: A patched handler
    '''
    def rts_send(message):
        self.socket.setRTS(True)
        result = self.socket.write(message)
        time.sleep(0.0022)
        self.socket.setRTS(False)
        return result

    def rts_recv(count):
        self.socket.setRTS(False)
        result = self.socket.read(count)
        return result

    request = self.socket
    request.send = rts_send
    request.recv = rts_recv
    handler = ModbusSingleRequestHandler(request,
        (self.device, self.device), self)
    return handler

---------------------------------------------------------------------------

configure the service logging

---------------------------------------------------------------------------

import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG)

slaves = { 1: ModbusSlaveContext( di = ModbusSequentialDataBlock(1, [17,18,19,20,21,22])), 2: ModbusSlaveContext( dd = ModbusSequentialDataBlock(1, [17]*100)), }

context = ModbusServerContext(slaves=slaves, single=False)

---------------------------------------------------------------------------

initialize the server information

---------------------------------------------------------------------------

If you don't set this or any fields, they are defaulted to empty strings.

---------------------------------------------------------------------------

identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.0'

---------------------------------------------------------------------------

run the server you want

---------------------------------------------------------------------------

server = ModbusServer(context,identity=identity, port='/dev/ttyS2',

timeout=1,\

bytesize=8, baudrate=9600, stopbits=1, parity='E')

StartTcpServer(context, identity=identity, address=("localhost", 5020))

StartUdpServer(context, identity=identity, address=("localhost", 502))

StartSerialServer(context, identity=identity, port='/dev/ttyS2', timeout=1, byt\ esize=8, baudrate=19200, stopbits=1, parity='E')

StartSerialServer(context, identity=identity, port='/dev/pts/3',

framer=Modbus\ AsciiFramer)

the output after i ran the server and client is : python synclient.py DEBUG:pymodbus.transaction:Running transaction 1 DEBUG:pymodbus.transaction:getting transaction 1 DEBUG:pymodbus.transaction:Running transaction 2 DEBUG:pymodbus.transaction:getting transaction 2 Traceback (most recent call last): File "synclient.py", line 73, in ? print rr.registers[0] AttributeError: 'NoneType' object has no attribute 'registers

On Tue, Oct 29, 2013 at 5:30 PM, Galen Collins notifications@github.comwrote:

No, you can overload classes in your own code. If you want to modify the source that is fine, bit it is harder for me to know how to help you when problems arise if I can't see your whole code base. If you can send me a single file with your subclassing it is much easier for me to help.

So just add those two blocks to the top of your library along with the necessary imports. On Oct 29, 2013 5:12 PM, "heming277" notifications@github.com wrote:

so if i understand correctly i should add this class into the sync.py in both master and slave sync.py?

thanks

Heming

On Tue, Oct 29, 2013 at 4:11 PM, Galen Collins notifications@github.comwrote:

Right, so in server/sync.py, there is the ModbusSerialServer. What you will have to do is something like::

class RtsModbusSerialServer(object):

def _build_handler(self): ''' A helper method to create and monkeypatch a serial handler. :returns: A patched handler '''

def rts_send(message): self.socket.setRTS(True) result = self.socket.write(message) self.socket.setRTS(False) return result

def rts_recv(count): self.socket.setRTS(False) result = self.socket.read(count) self.socket.setRTS(True) return result

request = self.socket request.send = rts_send request.recv = rts_recv handler = ModbusSingleRequestHandler(request, (self.device, self.device), self) return handler

— Reply to this email directly or view it on GitHub< https://github.com/bashwork/pymodbus/issues/33#issuecomment-27352576> .

— Reply to this email directly or view it on GitHub< https://github.com/bashwork/pymodbus/issues/33#issuecomment-27355550> .

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-27356341 .

ghost commented 11 years ago

sorry i need to ask again,

if Modpoll works without any converters, how come my code using pymodbus doesn't work?

is it because it is synchronous and not asynchronous? or is there a bug in my code?

thanks ..

On Wed, Oct 30, 2013 at 1:35 PM, Heming Liu hemingliu123@gmail.com wrote:

ok so it still doesn't work. i have my codes below. would you kindly check if i did anything wrong? thanks very much i've noticed that when using sync client it can only work with async server? but async.py in the library code doesn't have ModbusSerialClient. does Sync server work with sync client?

here's my client code:

!/usr/bin/python2.4

from pymodbus.client.sync import ModbusTcpClient as ModbusClient

from pymodbus.client.sync import ModbusUdpClient as ModbusClient

from pymodbus.client.sync import ModbusSerialClient as ModbusClient from pymodbus.client.sync import ModbusSerialClient import serial,time

class RtsModbusSerialClient(ModbusSerialClient):

def _send(self, request):
    ''' Sends data on the underlying socket

    :param request: The encoded request to send
    :return: The number of bytes written
                '''
    if not self.socket:
        raise ConnectionException(self.__str__())
    if request:
        self.socket.setRTS(True)
        result = self.socket.write(request)
        time.sleep(0.0022)

        self.socket.setRTS(False)
        return result
    return 0

def _recv(self, size):
    ''' Reads data from the underlying descriptor

    :param size: The number of bytes to read
    :return: The bytes read
    '''
    if not self.socket:
        raise ConnectionException(self.__str__())
    self.socket.setRTS(False)
    result = self.socket.read(size)
    return result

---------------------------------------------------------------------------

configure the client logging

---------------------------------------------------------------------------

import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG)

client = ModbusClient('localhost', port=502)

client = ModbusClient(method='ascii', port='/dev/pts/2', timeout=1)

client = ModbusClient(method='rtu', retry_on_empty=True, retries=10, port='/dev\ /ttyS2', timeout=1, bytesize=8, baudrate=19200, stopbits=1, parity='E')

client.socket = serial.Serial(port='/dev/ttyS2')

client.socket.setRTS(True)

client.connect()

dw = client.write_coil(1, True)

dr = client.read_coils(1,1)

assert(dw.function_code < 0x80)

assert(dr.bits[0] == True)

rq = client.write_register(1, 10, unit = 1) rr = client.read_holding_registers(1, count = 1, unit = 1) print rr.registers[0]

assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value

---------------------------------------------------------------------------

close the client

---------------------------------------------------------------------------

client.close()

And my Server code:

      #!/usr/bin/python2.4

'''

from pymodbus.server.async import StartTcpServer

from pymodbus.server.async import StartUdpServer

from pymodbus.server.sync import StartSerialServer from pymodbus.server.sync import ModbusSerialServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer import serial,time

class RtsModbusSerialServer(object):

def _build_handler(self):
    ''' A helper method to create and monkeypatch
        a serial handler.

    :returns: A patched handler
    '''
    def rts_send(message):
        self.socket.setRTS(True)
        result = self.socket.write(message)
        time.sleep(0.0022)

        self.socket.setRTS(False)
        return result

    def rts_recv(count):
        self.socket.setRTS(False)
        result = self.socket.read(count)
        return result

    request = self.socket
    request.send = rts_send
    request.recv = rts_recv
    handler = ModbusSingleRequestHandler(request,
        (self.device, self.device), self)
    return handler

---------------------------------------------------------------------------

configure the service logging

---------------------------------------------------------------------------

import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG)

slaves = {

1:   ModbusSlaveContext(
        di = ModbusSequentialDataBlock(1, [17,18,19,20,21,22])),
2:   ModbusSlaveContext(
        dd = ModbusSequentialDataBlock(1, [17]*100)),

}

context = ModbusServerContext(slaves=slaves, single=False)

---------------------------------------------------------------------------

initialize the server information

---------------------------------------------------------------------------

If you don't set this or any fields, they are defaulted to empty strings.

---------------------------------------------------------------------------

identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.0'

---------------------------------------------------------------------------

run the server you want

---------------------------------------------------------------------------

server = ModbusServer(context,identity=identity, port='/dev/ttyS2',

timeout=1,\

bytesize=8, baudrate=9600, stopbits=1, parity='E')

StartTcpServer(context, identity=identity, address=("localhost", 5020))

StartUdpServer(context, identity=identity, address=("localhost", 502))

StartSerialServer(context, identity=identity, port='/dev/ttyS2', timeout=1, byt\ esize=8, baudrate=19200, stopbits=1, parity='E')

StartSerialServer(context, identity=identity, port='/dev/pts/3',

framer=Modbus\ AsciiFramer)

the output after i ran the server and client is :

python synclient.py DEBUG:pymodbus.transaction:Running transaction 1 DEBUG:pymodbus.transaction:getting transaction 1 DEBUG:pymodbus.transaction:Running transaction 2 DEBUG:pymodbus.transaction:getting transaction 2

Traceback (most recent call last): File "synclient.py", line 73, in ? print rr.registers[0] AttributeError: 'NoneType' object has no attribute 'registers

On Tue, Oct 29, 2013 at 5:30 PM, Galen Collins notifications@github.comwrote:

No, you can overload classes in your own code. If you want to modify the source that is fine, bit it is harder for me to know how to help you when problems arise if I can't see your whole code base. If you can send me a single file with your subclassing it is much easier for me to help.

So just add those two blocks to the top of your library along with the necessary imports. On Oct 29, 2013 5:12 PM, "heming277" notifications@github.com wrote:

so if i understand correctly i should add this class into the sync.py in both master and slave sync.py?

thanks

Heming

On Tue, Oct 29, 2013 at 4:11 PM, Galen Collins < notifications@github.com>wrote:

Right, so in server/sync.py, there is the ModbusSerialServer. What you will have to do is something like::

class RtsModbusSerialServer(object):

def _build_handler(self): ''' A helper method to create and monkeypatch a serial handler. :returns: A patched handler '''

def rts_send(message): self.socket.setRTS(True) result = self.socket.write(message) self.socket.setRTS(False) return result

def rts_recv(count): self.socket.setRTS(False) result = self.socket.read(count) self.socket.setRTS(True) return result

request = self.socket request.send = rts_send request.recv = rts_recv handler = ModbusSingleRequestHandler(request, (self.device, self.device), self) return handler

— Reply to this email directly or view it on GitHub< https://github.com/bashwork/pymodbus/issues/33#issuecomment-27352576> .

— Reply to this email directly or view it on GitHub< https://github.com/bashwork/pymodbus/issues/33#issuecomment-27355550> .

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-27356341 .

bashwork commented 11 years ago

They are doing the bit banging just like you would be doing (the -4 flag). It doesn't really matter if you use sync or async. I just don't know the timing of how long the flags should be held and how much time it takes to move between the python interpreter and the OS (to bring the serial lines high and low). It really is a duct tape solution and may require a bit of hacking to get it working correctly.

As I don't have any devices, I really cannot do much to help you with debugging, sorry.

ghost commented 11 years ago

but does my code look correct?

thanks

On Thu, Nov 7, 2013 at 11:34 PM, Galen Collins notifications@github.comwrote:

They are doing the bit banging just like you would be doing (the -4 flag). It doesn't really matter if you use sync or async. I just don't know the timing of how long the flags should be held and how much time it takes to move between the python interpreter and the OS (to bring the serial lines high and low). It really is a duct tape solution and may require a bit of hacking to get it working correctly.

As I don't have any devices, I really cannot do much to help you with debugging, sorry.

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-28042549 .

bashwork commented 11 years ago

Looks okay, but I would really need to play with the code while looking at a wire trace of the serial line to see how things are actually working.

ghost commented 10 years ago

seems like it just won't work..

do anyone has other suggestions?

thanks

On Fri, Nov 8, 2013 at 3:54 PM, Galen Collins notifications@github.comwrote:

Looks okay, but I would really need to play with the code while looking at a wire trace of the serial line to see how things are actually working.

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-28107935 .

ghost commented 10 years ago

when i'm getting transcations from the client side,

the server side doesn't have any output after i start the sync server.

does that mean my client's request never got to the server?

On Mon, Nov 11, 2013 at 3:51 PM, Heming Liu hemingliu123@gmail.com wrote:

seems like it just won't work..

do anyone has other suggestions?

thanks

On Fri, Nov 8, 2013 at 3:54 PM, Galen Collins notifications@github.comwrote:

Looks okay, but I would really need to play with the code while looking at a wire trace of the serial line to see how things are actually working.

— Reply to this email directly or view it on GitHubhttps://github.com/bashwork/pymodbus/issues/33#issuecomment-28107935 .

dhoomakethu commented 3 years ago

closing stale issue

mjm987 commented 3 years ago

I know this is is old thread but following works for me like a charm by .. a) overloading ModbusSerialClient and b) calculate the correct RTS delay time

class MyModbusSerialClient(ModbusSerialClient):
  def __init__(self, **args):
    super(MyModbusSerialClient, self).__init__(**args)

  def _send(self, request):
    if not self.socket:
        raise ConnectionException(self.__str__())
    if request:
        print("in _send")
        txtime = (len(request)+1) * (10.0 / self.baudrate)
        self.socket.setRTS(False)
        time.sleep(0.001)
        rc = self.socket.write(request)
        time.sleep(txtime)
        self.socket.setRTS(True)
        return rc
    return 0

client = MyModbusSerialClient(method='rtu', port='/dev/ttyUSB0', baudrate=4800)
...