Skip to main content
Blog
Blog · LRI AEM-60DC8

Modbus data types explained — coils, discrete inputs, input registers and holding registers

Modbus data types in practice: coils, discrete inputs, input registers and holding registers, addressing, function codes and real field pitfalls.

LRI EngineeringMon May 25 2026 21:00:00 GMT-0300 (Brasilia Standard Time)

The engineer reads PLC Modbus documentation for the first time and finds four tables with similar names, five-digit prefixes starting at 0, 1, 3 or 4, and a function code per operation. Then comes the discovery that logical address 40001 becomes 0x0000 on the wire, that the most obvious register shows up off by one position, and that half of the test clients display a different value than the other half. That confusion is historical baggage from a 1979 protocol whose data type names carry the memory map of the Modicon 984 PLC. This post separates the four tables, shows the function codes that touch each one, and closes with the pitfalls that appear on day one in the field.

The 4 Modbus data types

The original Modbus data model has exactly four tables. Each one defines the type (bit or 16-bit word), the direction (read, write or both), the logical address prefix and the function codes that operate on it.

Table Type Access Logical prefix Function codes
Coils 1 bit Read and write 0xxxx (00001–09999) 0x01, 0x05, 0x0F
Discrete inputs 1 bit Read only 1xxxx (10001–19999) 0x02
Input registers 16 bits Read only 3xxxx (30001–39999) 0x04
Holding registers 16 bits Read and write 4xxxx (40001–49999) 0x03, 0x06, 0x10

Coils and discrete inputs travel packed, eight per byte. Input and holding registers travel as 16-bit big-endian words. Trying to write an input register returns exception 0x01 (Illegal Function); reading a coil with function code 0x02 also returns 0x01.

Coils (0x) — read/write bit

Coils represent individual digital outputs. The name comes from the coil of an internal relay: switching contacts on and off, actuating solenoid valves, energizing lamps, commanding starts and stops. In modern devices, coils also appear as command flags — counter reset, alarm clear, calibration trigger.

Three function codes touch coils:

  • 0x01 Read Coils: reads from 1 to 2000 consecutive coils; the response carries ceil(N/8) packed bytes.
  • 0x05 Write Single Coil: writes a single coil. The value is 0xFF00 for on and 0x0000 for off; any other value is illegal.
  • 0x0F Write Multiple Coils: writes up to 1968 coils in a single transaction.

Frequent pitfall: a coil written as 0xFF00 reads back as bit 1 on the next read. A client that persists the written value instead of re-reading may flag inconsistency where there is none. Always compare against the read representation.

Discrete inputs (1x) — read-only bit

Discrete inputs represent digital inputs wired to binary sensors. The classic case is a dry contact: limit switch, inductive sensor, pressure switch, thermostat, emergency button, breaker auxiliary contact. In measurement devices they also expose state flags.

Only one function code operates:

  • 0x02 Read Discrete Inputs: reads from 1 to 2000 consecutive inputs, with eight bits packed per byte.

There is no write on a discrete input. The attempt returns exception 0x01 or 0x02 depending on the implementation. The most common confusion happens when the same device exposes a status (sensor) and a command (actuator) over the same concept: the careful vendor allocates a discrete input for "alarm active" and a coil for "clear alarm".

Input registers (3x) — read-only word

Input registers are 16-bit read-only words. The original use was to expose the digitized value of a PLC analog input (thermocouple, 4–20 mA, pressure sensor, encoder). In dedicated devices, they hold measured quantities: voltage, current, temperature, counters.

  • 0x04 Read Input Registers: reads from 1 to 125 consecutive registers. The response carries 2×N bytes in big-endian order.

The semantic split matters: the input register is the contract "this is what the device measures, you cannot touch it". In Secure by Design architectures, keeping measured quantities in a read-only table simplifies audit. In practice, many vendors consolidate everything into holding registers; the AEM-60DC8 follows that approach with 147 holding registers documented.

Holding registers (4x) — read/write word

Holding registers are 16-bit read/write words. It is the most used table because everything fits: setpoints, alarm limits, configuration, status, telemetry, counters, identification, calibration.

Three function codes dominate:

  • 0x03 Read Holding Registers: reads from 1 to 125 consecutive registers. The most frequent operation in industrial supervisors.
  • 0x06 Write Single Register: writes a 16-bit word to a single register.
  • 0x10 Write Multiple Registers: writes up to 123 registers in a network-atomic transaction.

Secondary function codes include 0x16 (mask write) and 0x17 (read/write multiple), with support that varies by vendor. The contract of a holding register must document, beyond the address: unit, scale, offset, valid range, sentinel value for "invalid data" (0x8000 in int16) and persistence.

Addressing — 0-origin vs 1-origin, Modicon offset, hex × decimal map

The point where most integrations stumble is not the function code, it is the address. Two conventions coexist:

  • Logical address (1-based, with prefix): the Modicon notation. First coil 00001, first discrete input 10001, first input register 30001, first holding register 40001.
  • Protocol address (0-based, no prefix): what goes inside the frame. First register of any table is 0x0000. What distinguishes the table is the function code.
Table Logical address Protocol address Function code
Coils 00001 0x0000 0x01
Coils 00100 0x0063 0x01
Discrete inputs 10001 0x0000 0x02
Input registers 30016 0x000F 0x04
Holding registers 40001 0x0000 0x03
Holding registers 40147 0x0092 0x03

Practical rule: subtract the prefix, subtract another 1 and convert to hex. Some tools adopt a third convention with prefix 4 and 0-based address, where 40000 refers to the same register as 40001 in the classic notation. When documentation is ambiguous, capture the frame with a serial analyzer and read the byte directly.

How the AEM-60DC8 organizes its 147 holding registers

The AEM-60DC8 (Industrial DC Monitoring Platform, firmware v1.03) exposes 147 holding registers in 17 functional blocks. The excerpt below is a simplified example for didactic purposes; the authoritative map is the annex of the technical manual.

Block Range (hex) Registers Direction Content
Voltage CH1–CH8 instantaneous 0x0000–0x0007 8 Effective RO uint16 with ×100 scale
Voltage CH1–CH8 1 s average 0x0008–0x000F 8 Effective RO 1-second moving window
Per-channel status 0x0010–0x0017 8 Effective RO Bitfield: present, in alarm, calibrated
Upper/lower limits 0x0020–0x002F 16 RW Setpoints per channel
Channel configuration 0x0040–0x004F 16 RW Enable, mode, oversampling
Communication configuration 0x0050–0x0057 8 RW Slave ID, baudrate, parity, timeout
Event counters 0x0060–0x006F 16 Effective RO Alarm rise and fall
Forensic telemetry 0x0070–0x007F 16 Effective RO Uptime, watchdog resets, CRC errors
Factory calibration 0x0080–0x008F 16 RW (protected) Gain and offset, requires code
Identification 0x0090–0x0092 3 Effective RO Model, serial, firmware v1.03

Holding registers marked "Effective RO" accept writes at protocol level, but the firmware rejects them with exception 0x04 when the content is a measurement. Publishing a map without indicating effective direction, unit, scale and persistence turns every integration into a negotiation.

Common reading errors

Five pitfalls explain most of the "Modbus that doesn't work" complaints in the field:

  1. Off-by-one: reading holding register 40016 by writing 0x0010 instead of 0x000F. The server returns the value of 40017. Fix: subtract the prefix and another 1 before converting.

  2. Byte order: Modbus is big-endian within the 16-bit register — high byte first. Little-endian implementations read with bytes swapped. Symptom: 24.00 V becomes 0.06 V (0x0960 read as 0x6009).

  3. Word swapping in 32-bit values: floats, int32 and counters occupy two consecutive registers. Word order (high word first vs low word first) is not standardized. All four combinations (ABCD, CDAB, BADC, DCBA) appear in the field. Symptom: small values correct, large values absurd.

  4. Signed vs unsigned: the protocol carries raw 16 bits without sign. -100 arrives as 0xFF9C; read as uint16 it becomes 65436. Voltages with floating reference can be negative and require signed interpretation.

  5. Quantity out of range: 0x03 and 0x04 accept up to 125 registers per request; 0x10 accepts up to 123. Larger requests return exception 0x03. Reading 147 registers in a single request does not work — split into two reads.

Frequently asked questions

Why are there four tables instead of just one? The split reflects the memory of the 1980s Modicon 984 PLC, where digital outputs, digital inputs, analog inputs and configuration memory occupied distinct physical areas. The specification preserved the split to make the RO vs RW and bit vs word semantics explicit.

Can I use holding registers for everything? Technically yes, and many vendors consolidate measurements, setpoints and status into holding registers. The loss is semantic: the client has to know by convention which registers are effectively read-only.

How do I read a 32-bit float in Modbus? By reading two consecutive holding registers and reconstructing the float per the documented word order. The most common order in modern devices is CDAB (low word first), but ABCD appears in European devices.

Does the AEM-60DC8 expose coils or discrete inputs? No. The AEM-60DC8 consolidates all 147 variables into holding registers, with support for function codes 0x03 and 0x10. Channel status and alarm flags are exposed as bitfields.

How do I know if the address in the documentation is logical or protocol? By the number: five digits starting with 0, 1, 3 or 4 is a logical address. Hexadecimal or starting with 0x0000 is a protocol address. In ambiguity, capture the frame and read the byte directly.


Related content

More LRI technical materials on adjacent topics.