1

Background:

We have been using the Schneider M251 PLC with the TM3AI8, TM3DQ8R and TM3DI8 modules for some time now and I have noticed that we are getting unexpected values being sent over Modbus tcp.

Most of the time, the signal we receive over the Modbus protocol is in the 4mA range at the lower portion of our expected signal. However, we are seeing an intermittent 0 that is infrequently sent to our java software over modbus tcp using the easy modbus tcp library

Library: http://easymodbustcp.net/en/

Specifically, when we call:

val = modbusClient.ReadHoldingRegisters(3005, 1)[0];

We are seeing an intermittent infrequent val = 0 appearing but only infrequently. When I check the PLC software called soMachine, it does not appear to ever show a zero value. Has anyone encountered this kind of issue before? I set up a POU using structure text to count the number of times the IO reading is 0 and apparently it never is. So I think it has something to do with the modbus library.

I have provided a system.out.println(val) below showing the issue:

VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3986
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3992
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 0        <- Random intermittent 0
VAL IS THIS NOW: 3986
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3992
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3989 

The I/O config on the plc is 0-20mA extended and the sensor is A 4-20mA sensor. A zero reading on our unit scale represents -2500units which poses an issue for our system given it represents a large discrepancy in our measurement. We are using the diagnostic bits which are available on the Schneider card as well as well to detect sensor faults. However, the sensor does not appear to be faulty and is working fine.

As you can see, the signal off the IO card is a bit noisy as well. A multimeter does not fluctuate when reading the voltage for this sensor and it is really stable on our multimeters at the 3.989mV range.

When we see the intermittent 0's printed to the console, we do not see the 0's in soMachine. Further, the method polling for the values is being called every 250ms.

We have resorted to the following "filter" but it is not an ideal solution to the issue.

static int pt05ErrorCount = 0, pt05lastVal;
private static double readRegister3005() throws IOException{
    try {
    int val;
    if(PLC.isConnected()) {
        val = modbusClient.ReadHoldingRegisters(3005, 1)[0];

        if(val > 0) {
            pt05ErrorCount = 0;
            pt05lastVal = val;
        }
    }else {
        val = -1000;
    }
    System.out.println("VAL IS THIS NOW: "+val);
    if(val > 0) {
        return (double) (10000-0)/(20-4)*((double) val/1000-4)+0; //convert mA to psig
    }else {
        if(pt05ErrorCount > maxErrorCount) {
        pt05ErrorCount++;
        return (double) (10000-0)/(20-4)*((double) val/1000-4)+0;
    }else {
        return (double) (10000-0)/(20-4)*((double) pt05lastVal/1000-4)+0;
    }
    }
    } catch(Exception e) {
        System.out.println(e);
        throw new IOException(Globals.getSystemCalendarDateandTime() + " PLC.java > readRegister3005(): ERROR reading PLC address 3005.");
    }
}

Please let me know if you require more information about the issue.

The ReadHoldingRegisters method is as follows:

 /**
        * Read Holding Registers from Server
        * @param        startingAddress      Fist Address to read; Shifted by -1    
        * @param        quantity            Number of Inputs to read
        * @return       Holding Registers from Server
        * @throws de.re.easymodbus.exceptions.ModbusException
        * @throws UnknownHostException
        * @throws SocketException
        * @throws SerialPortTimeoutException 
        * @throws SerialPortException 
        */
    public int[] ReadHoldingRegisters(int startingAddress, int quantity) throws de.re.easymodbus.exceptions.ModbusException,
                UnknownHostException, SocketException, IOException, SerialPortException, SerialPortTimeoutException
    {
        if (tcpClientSocket == null)
            throw new de.re.easymodbus.exceptions.ConnectionException("connection Error");
        if (startingAddress > 65535 | quantity > 125)
            throw new IllegalArgumentException("Starting adress must be 0 - 65535; quantity must be 0 - 125");
        int[] response = new int[quantity];
        this.transactionIdentifier = toByteArray(0x0001);
        this.protocolIdentifier = toByteArray(0x0000);
        this.length = toByteArray(0x0006);
        //serialdata = this.unitIdentifier;
        this.functionCode = 0x03;
        this.startingAddress = toByteArray(startingAddress);
        this.quantity = toByteArray(quantity);

        byte[] data = new byte[]
                {
                    this.transactionIdentifier[1],
                    this.transactionIdentifier[0],
                    this.protocolIdentifier[1],
                    this.protocolIdentifier[0],
                    this.length[1],
                    this.length[0],
                    this.unitIdentifier,
                    this.functionCode,
                    this.startingAddress[1],
                    this.startingAddress[0],
                    this.quantity[1],
                    this.quantity[0],
                    this.crc[0],
                    this.crc[1]     
                };
            
            if (this.serialflag)
            {
                crc = calculateCRC(data, 6, 6);
                data[data.length -2] = crc[0];
                data[data.length -1] = crc[1];
            }
            byte[] serialdata = null;   
            if (serialflag)
            {          
                serialdata = new byte[8];
                java.lang.System.arraycopy(data, 6,serialdata,0,8);
                serialPort.purgePort(SerialPort.PURGE_RXCLEAR);
                serialPort.writeBytes(serialdata);
                if (debug) StoreLogData.getInstance().Store("Send Serial-Data: "+ Arrays.toString(serialdata));
               long dateTimeSend = DateTime.getDateTimeTicks();
               byte receivedUnitIdentifier = (byte)0xFF;
               serialdata = new byte[256];
               int expectedlength = 5+2*quantity;
               while (receivedUnitIdentifier != this.unitIdentifier & !((DateTime.getDateTimeTicks() - dateTimeSend) > 10000 * this.connectTimeout))
               {
                   serialdata = serialPort.readBytes(expectedlength, this.connectTimeout); 
               
                   receivedUnitIdentifier = serialdata[0];
               }
               if (receivedUnitIdentifier != this.unitIdentifier)
               {
                    data = new byte[256];                       
               }
               if (serialdata != null)
               {
                   data = new byte[262]; 
                   System.arraycopy(serialdata, 0, data, 6, serialdata.length);
                   if (debug) StoreLogData.getInstance().Store("Receive ModbusRTU-Data: " + Arrays.toString(data));
               }
                for (int i = 0; i < quantity; i++)
                {
                    byte[] bytes = new byte[2];
                    bytes[0] = data[3+i*2];
                    bytes[1] = data[3+i*2+1];
                    ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);                     
                    response[i] = byteBuffer.getShort();
        }   
            }

                
        if (tcpClientSocket.isConnected() | udpFlag)
        {
            if (udpFlag)
            {
                InetAddress ipAddress = InetAddress.getByName(this.ipAddress);
                DatagramPacket sendPacket = new DatagramPacket(data, data.length, ipAddress, this.port);
                DatagramSocket clientSocket = new DatagramSocket();
                clientSocket.setSoTimeout(500);
                clientSocket.send(sendPacket);
                data = new byte[2100];
                DatagramPacket receivePacket = new DatagramPacket(data, data.length);
                clientSocket.receive(receivePacket);
                clientSocket.close();
                data = receivePacket.getData();
            }
            else
            {
                outStream.write(data, 0, data.length-2);
                if (debug) StoreLogData.getInstance().Store("Send ModbusTCP-Data: "+Arrays.toString(data));   
                if (sendDataChangedListener.size() > 0)
                {
                    sendData = new byte[data.length-2];
                    System.arraycopy(data, 0, sendData, 0, data.length-2);
                    for (SendDataChangedListener hl : sendDataChangedListener)
                        hl.SendDataChanged();
                }
                data = new byte[2100];
                int numberOfBytes = inStream.read(data, 0, data.length);
                if (receiveDataChangedListener.size() > 0)
                {
                    receiveData = new byte[numberOfBytes];
                    System.arraycopy(data, 0, receiveData, 0, numberOfBytes);
                    for (ReceiveDataChangedListener hl : receiveDataChangedListener)
                        hl.ReceiveDataChanged();
                    if (debug) StoreLogData.getInstance().Store("Receive ModbusTCP-Data: " + Arrays.toString(data));
                }
                        }
            }
            if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x01)
            {
                if (debug) StoreLogData.getInstance().Store("FunctionCodeNotSupportedException Throwed");
                throw new de.re.easymodbus.exceptions.FunctionCodeNotSupportedException("Function code not supported by master");
            }
            if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x02)
            {
                if (debug) StoreLogData.getInstance().Store("Starting adress invalid or starting adress + quantity invalid");
                throw new de.re.easymodbus.exceptions.StartingAddressInvalidException("Starting adress invalid or starting adress + quantity invalid");
            }           
            if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x03)
            {
                if (debug) StoreLogData.getInstance().Store("Quantity invalid");
                throw new de.re.easymodbus.exceptions.QuantityInvalidException("Quantity invalid");
            }
            if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x04)
            {
                if (debug) StoreLogData.getInstance().Store("Error reading");
                throw new de.re.easymodbus.exceptions.ModbusException("Error reading");
            }
            for (int i = 0; i < quantity; i++)
            {
                byte[] bytes = new byte[2];
                bytes[0] = data[9+i*2];
                bytes[1] = data[9+i*2+1];
                ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
                        
                response[i] = byteBuffer.getShort();
            }
            
        
        return (response);
    }

Regards,

Thornack
  • 51
  • 1
  • 6
  • After further digging, I am beginning to suspect that each holding register is somehow mixing up the actual data it is supposed to return with a different holding register's data. – Thornack Mar 31 '21 at 18:04
  • Can you share the output of `StoreLogData.getInstance().Store("Receive ModbusTCP-Data: " + Arrays.toString(data));`? – Bosz Mar 31 '21 at 19:36
  • I think what you are experiencing is related to the polling time, I don't see how you are timing on your code. You are probably receiving `SLAVE BUSY` errors that are not properly handled by the Modbus library. How many simultaneous polls to the PLC you have? My two cents of advice: try with [QModMaster](https://sourceforge.net/projects/qmodmaster/), reduce the scanning rate to 250 ms and open as many instances as you have, do you see any errors? – Marcos G. Apr 01 '21 at 08:19

1 Answers1

0

I too have the exact same problem and I have not found a way to trap an error.

With another library (Fluent Modbus) randomly in the same context I get error messages like "The server has closed the communication unexpectedly".

I think it's the same condition in which Easymodbus returns the zeros.

Since I open and close the connection every time, it seems to me that there has been an improvement by putting a Sleep of 100 msec after the open, before doing the readings.

But it is quite empirical. I don't know if anyone had the same problem.

  • This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post. Please do not comment in the answer and take the tour and read How to Answer: https://stackoverflow.com/help/how-to-answer If you have a different question, you can ask it by clicking Ask Question: https://stackoverflow.com/questions/ask. You can also add a bounty to draw more attention to this question once you have enough reputation: https://stackoverflow.com/help/privileges/set-bounties – borchvm Mar 07 '23 at 07:25