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?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
(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?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
There can be 10's of seconds between requests. The two cases are as follow:
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.
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...
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?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Correct - the timeout value (which is in milliseconds) needs to include the turnaround time, transmission time, and whatever delays are on the master side.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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:
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.
Here is the class for the test program:
I'm at a loss. Is there anything you can do to help?
(facepalm) Just found an error in my CRC check on the slave.
All is well? RTU is working great now?
CRC check on the slave is fixed. Test program is still giving the same error:
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.
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:
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.
Glad to hear you sorted everything out!
Please spread the word about j2mod.
(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?
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.
There can be 10's of seconds between requests. The two cases are as follow:
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.
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...
...and...
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?
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.
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
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
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
Just to make sure, by read timeout, do you mean something like this?
((ModbusSerialTransport) con.getModbusTransport()).setReceiveTimeout(5000);
Correct - the timeout value (which is in milliseconds) needs to include the turnaround time, transmission time, and whatever delays are on the master side.
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.
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.