Modbus FC03 (Read Holding Registers)
Function code 03 reads holding registers, the sixteen-bit read-write words that carry most of the analog and numeric data on a Modbus device. It is the workhorse read code, the one an integrator uses to pull a level, a flow, a setpoint, or a status word. This reference covers what FC03 targets, its per-request register limit, and how it differs from the input-register read it is constantly confused with.
Modbus Function Code 03 Read Holding Registers in one line: Modbus function code 03 reads holding registers: the read-write sixteen-bit words that hold setpoints, measured values, and status data. The request names a starting register and a quantity, and the device returns a byte count followed by two bytes per register, most significant byte first. A single FC03 request can read up to 125 registers, and it reads only the holding-register table.
What FC03 Reads and Its 125-Register Limit
Holding registers are the sixteen-bit read-write words that make up most of a device's usable data: setpoints you can change, measured values the device updates, and status words. Function code 03 reads a contiguous block of them. The request carries a starting register address and a quantity, and the device returns a byte count followed by two bytes per register, high byte first. Values wider than sixteen bits, such as a 32-bit float or long, occupy two consecutive registers, a packing detail covered in the 32-bit float across two registers reference.
A single FC03 request can read at most 125 registers, because the response byte count is one byte and the payload must fit the maximum protocol data unit. Asking for more returns an illegal data value exception, so large maps are read in blocks. The reason for the exact 125 figure is explained in the Modbus 125-register read limit reference.
FC03 Versus FC04 and Common Map Errors
The single most common confusion is between function code 03 and function code 04. FC03 reads the read-write holding-register table, usually mapped to the 40001 range, while FC04 reads the read-only input-register table, usually mapped to the 30001 range. A value that reads correctly under one code and returns an exception or garbage under the other is almost always in the other table. The dedicated FC4 input vs FC3 holding register read reference walks this distinction in detail.
When FC03 returns an illegal data address exception, the register either does not exist on the device or your map is expressed in a different addressing base, since the wire uses a zero-based offset while documentation often uses one-based 4xxxx numbers. Resolving that offset is the subject of the Modbus register addressing reference.
The Request and Response, Field by Field
The FC03 protocol data unit is compact enough to decode by eye. The request is five bytes: function 0x03, a two-byte starting address, and a two-byte quantity. Worked example: a value documented at register 40108 sits at wire offset 107, which is 0x006B, so reading three registers from there produces the request bytes 03 00 6B 00 03. The normal response is the function code 0x03, a byte count of 06 (three registers at two bytes each), and then the six data bytes, each register most significant byte first. On Modbus RTU this PDU is wrapped with the slave address in front and a CRC behind; on Modbus TCP it is prefixed by the MBAP header carrying the unit identifier and length, on TCP port 502.
The per-register byte order is fixed by the specification, but how a device arranges the two registers of a 32-bit value is not, which is why the same response bytes can decode to a sane number or garbage depending on assumptions. When a combined value looks wrong, decode the raw registers by hand against the guide to register byte order and endianness before touching the configuration.
Reading Exception Responses to FC03
When an FC03 request fails, the device sets the high bit of the function code, so the response arrives as 0x83 followed by a single exception byte. Exception 02, illegal data address, means the requested block touches at least one register the device does not expose. Exception 03, illegal data value, most often means the quantity field is out of the allowed range, such as asking for more than 125 registers. Exception 04 reports an internal device failure, and 06 says the device is busy and the request should be retried. The full list with causes and fixes lives in the exception codes reference table.
One subtlety catches integrators constantly: many devices reject an entire block read if any single register inside the span is undefined, returning exception 02 for a request where the first and last registers are perfectly valid. Others silently return filler for the undefined middle. Which behavior you get is device-specific, so probe it deliberately during integration - read across a documented gap once and see what comes back - rather than discovering it in production.
Polling FC03 Without Wasting the Bus
Every FC03 transaction pays a fixed overhead of framing and, on serial, line turnaround, so one read of a large contiguous block always beats many small reads of the same registers. The practical method: sort the map, group it into contiguous blocks up to the 125-register limit, and split blocks only at gaps the device refuses to read across. Then separate the map into poll classes - live process values on a fast scan, configuration and totalizer registers on a slow one - because reading a static serial number every second buys nothing and costs bus time.
On an RS-485 line the whole poll cycle is the serialized sum of every transaction plus turnaround, so an over-ambitious scan rate does not produce faster data - it produces timeouts, retries, and a cycle that slips further behind. If the trend data looks stale or the master logs sporadic timeouts, work through diagnosing slow Modbus polling cycles before adding retries, because retries on a saturated bus only deepen the hole.
Frequently Asked Questions
How many registers can one FC03 request read?
Up to 125 sixteen-bit holding registers per request. The limit comes from the single-byte response byte count and the maximum size of the Modbus protocol data unit. To read more you split the map into blocks of 125 registers or fewer. Requesting more than 125 in one FC03 returns an illegal data value exception.
What is the difference between FC03 and FC04?
FC03 reads holding registers, which are read-write and usually documented in the 40001 range, while FC04 reads input registers, which are read-only and usually in the 30001 range. Both return sixteen-bit words the same way; the difference is entirely which table they target and whether those values can also be written.
How is a value larger than 16 bits read with FC03?
A 32-bit float or long occupies two consecutive holding registers, so you read both in one FC03 request and combine them in the master. The byte and word order of those two registers is device-specific and is the source of many decoding errors, so confirm the device's documented order before trusting the combined value.
Why does my FC03 read work for single registers but fail for a block?
Almost always because a register somewhere inside the block's span is undefined on the device, and the device rejects the whole request with exception 02 even though the endpoints are valid. Check the register map for gaps in the range you requested and split the read into blocks that avoid them. Some devices tolerate gaps and return filler instead; the behavior is device-specific, so test it rather than assume.
Is FC03 different over Modbus RTU and Modbus TCP?
The protocol data unit is identical - the same function code, addressing, and limits. What changes is the wrapper: RTU frames the PDU with a slave address and CRC on the serial line, while TCP prefixes it with an MBAP header carrying the unit identifier and uses port 502. The distinction matters mostly at gateways, where the unit identifier must route to the right serial device.
Sources and verification
This page references the protocol specifications published by the organizations below. Editions, product capabilities, and documentation change over time - confirm current requirements and specifications directly with the source.
- Modbus Application Protocol Specification - Modbus Organization
Merobix is not affiliated with, endorsed by, or sponsored by these organizations; their names are used only to identify the standards and products discussed.
Automation services
Need help turning this into a working system?
Merobix integrates SCADA, programs Allen-Bradley and Siemens PLCs, and designs and fabricates industrial control panels.
Meeting requests are reviewed before confirmation.