PyModbus failing to read holding registers - modbus

I'm trying to use PyModbus 2.3.0 to read the values from some holding registers on a Siemens S7-1200 PLC. I've set up some ladder logic to test this out on the PLC, with some registers holding random floating-point test values. I'm trying to do this all from the standard Raspbian installation on a Raspberry Pi 3. The connection is over Ethernet, and I've confirmed that PyModbus can connect to the PLC (I've pinged the PLC and the PyModbus status request returns true, as you'll see shortly). The incriminating piece of code is this:
import pymodbus
import logging
import logging.handlers as Handlers
from pymodbus.client.sync import ModbusTcpClient
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
client = ModbusTcpClient('192.168.15.16', port = '443', timeout = 10, baudrate = 38400)
connection = client.connect()
print('PLC connection status: ', connection)
response = client.read_holding_registers(address = 50, length = 64, unit = 1)
print(response)
client.close()
and the result is this:
PLC connection status: True
DEBUG:pymodbus.transaction:Current transaction state - IDLE
DEBUG:pymodbus.transaction:Running transaction 1
DEBUG:pymodbus.transaction:SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x32 0x0 0x1
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Transaction failed. (Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received))
DEBUG:pymodbus.framer.socket_framer:Processing:
DEBUG:pymodbus.transaction:Getting transaction 1
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)
I'm not able to show the ladder logic I used exactly right now, however the address given by TIA for the register with the test value is "%MD50", which I believe means I need to do as I did above and look to register 50 (the holding registers) with 64 bits of data. However, the PLC doesn't seem to send anything in response; am I doing something wrong? I'm fairly confused on why this isn't working right now.

You can use Wireshark to monitor the communication between the client and server. This will help you to better diagnose the error. Each location of the holding register for Modbus holds 16bits of data. The instruction :
client.read_holding_registers(address = 50, length = 64, unit = 1)
will return the values from address location 50 to 50+64 of the holding register.

Related

How to access particular registers using PyModbus rtu?

I am new to Python and Modbus in turn I have spent a vast amount of time trying to research, gather and experiment as much as possible before asking a possible easy problem to solve. If anyone could point me in the right direction I would be truly grateful.
Essentially I am attempting to read a register of a device, using the vendors Modbus map provided to me... I can establish a connection (I think), but having issues snooping in on a register I want to read.
from pymodbus.client.sync import ModbusSerialClient
# Connection to device
client = ModbusSerialClient(
port="COM7",
startbit=1,
databits=8,
parity="N",
stopbits=2,
errorcheck="crc",
baudrate=38400,
method="RTU",
timeout=3,
)
if client.connect(): # Connection to slave device
print("Connection Successful")
register = client.read_coils(54, 2)
print(register)
client.close()
else:
print("Failed to connect to Modbus device")
And this result is received.
Connection Successful
Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 2 bytes (0 received)
The register address = 54, words = 1 and data type = INT16.
I am probably going about this all wrong, however, a push in the right direction would be appreciated.
So with a little more research I was able to access the data required.
from atexit import register
from pymodbus.client.sync import ModbusSerialClient
client = ModbusSerialClient(
port="COM7",
startbit=1,
databits=8,
parity="N",
stopbits=2,
errorcheck="crc",
baudrate=38400,
method="RTU",
timeout=3,
)
if client.connect(): # Trying for connect to Modbus slave
# Read holding register
print("Connection Successful")
res = client.read_holding_registers(address=53, count=1, unit=1)
# Where "address" is register address
# Where "count" is the number of registers to read
# Where "unit" is the slave address, found in vendor documentation
Output:
res = holding register value

TCP connection and a different buffer size for a client and a server

What will happen if I will establish a connection between a client and a server, and configure a different buffer size for each of them.
This is my client's code:
import socket,sys
TCP_IP = sys.argv[1]
TCP_PORT = int(sys.argv[2])
BUFFER_SIZE = 1024
MESSAGE = "World! Hello, World!"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()
print "received data:", data
Server's code:
import socket,sys
TCP_IP = '0.0.0.0'
TCP_PORT = int(sys.argv[1])
BUFFER_SIZE = 5
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
while True:
conn, addr = s.accept()
print 'New connection from:', addr
while True:
data = conn.recv(BUFFER_SIZE)
if not data: break
print "received:", data
conn.send(data.upper())
conn.close()
That means I will be limited to only 5 bytes? Which means I won't be able to receive the full packet and will lose 1024-5 packets?
I or does it mean I am able to get only packets of 5 bytes, which means that instead of receiving one packets of 1024 bytes as the client sent it, I'll have to divide 1024 by 5 and get 204.8 packets (?) which sounds not possible.
What in general is happing in that code?
Thanks.
Your arguments are based on the assumption that a single send should match a single recv. But this is not the case. TCP is a byte stream and not a message based protocol. This means all what matters are the transferred bytes. And for this is does not matter if it does not matter if one or 10 recv are needed to read 50 bytes.
Apart from that send is not guaranteed to send the full buffer either. It might only send parts of the buffer, i.e. the sender need actually check the return code to find out how much of the given buffer was actually send now and how much need to be retried for sending later.
And note that the underlying "packet" is again a different thing. If there is a send for 2000 bytes it will usually need multiple packets to be send (depending on the maximum transfer unit of the underlying data link layer). But this does not mean that one also need multiple recv. If all the 2000 bytes are already transferred to the OS level receive buffer at the recipient then they can be also be read at once, even if they traveled in multiple packets.
Your socket won't lose the remaining 1024 - 5 (1019) bytes.it just stored on the socket and ready to read again! so , all you need to do is to read from the socket again. the size of buffer you want to read to is decided by yourself. and you are not limited to 5 bytes, you are just limiting the read buffer for each single read to 5 bytes. so for 1024 bytes to read you have to read for 204 times plus another time read which would be the last one. but remember that the last time read fills your last buffer index with null. and that means there is no more bytes available for now.

I can not sent short messages by TCP protocol

I have a trouble to tune TCP client-server communication.
My current project has a client, running on PC (C#) and a server,
running on embedded Linux 4.1.22-ltsi.
Them use UDP communication to exchanging data.
The client and server work in blocking mode and
send short messages one to 2nd
(16, 60, 200 bytes etc.) that include either command or set of parameters.
The messages do note include any header with message length because
UDP is message oriented protocol. Its recvfrom() API returns number of received bytes.
For my server's program structure is important to get and process entire alone message.
The problem is raised when I try to implement TCP communication type instead of UDP.
The server's receive buffer (recv() TCP API) is 2048 bytes:
#define UDP_RX_BUF_SIZE 2048
numbytes = recv(fd_connect, rx_buffer, UDP_RX_BUF_SIZE, MSG_WAITALL/*BLOCKING_MODE*/);
So, the recv() API returns from waiting when rx_buffer is full, i.e after it receives
2048 bytes. It breaks all program approach. In other words, when client send 16 bytes command
to server and waits an answer from it, server's recv() keeps the message
"in stomach", until it will receive 2048 bytes.
I tried to fix it as below, without success:
On client side (C#) I set the socket parameter theSocket.NoDelay.
When I checked this on the sniffer I saw that client sends messages "as I want",
with requested length.
On server side I set TCP_NODELAY socket option to 1
int optval= 1;
setsockopt(fd,IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval);
On server side (Linux) I checked socket options SO_SNDLOWAT/SO_RCVLOWAT and they are 1 byte each one.
Please see the attached sniffer's log picture. 10.0.0.10 is a client. 10.0.0.106 is a server. It is seen, that client activates PSH flag (push), informing the server side to move the incoming data to application immediately and do not fill a buffer.
Additional question: what is SSH encrypted packets that runs between the sides. I suppose that it is my Eclipse debugger on PC (running server application through the same Ethernet connection) sends them. Am I right?
So, my problem is how to cause `recv() API to return each short message (16, 60, 200 bytes etc.) instead of accumulating them until receiving buffer fills.
TCP is connection oriented and it also maintains the order in which packets are sent and received.
Having said that, in TCP client, you will receive the stream of bytes and not the individual udp message as in UDP. So you will need to send the packet length and marker as the initial bytes.
So client can first find the packet length and then read data till packet length is reached and then expect new packet length.
You can also check for library like netty, zmq to do this extra work

How does socket recv function detects end of message

Look at this small basic python programs:
import socket
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind(("", 10000))
tcpsock.listen(10)
(sock, (ip, port)) = tcpsock.accept()
s = sock.recv(1024)
print(s)
Second program:
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 10000))
time.sleep(1)
sock.sendall(b'hello world')
The first program is a socket server. It recv a message through the socket and display it on the console. The second program is a client which connects to the server and sends it a message.
As you can see, the server reads a 1024 bytes max length message. My client send a few bytes.
My question is: How does the server knows the message ends after the 'd' char ?
I am working with sockets since years and i have always implemented a delimiter mechanism in order to know when the message stops.
But it seems to work automaticly. My question is: How ?
I know TCP car fragment messages. So what's happen if the paquet is trucated in the middle of my message ? Is it managed by OS ?
Thanks
How does the server knows the message ends after the 'd' char ?
It does not. There is not even a concept of a message in TCP. recv simply returns what is there: it blocks if no data are available and returns what can be read up to the given size if data are available. "Data available" means that there are data in the sockets receive buffer, which are put by the OS kernel there. In other words: recv will not block until the requested number of bytes can be returned but it will already return when at least a single byte is in the sockets receive buffer.
For example if the client would do two send or sendall shortly after each other a single recv might return both "messages" together. This can be easily triggered by deferring the recv (add some sleep before it) so that both "messages" are guaranteed to be arrived at the client.

recv() returns 0

I have a very annoying problem that I found several times on other forums but I can't find a proper solution.
The problem is recv() returns 0 on the last few bytes of a connection. Here are some background information.
Both (client / server) applications run on the same machine.
Both (client / server) sockets are non-blocking
The transfered data size is 53 bytes.
Both (client / server) call shutdown and closesocket when the last send()/recv() was executed.
I also tried with SO_LINGER and 10 seconds, no success either
I call send() several times (small chunks) and from the client side 53 bytes are transfered.
The server calls recv() several times (4 byte requests) and read 49 bytes and then returns 0 (54 Bytes - 49 Bytes, so 4 bytes are missing).
MSDN and some forums write for non-blocking sockets:
recv() definitely returns < 0 on error and errno / WSAGetLastError is set
recv() definitely returns = 0 when the other side closed the connection
recv() definitely returns > 0 when data was read
MSDN also says:
Using the closesocket or shutdown functions with SD_SEND or SD_BOTH
results in a RELEASE signal being sent out on the control channel. Due
to ATM's use of separate signal and data channels, it is possible that
a RELEASE signal could reach the remote end before the last of the
data reaches its destination, resulting in a loss of that data. One
possible solutions is programming a sufficient delay between the last
data sent and the closesocket or shutdown function calls for an ATM
socket.
This is regarded in the example of recv() and send(): http://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx
But still no success, I still get some interrupts in 10% of all connections after the 49 Byte is received, 90% of the connections succeed. Any ideas? Thx.
recv() returns 0 only when you request a 0-byte buffer or the other peer has gracefully disconnected. If you are not receiving all of the data you are expecting, then you are not reading the data correctly to begin with. Please update your question with your actual code.
My guess is that you're not really sending all the data you think your are sending. Check out:
The Ultimate SO_LINGER page
recv() definitely returns = 0 when the other side closed the connection
This is not completely true, in the following code using non-blocking winsock2 tcp, when no data is available, select returns 1 and recv returns 0, as does WSAGetLastError().
fd_set test = {1, socket};
const timeval timeout = {0, 0};
if (!::select(0, &test, nullptr, nullptr, &timeout)) return 0;
int done = ::recv(socket, buffer, 1, 0);
This continues even after the other end has called:
::shutdown(socket, SD_BOTH);
::closesocket(socket);
and then ended. Communication works as expected, it is just ::recv that seems to be "broken".