Skip to content

Virtual Slave Modbus RTU #2649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
R-a-f-a-e-l-s opened this issue Apr 24, 2025 · 5 comments
Closed

Virtual Slave Modbus RTU #2649

R-a-f-a-e-l-s opened this issue Apr 24, 2025 · 5 comments

Comments

@R-a-f-a-e-l-s
Copy link

R-a-f-a-e-l-s commented Apr 24, 2025

I'm trying to simulate a Modbus RTU slave through a COM port using a USB/RS485 converter. The problem is that the slave only sends zeros instead of the simulated data.

**# Code
from pymodbus.server import StartSerialServer
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext

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

def run_single_port_multi_slave_server():
slaves = {}
for i in range(0, 17): # Crear 16 esclavos con IDs del 1 al 16
slave_id = i
# Inicializar los registros 40001-40008 para cada esclavo
initial_values = [slave_id * 10 + j for j in range(10)]
store = ModbusSlaveContext(
hr=ModbusSequentialDataBlock(0, initial_values)
)
slaves[slave_id] = store

# Crear el contexto del servidor con los múltiples esclavos
context = ModbusServerContext(slaves=slaves, single=False)

# Configuración del puerto serie (todos los esclavos en el mismo puerto)
settings = {
    'port': 'COM9',
    'baudrate': 19200,
    'bytesize': 8,
    'parity': 'N',
    'stopbits': 1
}


try:
    # Iniciar el servidor Modbus RTU
    StartSerialServer(context, **settings)
    print(f"Servidor Modbus RTU simulando 16 esclavos (IDs 1-16) en el puerto {settings['port']}...")
    print("Cada esclavo responderá a su unidad ID en el mismo puerto.")
except Exception as e:
    print(f"Error al iniciar el servidor Modbus RTU: {e}")
    print(f"Asegúrate de que el puerto {settings['port']} existe y no está en uso.")

if name == "main":
run_single_port_multi_slave_server()**

Output:

DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:pymodbus.logging:Awaiting connections server_listener
INFO:pymodbus.logging:Server listening.
DEBUG:pymodbus.logging:Connected to server
DEBUG:pymodbus.logging:recv: 0x1 0x3 0x0 0x0 0x0 0x8 0x44 0xc old_data: addr=None
DEBUG:pymodbus.logging:Processing: 0x1 0x3 0x0 0x0 0x0 0x8 0x44 0xc
DEBUG:pymodbus.logging:decoded PDU function_code(3 sub -1) -> ReadHoldingRegistersRequest(dev_id=0, transaction_id=0, address=0, count=8, bits=[], registers=[], status=1)
DEBUG:pymodbus.logging:Frame advanced, resetting header!!
DEBUG:pymodbus.logging:getValues: fc-[3] address-1: count-8
DEBUG:pymodbus.logging:send: 0x1 0x3 0x10 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xe4 0x59
DEBUG:pymodbus.logging:recv: 0x2 0x3 0x0 0x0 0x0 0x8 0x44 0x3f old_data: addr=None
DEBUG:pymodbus.logging:Processing: 0x2 0x3 0x0 0x0 0x0 0x8 0x44 0x3f
DEBUG:pymodbus.logging:decoded PDU function_code(3 sub -1) -> ReadHoldingRegistersRequest(dev_id=0, transaction_id=0, address=0, count=8, bits=[], registers=[], status=1)
DEBUG:pymodbus.logging:Frame advanced, resetting header!!
DEBUG:pymodbus.logging:getValues: fc-[3] address-1: count-8
DEBUG:pymodbus.logging:send: 0x2 0x3 0x10 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xa0 0x1d

pymodbus
Version: 3.9.2

Windows 10 Pro

@janiversen
Copy link
Collaborator

slaves cannot have id=0 that is reserved for broadcast.

Apart from that the code looks ok, after just a short glance.

As you can see in our examples the sequential data block is tested with the value 17 (more details in the test harness) so I am not sure why you get zeroes back, did you try with just one slave?

@R-a-f-a-e-l-s
Copy link
Author

slaves cannot have id=0 that is reserved for broadcast.

Apart from that the code looks ok, after just a short glance.

As you can see in our examples the sequential data block is tested with the value 17 (more details in the test harness) so I am not sure why you get zeroes back, did you try with just one slave?

Thanks for the note! Just to clarify, I'm not actually using slave id=0—that’s what’s puzzling me.

I also tried running the test with just a single slave, but I'm still getting all zeroes in return. I’ll double-check again in case I missed something subtle, but at this point it seems like there might be something else going on.

Appreciate any further suggestions you might have!

@janiversen
Copy link
Collaborator

if you look at the examples:

server_async.py, you will find it defines a sequential datastore that contains

ModbusSequentialDataBlock(0x00, [17] * 100)

and in client_async.py you will find:

    rr = await client.read_holding_registers(4, count=2, slave=1)
    assert rr.registers[0] == 17
    assert rr.registers[1] == 17

so you see there are examples that controls that data get back, these examples are run as part of every pull request (with a single slave), so I am quite sure that works.

your code contains for i in range(0, 17)so you define device_id 0-16.

@janiversen
Copy link
Collaborator

Did you solve your problem?

@R-a-f-a-e-l-s
Copy link
Author

Yes, I solved it, thank a lot

My final code:

from pymodbus.server import StartSerialServer
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext

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

def run_single_port_multi_slave_server():
slaves = {}
for i in range(1, 16): # Crear 16 esclavos con IDs del 1 al 16
slave_id = i
# Inicializar los registros 40001-40008 para cada esclavo
#initial_values = [slave_id * 10 + j for j in range(8)]
#store = ModbusSlaveContext(
#hr=ModbusSequentialDataBlock(0, initial_values)
#)
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [0]*100),
co=ModbusSequentialDataBlock(0, [0]*100),
hr=ModbusSequentialDataBlock(0, [0]*100),
ir=ModbusSequentialDataBlock(0, [0]*100))
slaves[slave_id] = store

# Crear el contexto del servidor con los múltiples esclavos
slaves[15].setValues(3, 0, [1, 2, 3,4,5,6,7,8])
context = ModbusServerContext(slaves=slaves, single=False)

# Configuración del puerto serie (todos los esclavos en el mismo puerto)
settings = {
    'port': 'COM12',
    'baudrate': 19200,
    'bytesize': 8,
    'parity': 'N',
    'stopbits': 1
}

try:
    # Iniciar el servidor Modbus RTU
    StartSerialServer(context, **settings)
    print(f"Servidor Modbus RTU simulando 16 esclavos (IDs 1-16) en el puerto {settings['port']}...")
    print("Cada esclavo responderá a su unidad ID en el mismo puerto.")
except Exception as e:
    print(f"Error al iniciar el servidor Modbus RTU: {e}")
    print(f"Asegúrate de que el puerto {settings['port']} existe y no está en uso.")

if name == "main":
run_single_port_multi_slave_server()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants