Menu

Write Single Coil gives I/O exception - failed to read

2015-09-03
2015-09-08
  • Dennis Carmen

    Dennis Carmen - 2015-09-03

    Hello.

    I just converted a slave device we've been developing inhouse (C and assembly based, embedded OS) from Modbus/ASCII to Modbus/RTU. I have a java test program with the following dependencies:

    • JavaSE-1.8
    • RXTX 2.2 with mfz-rxtx-2.2-20081207-win-x86.zip native libarary.
    • J2MOD 1.03

    When I run a WriteSingleCoil transaction, the slave is accepting the message (coil 1010 causes a digital output to change on the slave device), and an oscilloscope check shows that the slave sends a response about 3.2ms after the last byte of the request. (Tried a 6.5ms delay between request and response as well with no change.) So the CRC calculator on the slave device appears to be working correctly. The test program, however, gives me the following result.

    C:\Users\this>java -jar modbusTest.jar off
    turn off
    Sent: 64 05 03 f2 00 00 65 88
    Echo: 64 05 03 f2 00 00 c0 00
    Last request: 64 05 03 f2 00 00 65 88
    Error reading response
    com.ghgande.j2mod.modbus.ModbusIOException: I/O exception - failed to read
            at com.ghgande.j2mod.modbus.io.ModbusRTUTransport.readResponse(ModbusRTU
    Transport.java:213)
            at com.ghgande.j2mod.modbus.io.ModbusSerialTransaction.execute(ModbusSer
    ialTransaction.java:189)
            at main.modbusTest.main(modbusTest.java:95)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa
    der.java:58)
    

    Here is the class for the test program:

    public class modbusTest {
    
      public static void main(String[] args) {
      SerialConnection con = null; //the connection
      ModbusSerialTransaction trans = null; //the transaction
      WriteCoilRequest req = null; //the request
      ReadInputRegistersResponse res = null; //the response
    
      boolean onlineState = false;
      int unitid = 100; //the unit identifier we will be talking to
    
        if(args.length >= 1)
        {
          if(args[0].equalsIgnoreCase("on")) { onlineState = true; }
        }
        System.out.println(onlineState ? "turn on" : "turn off");
    
        try {
    //      System.setProperty("com.ghgande.j2mod.modbus.debug", "true");
          System.setProperty("com.ghgande.modbus.debug", "true");
    
          //2. Set master identifier
          ModbusCoupler.getReference().setUnitID(unitid);
    
          //3. Setup serial parameters
          SerialParameters params = new SerialParameters();
          params.setPortName("COM4");
          params.setBaudRate(115200);
          params.setDatabits(8);
          params.setParity("Even");
          params.setStopbits(1);
          params.setEncoding(Modbus.SERIAL_ENCODING_RTU);
          params.setEcho(true);
    
          //4. Open the connection
          con = new SerialConnection(params);
          con.open();
    //    ((ModbusSerialTransport) con.getModbusTransport()).setReceiveTimeout(10000); // Adds 10 seconds. Then fails.
    
          //5. Prepare a request
          req = new WriteCoilRequest(1010, onlineState); // Set online output
          req.setUnitID(unitid);
          req.setHeadless();
    
          //6. Prepare a transaction
          trans = new ModbusSerialTransaction(con);
          trans.setRequest(req);
          trans.setRetries(0);
    
          //7. Execute the transaction
    //      trans.setTransDelayMS(50); // Doesn't help.
        trans.execute();
        res = (ReadInputRegistersResponse) trans.getResponse();
        for (int n = 0; n < res.getWordCount(); n++) {
          System.out.println("Word " + n + "=" + res.getRegisterValue(n));
        }
    
        } catch (Exception ex) {
          ex.printStackTrace();
        } finally {
          con.close();
        }
      }
    
    }
    

    I'm at a loss. Is there anything you can do to help?

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-03

    (facepalm) Just found an error in my CRC check on the slave.

     
  • Julie Haugh

    Julie Haugh - 2015-09-03

    All is well? RTU is working great now?

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-03

    CRC check on the slave is fixed. Test program is still giving the same error:

    C:\Users\this>java -jar modbusTest.jar on
    turn on
    Sent: 64 05 03 f2 ff 00 24 78
    Echo: 64 05 03 f2 ff 00 24 78
    Last request: 64 05 03 f2 ff 00 24 78
    Error reading response
    com.ghgande.j2mod.modbus.ModbusIOException: I/O exception - failed to read
            at com.ghgande.j2mod.modbus.io.ModbusRTUTransport.readResponse(ModbusRTU
    Transport.java:213)
            at com.ghgande.j2mod.modbus.io.ModbusSerialTransaction.execute(ModbusSer
    ialTransaction.java:189)
            at main.modbusTest.main(modbusTest.java:96)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa
    der.java:58)
    
     
  • Julie Haugh

    Julie Haugh - 2015-09-03

    I'd look at the samples given in the "modbus.cmd" package. They've all been tested on various platforms and they work.

    There are some interesting things in the code you've posted that seems strange, like setting the unit ID of the master. That's incorrect, and I don't know how the code will react when the master's unit ID is (hypothetically, because I've never done that, because its wrong) is set to a value other than 0.

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-03

    You're far too kind, and I've had far too little sleep. The number of errors in that code segment I posted made me cringe. (It's only been a few weeks since I started working with Java.) Here's what finally worked:

    public class modbusTest {
    
        public static void main(String[] args) {
            SerialConnection con;
            boolean onlineState = false;
    
            if(args.length >= 1)
            {
                if(args[0].equalsIgnoreCase("on")) { onlineState = true; }
            }
            System.out.println(onlineState ? "turn on" : "turn off");
    
            System.setProperty("com.ghgande.modbus.debug", "true");
    
            //3. Setup serial parameters
            SerialParameters params = new SerialParameters();
            params.setPortName("COM4");
            params.setBaudRate(115200);
            params.setDatabits(8);
            params.setParity("Even");
            params.setStopbits(1);
            params.setEncoding(Modbus.SERIAL_ENCODING_RTU);
            params.setEcho(false);
    
            try {
                con = new SerialConnection(params);
                con.open();
    
                try {
                    //5. Prepare a request
                    WriteCoilRequest req = new WriteCoilRequest(1010, onlineState); // Set online output
                    req.setUnitID(100);
    
                    //6. Prepare a transaction
                    ModbusTransaction trans = new ModbusSerialTransaction(con);
                    trans.setRequest(req);
                    trans.setRetries(0);
    
                    //7. Execute the transaction
                    trans.execute();
                    WriteCoilResponse res = (WriteCoilResponse) trans.getResponse();
                    System.out.println("Coil=" + res.getCoil());
                } catch (Exception ex) {
                    ex.printStackTrace();
                } finally {
                    con.close();
                }
            }
            catch(Exception ex) {
                ex.printStackTrace();
                System.exit(1);
            }
        }
    }
    

    I tried following the WriteCoilTest.java example more closely. However, there didn't seem to be a way to directly set the line settings via the ModbusTransport class.

    Thanks again for your help.

     
  • Julie Haugh

    Julie Haugh - 2015-09-03

    Glad to hear you sorted everything out!

    Please spread the word about j2mod.

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-03

    (sighs) I'm closer. Not sure what I'm getting wrong this time.

    Now the more sophisticated test app (Simple JavaFX gui, more complicated class structure, etc) is able to perform one Modbus transaction successfully, but the second one fails to read the serial port. Each transaction is done by passing a ModbusRequest object to the ModbusSerialTransaction object and then "execute()"'ing the transaction. This doesn't happen when I "execute()" a transaction object multiple times in a row with the same request object.

    What am I missing this time?

     
  • Julie Haugh

    Julie Haugh - 2015-09-04

    There can be issues where you're sending transactions faster than the device can process them. You may get that one response, but then the device isn't ready for the second.

    I'd start with the WriteCoilsTest program and verify your hardware is able to handle multiple requests. Coincidentally it does exactly what it sounds like you're doing -- re-using the same transaction for each request.

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-04

    There can be 10's of seconds between requests. The two cases are as follow:

    1. Gui App Creates a new request object for each transaction. Run two write
      coil transactions with ~10 seconds in between transactions. First transaction succeeds.
      Second transaction fails.

    2. Last test app shown above. Added a loop to repeat the same request 5 times.
      Ran the app with no interloop delay. The same transaction runs 5 times without error.

    And... (tests) ...OK. I'm playing with different values for...

    ((ModbusSerialTransport) con.getModbusTransport()).setReceiveTimeout(2000);
    

    ...and...

    trans.setTransDelayMS(50);
    

    It looks like "serReceiveTimeout(100000)" was... nevermind. I can't even replicate scenario 1 for WriteSingleCoil. Reading 100 or so registers, on the other hand, is exhibiting the same behavior as scenario 1. I don't get it. Semi-repeatable errors? Is everything I'm doing synchronous? Or are there background threads that I'm not aware of?

     
  • Julie Haugh

    Julie Haugh - 2015-09-04

    Everything is fully synchronous. But also, it's physcal hardware. If you are trying to read too many registers at once, especially at that baud rate, you may be overrunning your receiver. And especially if you are running on Windows, which you seem to be based on using the Windows version of RxTx.

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-04

    I've tried all of this at 19.2K baud as well with the same behavior. What concerns me is that there's a failure mode where a serial transaction always succeeds once, after the app starts up, and then fails consistently the second time. That's a disturbingly consistent failure mode.

    It pains me to say this, but at present, we're using RxTx with a (much more primitive) homegrown protocol. It operates reliably at 115K baud with 1,000 byte transfers (master-to-slave and slave-to-master). So I'm stuck with two possibilities. Either I still haven't figured out how to use J2mod correctly or the library implements serial operations in a less than robust fashion.

    Edited for spelling.

     

    Last edit: Dennis Carmen 2015-09-04
  • Julie Haugh

    Julie Haugh - 2015-09-04

    Let me see if I'm doing anything special, like flushing the serial port input, to get this to work on my products.

    What can happen with Modbus is the senders and receivers get out of sync with each other because Modbus packets are partially framed by timing. If 3.5 character times pass without a character, the packet has ended. At 115kBaud 3.5 characters is 300 microseconds. The one thing that makes me think that isn't it is that you're having the same problem at 19.2kBaud.

    One question, though -- why are you using ECHO mode?

     

    Last edit: Julie Haugh 2015-09-04
  • Julie Haugh

    Julie Haugh - 2015-09-04

    On the off chance that this is caused by the Windows serial input buffers, I've changed the handling of serial (RTU) input for Windows so it will more aggressively read from the input. I'd also suggest you set your read timeout to 250ms. Windows timing is a bit sloppier than Linux and the library could be assuming the response has timed out when it hasn't.

    There is a new JAR - 1.04 -- give that a try, or refresh your SVN repository and rebuild the JAR yourself.

     

    Last edit: Julie Haugh 2015-09-04
  • Dennis Carmen

    Dennis Carmen - 2015-09-04

    Just to make sure, by read timeout, do you mean something like this?

    ((ModbusSerialTransport) con.getModbusTransport()).setReceiveTimeout(5000);

     
  • Julie Haugh

    Julie Haugh - 2015-09-04

    Correct - the timeout value (which is in milliseconds) needs to include the turnaround time, transmission time, and whatever delays are on the master side.

     
  • Dennis Carmen

    Dennis Carmen - 2015-09-08

    I tried that. All it did was change the amount of time it took to get the failure message in the succeeds the first time and fails the second time scenario.

     
  • Julie Haugh

    Julie Haugh - 2015-09-08

    Did you try that with the new JAR? I've not run the Modbus Master tests recently, but the packet reading code is the same deep down. Also, the "Read Coils" tests that are in the JAR have the ability to repeat the request. I'd like to know if the test code works and your code doesn't. That would provide some idea what's going on.

     

Log in to post a comment.