# Mitsubishi Electric MELSEC — Modbus TCP quirks

Mitsubishi's MELSEC family speaks Modbus TCP through a patchwork of add-on modules
and built-in Ethernet ports, not a single unified stack. The module names are
confusingly similar (`QJ71MB91` is *serial* RTU, `QJ71MT91` is the TCP/IP module
[9]; `LJ71MT91` is the L-series equivalent; `RJ71EN71` is the iQ-R Ethernet module
with a MODBUS/TCP *slave* mode bolted on [8]; `FX3U-ENET`, `FX3U-ENET-P502`,
`FX3U-ENET-ADP`, `FX3GE` built-in, and `FX5U` built-in are all different code
paths) — and every one of the categories below has at least one trap a textbook
Modbus client gets wrong: hex-numbered X/Y devices colliding with decimal Modbus
addresses, a user-defined "device assignment" parameter block that means *no two
sites are identical*, CDAB-vs-ABCD word order driven by how the ladder built the
32-bit value, sub-spec FC16 caps on the older QJ71MT91, and an FX3U port-502
licensing split that makes `FX3U-ENET` and `FX3U-ENET-P502` different SKUs.
This document catalogues each quirk, cites primary sources, and names the
ModbusPal integration test we'd write for it (convention from
`docs/v2/modbus-test-plan.md`: `Mitsubishi_<model>_<behavior>`).

## Models and server/client capability

| Model                  | Family   | Modbus TCP server | Modbus TCP client | Source |
|------------------------|----------|-------------------|-------------------|--------|
| `QJ71MT91`             | MELSEC-Q | Yes (slave)       | Yes (master)      | [9]    |
| `QJ71MB91`             | MELSEC-Q | **Serial only** — RS-232/422/485 RTU, *not TCP* | — | [1][3] |
| `LJ71MT91`             | MELSEC-L | Yes (slave)       | Yes (master)      | [10]   |
| `RJ71EN71` / `RnENCPU` | MELSEC iQ-R | Yes (slave)    | Yes (master)      | [8]    |
| `RJ71C24` / `RJ71C24-R2` | MELSEC iQ-R | RTU (serial)  | RTU (serial)      | [13]   |
| iQ-R built-in Ethernet | CPU      | Yes (slave)       | Yes (master)      | [7]    |
| iQ-F `FX5U` built-in Ethernet | CPU | Yes, firmware ≥ 1.060 [11] | Yes | [7][11][12] |
| `FX3U-ENET`            | FX3U bolt-on | Yes (slave), but **not on port 502** [5] | Yes | [4][5] |
| `FX3U-ENET-P502`       | FX3U bolt-on | Yes (slave), port 502 enabled | Yes | [5] |
| `FX3U-ENET-ADP`        | FX3U adapter | **No MODBUS** [5] | No MODBUS     | [5]    |
| `FX3GE` built-in       | FX3GE CPU | No MODBUS (needs ENET module) [6] | No | [6] |
| `FX3G` + `FX3U-ENET`   | FX3G | Yes via ENET module | Yes | [6] |

- A common integration mistake is to buy `FX3U-ENET-ADP` expecting MODBUS —
  that adapter speaks only MC protocol / SLMP. Our driver should surface a clear
  capability error, not "connection refused", when the operator's device tag
  says `FX3U-ENET-ADP` [5].
- Older forum threads assert the FX5U is "client only" [12] — that was true on
  firmware ≤ 1.040. Firmware 1.060 and later ship the parameter-driven MODBUS
  TCP server built-in and need no function blocks [11].

## Modbus device assignment (the parameter block)

Unlike a DL260 where the CPU exposes a *fixed* V-memory-to-Modbus mapping, every
MELSEC MODBUS-TCP module exposes a **Modbus Device Assignment Parameter** block
that the engineer configures in GX Works2 / GX Configurator-MB / GX Works3.
Each of the four Modbus tables (Coil, Input, Input Register, Holding Register)
can be split into up to 16 independent "assignment" entries, each binding a
contiguous Modbus address range to a MELSEC device head (`M0`, `D0`, `X0`,
`Y0`, `B0`, `W0`, `SM0`, `SD0`, `R0`, etc.) and a point count [3][7][8][9].

- **There is no canonical "MELSEC Modbus mapping"**. Two sites running the same
  QJ71MT91 module can expose completely different Modbus layouts. Our driver
  must treat the mapping as site-data (config-file-driven), not as a device
  profile constant.
- **Default values do exist** — both GX Configurator-MB (for Q/L series) and
  GX Works3 (for iQ-R / iQ-F / FX5) ship a "dedicated pattern" default that is
  applied when the engineer does not override the assignment. Per the FX5
  MODBUS Communication manual (JY997D56101) and the QJ71MT91 manual, the FX5
  dedicated default is [3][7][11]:

  | Modbus table       | Modbus range (0-based) | MELSEC device | Head |
  |--------------------|------------------------|---------------|------|
  | Coil (FC01/05/15)  | 0 – 7679               | M             | M0   |
  | Coil               | 8192 – 8959            | Y             | Y0   |
  | Input (FC02)       | 0 – 7679               | M             | M0   |
  | Input              | 8192 – 8959            | X             | X0   |
  | Input Register (FC04) | 0 – 6143            | D             | D0   |
  | Holding Register (FC03/06/16) | 0 – 6143    | D             | D0   |

  This matches the widely circulated "FC03 @ 0 = D0" convention that shows up
  in Ubidots / Ignition / AdvancedHMI integration guides [6][12].

- **X/Y in the default mapping occupy a second, non-zero Modbus range** (8192+
  on FX5; similar on Q/L/iQ-R). Driver users who expect "X0 = coil 0" will be
  reading M0 instead. Document this clearly.
- **Assignment-range collisions silently disable the slave.** The QJ71MT91
  manual states explicitly that if any two of assignments 1-16 duplicate the
  head Modbus device number, the slave function is inactive with no clear
  error — the module just won't respond [9]. The driver probe will look like a
  simple timeout; the site engineer has to open GX Configurator-MB to diagnose.

Test names:
`Mitsubishi_FX5U_default_mapping_coil_0_is_M0`,
`Mitsubishi_FX5U_default_mapping_holding_0_is_D0`,
`Mitsubishi_QJ71MT91_duplicate_assignment_head_disables_slave`.

## X/Y addressing — hex on MELSEC, decimal on Modbus

**MELSEC X (input) and Y (output) device numbers are hexadecimal on Q / L /
iQ-R** and **octal** on FX / iQ-F (with a GX Works3 toggle) [14][15].

- On a Q CPU, `X20` means decimal **32**, not 20. On an FX5U in default (octal)
  mode, `X20` means decimal **16**. GX Works3 exposes a project-level option to
  display FX5U X/Y in hex to match Q/L/iQ-R convention — the same physical
  input is then called `X10` [14].
- The Modbus Device Assignment Parameter block takes the *head device* as a
  MELSEC-native number, which is interpreted in the CPU's native base
  (hex for Q/L/iQ-R, octal for FX/iQ-F). After that, **Modbus offsets from
  the head are plain decimal** — the module does not apply a second hex
  conversion [3][9].
- Example (QJ71MT91 on a Q CPU): assignment "Coil 0 = X0, 512 points" exposes
  physical `X0` through `X1FF` (hex) as coils 0-511. A client reading coil 32
  gets the bit `X20` (hex) — i.e. the 33rd input, not the value at "input 20"
  that the operator wrote on the wiring diagram in decimal.
- **Driver bug source**: if the operator's tag configuration says "read X20" and
  the driver helpfully converts "20" to decimal 20 → coil offset 20, the
  returned bit is actually `X14` (hex) — off by twelve. Our config layer must
  preserve the MELSEC-native base that the site engineer sees in GX Works.
- Timers/counters (`T`, `C`, `ST`) are always decimal in MELSEC notation.
  Internal relays (`M`, `B`, `L`), data registers (`D`, `W`, `R`, `ZR`),
  and special relays/registers (`SM`, `SD`) also decimal. **Only `X` and `Y`
  (and on Q/L/iQ-R, `B` link relays and `W` link registers) use hex**, and
  the X/Y decision is itself family-dependent [14][15].

Test names:
`Mitsubishi_Q_X_address_is_hex_X20_equals_coil_offset_32`,
`Mitsubishi_FX5U_X_address_is_octal_X20_equals_coil_offset_16`,
`Mitsubishi_W_link_register_is_hex_W10_equals_holding_offset_16`.

## Word order for 32-bit values

MELSEC stores 32-bit ladder values (`DINT`, `DWORD`, `REAL` / single-precision
float) across **two consecutive D-registers, low word first** — i.e., `CDAB`
when viewed as a Modbus register pair [2][6].

```
D100 (low word)  : 0xCC 0xDD   (big-endian bytes within the word)
D101 (high word) : 0xAA 0xBB
```

A Modbus master reading D100/D101 as a `float` with default (ABCD) word order
gets garbage. Ignition's built-in Modbus driver notes Mitsubishi as a "CDAB
device" specifically for this reason [2].

- **Q / L / iQ-R / iQ-F all agree** — this is a CPU-level convention, not a
  module choice. Both the QJ71MT91 manual and the FX5 MODBUS Communication
  manual describe 32-bit access by "reading the lower 16 bits from the start
  address and the upper 16 bits from start+1" [6][11].
- **Byte order within each register is big-endian** (Modbus standard). The
  module does not byte-swap.
- **Configurable?** The MODBUS modules themselves do **not** expose a word-
  order toggle; the behavior is fixed to how the CPU laid out the value in the
  two D-registers. If the ladder programmer used an `SWAP` instruction or a
  union-style assignment, the word order can be whatever they made it — but
  for values produced by the standard `D→DBL` and `FLT`/`FLT2` instructions
  it is always CDAB [2].
- **FX5U quirk**: the FX5 MODBUS Communication manual tells the programmer to
  use the `SWAP` instruction *if* the remote Modbus peer requires
  little-endian *byte* ordering (BADC) [11]. This is only relevant when the
  FX5U is the Modbus *client*, but it confirms the FX5U's native wire layout
  is big-endian-byte / little-endian-word (CDAB) on the server side too.
- **Rumoured exception**: a handful of MrPLC forum threads report iQ-R
  RJ71EN71 firmware < 1.05 returning DWORDs in `ABCD` order when accessed via
  the built-in Ethernet port's MODBUS slave [8]. _Unconfirmed_; treat as a
  per-site test.

Test names:
`Mitsubishi_Float32_word_order_is_CDAB`,
`Mitsubishi_Int32_word_order_is_CDAB`,
`Mitsubishi_FX5U_SWAP_instruction_changes_byte_order_not_word_order`.

## BCD vs binary encoding

**MELSEC stores integer values in D-registers as plain binary two's-complement**,
not BCD [16]. This is the opposite of AutomationDirect DirectLOGIC, where
V-memory defaults to BCD and the ladder must explicitly request binary.

- A ladder `MOV K1234 D100` stores `0x04D2` (1234 decimal) in D100, not
  `0x1234`. The Modbus master reads `0x04D2` and decodes it as an integer
  directly — no BCD conversion needed [16].
- **Timer / counter current values** (`T0` current value, `C0` count) are
  stored in binary as word devices on Q/L/iQ-R/iQ-F. The ladder preset
  (`K...`) is also binary [16][17].
- **Timer / counter preset `K` operand in FX3U / earlier FX**: also binary when
  loaded from a D-register or a `K` constant. The older A-series CPUs had BCD
  presets on some timer types, but MELSEC-Q, L, iQ-R, iQ-F, and FX3U all use
  binary presets by default [17].
- The FX3U programming manual dedicates `FNC 18 BCD` and `FNC 19 BIN` to
  explicit conversion — their existence confirms that anything in D-registers
  that came from a `BCD` instruction output is BCD, but nothing is BCD by
  default [17].
- **7-segment display registers** are a common site-specific exception — many
  ladders pack `BCD D100` into a D-register so the operator panel can drive
  a display directly. Our driver should not assume; expose a per-tag
  "encoding = binary | BCD" knob.

Test names:
`Mitsubishi_D_register_stores_binary_not_BCD`,
`Mitsubishi_FX3U_timer_current_value_is_binary`.

## Max registers per request

From the FX5 MODBUS Communication manual Chapter 11 [11]:

| FC | Name                       | FX5U (built-in) | QJ71MT91     | iQ-R (RJ71EN71 / built-in) | FX3U-ENET |
|----|----------------------------|-----------------|--------------|-----------------------------|-----------|
| 01 | Read Coils                 | 1-2000          | 1-2000 [9]   | 1-2000 [8]                  | 1-2000    |
| 02 | Read Discrete Inputs       | 1-2000          | 1-2000       | 1-2000                      | 1-2000    |
| 03 | Read Holding Registers     | **1-125**       | 1-125 [9]    | 1-125 [8]                   | 1-125     |
| 04 | Read Input Registers       | 1-125           | 1-125        | 1-125                       | 1-125     |
| 05 | Write Single Coil          | 1               | 1            | 1                           | 1         |
| 06 | Write Single Register      | 1               | 1            | 1                           | 1         |
| 0F | Write Multiple Coils       | 1-1968          | 1-1968       | 1-1968                      | 1-1968    |
| 10 | Write Multiple Registers   | **1-123**       | 1-123        | 1-123                       | 1-123     |
| 16 | Mask Write Register        | 1               | not supported | 1                          | not supported |
| 17 | Read/Write Multiple Regs   | R:1-125, W:1-121 | not supported | R:1-125, W:1-121          | not supported |

- **The FX5U / iQ-R native-port limits match the Modbus spec**: 125 for FC03/04,
  123 for FC16 [11]. No sub-spec caps like DL260's 100-register ceiling.
- **QJ71MT91 does not support FC16 (0x16, Mask Write Register) or FC17
  (0x17, Read/Write Multiple)** — requesting them returns exception `01`
  Illegal Function [9]. FX5U and iQ-R *do* support both.
- **QJ71MT91 device size**: 64k points (65,536) for each of Coil / Input /
  Input Register / Holding Register, plus up to 4086k points for Extended
  File Register via a secondary assignment range [9].
- **FX3U-ENET / -P502 function code list is a strict subset** of the common
  eight (FC01/02/03/04/05/06/0F/10). FC16 and FC17 not supported [4].

Test names:
`Mitsubishi_FX5U_FC03_126_registers_returns_IllegalDataValue`,
`Mitsubishi_FX5U_FC16_124_registers_returns_IllegalDataValue`,
`Mitsubishi_QJ71MT91_FC16_MaskWrite_returns_IllegalFunction`,
`Mitsubishi_QJ71MT91_FC23_ReadWrite_returns_IllegalFunction`.

## Exception codes

MELSEC MODBUS modules return **only the standard Modbus exception codes 01-04**;
no proprietary exception codes are exposed on the wire [8][9][11]. Module-
internal diagnostics (buffer-memory error codes like `7380H`) are logged but
not returned as Modbus exceptions.

| Code | Name                 | MELSEC trigger                                          |
|------|----------------------|---------------------------------------------------------|
| 01   | Illegal Function     | FC17 or FC16 on QJ71MT91/FX3U; FC08 (Diagnostics); FC43 |
| 02   | Illegal Data Address | Modbus address outside any assignment range             |
| 03   | Illegal Data Value   | Quantity out of per-FC range (see table above); odd coil-byte count |
| 04   | Server Device Failure | See below                                              |

- **04 (Server Failure) triggers on MELSEC**:
    - CPU in STOP or PAUSE during a write to an assignment whose "Access from
      External Device" permission is set to "Disabled in STOP" [9][11].
      *With the default "always enabled" setting the write succeeds in STOP
      mode* — another common trap.
    - CPU errors (parameter error, watchdog) during any access.
    - Assignment points to a device range that is not configured (e.g. write
      to `D16384` when CPU D-device size is 12288).
- **Write to a "System Area" device** (e.g., `SD` special registers that are
  CPU-reserved read-only) returns `04`, not `02`, on QJ71MT91 and iQ-R — the
  assignment is valid, the device exists, but the CPU rejects the write [8][9].
- **FX3U-ENET / -P502** returns `04` on any write attempt while the CPU is in
  STOP, regardless of permission settings — the older firmware does not
  implement the "Access from External Device" granularity that Q/L/iQ-R/iQ-F
  expose [4].
- **No rumour of proprietary codes 05-0B** from MELSEC; operators sometimes
  report "exception 0A" but those traces all came from a third-party gateway
  sitting between the master and the MELSEC module.

Test names:
`Mitsubishi_QJ71MT91_STOP_mode_write_with_Disabled_permission_returns_ServerFailure`,
`Mitsubishi_QJ71MT91_STOP_mode_write_with_default_permission_succeeds`,
`Mitsubishi_SD_system_register_write_returns_ServerFailure`,
`Mitsubishi_FX3U_STOP_mode_write_always_returns_ServerFailure`.

## Connection behavior

Max simultaneous Modbus TCP clients, per module [7][8][9][11]:

| Model                | Max TCP connections | Port 502 | Keepalive | Source |
|----------------------|---------------------|----------|-----------|--------|
| `QJ71MT91`           | 16 (shared with master role) | Yes | No | [9]    |
| `LJ71MT91`           | 16                  | Yes      | No        | [10]   |
| iQ-R built-in / `RJ71EN71` | 16            | Yes      | Configurable (KeepAlive = ON in parameter) | [8] |
| iQ-F `FX5U` built-in | 8                   | Yes      | Configurable | [7][11] |
| `FX3U-ENET`          | 8 TCP, but **not port 502** | No (port < 1024 blocked) | No | [4][5] |
| `FX3U-ENET-P502`     | 8, port 502 enabled | Yes      | No        | [5]    |

- **QJ71MT91's 16 is total connections shared between slave-listen and
  master-initiated sockets** [9]. A site that uses the same module as both
  master to downstream VFDs and slave to upstream SCADA splits the 16 pool.
- **FX3U-ENET port-502 gotcha**: if the engineer loads a configuration with
  port 502 into a non-P502 ENET module, GX Works shows the download as
  successful; on next power cycle the module enters error state and the
  MODBUS listener never starts. This is documented on third-party FX3G
  integration guides [6].
- **CPU STOP → RUN transition**: does **not** drop Modbus connections on any
  MELSEC family. Existing sockets stay open; outstanding requests during the
  transition may see exception 04 for a few scans but then resume [8][9].
- **CPU reset (power cycle or `SM1255` forced reset)** drops all Modbus
  connections and the module re-listens after typically 5-10 seconds.
- **Idle timeout**: QJ71MT91 and iQ-R have a per-connection "Alive-Check"
  (idle timer) parameter, default 0 (disabled). If enabled, default 10 s
  probe interval, 3 retries before close [8][9]. FX5U similar defaults.
- **Keep-alive (TCP-level)**: only iQ-R / iQ-F expose a TCP keep-alive option
  (parameter "KeepAlive" in the Ethernet settings); QJ71MT91 and FX3U-ENET
  do not — so NAT/firewall idle drops require driver-side pinging.

Test names:
`Mitsubishi_QJ71MT91_17th_connection_refused`,
`Mitsubishi_FX5U_9th_connection_refused`,
`Mitsubishi_STOP_to_RUN_transition_preserves_socket`,
`Mitsubishi_CPU_reset_closes_all_sockets`.

## Behavioral oddities

- **Transaction ID echo**: QJ71MT91 and iQ-R reliably echo the MBAP TxId on
  every response across firmware revisions; no reports of TxId drops under
  load [8][9]. FX3U-ENET has an older, less-tested TCP stack; at least one
  MrPLC thread reports out-of-order TxId echoes under heavy polling on
  firmware < 1.14 [4]. _Unconfirmed_ on current firmware.
- **Per-connection request serialization**: all MELSEC slaves serialize
  requests within a single TCP connection — a new request is not processed
  until the prior response has been sent. Pipelining multiple requests on one
  socket causes the module to queue them in buffer memory and respond in
  order, but **the queue depth is 1** on QJ71MT91 (a second in-flight request
  is held on the TCP receive buffer, not queued) [9]. Driver should treat
  Mitsubishi slaves as strictly single-flight per socket.
- **Partial-frame handling**: QJ71MT91 and iQ-R close the socket on malformed
  MBAP length fields. FX5U resynchronises at the next valid MBAP header
  within 100 ms but will emit an error to `SD` diagnostics [11]. Driver must
  reconnect on half-close and replay.
- **FX3U UDP vs TCP**: `FX3U-ENET` supports both UDP and TCP MODBUS transports;
  UDP is lossy and reorders under load. Default is TCP. Some legacy SCADA
  configurations pinned the module to UDP for multicast discovery — do not
  select UDP unless the site requires it [4].
- **Known firmware-revision variants**:
    - QJ71MT91 ≤ firmware 10052000000 (year-month format): FC15 with coil
      count that forces byte-count to an odd value silently truncates the
      last coil. Fixed in later revisions [9]. _Operator-reported_.
    - FX5U firmware < 1.060: no native MODBUS TCP server — only accessible via
      a predefined-protocol function block hack. Firmware ≥ 1.060 ships
      parameter-based server. Our capability probe should read `SD203`
      (firmware version) and flag < 1.060 as unsupported for server mode [11][12].
    - iQ-R RJ71EN71 early firmware: possible ABCD word order (rumoured,
      unconfirmed) [8].
- **SD (special-register) reads during assignment-parameter load**: while
  the CPU is loading a new MODBUS device assignment parameter (~1-2 s), the
  slave returns exception 04 Server Failure on every request. Happens after
  a parameter write from GX Configurator-MB [9].
- **iQ-R "Station-based block transfer" collision**: if the RJ71EN71 is also
  running CC-Link IE Control on the same module, a MODBUS/TCP request that
  arrives during a CCIE cyclic period is delayed to the next scan — visible
  as jittery response time, not a failure [8].

Test names:
`Mitsubishi_QJ71MT91_single_flight_per_socket`,
`Mitsubishi_FX5U_malformed_MBAP_resync_within_100ms`,
`Mitsubishi_FX3U_TxId_preserved_across_burst`,
`Mitsubishi_FX5U_firmware_below_1_060_reports_no_server_mode`.

## Model-specific differences for test coverage

Summary of which quirks differ per model, so test-class naming can reflect them:

| Quirk                                    | QJ71MT91 | LJ71MT91 | iQ-R (RJ71EN71 / built-in) | iQ-F (FX5U) | FX3U-ENET(-P502) |
|------------------------------------------|----------|----------|----------------------------|-------------|------------------|
| FC16 Mask-Write supported                | No       | No       | Yes                        | Yes         | No               |
| FC17 Read/Write Multiple supported       | No       | No       | Yes                        | Yes         | No               |
| Max connections                          | 16       | 16       | 16                         | 8           | 8                |
| X/Y numbering base                       | hex      | hex      | hex                        | octal (default) | octal        |
| 32-bit word order                        | CDAB     | CDAB     | CDAB (firmware-dependent rumour of ABCD) | CDAB | CDAB    |
| Port 502 supported                       | Yes      | Yes      | Yes                        | Yes         | P502 only        |
| STOP-mode write permission configurable  | Yes      | Yes      | Yes                        | Yes         | No (always blocks) |
| TCP keep-alive parameter                 | No       | No       | Yes                        | Yes         | No               |
| Modbus device assignment — max entries   | 16       | 16       | 16                         | 16          | 8                |
| Server via parameter (no FB)             | Yes      | Yes      | Yes                        | Yes (fw ≥ 1.060) | Yes         |

- **Test file layout**: `Mitsubishi_QJ71MT91_*`, `Mitsubishi_LJ71MT91_*`,
  `Mitsubishi_iQR_*`, `Mitsubishi_FX5U_*`, `Mitsubishi_FX3U_ENET_*`,
  `Mitsubishi_FX3U_ENET_P502_*`. iQ-R built-in Ethernet and the RJ71EN71
  behave identically for MODBUS/TCP slave purposes and can share a file
  `Mitsubishi_iQR_*`.
- **Cross-model shared tests** (word order CDAB, binary not BCD, standard
  exception codes, 125-register FC03 cap) can live in a single
  `Mitsubishi_Common_*` fixture.

## References

1. Mitsubishi Electric, *MODBUS Interface Module User's Manual — QJ71MB91*
   (SH-080578ENG), RS-232/422/485 MODBUS RTU serial module for MELSEC-Q —
   https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/sh080578eng/sh080578engk.pdf
2. Inductive Automation, *Ignition Modbus Driver — Mitsubishi Q / iQ-R word
   order*, documents CDAB convention —
   https://docs.inductiveautomation.com/docs/8.1/ignition-modules/opc-ua/drivers/modbus-v2
   and forum discussion https://forum.inductiveautomation.com/t/modbus-tcp-device-word-byte-order/65984
3. Mitsubishi Electric, *Programmable Controller User's Manual QJ71MB91 MODBUS
   Interface Module*, Chapter 7 "Parameter Setting" describing the Modbus
   Device Assignment Parameter block (assignments 1-16, head-device
   configuration) —
   https://www.lcautomation.com/dbdocument/29156/QJ71MB91%20Users%20manual.pdf
4. Mitsubishi Electric, *FX3U-ENET User's Manual* (JY997D18101), Chapter on
   MODBUS/TCP communication; function code support and connection limits —
   https://dl.mitsubishielectric.com/dl/fa/document/manual/plc_fx/jy997d18101/jy997d18101h.pdf
5. Venus Automation, *Mitsubishi FX3U-ENET-P502 Module — Open Port 502 for
   Modbus TCP/IP* —
   https://venusautomation.com.au/mitsubishi-fx3u-enet-p502-module-open-port-502-for-modbus-tcp-ip/
   and FX3U-ENET-ADP user manual (JY997D45801), which confirms the -ADP
   variant does not support MODBUS —
   https://dl.mitsubishielectric.com/dl/fa/document/manual/plc_fx/jy997d45801/jy997d45801h.pdf
6. XML Control / Ubidots integration notes, *FX3G Modbus* — port-502 trap,
   D-register mapping default, word order reference —
   https://sites.google.com/site/xmlcontrol/archive/fx3g-modbus
   and https://ubidots.com/blog/mitsubishi-plc-as-modbus-tcp-server/
7. FA Support Me, *Modbus TCP on Built-in Ethernet port in iQ-F and iQ-R* —
   confirms 16-connection limit on iQ-R, 8 on iQ-F, parameter-driven
   configuration via GX Works3 —
   https://www.fasupportme.com/portal/en/kb/articles/modbus-tcp-on-build-in-ethernet-port-in-iq-f-and-iq-r-en
8. Mitsubishi Electric, *MELSEC iQ-R Ethernet User's Manual (Application)*
   (SH-081259ENG) and *MELSEC iQ-RJ71EN71 User's Manual* Chapter on
   "Communications Using Modbus/TCP" —
   https://www.allied-automation.com/wp-content/uploads/2015/02/MITSUBISHI_manual_plc_iq-r_ethernet_users.pdf
   and https://www.manualslib.com/manual/1533351/Mitsubishi-Electric-Melsec-Iq-Rj71en71.html?page=109
9. Mitsubishi Electric, *MODBUS/TCP Interface Module User's Manual — QJ71MT91*
   (SH-080446ENG), exception codes page 248, device assignment parameter
   pages 116-124, duplicate-assignment-disables-slave note —
   https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/sh080446eng/sh080446engj.pdf
10. Mitsubishi Electric, *MELSEC-L Network Features* — LJ71MT91 documented as
    L-series equivalent of QJ71MT91 with identical MODBUS/TCP behavior —
    https://us.mitsubishielectric.com/fa/en/products/cnt/programmable-controllers/melsec-l-series/network/features/
11. Mitsubishi Electric, *MELSEC iQ-F FX5 User's Manual (MODBUS Communication)*
    (JY997D56101), Chapter 11 "Modbus/TCP Communication Specifications" —
    function code max-quantity table, frame specification, device assignment
    defaults —
    https://dl.mitsubishielectric.com/dl/fa/document/manual/plcf/jy997d56101/jy997d56101h.pdf
12. MrPLC forum, *FX5U Modbus-TCP Server (Slave)*, firmware ≥ 1.60 enables
    native server via parameter; earlier firmware required function block —
    https://mrplc.com/forums/topic/31883-fx5u-modbus-tcp-server-slave/
    and Industrial Monitor Direct's "FX5U MODBUS TCP Server Workaround"
    article (reflects older firmware behavior) —
    https://industrialmonitordirect.com/blogs/knowledgebase/mitsubishi-fx5u-modbus-tcp-server-configuration-workaround
13. Mitsubishi Electric, *MELSEC iQ-R MODBUS and MODBUS/TCP Reference Manual —
    RJ71C24 / RJ71C24-R2* (BCN-P5999-1060) — RJ71C24 is serial RTU only,
    not TCP —
    https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/bcn-p5999-1060/bcnp59991060b.pdf
14. HMS Industrial Networks, *eWON and Mitsubishi FX5U PLC* (KB-0264-00) —
    documents that FX5U X/Y are octal in GX Works3 but hex when viewed as a
    Q-series PLC through eWON; the project-level hex/octal toggle —
    https://hmsnetworks.blob.core.windows.net/www/docs/librariesprovider10/downloads-monitored/manuals/knowledge-base/kb-0264-00-en-ewon-and-mitsubishi-fx5u-plc.pdf
15. Fernhill Software, *Mitsubishi Melsec PLC Data Address* — documents
    hex-vs-octal device numbering split across MELSEC families —
    https://www.fernhillsoftware.com/help/drivers/mitsubishi-melsec/data-address-format.html
16. Inductive Automation support, *Understanding Mitsubishi PLCs* — D registers
    store signed 16-bit binary, not BCD; DINT combines two consecutive D
    registers —
    https://support.inductiveautomation.com/hc/en-us/articles/16517576753165-Understanding-Mitsubishi-PLCs
17. Mitsubishi Electric, *FXCPU Structured Programming Manual [Device &
    Common]* (JY997D26001) — FNC 18 BCD and FNC 19 BIN explicit-conversion
    instructions confirm binary-by-default storage —
    https://dl.mitsubishielectric.com/dl/fa/document/manual/plc_fx/jy997d26001/jy997d26001l.pdf
