pythonmodbusminimalmodbus

Python Minimal Modbus CRC byte order


I'm looking to use Minimal Modbus to connect to my Solar Invertor (GivEnergy) via an RS485-USB adaptor. Unfortunately the implementation of the Modbus Protocol has reversed the CRC bytes, so Minimal Modbus fails on the CRC check. Is there a way of reversing the CRC bytes in the call, perhaps with an option flag, or can I manually build the CRC and inject my own?


Solution

  • You don't need to inject your own CRC, that would be more difficult than in fact is to solve your problem (you are free to do it if you want of course).

    Just change this line in your minimalmodbus.py:

    return _num_to_twobyte_string(register, lsb_first=True)
    

    to:

    return _num_to_twobyte_string(register, lsb_first=False)
    

    You can find the location of the file running this command: python -m site and looking for the site-packages folder.

    On most Linux distros with Python 3.x the folder is:

    '/home/your_user_name/.local/lib/python3.x/site-packages'

    If you are asking why should you be messing with the code, you are not alone. While I was musing that very same question myself (after writing this answer I swear) I found this issue.

    I guess the short answer is: you should be complaining to the developers of your device.

    For completeness: if you don't want to edit minimalmodbus.py as the answer to the issue recommends you should just overload the whole _calculate_crc_string(inputstring) function changing the last line as I wrote above.

    For total completeness, I'm adding the code on the link above here. To overload the function just write all its code again in your Python script right after you import minimalmodbus:

    import minimalmodbus
    
    def _calculate_crc_string(inputstring):
        """Calculate CRC-16 for Modbus.
    
        Args:
            inputstring (str): An arbitrary-length message (without the CRC).
    
        Returns:
            A two-byte CRC string, where the least significant byte is first.
    
        """
        minimalmodbus._check_string(inputstring, description="input CRC string")
    
        # Preload a 16-bit register with ones
        register = 0xFFFF
    
        for char in inputstring:
            register = (register >> 8) ^ minimalmodbus._CRC16TABLE[(register ^ ord(char)) & 0xFF]
    
        return minimalmodbus._num_to_twobyte_string(register, lsb_first=False)
    
    minimalmodbus._calculate_crc_string = _calculate_crc_string
    

    Note the last line outside of the function to overload the _calculate_crc_string. After that, you can add the rest of your code.