·
Field reference

Capture gallery

Real-shaped Modbus frames, annotated byte by byte. Hover any byte to see what it means; hover any field to see the bytes it covers. Every capture opens straight into the decoder.

Transport
Outcome
rturequestgood

RTU · Read Holding Registers request

HMI asks meter at slave 1 for 10 holding registers starting at documented 40001.

Bytes (8)hex

Takeaway
Read FC 03 is asking for wire address 0 quantity 10 — exactly 40001–40010 in the docs.

  1. bytes 0 · Slave address
    0x01 — slave 1
  2. bytes 1 · Function code
    0x03 — Read Holding Registers
  3. bytes 2–3 · Start address
    0x0000 — wire 0 ⇒ documented 40001
    Wire addresses are 0-based. Subtract 40001 from the vendor map.
  4. bytes 4–5 · Quantity
    0x000A — 10 registers
  5. bytes 6–7 · CRC-16
    0xCDC5 — low byte first on the wire
    Computed over the addr+FC+data. Modbus appends LO then HI.
rturesponsegood

RTU · Read Holding Registers response (success)

Meter returns 10 registers. Look at the byte count and the data layout.

Bytes (25)hex

Takeaway
Byte count is the first sanity check on every read response. Then split the data into 2-byte registers and apply the map.

  1. bytes 0 · Slave address
    0x01
  2. bytes 1 · Function code
    0x03 — echoed
  3. bytes 2 · Byte count
    0x14 — 20 bytes = 10 × 16-bit registers
    Always 2 × quantity. If this is odd, something is very wrong.
  4. bytes 3–4 · Reg 0 (40001)
    0x00E0 = 224 — temperature ×10 → 22.4 °C
  5. bytes 5–6 · Reg 1 (40002)
    0x03F5 = 1013 — pressure in hPa
  6. bytes 7–8 · Reg 2
    0x0000
  7. bytes 9–10 · Reg 3
    0x0000
  8. bytes 11–12 · Reg 4 — FLOAT hi
    0x4248 — first word of a FLOAT32 (ABCD order)
  9. bytes 13–14 · Reg 5 — FLOAT lo
    0x0000 — second word → 50.0 °C
    ABCD = big-endian. CDAB swap is the next most common.
  10. bytes 15–16 · Reg 6
    0x0000
  11. bytes 17–18 · Reg 7
    0x0000
  12. bytes 19–20 · Reg 8
    0x0000
  13. bytes 21–22 · Reg 9
    0x0000
  14. bytes 23–24 · CRC-16
    0xCE34
rtuexceptionerror

RTU · Exception: Illegal Data Address

Same request, but the meter only has 8 holding registers — not 10.

Bytes (5)hex

Takeaway
Exception frames are tiny: addr, FC|0x80, 1-byte code, CRC. Memorise the high-bit-set trick — it's how you spot them at a glance.

  1. bytes 0 · Slave address
    0x01
  2. bytes 1 · Function code
    0x83 — 0x03 with high bit set
    Server signals an exception by ORing the function code with 0x80. The payload is one byte.
  3. bytes 2 · Exception code
    0x02 — Illegal Data Address
    The address+quantity range falls outside what this server implements. Re-check the vendor map.
  4. bytes 3–4 · CRC-16
    0xF1C0
rturequesterror

RTU · CRC mismatch (corrupt frame)

Reading registers, but the last byte got flipped — possibly noise on a long unterminated bus.

Bytes (8)hex

Takeaway
A timeout means no valid response reached the client before its deadline. Check reachability, settings, address or Unit Identifier, framing, timing, gateway routing, and physical evidence.

  1. bytes 0 · Slave address
    0x01
  2. bytes 1 · Function code
    0x03 — Read Holding Registers
  3. bytes 2–3 · Start address
    0x0000
  4. bytes 4–5 · Quantity
    0x000A — 10 registers
  5. bytes 6–7 · CRC-16 (received)
    0xCEC5 — should be 0xCDC5
    Server stays silent on CRC failure — never replies. Symptoms: timeouts on one slave, biased toward one direction (busmaster→slave or vice-versa).
tcprequestgood

TCP · Write Single Register via gateway (unit 5)

SCADA writes setpoint 1500 to register 40010 of an RTU slave behind a TCP gateway.

Bytes (12)hex

Takeaway
TCP has no CRC — TCP itself protects the bytes. The Unit ID is what tells a gateway where to send the PDU.

  1. bytes 0–1 · Transaction ID
    0x002A — client picks any 16-bit value; server echoes it back
  2. bytes 2–3 · Protocol ID
    0x0000 — always zero for Modbus
  3. bytes 4–5 · Length
    0x0006 — number of bytes following (unit ID + PDU)
    Length covers everything from byte 6 onward. Mismatches usually mean a truncated frame.
  4. bytes 6 · Unit ID
    0x05 — RTU slave 5 on the far side of the gateway
    The official guide recommends 0xFF or accepts 0 for direct communication. A transparent serial gateway commonly routes 1–247; verify product-specific behavior.
  5. bytes 7 · Function code
    0x06 — Write Single Register
  6. bytes 8–9 · Register address
    0x0009 — wire 9 ⇒ 40010
  7. bytes 10–11 · Value
    0x05DC = 1500
asciirequestgood

ASCII · Read Holding Registers request

Same request as #1, but on a Modbus ASCII bus. Notice the framing characters.

Bytes (17)hex

Takeaway
ASCII doubles the bytes on the wire and is forgiving of long delays — useful on radio/satellite links where RTU's 3.5-char gap is impossible.

  1. bytes 0 · Start
    ':' (0x3A) — frame start
  2. bytes 1–2 · Slave address
    '01' — ASCII hex for 0x01
  3. bytes 3–4 · Function code
    '03' — Read Holding Registers
  4. bytes 5–8 · Start address
    '0000' — wire 0
  5. bytes 9–12 · Quantity
    '000A' — 10 registers
  6. bytes 13–14 · LRC
    'F2' — two's complement of the sum of the decoded bytes
    LRC is computed over the decoded binary values, not the ASCII characters.
  7. bytes 15–16 · End
    CR LF (0x0D 0x0A) — frame end
rturequesttricky

RTU · Off-by-one addressing bug

Vendor doc says 'Voltage is at register 40010'. Integrator sends address 10. They get the wrong register.

Bytes (8)hex

Takeaway
wire_addr = doc_addr − base. For 4x registers the base is 40001, so 40010 → wire 9, not 10.

  1. bytes 0 · Slave address
    0x01
  2. bytes 1 · Function code
    0x03
  3. bytes 2–3 · Start address — WRONG
    0x000A = wire 10 ⇒ 40011
    Wanted 40010, but sent 10. Correct value is wire 9 (0x0009). Off-by-one because doc range is 1-based.
  4. bytes 4–5 · Quantity
    0x0001 — 1 register
  5. bytes 6–7 · CRC-16
    0x08A4
rturesponsetricky

RTU · FLOAT32 with word swap

Two registers carry a temperature as IEEE-754 float. Decoded as ABCD: 50.0 °C. As CDAB: 1.8e-41 nonsense.

Bytes (9)hex

Takeaway
If a float is wildly out of range, swap word order before you blame the device. Use the FLOAT decoder in /tools to try all four permutations.

  1. bytes 0 · Slave address
    0x01
  2. bytes 1 · Function code
    0x03
  3. bytes 2 · Byte count
    0x04 — 2 registers
  4. bytes 3–4 · Word A (hi) — 0x4248
    ABCD order: high word of FLOAT32
  5. bytes 5–6 · Word B (lo) — 0x0000
    ABCD order: low word
    Bytes 42 48 00 00 → 50.0 (correct). Swap word order to 00 00 42 48 and you get 1.847e-41.
  6. bytes 7–8 · CRC-16
    0x5D6E