Every Modbus request carries a single byte that tells the device what to do - read a set of bits, read a set of registers, write one value, write many. That byte is the function code, the verb of the Modbus sentence, and it is inseparable from the address it acts on. This guide walks through the common function codes, shows which one reads a level transmitter versus writes a valve command, and explains why a request with the wrong function code comes back empty even when the register address is perfectly correct.
Modbus Function Code in one line: A Modbus function code is the one-byte value in a request that specifies the operation to perform - which data table to access and whether to read or write it. The common codes are 01 read coils, 02 read discrete inputs, 03 read holding registers, 04 read input registers, 05 write single coil, 06 write single holding register, 15 write multiple coils, and 16 write multiple holding registers. Because each code is bound to a specific table, using the wrong code targets the wrong table, so a request can name a valid address and still return nothing useful.
The read codes come in four flavors, one per data table. Function code 01 reads coils, the single read-write bits. Function code 02 reads discrete inputs, the single read-only bits. Function code 03 reads holding registers, the sixteen-bit read-write words. Function code 04 reads input registers, the sixteen-bit read-only words. Each of these takes a starting address and a quantity, and returns that many bits or registers from the one table it is bound to - code 03 will only ever read holding registers, never coils or input registers.
The write codes cover the two writable tables. Function code 05 writes a single coil, forcing one bit on or off. Function code 06 writes a single holding register, setting one sixteen-bit value. When more than one value must change at once, function code 15 writes multiple coils and function code 16 writes multiple holding registers, each taking a starting address, a count, and the block of data to write. There are no write codes for discrete inputs or input registers, because those tables are read-only by definition - which is enforced precisely by the absence of any function code that could write them.
The pairing of codes to tables is the whole point of the design. Reading and writing are separate verbs, and each verb is further specialized by which table it touches, so a Modbus master builds a request by first deciding what it wants - read a measurement, write a setpoint, force an output - and then selecting the single function code that expresses exactly that. A device advertises which function codes it supports, and a well-behaved master only issues codes the device implements, since an unsupported code draws an error rather than an action.
Reading a level transmitter means reading a number the device measures, so the relevant codes are 04 for an input register or 03 for a holding register, depending on where the device exposes the measurement. A master polling a wellsite RTU for tank level, flow, and pressure is almost always issuing code 03 or 04, retrieving blocks of sixteen-bit registers that it then scales into engineering units. If a status bit such as a pump-running contact needs reading instead, that is code 02 for a discrete input, or code 01 if the device happens to expose it as a coil.
Writing a valve command is the opposite direction, and the code depends on whether the target is a bit or a register. Commanding a discrete output - open or close, start or stop - is code 05 to write a single coil, or code 15 to set several outputs at once. Pushing a numeric setpoint down to the device, such as a new target level or a timer preset, is code 06 to write a single holding register, or code 16 to write a block of them. The master picks the write code to match both the direction and the data size, so a one-bit command and a one-register command use different codes even though both are writes.
The mapping is therefore mechanical once the table is known: choose read or write, choose bit or register, and the function code follows. This is why the register map an integrator works from lists not only the address of each value but also the table it lives in - the table dictates the legal codes. A value in an input register can only be read with code 04, a value in a coil can be read with 01 and written with 05 or 15, and knowing the table removes any guesswork about which code the master should send.
A frequent and confusing failure is a poll that returns nothing even though the register address is right, and the cause is often the function code rather than the address. Because each code is bound to a specific table, sending code 04 to read an input register at offset 100 asks the device for input register 100 - and if the value actually lives in holding register 100, the device either returns whatever unrelated data sits at input register 100 or, if that offset does not exist in the input register table, replies with an exception. The address matched a table the value was never in.
The trap is that the same numeric offset exists independently in multiple tables, so a wrong code does not always fail loudly. If both the input register table and the holding register table have an offset 100, a wrong code returns real bytes that decode to a plausible-looking but incorrect value, which is harder to diagnose than an outright error. This is why an integrator debugging a dead or wrong poll checks the function code and the target table together: the right address in the wrong table is still the wrong data.
In a cloud SCADA context, this shows up whenever a newly added tag reads zero, garbage, or a frozen value while the address in the register map looks correct. A platform such as Merobix builds each Modbus poll from a function code plus an address, and getting a live tag to read correctly means matching the code to the table the vendor documented, not just copying the offset. Once the code and table agree, the same address that returned nothing begins returning the real measurement - the fix is almost always aligning the verb, not the address.
Function code 03 reads holding registers, the sixteen-bit read-write words that commonly hold measurements and setpoints. It takes a starting address and a quantity and returns that many holding registers. If the value you want lives in an input register instead, you use function code 04, since code 03 only ever reads the holding register table.
Both write to holding registers, but code 06 writes a single register - one sixteen-bit value - while code 16 writes multiple registers in one request, taking a starting address, a count, and a block of data. You use code 06 to change one setpoint and code 16 to update several consecutive registers at once, such as writing both words of a 32-bit value in a single transaction.
Usually because the function code targets the wrong table. Each code is bound to one table, so reading a holding register value with code 04, which reads input registers, asks for a different piece of data at that offset, and the device returns unrelated bytes or an exception. Check that the function code matches the table the value actually lives in - the right address in the wrong table still fails.
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.
Last reviewed: July 27, 2026. Merobix is not affiliated with, endorsed by, or sponsored by these organizations; their names are used only to identify the standards and products discussed.
Merobix reads your field devices into a cloud SCADA - the real thing behind these terms, live in days from any browser.