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.
The 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()
Hello James Saunders,
So much thanks you for this post. We have used this code for our implementations in a Raspberry Pi 2 in a U project. We wrote a papers about our projects and included a appointment about your post.
Thank again.
Thank you for a great tutorial. Thank you for sharing.
Unfortunately for me, I’m more familiar with C than Python. I’m hoping you can explain a couple things about the following line of your code:
req = struct.pack(‘>3H 2B 2H’, 0, 0, 6, int(unitId), int(functionCode), int(startRegister), int(numRegister))
I figured out that the > means big indian, but I’m not sure what the purpose of the 3H 2B 2H is. I thought this first part was to describe the length of the packet. I also thought the 6 should be 4 since there are only 4 bits that follow and not 6. Sorry if these are basics. Thank you in advance for your help.
Hi Jeff,
Thanks for your feedback. I am (still) not a Python expert but I will try to explain this struct.pack line.
struct.pack('>3H 2B 2H', 0, 0, 6, int(unitId), int(functionCode), int(startRegister), int(numRegister))
Also refer to https://docs.python.org/2/library/struct.html table 7.3.2:
This is saying “Construct me a binary data packet made up of…”
So the end result is a packet 12 bytes long. The above is actually a shorthand way of writing:
struct.pack('>1H 1H 1H 1B 1B 1H 1H', 0, 0, 6, int(unitId), int(functionCode), int(startRegister), int(numRegister))
Which I think is easier to read, with the number of elements in the struct.pack definition matching the number of parameters following:
You will see this now matches the Modbus packet definition in part 1 of this tutorial.
Hope this helps, it too me a little while to get my head around Python’s struct also!
Hi James, Thank you for a great tutorial.
I’m from brazil and I’m using your code as a base for my aplication.
I’m trying to get a float number in one of the registers, but your code gives me only interger results, what i nedd to chage to see a float result for exemple: “0.16”?
Thank you!
Thank U sir for your great tutorial , it’s really helpful
I just have a question about the script in line ’43’
s = struct.Struct(‘>3H 3B %s%s’ %(numRegister, BH))
I’ll be so thankful if you can explain to me what does it mean ?
thank you in advance for your efforts 🙂