I am doing some testing with Modbus TCP and I am struggling to understand the theoretical speed limit calculated here regarding maximum number of registers scanned per second.
My test is using a beaglebone to host a modbus server (slave) which I poll from my laptop. Both devices are on a private subnet and they are the only two devices on that subnet. The server and client are implemented in python with pymodbus. As a test I used block reads to poll 6000 holding registers (125 registers in each of 48 block reads), and it took 0.17163 seconds. This translates to a scan rate of about 35,000 registers/second. This a fast, but it is no where near the theoretical limit for the Base100T interface as calculated in the page linked above.
According to that page, the theoretical limit for Base100T connection should be somewhere around 3,600,000 registers/second. I realize that there will be some losses due to the throughput of the python program etc, however it's so much lower than the theoretical that I am having a hard time understanding where those major losses are. Where could the throughput be limited?
Below is a listing of the code I used for this test:
Server (Beaglebone)
from pymodbus.server.sync import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
def run_server():
holding_registers = ModbusSequentialDataBlock(0,[i for i in range(15000)])
store = ModbusSlaveContext(hr = holding_registers)
context = ModbusServerContext(slaves=store, single=True)
StartTcpServer(context, address=("localhost", 5020))
if __name__ == "__main__":
run_server()
Client (Laptop)
import time
import math
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
modbus = ModbusClient('192.168.0.10', port=5020)
modbus.connect()
def timeit(f):
def timed(*args, **kw):
ts = time.time()
result = f(*args, **kw)
te = time.time()
print('func: {} took: {} sec'.format(f.__name__, te-ts))
return result
return timed
@timeit
def block_read_arbitrary(bus, n):
last_read = n % 125
if last_read > 0:
nreads = int(math.ceil(n/125))
else:
nreads = int(n/125)
print("Reading {} Blocks...".format(nreads))
data = []
for i in range(nreads):
if i == nreads - 1 and last_read > 0:
rr = bus.read_holding_registers(i*125, last_read)
data.extend(rr.registers)
else:
rr = bus.read_holding_registers(i*125,125)
data.extend(rr.registers)
return data
block_read_arbitrary(bus, 6000)