Search code examples
pythonminimalmodbus

minimalmodbus read data bits


data blocks I cant seem to be able to read

I'm only quite new to python and modbus and I have been struggling to work out how to read the MSBytes and LSBytes of this controller using both pymodbus and minimalmodbus for a week or two now so hopefully someone in the brains trust here might be able to poke me in the right direction.

This particular controller has 3 digital/coil registers (2 register addresses are read only with 8 MSBytes and 8 LSBytes and one register 1536 as pictured above which has read and write 8 MSbytes and 8 LSBytes) However I'm confused because I can't seem to be able to work out how to read them correctly.

I only seem to get errors when I try to read them with a read_coil/bits only function but read_register and read_registers functions returns a single boolean result of 0 or 1 with a count of 1 register.

Using minimalmodbus

instrument.read_register(1536)

returns: 0

instrument.read_registers(1536, 1)

returns: [0]

instrument.read_bit(1536)

returns: error

UPDATE 12-09-2018:

Reading register when control is Off/standby.

In: client.read_register(1536, 0, 3, False) Out: 1

Reading register when control is On.

In: client.read_register(1536, 0, 3, False) Out: 0

Reading register when control is in defrost.

In: client.read_register(1536, 0, 3, False) Out: 4

Response from trying to write to registers:

The control documentation says to use functioncode 6 to write changes to registers however it seems to take the new value without error but doesn't update or change the controller register.

If I use functioncode 6

In: client.write_register(1536, 1, 0, 6, False) (no error or output, and register value doesn't change)

If I use functioncode 16 as suggested it leaves the following error.

In: client.write_register(1536, 1, 0, 16, False)


ValueError                                Traceback (most recent call last)
<ipython-input-22-66ccb391e76c> in <module>()
----> 1 client.write_register(1536, 1, 0, 16, False)

/usr/local/lib/python3.5/dist-packages/minimalmodbus.py in write_register(self, registeraddress, value, numberOfDecimals, functioncode, signed)
    294         _checkNumerical(value, description='input value')
    295
--> 296         self._genericCommand(functioncode, registeraddress, value, numberOfDecimals, signed=signed)
    297
    298

/usr/local/lib/python3.5/dist-packages/minimalmodbus.py in _genericCommand(self, functioncode, registeraddress, value, numberOfDecimals, numberOfRegisters, signed, payloadformat)
    695
    696         ## Communicate ##
--> 697         payloadFromSlave = self._performCommand(functioncode, payloadToSlave)
    698
    699         ## Check the contents in the response payload ##

/usr/local/lib/python3.5/dist-packages/minimalmodbus.py in _performCommand(self, functioncode, payloadToSlave)
    796
    797         # Extract payload
--> 798         payloadFromSlave = _extractPayload(response, self.address, self.mode, functioncode)
    799         return payloadFromSlave
    800

/usr/local/lib/python3.5/dist-packages/minimalmodbus.py in _extractPayload(response, slaveaddress, mode, functioncode)
   1086
   1087     if receivedFunctioncode == _setBitOn(functioncode, BITNUMBER_FUNCTIONCODE_ERRORINDICATION):
-> 1088         raise ValueError('The slave is indicating an error. The response is: {!r}'.format(response))
   1089
   1090     elif receivedFunctioncode != functioncode:

    ValueError: The slave is indicating an error. The response is: '\x02\x90\x01}À'`

Solution

  • If you use read_bit function:

    read_bit(registeraddress, functioncode=2)
    
    read_bit(1536, 2)
    

    EDIT: This function can read only the first bit of the address. If you have more than one bit on in the address you can't use this function or you will recive the error.

    If you use the read_register function:

    read_register(registeraddress, numberOfDecimals=0, functioncode=3, signed=False)
    
    read_register(1536,0,3,False)
    

    as output you will recive an Unsigned Int

    If you use read_registers:

    read_registers(registeraddress, numberOfRegisters, functioncode=3)
    
    read_registers(1536, 1, 3)
    

    As you can read here:

    enter image description here

    For asking modification to the device you have to write the MSByte and the LSByte.

    SOLUTION:

    import minimalmodbus
    
    def _intToBin(toConvert):
        #Here you convert the int value to binary, after that to string getting from index 2 to 10
        MSByte = str(bin(toConvert))[2:10]
        #Here you convert the int value to binary, after that to string getting from index 10 to 18
        LSByte = str(bin(toConvert))[10:18]
    
        final = MSByte+LSByte
    
        return final
    
    def _binToInt():
        return int(value,2)
    
    def _changeBit(bitToChange, binVal, valueToSet):
        #Set the single bit
        tmpList = list(binVal)
        finalString = ""
    
        tmpList[bitToChange] = str(int(valueToSet))
    
        for x in tmpList:
            finalString += x
    
        return finalString
    
    
    # DEFAULT CONFIG OF minimalmodbus
    ReadType = minimalmodbus.MODE_RTU
    minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True
    minimalmodbus.BAUDRATE = 19200
    minimalmodbus.PARITY = 'E'
    minimalmodbus.BYTESIZE = 8
    minimalmodbus.STOPBITS = 1
    minimalmodbus.TIMEOUT = 0.05
    
    modbusAddress = 1536
    
    instrument = minimalmodbus.Instrument("/dev/tty.usbserial-A9CVVTT5",1,mode="rtu")
    instrument.debug = True
    
    readValue = instrument.read_register(modbusAddress,0,3,False)
    #This is to demostrate that the conversion works fine
    print "This is the pure readed value: " + str(readValue)
    binValue = _intToBin(readValue)
    print "This is the value after the binary conversion, if you want to come back to int: " + str(int(binValue,2))
    
    #Here you can change the state of your converted value
    print "Before change binary value: " + binValue
    changeBit = _changeBit(3,binValue,False)
    print "Single bit change: " + str(changeBit)
    
    print "Int after bit value change: " + str(_binToInt(changeBit))
    #After that you can write back your register
    instrument.write_register(modbusAddress,_binToInt(changeBit),0,16,False)
    

    OUTPUT:

    This is the pure readed value: 65472
    This is the value after the binary conversion, if you want to come back to int: 65472
    Before change binary value: 1111111111000000
    Single bit change: 1110111111000000
    Int after bit value change: 61376
    

    UPDATE 12-09-2018:

    Reading:

    You are reading the register 1536, and it correctly returns the int value. So you only have to translate the int value to bin value, and to associate the translated bin value to the picture.

    Writing:

    As you can read in the documentation:

    1. Function code 6: Write single register
    2. Function code 16: Write multiple register

    So this is the correct command:

    client.write_register(1536, 1, 0, 6, False)
    

    now, the problem is:

    If you read under the picture the note is talking about writing LSByte and MSByte to make bit status changes.

    So, you are writing the value 1 to the register 1536, but you are writing it only in the LSByte.

    You have to write also in the MSByte, then:

    LSByte = "00000001" # it is 1 in decimal
    MSByte = "00000001" # it is 1 in decimal
    
    ValueToSend = MSByte + LSByte
    # The result value will be: "0000000100000001"
    # If you convert it to decimal is: 257
    #Then here you have to write
    client.write_register(1536, 257, 0, 6, False)
    

    The MSByte must be written to 1, to the LSByte corresponding bit.

    For example:

    • Change standby status to 1: MSByte = "00000001" and the LSByte = "00000001"
    • Change standby status to 0: MSByte = "00000001" and the LSByte = "00000000"
    • Change cold room light to 1: MSByte = "00000010" and the LSByte = "00000010"
    • Change cold room light to 0: MSByte = "00000010" and the LSByte = "00000000"

    You have to use the conversion from int to bin, change the bit value of the MSByte and LSByte, convert again from bin to int, and write the value.