Search code examples
pythonmodbusmodbus-tcp

How do i make a modbus simulation?


I am trying to make a Modbus simulation in python and a Modbus client to read the values from the server.

But i keep getting this error: error screenshot

Here is my server code:

import random
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
from pymodbus.server.async_io import StartTcpServer
from pymodbus.server.async_io import ModbusTcpServer

# Define the Modbus registers
coils = ModbusSequentialDataBlock(1, [False] * 100)
discrete_inputs = ModbusSequentialDataBlock(1, [False] * 100)
holding_registers = ModbusSequentialDataBlock(1, [0] * 100)
input_registers = ModbusSequentialDataBlock(1, [0] * 100)

temperature_values = [random.uniform(4, 15) for _ in range(7)]
flow_values = [random.uniform(-1, 1) for _ in range(2)]

holding_registers.setValues(0, temperature_values)
holding_registers.setValues(10, flow_values)

# Define the Modbus slave context
slave_context = ModbusSlaveContext(
    di=discrete_inputs,
    co=coils,
    hr=holding_registers,
    ir=input_registers
)

# Define the Modbus server context
server_context = ModbusServerContext(slaves=slave_context, single=True)

# Start the Modbus TCP server
server = ModbusTcpServer(context=server_context, address=("localhost", 502))
server_thread = StartTcpServer()

# Start the server in a separate thread
if __name__ == "__main__":
    server_thread.start()

and this is my client:

from pymodbus.client.tcp import ModbusTcpClient
from time import sleep

# Connect to the Modbus TCP server
client = ModbusTcpClient('localhost', port=502)

# Read the values from the Modbus registers

coils = client.read_coils(address=1, count=100)
discrete_inputs = client.read_discrete_inputs(address=1, count=100)
holding_registers = client.read_holding_registers(address=1, count=100)
input_registers = client.read_input_registers(address=1, count=100)

# Print the values
while True:
    print("Coils:", coils)
    print("Discrete Inputs:", discrete_inputs)
    print("Holding Registers:", holding_registers)
    print("Input Registers:", input_registers)
    sleep(5)

# Close the Modbus TCP client
#client.close()

This is my first time using Modbus, so I would appreciate the help

I have tried using different software such as Modscan and CAS Mosbus Scanner, but didn't seem to help


Solution

  • There are a number of issues with your code; for example you call ModbusTcpServer but don't make use of the server that is returned. I've edited your code so that it runs and, hopefully, demonstrates the features you appear to require. This might not be exactly what you want but should provide a starting point.

    import random
    import logging
    from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
    from pymodbus.server.async_io import StartTcpServer
    
    # Enable logging (makes it easier to debug if something goes wrong)
    logging.basicConfig()
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)
    
    # Define the Modbus registers
    coils = ModbusSequentialDataBlock(1, [False] * 100)
    discrete_inputs = ModbusSequentialDataBlock(1, [False] * 100)
    holding_registers = ModbusSequentialDataBlock(1, [0] * 100)
    input_registers = ModbusSequentialDataBlock(1, [0] * 100)
    
    temperature_values = [random.randint(4, 15) for _ in range(7)]
    holding_registers.setValues(1, temperature_values)
    print("temperature_values:", temperature_values)
    
    
    # Define the Modbus slave context
    slave_context = ModbusSlaveContext(
        di=discrete_inputs,
        co=coils,
        hr=holding_registers,
        ir=input_registers
    )
    
    # Define the Modbus server context
    server_context = ModbusServerContext(slaves=slave_context, single=True)
    
    # Start the Modbus TCP server
    StartTcpServer(context=server_context, address=("localhost", 502))
    

    and a basic client (yours was printing the same data continually):

    from pymodbus.client.tcp import ModbusTcpClient
    
    # Connect to the Modbus TCP server
    client = ModbusTcpClient('localhost', port=502)
    
    # Read the values from the Modbus registers
    coils = client.read_coils(address=0, count=100)
    discrete_inputs = client.read_discrete_inputs(address=0, count=100)
    holding_registers = client.read_holding_registers(address=0, count=100)
    input_registers = client.read_input_registers(address=0, count=100)
    
    # Should check for errors here... i.e.
    if coils.isError():
        print('Error getting coils: {coils}')
        raise Exception('Error getting coils: {coils}') # trying to display coils.bits would fail
    
    
    # Print the values
    print("Coils:", coils.bits)
    print("Discrete Inputs:", discrete_inputs.bits)
    print("Holding Registers:", holding_registers.registers)
    print("Input Registers:", input_registers.registers)
    
    # Close the Modbus TCP client
    client.close()
    

    Running the server I get:

    python ./server.py
    temperature_values: [4, 4, 15, 13, 8, 14, 10]
    DEBUG:asyncio:Using proactor: IocpProactor
    INFO:pymodbus.logging:Server(TCP) listening.
    (followed by more stuff when a query is made)
    

    and the client:

    Coils: [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, Fal
    se, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
    Discrete Inputs: [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, 
    False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
    Holding Registers: [4, 4, 15, 13, 8, 14, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    Input Registers: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    

    You will note that the random values are returned as expected.