-1

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

Brits
  • 14,829
  • 2
  • 18
  • 31

2 Answers2

3

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.

Brits
  • 14,829
  • 2
  • 18
  • 31
0

SlaveFailure means that something goes wrong inside your server implementation. pymodbus is a complexity minefield, but one thing I think is wrong in your server code is the line

server_thread = StartTcpServer()

I think you should pass the serverContext directly here:

server_thread = StartTcpServer(serverContext)

I think right now you are starting a server with no context and therefore no values at all.

I wrote my own Modbus server implementation after being frustrated with pymodbus, its very simple and doesn't support every Modbus feature, but maybe it helps in your case: https://github.com/ckattmann/modbus_server.

Minimal example:

import modbus_server

s = modbus_server.Server(port=502)  # Port<1000 requires sudo
s.start()  # Start the seperate server thread
s.set_coil(1,True)
set_input_registers(start_address=1, values=[1,2,3,4,5], encoding='H')  # encode as 5 16-bit shorts

# will run until you call s.stop()
ckattmann
  • 36
  • 4
  • Looks like ‘’’StartTcpServer()’’’ takes 0 arguments, so therefore I can’t use ‘’’StartTcpServer(serverContext)’’’ but I will take a look at your implementation and see if that works – rasmusdamlarsen May 17 '23 at 05:46
  • Unfortunately i can't seem to get your implementation to work – rasmusdamlarsen May 17 '23 at 08:49
  • `StartTcpServer()` takes **kwargs, which are blindly passed to `StartAsyncTcpServer()`, which passes them on to `ModbusTcpServer()` which does nothing with them, but they are somehow picked up by `ModbusBaseRequestHandler()` - thats what I meant with 'complexity minefield'. If you don't include context in these kwargs, pymodbus will happily start a server with no Modbus addresses available. – ckattmann May 18 '23 at 11:20
  • What didn't work? I really interested to get some feedback so I can improve the documentation! I included an example in the response now. – ckattmann May 18 '23 at 11:27