Skip to content

Modbus RTU CRC errors when request and response are read “glued together” on /dev/ttyS0 #39

@akiracc

Description

@akiracc

Body:

Hi,

I’m using the EPICS modbus module over RS‑485 (Modbus RTU) on Linux (/dev/ttyS0) and I’m seeing systematic CRC error messages after adding a new device to the bus.

At the protocol level, the individual Modbus frames on the wire look correct. The problem seems to be that my IOC sometimes reads both the request and the slave response in a single serial read, and the current receive logic appears to treat that whole chunk as one frame when checking the CRC. That leads to repeated CRC error messages even though the request and response, taken separately, each have a valid CRC.

Environment

  • EPICS base: (please fill in)
  • modbus module version: (please fill in)
  • OS: Linux (serial port /dev/ttyS0)
  • Link: RS‑485, 2‑wire, 9600 baud
  • Cable length: ~1–2 m, mixed star + daisy‑chain wiring
  • No physical termination resistors currently installed on the bus
  • Multiple Modbus RTU slaves on the same line

Observed behavior
After adding a new Modbus device on the RS‑485 bus, the IOC log shows repeated CRC errors, even when I later remove that new device and restore the wiring to the previous configuration.

Here is an excerpt from the IOC log (timestamps and direction shown by my logging):

========================================================================================

\x04\x03 \x02\x00\x02n^
2026/05/08 14:53:20.027 /dev/ttyS0 read 8
\x04\x03 \x02\x00\x02n^
2026/05/08 14:53:20.052 /dev/ttyS0 read 9
\x04\x03\x04\x00,\xfb\x84-\xa9
2026/05/08 14:53:20.052 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.053 /dev/ttyS0 write 8
\x04\x03 \x04\x00\x02\x8e_
2026/05/08 14:53:20.066 /dev/ttyS0 read 8
\x04\x03 \x04\x00\x02\x8e_
2026/05/08 14:53:20.089 /dev/ttyS0 read 9
\x04\x03\x04\xff\xff\xe0"f\xce
2026/05/08 14:53:20.090 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.091 /dev/ttyS0 write 8
\x04\x03 \x06\x00\x02/\x9f
2026/05/08 14:53:20.104 /dev/ttyS0 read 8
\x04\x03 \x06\x00\x02/\x9f
2026/05/08 14:53:20.128 /dev/ttyS0 read 9
\x04\x03\x04\xff\xff\xe67\xa4\xa1
2026/05/08 14:53:20.128 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.129 /dev/ttyS0 write 8
\x04\x03 \b\x00\x02N\
2026/05/08 14:53:20.142 /dev/ttyS0 read 8
\x04\x03 \b\x00\x02N\
2026/05/08 14:53:20.166 /dev/ttyS0 read 9
\x04\x03\x04\x00\x00\x00\x00\xaf3
2026/05/08 14:53:20.166 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.167 /dev/ttyS0 write 8
\x04\x03 \n\x00\x02\xef\x9c
2026/05/08 14:53:20.181 /dev/ttyS0 read 8
\x04\x03 \n\x00\x02\xef\x9c
2026/05/08 14:53:20.205 /dev/ttyS0 read 9
\x04\x03\x04\x00\x00\x10\xce#g
2026/05/08 14:53:20.205 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.782 /dev/ttyS0 write 8
\x02\x03\x00\b\x00\x02E\xfa
2026/05/08 14:53:20.806 /dev/ttyS0 read 17
\x02\x03\x00\b\x00\x02E\xfa\x02\x03\x04A\xfa\x00\x00\xfd>
2026/05/08 14:53:20.807 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.808 /dev/ttyS0 write 8
\x02\x03\x00\x04\x00\x02\x85\xf9
2026/05/08 14:53:20.831 /dev/ttyS0 read 17
\x02\x03\x00\x04\x00\x02\x85\xf9\x02\x03\x04B\xaaT\x8b\x83\xcc
2026/05/08 14:53:20.832 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.835 /dev/ttyS0 write 8
\x02\x03\x00\x00\x00\x02\xc48
2026/05/08 14:53:20.859 /dev/ttyS0 read 17
\x02\x03\x00\x00\x00\x02\xc48\x02\x03\x04\x00\x00\x00\x00\xc93
2026/05/08 14:53:20.860 modbusInterpose::readIt, CRC error
2026/05/08 14:53:20.887 /dev/ttyS0 write 8
\x02\x03\x00\x06\x00\x02$9
2026/05/08 14:53:20.910 /dev/ttyS0 read 17
\x02\x03\x00\x06\x00\x02$9\x02\x03\x04C8;\xd0O\xd6
2026/05/08 14:53:20.911 modbusInterpose::readIt, CRC error
2026/05/08 14:53:21.154 /dev/ttyS0 write 8
\x02\x03\x00\f\x00\x04\x849
2026/05/08 14:53:21.182 /dev/ttyS0 read 21
\x02\x03\x00\f\x00\x04\x849\x02\x03\b@g\az\v\xdd\xa6\xa0\xb9\xde
2026/05/08 14:53:21.183 modbusInterpose::readIt, CRC error
2026/05/08 14:53:21.183 /dev/ttyS0 write 8
\x01\x03\x00\b\x00\x02E\xc9
2026/05/08 14:53:21.196 /dev/ttyS0 read 8

If I split these into separate frames manually, they seem valid:

  • Request: 01 03 00 04 00 02 85 ca (slave 1, function 3, start 0x0004, 2 registers)
  • Response: 01 03 04 43 2d 74 4d 98 8b (4 data bytes + CRC)

Same pattern for the other examples: the first part of the read buffer is identical to the request I just wrote, and the trailing bytes match what looks like a valid Modbus RTU response (address, function, byte count, data, CRC).

However, modbusInterpose::readIt reports a CRC error, which suggests that the code is probably computing CRC over the entire 17 or 21 bytes (request + response) instead of over each frame individually.

What I suspect is happening

  1. The serial driver sometimes returns both the outgoing request (echoed back) and the incoming response in a single read.
  2. The current Modbus receive logic in the module appears to:
  • Treat the entire read buffer as one frame, and

  • Compute CRC over that whole buffer instead of:

    • Stripping any echoed request, and
      
    • Parsing one Modbus RTU response frame at a time based on address/function/byte count (for function 3/4), or length for other functions.
      
  1. As a result, I get CRC error in the log even though the actual response frame on the wire has a correct CRC.

Note: I understand that my RS‑485 wiring is not perfect (short bus, mixed star + daisy chain, no terminators). But at 9600 baud, 1–2 m, this ran stably for about a year before adding the new device. The CRC errors now appear systematically when the request and response bytes are read “glued together”, which looks more like a framing/parse issue than random bit errors.

What I’d like to ask / propose

  • Could the Modbus RTU receive logic be adjusted so that it:

    • Treats the serial input as a byte stream and parses one Modbus RTU frame at a time, rather than assuming that one read() corresponds to exactly one frame?
    • For function 03/04 responses, uses the byte count field ([addr][func][byteCount]…) to determine the expected frame length (3 + byteCount + 2 bytes) and only then performs CRC on that exact slice.
    • Optionally detects and discards an echoed request (if the first N bytes of the buffer are identical to the last transmitted request), then parses the following response frame(s) normally.
  • If there is already a recommended configuration or an option in the current module that:

  • Disables request echo handling,

    • Or enables stricter RTU framing based on byteCount,
    • please let me know how I should configure it.

Reproduction outline

  1. RS‑485, 9600 baud, short cable (~1–2 m), multiple slaves.
  2. Use modbus module to poll holding registers from multiple slave addresses (e.g. 1 and 4).
  3. Enable detailed logging around modbusInterpose::readIt and serial I/O.
  4. Observe that some read() calls return “request + response” concatenated, and that these cases are logged as CRC error even though the request and response, if separated, have valid CRCs.

If you need more details (EPICS/modbus version, device list, or additional logs), I can provide them. I’m also happy to test a patch that changes the receive framing logic.

Thanks for your work on this module, and for any guidance on how to fix or work around this “glued frames” CRC issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions