AdvancedClimateSystems / uModbus

Python implementation of the Modbus protocol.
Mozilla Public License 2.0
211 stars 81 forks source link

Multiple slaves with RTU #127

Open AareAa opened 7 months ago

AareAa commented 7 months ago

hello, how to work with multiple slaves RTU, e.g. 1,2,3? After initialising port with calling Serial(), got connection an reads (and writes) with slave_id=1 ok. Putting slave_id=2 and slave_id=3 don't work, something get messed, wrong answers. Only way is to init new Serial(), but sometimes "could not open port 'COM7': PermissionError(13, 'Access is denied.', None, 5)". Have to reset COM port/USB device. So looking way to change slave without new Serial()? There seems to be no function setID()...

OrangeTux commented 7 months ago

I'm not surprised that creating multiple instance of serial.Serial() doesn't work. Usually, access to a serial device can't be shared.

I'm surprised that you run into issues when executing requests on slave id's 2 and 3. This library doesn't do any magic when encoding the slave id in the request. It just encode the slave id to a byte.

I'm wondering if your serial network is unstable. Can you leave only 1 device with slave id 2 on the serial network and disconnect all other devices. And than, try to interact with this device. Is the communication now stable?

AareAa commented 7 months ago

Thanks for answer, not creating multiple instances, but closing on slave change previous serial(), then reopening, delay 0.2 sec. Tried 0.1 sec also, same results. With close/open works, but have to reset COM/USB sometimes, 2-3 times a month. Client operates on 3 slaves. Seems, that something going wrong in serial() when requesting with slave_id=2 or 3, without renewing serial(). Serial network is stable, all working otherways, polling 3 slaves in each 7 sec. But have to be a way? Some other librarys have function setID(), but lot to test/programming probably.

AareAa commented 7 months ago

Multiple slaves works, sorry, my error. But this thread is useful I think, in Internet there is no information about using multiple slaves simultaneously, example code can be reachable maybe.

MachineSaver commented 3 months ago

I am running into the same, or what reads like a very similar issue.

If I change the value of the serial slaveAddress in line #7 to anything other than 1 the ModbusRTU server stops seeing the function call requests to read and write data that originate with the ModbusRTU client.

from serial import Serial
from collections import defaultdict
from umodbus.server.serial import get_server
from umodbus.server.serial.rtu import RTUServer

s = Serial('COM13')
s.timeout = 1
s.slaveAddress = 1
s.baudrate = 115200
s.bytesize = 8
s.parity = 'N'
s.stopbits = 1

data_store = defaultdict(int)
app = get_server(RTUServer, s)

@app.route(slave_ids=[1], function_codes=[3, 4], addresses=list(range(0, 32)))
def read_data_store(slave_id, function_code, address):
    """Return value of address."""
    print(f'Register address {address} is being read.')
    return data_store[address]

@app.route(slave_ids=[1], function_codes=[6, 16], addresses=list(range(0, 32)))
def write_data_store(slave_id, function_code, address, value):
    """ Set value for address. """
    print(f'Register address {address} is being written to.')
    data_store[address] = value

if __name__ == '__main__':
    try:
        app.serve_forever()
    finally:
        app.shutdown()

You can see that the device works if the slave address is 1. image

Here you can see that the device doesn't work with slave address 207 (note that I have tried to change this value to 0, 99, 137, 202 but I get no communication unless the value is set to 1) image

Any help would be much appreciated.

AareAa commented 3 months ago

With my example was problem, that in specific RTU library (not umodbus) slave_id did'nt change, so I forced it. Not going to details, might worth to try smaller slave address, e.g. if 1 is working, use 2 instead 99 etc.

MachineSaver commented 3 months ago

With my example was problem, that in specific RTU library (not umodbus) slave_id did'nt change, so I forced it. Not going to details, might worth to try smaller slave address, e.g. if 1 is working, use 2 instead 99 etc.

I appreciate the suggestion. I have tried many many different slave addresses and other tricks to try to get it going. If implemented appropriately it should accept values for the RTU from 1 up to 255 (1 byte).

I mean at this point my project is urgent enough where I may comment out/edit the section of the code where the slave address is parsed or allow the system to respond to any slaveID. Ideally I could just set the slaveID the same as if it was set to 1 but instead using a different value. If I can figure that out my plan is to simply give the same data Store response no matter which slaveID is in the request.

Thanks for any help or suggestions!

MachineSaver commented 3 months ago

Found the issue for future users/readers

from serial import Serial
from collections import defaultdict
from umodbus.server.serial import get_server
from umodbus.server.serial.rtu import RTUServer

s = Serial('COM13')
s.timeout = 1
s.slaveAddress = 1 #THIS IS NOT WHERE THE MODBUS RTU SERVER ADDRESS IS SET, SEE BOLDED SECTIONS BELOW
s.baudrate = 115200
s.bytesize = 8
s.parity = 'N'
s.stopbits = 1

data_store = defaultdict(int)
app = get_server(RTUServer, s)

@app.route(slave_ids=[99], function_codes=[3, 4], addresses=list(range(0, 32)))

def read_data_store(slave_id, function_code, address):
    """Return value of address."""
    print(f'Register address {address} is being read.')
    return data_store[address]

@app.route(slave_ids=[99], function_codes=[6, 16], addresses=list(range(0, 32)))

def write_data_store(slave_id, function_code, address, value):
    """ Set value for address. """
    print(f'Register address {address} is being written to.')
    data_store[address] = value

if __name__ == '__main__':
    try:
        app.serve_forever()
    finally:
        app.shutdown()
MachineSaver commented 3 months ago

You can set a single integer to use a single slaveID in a list or multiple integers in the list to respond to slave addresses.