Modbus TCP/IP Basic Python Script – Part 2

Taking my previous Modbus Python Script further I wanted to extend my Python script to read multiple Modbus Registers from my 4-Noks Modbus TCP/IP Zigbee gateway.

4-Noks Modbus CodeThe following script asks the user to provide options such as Unit ID, Function Code and Register Address which are then used to construct the appropriate Modbus TCP/IP packet to be sent to the 4-Noks gateway.

The script then finishes after displaying the contents of the requested registers, contained within these register readings could be information such as sensor readings, device addresses, serial numbers, counters or status.

In principle, there is not a massive difference between the below script and that in my previous post other than some more advanced mathematics required to calculate packet and buffer sizes.

import socket
import struct
import time

# Create a TCP/IP socket
TCP_IP = '192.168.0.107'
TCP_PORT = 502
BUFFER_SIZE = 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((TCP_IP, TCP_PORT))

try:
    # Ask user for Modbus options
    print("\nPLEASE ENTER MODBUS OPTIONS")
    unitId = input(" Unit Identifier : ")
    functionCode = input(" Function Code : ")
    startRegister = input(" Start Register : ")
    numRegister = input(" Number of Registers : ")

    # Construct request packet
    req = struct.pack('>3H 2B 2H', 0, 0, 6, int(unitId), int(functionCode), int(startRegister), int(numRegister))
    sock.send(req)

    # Calculate receipt packet buffer and structure
    BUFFER_SIZE = (3*2) + (3*1) + (int(numRegister)*2)
    rec = sock.recv(BUFFER_SIZE)

    def setB():
        global BH
        BH = 'B' #1
    def setH():
        global BH
        BH = 'H' #2

    functionLookup = {
        1 : setB, # Read Coils (1 byte)
        2 : setB, # Read Input Discrete Registers (1 byte)
        3 : setH, # Read Holding Registers (2 byte)
        4 : setH  # Read Input Registers (2 byte)
    }
    functionLookup[int(functionCode)]()

    s = struct.Struct('>3H 3B %s%s' %(numRegister, BH))
    data = s.unpack(rec)

    # Print out packet header
    print("\nMODBUS PACKET HEADER")
    print(" Transaction Identifier : %s" %data[0])
    print(" Protocol Identifier : %s" %data[1])
    print(" Length : %s" %data[2])
    print(" Unit Identifier : %s" %data[3])
    print(" Function Code : %s" %data[4])
    print(" Byte Count : %s" %data[5])

    # Print out register values
    print("\nREGISTER VALUES")
    for i in range(6, 6+int(numRegister)):
        currentRegister = str((i - 6) + int(startRegister)).zfill(2)
        print(" Register #%s : %s" %(currentRegister, data[i]))

    # Wait a couple of seconds before disconnecting
    time.sleep(2);

finally:
    print('\nCLOSING SOCKET')
    sock.close()
4 Comments

Add a Comment

Your email address will not be published. Required fields are marked *