1

I'm new to pymodbus. I am trying to create a custom request to be sent to a TCP client, this specific request needs to have 3 exclamation marks at the beginning of the message. an example message request would be 21 21 21 01 7F 07 40 01 00 88 00 00 in hex, I've tried to follow the custom message example from the pymodbus documentation examples and change the encode function in the request class but I'm not getting any response back. is there any way I can send this specific message and then read the response from the modbus device? this specific function should return the date and time in the device. Thanks!

class CustomBSCRACRequest(ModbusRequest):
    """Custom modbus request."""

    function_code = 127
    _rtu_frame_size = 8
    command = 136
    flags = 64
    dest_task = 0

    def __init__(self, address=None, **kwargs):
        """Initialize."""
        ModbusRequest.__init__(self, **kwargs)
        self.address = address
        self.count = 7

    def encode(self):
        """Encode."""
        return struct.pack(">HHHHHHHHH", 33, 33, 33, self.address, self.count, self.flags, self.address, self.dest_task, self.command)
djvg
  • 11,722
  • 5
  • 72
  • 103
  • Hello Juan, one question: what **modbus device** are you testing against? what is it supposed to answer back? – Marcos G. Jun 14 '22 at 13:24
  • Hi @MarcosG. thanks for replying It's a Brodersen RTU8 and an example response would be _21 21 21 01 7F 0E C0 01 00 00 1E 12 0D 1E 06 62 02 00 00_ keep in mind that the last 2 00 00 are my CRC which is an example, not the real CRC, that's the same case for the request. – Juan Navarro Jun 15 '22 at 01:01
  • I see... The info I can find indicates this device is Modbus compliant, can you point to any documents that explain those custom frames? – Marcos G. Jun 15 '22 at 07:40
  • This is part of an internal document, I can't share more than this: _The RTU8 system is based on the Intel DCX51 distributed control executive. This system uses standard RAC commands which are used with the bitbus system.... BCSRAC commands are additional to normal RAC commands to enable functions these RAC commands are transmitted using the Modbus RTU mode protocol. In order to be able to distinguish between Modbus and RAC requests, all RAC requests will be padded with “!!!”. A RAC request will be sent as. !!!<5 byte RAC header><2 byte CRC>_ – Juan Navarro Jun 16 '22 at 00:00
  • Great, that's what I was looking for. I will take a look at your code later today. Just to be sure: Have you verified normal Modbus traffic works on your device? – Marcos G. Jun 16 '22 at 05:29
  • Yes, function 3 (reading holding registers) works fine – Juan Navarro Jun 16 '22 at 06:47
  • great, that's something... I have been looking at your code but I don't see why you add the address twice, among other things... If you can't post a fully functional example you can try something like [this](https://stackoverflow.com/questions/57056042/i-am-sending-commands-through-serial-port-in-python-but-they-are-sent-multiple-t/57062146#57062146) to monitor what is going on through your port. – Marcos G. Jun 17 '22 at 06:23

1 Answers1

1

To solve this problem I used created a new framer that edits the header of the message that is sent to the device. Pymodbus uses framers that are responsible to create the bytes message, in this case, I've created a framer that inherits from ModbusRtuFramer and overwritten the buildPacket method by adding the needed header. I also needed to overwrite the argument _hsize to the right size of my new header (everything before the function code) in my case this was 4. Lastly, because this is a special request message, it doesn't have the unit ID in the same position as normal RTU responses, thus, the populateHeader method was modified as well to point to the right byte for the unit ID. This is a very particular case but in general, what someone with a similar problem needs to do is create a new framer class and modify the buildPacket method initially and then all the other header-dependent methods. After creating the framer, it is easier to follow the custom message example. I included the buildPacket method for my particular case

def buildPacket(self, message) -> bytes:
    data = message.encode()
    packet = struct.pack(
        ">BBBBB",
        33,
        33,
        33,
        message.unit_id,
        message.function_code,
    )
    packet += data
    packet += struct.pack(">H", computeCRC(packet))
    message.transaction_id = message.unit_id
    return packet