Follow the packet from the Modbus TCP client to the server. A function code 15 request with starting address 2, quantity 1, byte count 1, and data 0 targets only address 2. If address 3 also turns off, the server has processed unused bits beyond the declared quantity. Correct the server implementation or replace the simulator; changing the unused data bits only masks the defect.
Where does the write request travel?
The test path has two stated endpoints: a client constructs a Modbus TCP application data unit, and mod_RSsim.exe processes it as the server. The decisive comparison is between the bytes sent by the client and the range committed by the server. If another device or software layer exists in the actual path, capture the request on both sides of that hop.
| Path field | Reading to take | Decision |
|---|---|---|
| Client output | Record the complete request bytes | If they differ from the intended request, correct client serialization. |
| Transport destination | Record the configured address and TCP port | The TCP port is not encoded in the displayed Modbus message. Confirm that the capture belongs to the intended server connection. |
| Intermediate hop | Compare input and output captures if a gateway or proxy is present | Any changed PDU identifies the modifying hop. |
| Server result | Read addresses 2 and 3 immediately after the write |
A change at address 3 with quantity 1 places the fault in server-side range handling. |
| Timing | Match each response and readback to its request transaction | Do not attribute an older write or delayed display refresh to the current transaction. |
Layer one first when physical equipment is involved: verify link state and capture integrity before interpreting the PDU. In a local simulator test, start with the exact client byte stream and the simulator's memory display because no physical link details were supplied.
Does the request declare a one-coil range?
The failing decimal request is:
77 146 0 0 0 8 255 15 0 2 0 1 1 0
| Bytes | Field | Decoded value | Meaning |
|---|---|---|---|
77 146 |
Transaction identifier | Request-specific | Associates the reply with this request. |
0 0 |
Protocol identifier | 0 |
Modbus protocol identifier in this message. |
0 8 |
Length | 8 |
Counts the following unit identifier and PDU bytes. |
255 |
Unit identifier | 255 |
Server routing field used by this test. |
15 |
Function | Write Multiple Coils | Allows one or more packed coil values. |
0 2 |
Starting address | 2 |
First coil address in the requested range. |
0 1 |
Quantity | 1 |
The write range contains exactly one coil. |
1 |
Byte count | 1 |
One data byte carries the packed coil data. |
0 |
Coil data | All bits clear | Bit zero commands the first and only requested coil off. |
The quantity field defines the write boundary. The byte count describes storage used to transport the packed values; it does not expand the requested range to eight coils. Therefore, this request commands address 2 off and leaves address 3 outside the operation.
Do unused bits command neighboring coils?
Function code 15 packs coil states into bytes, beginning with the least-significant bit of the first data byte. Quantity 1 makes bit zero meaningful. Bits one through seven are padding for this request and must not modify later addresses.
| Data bit | Potential address from start 2
|
Status when quantity is 1
|
|---|---|---|
| Bit 0 | 2 |
Requested; data value 0 turns it off. |
| Bit 1 | 3 |
Outside the declared range; ignore it. |
| Bits 2-7 | Subsequent addresses | Outside the declared range; ignore them. |
Writing data value 2 would set bit one and might appear to preserve address 3 on a defective server. That is not a valid repair. It converts a single-coil write into a read-modify-write dependency on an address the request does not own, introduces race conditions with other writers, and leaves the faulty quantity handling in place.
What does the response prove?
Inspect the server response before relying on its display. A successful function code 15 response reports the accepted starting address and quantity. Match its transaction identifier to the request, then compare the echoed range with start 2, quantity 1.
| Observation | Meaning | Next check |
|---|---|---|
Response reports start 2 and quantity 1; only address 2 changes |
The request and server range handling agree. | Repeat with adjacent addresses. |
Response reports the expected range; address 3 also changes |
The server acknowledged one coil but wrote padding bits as additional coils. | Test another server or correct the server's write loop. |
| Response range differs | The server decoded or reported a different request. | Compare the capture at the server boundary. |
| No matching response | The transaction did not complete normally. | Check connection state, destination, transaction matching, and server diagnostics. |
Use a separate coil-read operation after every diagnostic write. A graphical memory display can lag, cache values, or apply its own address labels. Protocol readback distinguishes a storage change from a display artifact.
Should a single coil use FC15 or FC5?
| Choice | Range | Engineering use | Diagnostic implication |
|---|---|---|---|
FC5 |
One coil by definition | Expresses single-coil intent directly. | A neighboring change immediately points away from packed multi-coil data construction. |
FC15 with quantity 1
|
Exactly one coil | Valid when a client uses one write path for variable-sized coil blocks. | Tests whether the server respects quantity instead of processing every bit in the final byte. |
FC15 can replace FC5 for a single write at the protocol-operation level, but that does not make FC5 useless. Choose FC5 when the application always writes one coil and direct intent matters. Choose FC15 when the client already handles packed ranges correctly or must support both single and multiple writes. The server must honor the declared quantity in either design.
How do you isolate, fix, and verify the fault?
- Initialize a small group of adjacent coils to a known mixed pattern so an unintended neighboring write is visible.
- Send the original
FC15request for starting address2, quantity1, byte count1, and data0. - Capture the outbound bytes and confirm that the server receives the same PDU.
- Match the response by transaction identifier and verify that it reports starting address
2and quantity1. - Read back address
2and its adjacent addresses through the protocol. If address3changes, reproduce the test against another server implementation. Different results with the identical captured request isolate the behavior to the original simulator. - In server code, bound the write loop by the requested quantity, not by
byte count × 8. Use byte count only to locate and validate the packed payload. - Retest data values whose unused bits are both clear and set. Only bit zero may affect storage when quantity is
1. - Repeat the test with
FC5. Both operations must change only the addressed coil.
FAQ
Can Modbus FC15 write only one coil?
Yes. Set the starting address to the target and quantity to 1. Only bit zero of the first data byte belongs to that write range.
What happens if the FC15 byte count is one?
One byte transports up to eight packed bit positions, but the quantity field still limits how many coils may change. Byte count 1 with quantity 1 remains a one-coil operation.
What happens if unused FC15 data bits are zero?
The server must ignore bits beyond the declared quantity. With start 2 and quantity 1, a zero in bit one must not clear address 3.
What happens if I preserve adjacent states in the padding bits?
It may hide a server defect, but it creates an unsafe read-modify-write dependency. Correct quantity handling so padding bits never write adjacent coils.
What happens if FC5 works but FC15 changes two coils?
Capture the FC15 request, verify start 2 and quantity 1, then test the identical request on another server. After correcting or replacing the faulty server, perform the final verification: read back the target and both adjacent coils and confirm that only address 2 changed.