A Modbus RTU read that times out immediately after a Function Code 16 write points at the write transaction, not the link. The usual causes are a driver waiting for written data that a valid acknowledgement never carries, and a next request sent inside the 3.5 character gap. Capture the exchange, then fix format and timing in that order.
A read that updates three times a second and suddenly times out, but only on the request that follows a Function Code 16 write, is not a wiring fault. The fix is almost always one of two things: set your driver to expect the 8-byte acknowledgement that a Function Code 16 write returns (address, function, starting register, quantity, CRC, with no register data echoed back), or hold the full 3.5 character times of bus silence, about 4.0 ms at 9600 baud, before sending the next request. The reads run fine, so wiring and serial settings are sound; the fault lives in the write transaction or in the moment between the write and the next request. This page puts numbers on both, shows how to capture the bytes with timestamps, and gives the order to fix them in.
Why does the next Modbus RTU command time out right after a write?
The command right after a Function Code 16 write times out for one of two reasons: your slave program sent a write response the master does not expect, or the master fired the next request before the bus had its 3.5 character times of silence.
A bus that carries a healthy read every 333 ms has working wiring, matching serial settings and a sound CRC path, because any of those faults would break the reads too. What changes after a write is the master's receive state and the frame spacing. A write acknowledgement is shorter than the write request, so a driver that expects the response to mirror the request keeps waiting for data bytes that never come, gives up, and issues the read with its transaction bookkeeping already wrong. Or the read goes out so soon after the acknowledgement that the slave reads both frames as one, fails the CRC check on the merged bytes, and answers nothing. On screen that looks like a timeout on a command sent a moment ago while the write itself worked.
Treat the timeout alarm as evidence. Capture the exchange before you change a setting; suppressing the alarm removes the only signal that a transaction failed, and the same bus will go quiet later for a reason you do want to see.
What should a Function Code 16 write response contain?
A normal Function Code 16 response repeats the slave address, the function code, the starting register address and the quantity of registers written, and carries no register data: for a two-register write that acknowledgement is 8 bytes long.
The request already carried the data, so the acknowledgement only confirms the slave accepted the address range and the count. In bytes, a two-register write to slave 17 starting at register 40002 comes back as 11 10 00 01 00 02 plus two CRC bytes. A master driver that expects the four data bytes echoed back is waiting for 1 address + 1 function + 2 start + 2 quantity + 4 data + 2 CRC = 12 bytes, and those 4 data bytes never arrive, so it reports a timeout on a write that succeeded.
Expect the exception shape as well. A rejected write returns 11 90, one exception code byte and CRC: 5 bytes in total, with 0x90 carrying the exception flag on top of 0x10. A driver that only accepts the exact function code and computes the wrong frame length sits through its whole timeout on a reply that was valid and immediate. Report the exception code, then fix the request. And check that both ends count registers the same way: register 40002 in the 4xxxx convention is protocol address 1, so an off-by-one range writes to the wrong registers or draws the exception.
How much silence does Modbus RTU need between frames?
Modbus RTU separates frames with 3.5 character times of bus silence, about 4.0 ms at 9600 baud with 11-bit characters, and above 19200 baud the Modbus over Serial Line specification fixes a constant 1.75 ms gap in place of the character count.
Work the number from the serial settings. A character with even parity is 11 bits on the wire: start, 8 data, parity, stop. At 9600 baud one character takes 11 ÷ 9600 s = 1.146 ms, so 3.5 characters is that 4.0 ms figure. At 19200 baud the same count is 2.0 ms. Why the count is 3.5, and how it scales with baud rate, is the subject of the article on the Modbus RTU 3.5 character gap timer.
A read fired 1 ms after the acknowledgement ends sits inside that window. The slave is still listening for a continuation of the previous frame, so it folds the read-request bytes into it, the CRC check fails, and it discards everything without a reply. The master sees silence on a command it just sent and raises the timeout. Hold the full gap before every frame you transmit, on the master side and before a slave reply.
For the receive-side implementation, a knowledge-base page on Modbus RTU framing and the 3.5 character inter-frame gap walks through how a parser keeps the frame states apart.
How do you capture the failing exchange?
Put a third serial device on the RS-485 pair with the same settings and let it record bytes with timestamps across one healthy read, the Function Code 16 request, its acknowledgement and the next read, because the fault is only visible as a sequence.
In this layout the panel is the serial master and your program is the slave, the arrangement the article on why the HMI panel must be the serial master argues for. A passive listener configured to the same 9600 baud, 8 data bits, even parity and 1 stop bit copies every frame without disturbing the traffic. Record four things:
- one healthy read request and its response, as the baseline;
- the Function Code 16 request and the reply to it, with the timestamp of the last byte of each;
- the next read request, and the gap in milliseconds between it and the reply before it;
- the serial settings at both ends and the register convention each device uses.
Then check in this order: slave address, function code, register start and quantity, CRC on every frame, the response delay from request end to reply start, and the gap before the next request. Before you loop writes during testing, put the driven equipment in a safe state or write to registers that control no outputs, because a debug session that rewrites registers three times a second can move real machinery.
Example: what should a two-register write and the next read look like?
The scenario: a panel master writes registers 40002 and 40003 on slave 17, values 200 and 100, at 9600 baud with even parity, a 500 ms driver response timeout and a read poll every 333 ms; here is the healthy exchange in bytes and milliseconds.
Register 40002 is protocol address 1, so the write request is 11 10 00 01 00 02 04 00 C8 00 64 plus CRC: 13 bytes, which is 13 × 1.146 ms = 14.9 ms on the wire. The slave waits out the full inter-frame gap and answers 11 10 00 01 00 02 plus CRC: 8 bytes, 9.2 ms. The master checks the echoed address, function, start and quantity against its request, holds the same gap again, and sends the read 11 03 00 01 00 02 plus CRC: 8 bytes, 9.2 ms. The slave replies 11 03 04 00 C8 00 64 plus CRC: 9 bytes, 10.3 ms, and both registers read back as written.
Add the times up: 14.9 ms request + 4.0 ms gap + 9.2 ms acknowledgement + 4.0 ms gap + 9.2 ms read request + 4.0 ms gap + 10.3 ms read response = 55.6 ms, so the whole write-then-read pair fits comfortably inside the 333 ms poll period and never approaches the 500 ms response timeout.
Now break it the way the alarm on the panel suggests. Move the read to 1.0 ms after the acknowledgement ends. That sits inside the window, so the slave never sees a frame boundary, its CRC check fails on the merged bytes, and it answers nothing. The read times out while the write succeeded: exactly the reported behavior, a write that works and a next command that times out as soon as it is sent. The capture shows a valid 8-byte acknowledgement and a 1.0 ms gap, so the fix is the gap. The wiring, the register map and the timeout setting are all innocent here.
What does each capture result point to?
Almost every write-then-timeout capture falls into one of five patterns, and each pattern names its own fix, so match your trace to a row before you touch a setting; the rows run in the order the bytes arrive.
| Capture result | What it points to | First check |
|---|---|---|
| No reply to the write request at all | The slave never accepted the frame: address, register range, serial settings or wiring | Verify address and register map, then serial settings and wiring |
| Acknowledgement arrives, the driver still reports the write as failed | The driver expects the written data echoed back | Expect the 8-byte shape: address, 0x10, starting address, quantity, CRC |
5-byte reply starting 11 90 |
The slave rejected the write with an exception | Read the exception code and correct the request |
| Acknowledgement arrives after the response timeout | The slave is genuinely slow to answer | Measure the delay and size the timeout from it |
| The next read draws no reply and the gap before it is under 3.5 character times | Turnaround violation: the slave read both frames as one | Enforce the full gap before every transmission |
The first and third rows make the request the problem, so work through the checks in the article on Modbus RTU master no-reply and address errors before touching any timing. The fourth row is a delay you can measure: the article on erratic Modbus RTU response delays covers a reply time that drifts between frames. The last row is the failure the example above produced on purpose: a healthy acknowledgement, an early next request, a silent slave.
How do you fix the driver and prove it on the bus?
Fix one variable per test with the capture running, in the order format, turnaround, timeout, because each change alters what the next capture shows and two changes at once tell you nothing about which one worked.
- Save the current driver configuration and the failing capture, so the before-and-after traces stay comparable.
- If a valid acknowledgement is present but rejected, set the expected Function Code 16 response to the 8-byte shape: slave address, function code, starting address, quantity, CRC. Delete any wait for echoed data.
- Enforce the full 3.5 character gap before every frame the master transmits and before the slave's own reply.
- After a failed or abandoned transaction, discard the partial bytes left in the receive buffer and resynchronize on the next silence, so a discarded frame's tail never becomes the first byte of the next response.
- Change the response timeout only when the capture shows a valid reply landing after the current limit, and size it from the slowest measured reply plus margin.
- Repeat the same write and the same read under capture. The trace should show one valid request, one valid 8-byte acknowledgement, the full gap, one valid read and its 9-byte response.
Then let normal polling run. The read refreshes as before, no timeout alarm appears across repeated write-then-read cycles, and a value written from the panel reads back correctly through the register. Keep the final capture and the driver settings with the maintenance record, so the next serial fault on that bus is diagnosed against a known-good exchange.
Recommendation: Capture one healthy read, the Function Code 16 write and the read that follows before changing anything. Fix the response format first when the acknowledgement is present but rejected, enforce the 3.5 character gap (4.0 ms at 9600 baud) when the next request goes out early, and touch the response timeout only when the capture shows a valid reply arriving late. Change one setting per test and keep the passing capture with the panel's maintenance record.
Questions
Does Function Code 06 behave differently from Function Code 16?
Yes. Write single register is acknowledged with an echo of the whole request: slave address, function 06, register address and the 16-bit value, 8 bytes including CRC. The echoed value confirms the write landed, so a driver that validates Function Code 16 responses needs a distinct response format for Function Code 06.
What do the exception codes after a rejected write mean?
A reply that begins 0x90 is an exception response. Exception code 02 reports a register address outside the slave's map, and code 03 reports a value in the write data that the register cannot hold. Both are request faults: correct the address range or the value, send the write again, and expect a normal 8-byte acknowledgement if the fix is right.
How should the response timeout be sized once the format and gap are right?
Size it from the capture. Take the slowest valid reply to a write in a run of normal traffic, set the limit comfortably above it, and confirm the full write-then-read sequence still fits inside the poll period. In the worked example that means a limit well above the 10.3 ms reply but safely below the 333 ms poll period.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.