2

I have an issue with a simple pymodbus server implementation. From what I have read in the docs, this implementation should have unique slave contexts for each slave device, i.e. writing to device 0x01, register address 1, should be a different register from device 0x02, register 1.

In my case, writing to register 1 writes to register 1 for ALL slave addresses. Could someone go over my server code to see if I am missing something, or perhaps clarify if my understanding of how the pymodbus server is supposed to work with the single flag set to False.

Cheers. Code here:

#!/usr/bin/env python3
"""
Pymodbus Synchronous Server
--------------------------------------------------------------------------

This synced server is implemented using TCP, with multiple slave contexts
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

def run_server():

    slaves = {

        0x01: ModbusSlaveContext(),
        0x02: ModbusSlaveContext()

    }

    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/riptideio/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = '2.3.0'

    # ----------------------------------------------------------------------- #
    # run the server
    # ----------------------------------------------------------------------- #
    StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020))

if __name__ == "__main__":
    run_server()

1 Answer 1

2

I had a similar issue with the RTU server and had more a less the same code as you otherwise.

But for me it was that I not had made separat ModbusSlaveContext objects in the slave dict. But that is not the case in your code.

I share my code here maybe it help someone.

Python code:
    #!/usr/bin/env python
    """
    Pymodbus Server With Updating Thread
    --------------------------------------------------------------------------
    
    This is an example of having a background thread updating the
    context while the server is operating. This can also be done with
    a python thread::
    
        from threading import Thread
    
        thread = Thread(target=updating_writer, args=(context,))
        thread.start()
    """
    # --------------------------------------------------------------------------- #
    # import the modbus libraries we need
    # --------------------------------------------------------------------------- #
    from pymodbus.server.asynchronous import StartSerialServer
    from pymodbus.device import ModbusDeviceIdentification
    from pymodbus.datastore import ModbusSequentialDataBlock
    from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
    from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
    
    # --------------------------------------------------------------------------- #
    # import the payload builder
    # --------------------------------------------------------------------------- #
    from pymodbus.constants import Endian
    from pymodbus.payload import BinaryPayloadDecoder
    from pymodbus.payload import BinaryPayloadBuilder
    
    # --------------------------------------------------------------------------- #
    # import the twisted libraries we need
    # --------------------------------------------------------------------------- #
    from twisted.internet.task import LoopingCall
    
    # --------------------------------------------------------------------------- #
    # configure the service logging
    # --------------------------------------------------------------------------- #
    import logging
    logging.basicConfig()
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)
    
    # --------------------------------------------------------------------------- #
    # define your callback process
    # --------------------------------------------------------------------------- #
    
    
    def updating_writer(a):
        """ A worker process that runs every so often and
        updates live values of the context. It should be noted
        that there is a race condition for the update.
    
        :param arguments: The input arguments to the call
        """
        context = a[0]
        register = 3
        
        #### Write to registers slave 1 ####
        slave_id = 0x01
        log.debug(f"::: Make payload to SLAVE={slave_id} :::")
    
        # Total energy
        builder = BinaryPayloadBuilder(
            byteorder=Endian.Big,
            wordorder=Endian.Little
            )
        builder.add_32bit_int(20000)  # kWh Tot*10
        energy = builder.to_registers()
    
        # Phase 1 variables
        builder = BinaryPayloadBuilder(
            byteorder=Endian.Big,
            wordorder=Endian.Little
            )
        builder.add_32bit_int(4000)  # VL1L2*10
        builder.add_32bit_int(2300)  # VL1N*10
        builder.add_32bit_int(1000)  # AL1*1000
        builder.add_32bit_int(2300)  # kWL1*10
        phase1 = builder.to_registers()
    
        log.debug(f"::: Write registers to SLAVE={slave_id} :::")
        context[slave_id].setValues(register, 0x0112, energy)
        context[slave_id].setValues(register, 0x011e, phase1)
        context[slave_id].setValues(register, 0x000b, [0x0155])
    
        #### Write to registers slave 100 ####
        slave_id = 0x64
        log.debug(f"::: Make payload to SLAVE={slave_id} :::")
    
        # Total energy
        builder = BinaryPayloadBuilder(
            byteorder=Endian.Big,
            wordorder=Endian.Little
            )
        builder.add_32bit_int(20000)  # kWh Tot*10
        energy = builder.to_registers()
    
        # Phase 1 variables
        builder = BinaryPayloadBuilder(
            byteorder=Endian.Big,
            wordorder=Endian.Little
            )
        builder.add_32bit_int(4000)  # VL1L2*10
        builder.add_32bit_int(2300)  # VL1N*10
        builder.add_32bit_int(2000)  # AL1*1000
        builder.add_32bit_int(4600)  # kWL1*10
        phase1 = builder.to_registers()
    
        log.debug(f"::: Write registers to SLAVE={slave_id} :::")
        context[slave_id].setValues(register, 0x0112, energy)
        context[slave_id].setValues(register, 0x011e, phase1)
        context[slave_id].setValues(register, 0x000b, [0x0155])
    
    
    def run_updating_server():
        # ----------------------------------------------------------------------- # 
        # initialize your data store
        # ----------------------------------------------------------------------- # 
    
        addresses = [1, 100]
        slaves = {}
        for adress in addresses:
            store = ModbusSlaveContext(zero_mode=True)
            slaves.update({adress: store})
    
        context = ModbusServerContext(slaves=slaves, single=False)
    
        # ----------------------------------------------------------------------- # 
        # initialize the server information
        # ----------------------------------------------------------------------- # 
        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 = '2.3.0'
    
        # ----------------------------------------------------------------------- # 
        # run the server you want
        # ----------------------------------------------------------------------- # 
        time = 5  # 5 seconds delay
        loop = LoopingCall(f=updating_writer, a=(context,))
        loop.start(time, now=False) # initially delay by time
        StartSerialServer(
            context=context,
            framer=ModbusRtuFramer,
            identity=identity,
            port='/dev/ttyUSB0',
            timeout=0.0001,
            baudrate=9600,
            parity='N',
            bytesize=8,
            stopbits=1,
            ignore_missing_slaves=True)

if __name__ == "__main__":
    run_updating_server()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.