From 27945fe0503588c1c0c0c3017d9125c7680c1796 Mon Sep 17 00:00:00 2001 From: Kai Jeschek Date: Mon, 7 Sep 2026 08:29:41 +0200 Subject: [PATCH 1/3] Fix serial timing and baud rate validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align serial inter-frame/inter-character timing with the Modbus spec by correcting microsecond calculations and using a 750µs char timeout above 19200 baud. Refactor SerialParameters constructors to centralize defaults, enforce baud rate > 0, and add tests for bit-per-character/stop-bit calculations plus invalid baud rate rejection. --- .../j2mod/modbus/io/ModbusRTUTransport.java | 4 +- .../modbus/io/ModbusSerialTransport.java | 42 ++++++--- .../modbus/net/AbstractSerialConnection.java | 8 +- .../modbus/slave/ModbusSlaveFactory.java | 1 + .../j2mod/modbus/util/SerialParameters.java | 91 ++++++++++--------- .../modbus/net/SerialConnectionTest.java | 85 +++++++++++++++++ .../modbus/util/SerialParametersTest.java | 10 ++ 7 files changed, 181 insertions(+), 60 deletions(-) create mode 100644 src/test/java/com/ghgande/j2mod/modbus/net/SerialConnectionTest.java diff --git a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java index 59c4b4a3..b12b8cb9 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java +++ b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java @@ -371,9 +371,9 @@ protected ModbusRequest readRequestIn(AbstractModbusListener listener) throws Mo if (!bytesAvailable) { // Sleep the 1.5t to see if there will be more data if (logger.isDebugEnabled()) { - logger.debug("Waiting for {} microsec", getMaxCharDelay()); + logger.debug("Waiting for {} microsec", getMaxCharTimeout()); } - bytesAvailable = spinUntilBytesAvailable(getMaxCharDelay()); + bytesAvailable = spinUntilBytesAvailable(getMaxCharTimeout()); } if (bytesAvailable) { diff --git a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java index 7abd4cf0..d42ff8d8 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java +++ b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java @@ -54,14 +54,19 @@ public abstract class ModbusSerialTransport extends AbstractModbusTransport { static final int FRAME_END = 2000; /** - * The number of nanoseconds there is in a millisecond + * The number of nanoseconds in a millisecond */ - private static final int NS_IN_A_MS = 1_000_000; + private static final double NS_IN_A_MS = 1_000_000.0; /** - * The number of nanoseconds there is in a second + * The number of microseconds in a second. */ - private static final int NS_IN_A_SEC = 1_000_000_000; + private static final double MICROS_IN_A_SEC = 1_000_000.0; + + /** + * The number of nanoseconds in a second + */ + private static final double NS_IN_A_SEC = 1_000_000_000.0; private static final String CANNOT_READ_FROM_SERIAL_PORT = "Cannot read from serial port"; private static final String COMM_PORT_IS_NOT_VALID_OR_NOT_OPEN = "Comm port is not valid or not open"; @@ -637,7 +642,7 @@ void waitBetweenFrames(int transDelayMS, long lastTransactionTimestamp) { int delay = getInterFrameDelay() / 1000; // How long since the last message we received - long gapSinceLastMessage = (System.nanoTime() - lastTransactionTimestamp) / NS_IN_A_MS; + final long gapSinceLastMessage = (long) ((System.nanoTime() - lastTransactionTimestamp) / NS_IN_A_MS); if (delay > gapSinceLastMessage) { long sleepTime = delay - gapSinceLastMessage; @@ -651,9 +656,14 @@ void waitBetweenFrames(int transDelayMS, long lastTransactionTimestamp) { } /** - * In microseconds + * Calculates the inter-frame delay according to the + * MODBUS over Serial Line Specification V1.02. + * * - * @return Delay between frames + * @return the inter-frame delay in microseconds */ int getInterFrameDelay() { if (commPort.getBaudRate() > 19200) { @@ -661,18 +671,23 @@ int getInterFrameDelay() { } else { long delay = Math.max(getCharIntervalMicro(Modbus.INTER_MESSAGE_GAP), Modbus.MINIMUM_TRANSMIT_DELAY * 1000L); - return delay > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) delay; + return (int) Math.min(Integer.MAX_VALUE, delay); } } /** - * The maximum delay between characters in microseconds + * Calculates the inter-character time-out according to the + * MODBUS over Serial Line Specification V1.02. + * * - * @return microseconds + * @return the inter-character time-out in microseconds */ - long getMaxCharDelay() { + long getMaxCharTimeout() { if (commPort.getBaudRate() > 19200) { - return 1750; + return 750; } else { return getCharIntervalMicro(Modbus.INTER_CHARACTER_GAP); @@ -690,7 +705,8 @@ long getCharIntervalMicro(double chars) { // Make use we have a gap of 3.5 characters between adjacent requests // We have to do the calculations here because it is possible that the caller may have changed // the connection characteristics if they provided the connection instance - return (long) (chars * NS_IN_A_MS * commPort.getBitsPerCharacter() / commPort.getBaudRate()); + final double microsPerChar = (commPort.getBitsPerCharacter() / (double) commPort.getBaudRate()) * MICROS_IN_A_SEC; + return (long) (microsPerChar * chars); } /** diff --git a/src/main/java/com/ghgande/j2mod/modbus/net/AbstractSerialConnection.java b/src/main/java/com/ghgande/j2mod/modbus/net/AbstractSerialConnection.java index 56a7037d..da49111f 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/net/AbstractSerialConnection.java +++ b/src/main/java/com/ghgande/j2mod/modbus/net/AbstractSerialConnection.java @@ -101,9 +101,11 @@ public abstract class AbstractSerialConnection { public abstract void close(); /** - * Returns current baud rate + * Returns current baud rate. + *

+ * For UART interfaces (RS-232 / RS-485), this is equal to the line bit rate in bits/s. * - * @return Baud rate + * @return Baud rate (bits/s) */ public abstract int getBaudRate(); @@ -212,7 +214,7 @@ public double getBitsPerCharacter() { final int numDataBits = getNumDataBits(); final int dataBits = numDataBits == 0 ? 8 : numDataBits; final double stopBits = getStopBits(); - final double parityBits = getParity() == SerialPort.NO_PARITY ? 0 : 1; + final double parityBits = getParity() == NO_PARITY ? 0 : 1; return startBit + dataBits + stopBits + parityBits; } diff --git a/src/main/java/com/ghgande/j2mod/modbus/slave/ModbusSlaveFactory.java b/src/main/java/com/ghgande/j2mod/modbus/slave/ModbusSlaveFactory.java index b6d02b8d..fc43cdf5 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/slave/ModbusSlaveFactory.java +++ b/src/main/java/com/ghgande/j2mod/modbus/slave/ModbusSlaveFactory.java @@ -165,6 +165,7 @@ public static synchronized ModbusSlave createSerialSlave(SerialParameters serial * Creates a serial modbus slave or returns the one already allocated to this port * * @param serialParams Serial parameters for serial type slaves + * @param listenerFactory Factory to create the listener for this slave * @return new or existing Serial modbus slave associated with the port * @throws ModbusException If a problem occurs e.g. port already in use */ diff --git a/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java b/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java index ca5601ac..d96ea123 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java +++ b/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java @@ -37,7 +37,7 @@ public class SerialParameters { private static final boolean DEFAULT_RS485_MODE = false; private static final boolean DEFAULT_RS485_TX_ENABLE_ACTIVE_HIGH = true; private static final boolean DEFAULT_RS485_ENABLE_TERMINATION = false; - private static final boolean DEFAULT_RS485_TX_DURING_RX = false; + private static final boolean DEFAULT_RS485_RX_DURING_TX = false; private static final int DEFAULT_RS485_DELAY_BEFORE_TX_MICROSECONDS = 1000; private static final int DEFAULT_RS485_DELAY_AFTER_TX_MICROSECONDS = 1000; @@ -65,24 +65,13 @@ public class SerialParameters { * default values. */ public SerialParameters() { - portName = ""; - baudRate = 9600; - flowControlIn = AbstractSerialConnection.FLOW_CONTROL_DISABLED; - flowControlOut = AbstractSerialConnection.FLOW_CONTROL_DISABLED; - databits = 8; - stopbits = AbstractSerialConnection.ONE_STOP_BIT; - parity = AbstractSerialConnection.NO_PARITY; - // Historically, the encoding has been null which got converted to RTU - // by SerialConnection.open(). Let's make it more explicit which serial - // protocol will be used by default. - encoding = Modbus.SERIAL_ENCODING_RTU; - echo = false; - openDelay = AbstractSerialConnection.OPEN_DELAY; - rs485Mode = DEFAULT_RS485_MODE; - rs485TxEnableActiveHigh = DEFAULT_RS485_TX_ENABLE_ACTIVE_HIGH; - rs485DelayBeforeTxMicroseconds = DEFAULT_RS485_DELAY_BEFORE_TX_MICROSECONDS; - rs485DelayAfterTxMicroseconds = DEFAULT_RS485_DELAY_AFTER_TX_MICROSECONDS; - rs485DisableControl = DEFAULT_RS485_DISABLE_CONTROL; + this("", 9600, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + 8, + AbstractSerialConnection.ONE_STOP_BIT, + AbstractSerialConnection.NO_PARITY, + false); } /** @@ -105,17 +94,17 @@ public SerialParameters(String portName, int baudRate, int stopbits, int parity, boolean echo) { - // Perform default initialization and update fields of interest - // afterwards. - this(); - this.portName = portName; - this.baudRate = baudRate; - this.flowControlIn = flowControlIn; - this.flowControlOut = flowControlOut; - this.databits = databits; - this.stopbits = stopbits; - this.parity = parity; - this.echo = echo; + this(portName, baudRate, + flowControlIn, + flowControlOut, + databits, + stopbits, + parity, + echo, + DEFAULT_RS485_MODE, + DEFAULT_RS485_TX_ENABLE_ACTIVE_HIGH, + DEFAULT_RS485_DELAY_BEFORE_TX_MICROSECONDS, + DEFAULT_RS485_DELAY_AFTER_TX_MICROSECONDS); } /** @@ -160,13 +149,26 @@ public SerialParameters(String portName, int baudRate, int rs485DelayBeforeTxMicroseconds, int rs485DelayAfterTxMicroseconds ) { - // Perform default non-RS-485 initialization and update fields of - // interest afterwards. - this(portName, baudRate, flowControlIn, flowControlOut, databits, stopbits, parity, echo); + this.portName = portName; + this.setBaudRate(baudRate); + this.flowControlIn = flowControlIn; + this.flowControlOut = flowControlOut; + this.databits = databits; + this.stopbits = stopbits; + this.parity = parity; + this.echo = echo; this.rs485Mode = rs485Mode; this.rs485TxEnableActiveHigh = rs485TxEnableActiveHigh; this.rs485DelayBeforeTxMicroseconds = rs485DelayBeforeTxMicroseconds; this.rs485DelayAfterTxMicroseconds = rs485DelayAfterTxMicroseconds; + // Historically, the encoding has been null which got converted to RTU + // by SerialConnection.open(). Let's make it more explicit which serial + // protocol will be used by default. + this.encoding = Modbus.SERIAL_ENCODING_RTU; + this.openDelay = AbstractSerialConnection.OPEN_DELAY; + this.rs485DisableControl = DEFAULT_RS485_DISABLE_CONTROL; + this.rs485EnableTermination = DEFAULT_RS485_ENABLE_TERMINATION; + this.rs485RxDuringTx = DEFAULT_RS485_RX_DURING_TX; } /** @@ -217,30 +219,35 @@ public void setPortName(String name) { } /** - * Sets the baud rate. + * Sets the baud rate. Has to be ≥1. * * @param rate the new baud rate. */ public void setBaudRate(int rate) { + if (rate < 1) { + throw new IllegalArgumentException("Baud rate must be greater than 0, but was: " + rate); + } baudRate = rate; } /** - * Return the baud rate as int. + * Sets the baud rate. * - * @return the baud rate as int. + * @param rate the new baud rate. */ - public int getBaudRate() { - return baudRate; + public void setBaudRate(String rate) { + setBaudRate(Integer.parseInt(rate)); } /** - * Sets the baud rate. + * Return the baud rate as int. + *

+ * Is guaranteed to return a value ≥1 * - * @param rate the new baud rate. + * @return the baud rate as int. */ - public void setBaudRate(String rate) { - baudRate = Integer.parseInt(rate); + public int getBaudRate() { + return baudRate; } /** diff --git a/src/test/java/com/ghgande/j2mod/modbus/net/SerialConnectionTest.java b/src/test/java/com/ghgande/j2mod/modbus/net/SerialConnectionTest.java new file mode 100644 index 00000000..a167128a --- /dev/null +++ b/src/test/java/com/ghgande/j2mod/modbus/net/SerialConnectionTest.java @@ -0,0 +1,85 @@ +package com.ghgande.j2mod.modbus.net; + +import com.ghgande.j2mod.modbus.util.SerialParameters; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SerialConnectionTest { + + @Test + public void testBitsPerCharacter_Standard8N1() { + SerialParameters parameters = new SerialParameters("", 9600, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + 8, + AbstractSerialConnection.ONE_STOP_BIT, + AbstractSerialConnection.NO_PARITY, + false + ); + SerialConnection serialCon = new SerialConnection(parameters); + + // 1 Startbit + 8 Datenbits + 0 Parität + 1 Stoppbit = 10 + assertEquals(10.0, serialCon.getBitsPerCharacter(), 0.001); + } + + @Test + public void testBitsPerCharacter_FiveBitsAndOnePointFiveStopBits() { + SerialParameters parameters = new SerialParameters("", 9600, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + 5, + AbstractSerialConnection.ONE_POINT_FIVE_STOP_BITS, + AbstractSerialConnection.NO_PARITY, + false + ); + SerialConnection serialCon = new SerialConnection(parameters); + + // 1 Startbit + 5 Datenbits + 0 Parität + 1.5 Stoppbit = 7.5 + assertEquals(7.5, serialCon.getBitsPerCharacter(), 0.001); + } + + @Test + public void testBitsPerCharacter_WithParityAndTwoStopBits() { + SerialParameters parameters = new SerialParameters("", 9600, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + AbstractSerialConnection.FLOW_CONTROL_DISABLED, + 8, + AbstractSerialConnection.TWO_STOP_BITS, + AbstractSerialConnection.MARK_PARITY, + false + ); + SerialConnection serialCon = new SerialConnection(parameters); + + // 1 Startbit + 8 Datenbits + 1 Parität + 2 Stoppbits = 12 + assertEquals(12.0, serialCon.getBitsPerCharacter(), 0.001); + } + + @Test + public void testStopBits_OneStopBits() { + SerialParameters parameters = new SerialParameters(); + parameters.setStopbits(AbstractSerialConnection.ONE_STOP_BIT); + + SerialConnection serialCon = new SerialConnection(parameters); + assertEquals(1.0, serialCon.getStopBits(), 0.001); + } + + @Test + public void testStopBits_OnePointFiveStopBits() { + SerialParameters parameters = new SerialParameters(); + parameters.setStopbits(AbstractSerialConnection.ONE_POINT_FIVE_STOP_BITS); + + SerialConnection serialCon = new SerialConnection(parameters); + assertEquals(1.5, serialCon.getStopBits(), 0.001); + } + + @Test + public void testStopBits_TwoStopBits() { + SerialParameters parameters = new SerialParameters(); + parameters.setStopbits(AbstractSerialConnection.TWO_STOP_BITS); + + SerialConnection serialCon = new SerialConnection(parameters); + assertEquals(2.0, serialCon.getStopBits(), 0.001); + } + +} diff --git a/src/test/java/com/ghgande/j2mod/modbus/util/SerialParametersTest.java b/src/test/java/com/ghgande/j2mod/modbus/util/SerialParametersTest.java index 5e2f92a0..3cf61c79 100644 --- a/src/test/java/com/ghgande/j2mod/modbus/util/SerialParametersTest.java +++ b/src/test/java/com/ghgande/j2mod/modbus/util/SerialParametersTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; public class SerialParametersTest { @@ -172,4 +173,13 @@ public void testSetFlowControlOut() { SerialParameters serialParameters = new SerialParameters(); serialParameters.setFlowControlOut(-1); } + + @Test + public void testInvalidBaudRatesThrowException() { + SerialParameters serialParameters = new SerialParameters(); + assertThrows(IllegalArgumentException.class, () -> serialParameters.setBaudRate(0)); + assertThrows(IllegalArgumentException.class, () -> serialParameters.setBaudRate(-1)); + assertThrows(IllegalArgumentException.class, () -> serialParameters.setBaudRate("0")); + assertThrows(IllegalArgumentException.class, () -> serialParameters.setBaudRate("-1")); + } } \ No newline at end of file From b277bea74fb078a0f9a4004a77d72783510cfc78 Mon Sep 17 00:00:00 2001 From: Kai Jeschek Date: Mon, 7 Sep 2026 08:47:59 +0200 Subject: [PATCH 2/3] quickfix --- .../java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java index d42ff8d8..d4141c17 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java +++ b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java @@ -126,7 +126,7 @@ private void waitForTransmission(long startTime, double transmissionTimeNanos) { if (transmissionTimeNanos >= NS_IN_A_MS) { try { final long adjustedDelay = (long) (transmissionTimeNanos * LONG_DELAY_FUDGE_FACTOR); - final long sleepMillis = adjustedDelay / NS_IN_A_MS; + final long sleepMillis = (long) (adjustedDelay / NS_IN_A_MS); final int sleepNanos = (int) (adjustedDelay % NS_IN_A_MS); Thread.sleep(sleepMillis, sleepNanos); From 9326378923451f5987df8b2e2a42b5e980a4fe7d Mon Sep 17 00:00:00 2001 From: da Kai <99220919+da-Kai@users.noreply.github.com> Date: Fri, 11 Sep 2026 21:33:40 +0200 Subject: [PATCH 3/3] Fix RTU serial timing calculations Adjust serial timing to honor the Modbus RTU 3.5 character inter-frame gap more accurately. --- src/main/java/com/ghgande/j2mod/modbus/Modbus.java | 2 +- .../ghgande/j2mod/modbus/io/ModbusRTUTransport.java | 12 +++++++----- .../j2mod/modbus/io/ModbusSerialTransport.java | 8 ++++---- .../ghgande/j2mod/modbus/util/SerialParameters.java | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/ghgande/j2mod/modbus/Modbus.java b/src/main/java/com/ghgande/j2mod/modbus/Modbus.java index 8bbfbba7..9edc16e2 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/Modbus.java +++ b/src/main/java/com/ghgande/j2mod/modbus/Modbus.java @@ -319,7 +319,7 @@ public interface Modbus { * The number of characters delay that must be maintained between adjacent requests on * the same serial port (within the same transaction) */ - double INTER_MESSAGE_GAP = 4; + double INTER_MESSAGE_GAP = 3.5; /** * The number of characters delay that is the allowed maximum between characters on diff --git a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java index b12b8cb9..4be33c27 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java +++ b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusRTUTransport.java @@ -365,15 +365,16 @@ protected ModbusRequest readRequestIn(AbstractModbusListener listener) throws Mo else { // This message is not for us, read and wait for the 3.5t delay + final long charTimeout = getMaxCharTimeout(); // Wait for max 1.5t for data to be available while (true) { boolean bytesAvailable = availableBytes() > 0; if (!bytesAvailable) { // Sleep the 1.5t to see if there will be more data if (logger.isDebugEnabled()) { - logger.debug("Waiting for {} microsec", getMaxCharTimeout()); + logger.debug("Waiting for {} microsec", charTimeout); } - bytesAvailable = spinUntilBytesAvailable(getMaxCharTimeout()); + bytesAvailable = spinUntilBytesAvailable(charTimeout); } if (bytesAvailable) { @@ -388,12 +389,13 @@ protected ModbusRequest readRequestIn(AbstractModbusListener listener) throws Mo } } - // Wait for 2t to complete the 3.5t wait + // Wait for the remaining time to complete the inter-frame interval + final long remainingWait = getInterFrameDelay() - charTimeout; // Is there is data available the interval was not respected, we should discard the message if (logger.isDebugEnabled()) { - logger.debug("Waiting for {} microsec", getCharIntervalMicro(2)); + logger.debug("Waiting for {} microsec", remainingWait); } - if (spinUntilBytesAvailable(getCharIntervalMicro(2))) { + if (spinUntilBytesAvailable(remainingWait)) { // Discard the message if (logger.isDebugEnabled()) { logger.debug("Discarding message (More than 1.5t between characters!) - {}", ModbusUtil.toHex(byteInputOutputStream.getBuffer(), 0, byteInputOutputStream.size())); diff --git a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java index d4141c17..ec010948 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java +++ b/src/main/java/com/ghgande/j2mod/modbus/io/ModbusSerialTransport.java @@ -636,10 +636,10 @@ void waitBetweenFrames(int transDelayMS, long lastTransactionTimestamp) { ModbusUtil.sleep(transDelayMS); } else { - // Make use we have a gap of 3.5 characters between adjacent requests + // Make sure we have a gap of 3.5 characters between adjacent requests // We have to do the calculations here because it is possible that the caller may have changed // the connection characteristics if they provided the connection instance - int delay = getInterFrameDelay() / 1000; + int delay = (int) Math.ceil(getInterFrameDelay() / 1000.0); // How long since the last message we received final long gapSinceLastMessage = (long) ((System.nanoTime() - lastTransactionTimestamp) / NS_IN_A_MS); @@ -706,7 +706,7 @@ long getCharIntervalMicro(double chars) { // We have to do the calculations here because it is possible that the caller may have changed // the connection characteristics if they provided the connection instance final double microsPerChar = (commPort.getBitsPerCharacter() / (double) commPort.getBaudRate()) * MICROS_IN_A_SEC; - return (long) (microsPerChar * chars); + return (long) Math.ceil(microsPerChar * chars); } /** @@ -714,7 +714,7 @@ long getCharIntervalMicro(double chars) { * This method will repeatedly poll the available bytes, so it should not have any side effects. * * @param waitTimeMicroSec The time to wait for the condition to be true in microseconds - * @return true if the condition ended the spin, false if the tim + * @return true if the condition ended the spin, false if the timeout was reached */ boolean spinUntilBytesAvailable(long waitTimeMicroSec) { long start = System.nanoTime(); diff --git a/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java b/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java index d96ea123..7a38abf6 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java +++ b/src/main/java/com/ghgande/j2mod/modbus/util/SerialParameters.java @@ -945,9 +945,9 @@ public String toString() { ", echo=" + echo + ", openDelay=" + openDelay + ", rs485Mode=" + rs485Mode + - ", rs485TxEnableActiveHight=" + rs485TxEnableActiveHigh + + ", rs485TxEnableActiveHigh=" + rs485TxEnableActiveHigh + ", rs485EnableTermination=" + rs485EnableTermination + - ", rs485RxDuringTx" + rs485RxDuringTx + + ", rs485RxDuringTx=" + rs485RxDuringTx + ", rs485DelayBeforeTxMicroseconds=" + rs485DelayBeforeTxMicroseconds + ", rs485DelayAfterTxMicroseconds=" + rs485DelayAfterTxMicroseconds + '}';