Modbus Basics Modbus
Modbus is older than most building automation. Modicon released it in 1979 so that a programmable controller and a host could trade a handful of values over a serial line, and the same protocol — barely changed — now carries readings between half the BMS-adjacent gear on a job site. Variable-frequency drives, power meters, BTU meters, packaged units with their own controllers, plenty of small specialty kit nobody else integrates: any of them might speak it. If you integrate equipment, you'll meet it. This page is the explainer the Modbus Register Viewer footnote keeps pointing toward.
What Modbus is, and isn't
At its core, Modbus is two things: a small set of function codes that say "read these locations" or "write this location," and a tiny data model of locations to read and write. The transport — whether the message runs over a serial line (Modbus RTU) or an Ethernet TCP connection (Modbus TCP) — changes the framing around the message but not the function codes or the data model. Either flavor, the same nine or ten function codes are doing the work.
What Modbus is not is self-describing. A BACnet object knows its own type, units, scale, and name; a Modbus register is just sixteen bits. Nothing on the wire says holding register 12 is in tenths of a degree Fahrenheit, or that the value 524 means 52.4 °F. That mapping lives only in the vendor's documentation. Modbus is dumb on purpose: the protocol stays simple, and the device's manual carries the load. Lose the manual and you have data without meaning.
The other thing Modbus assumes is one client talking to one or more servers. (Older docs say master/slave; the function codes and the wire are unchanged, only the names have moved on.) Every conversation starts when the client sends a request to a specific server's address; the server returns a response, then waits silently for the next request. Servers don't initiate — there is no asynchronous "by the way, this just changed" notification. If a sequence cares how fast it sees a new value, the client has to poll fast enough to catch it.
The four data tables
The data model has four tables, sorted along two dimensions: what each item is (a single bit or a 16-bit word), and whether the client can write it or only read it. The two single-bit tables — coils and discrete inputs — predate the registers; they map back to relay-style controllers, where "coil" meant the energizing coil of a control relay the master could write, and "discrete input" was a contact closure it could only read. The two 16-bit tables — input registers and holding registers — are where the analog values live.
Coils · 1-bit · read/write
A writable bit. Enables, overrides, commanded states. Read with FC01; write with FC05 (one) or FC15 (many). Rare in modern BMS gear; common in older industrial controllers.
Discrete inputs · 1-bit · read-only
A read-only bit. Switch positions, status flags, freezestats, alarm bits. Read with FC02. The server reports them; the client cannot change them.
Input registers · 16-bit · read-only
A read-only 16-bit value. Sensor readings, measured values, computed feedback. Read with FC04. On a VFD, the speed feedback typically lives here.
Holding registers · 16-bit · read/write
A read/write 16-bit value. Setpoints, commands, configuration. Read with FC03; write with FC06 (one) or FC16 (many). On a VFD, the speed command lives here.
Four tables, named for forty-year-old reasons, addressed independently — register 0 in the input table and register 0 in the holding table are different storage locations. The address alone doesn't tell you which table; the function code that names which table to act on does. That's where the next section starts.
Function codes — reading and writing
Each table gets two function codes: one to read it, and (where applicable) one to write it. Read function codes can return up to a few hundred items in one request — the protocol caps coils at 2000 per read and registers at 125, but most devices set tighter limits in their docs. Writes come in two flavors, single and multiple, so a single-register setpoint update doesn't drag a whole block along with it.
The pairs that come up most in building controls are FC03 / FC16 for holding registers (read a setpoint or a command, write a setpoint or a command), and FC04 for input registers (read a sensor). FC06 appears when only one register is being changed at a time. The bit-table function codes — FC01, FC02, FC05, FC15 — show up less often in BMS work and more in older industrial gear. The full list lives on the Modbus Register Viewer tool's reference panel; this page treats it as a lookup rather than reprinting it.
Every request follows the same shape. On Modbus RTU, the byte sequence is the server's address (which device on the bus), the function code (what to do), a data field whose contents depend on the function (which register, how many, what value), and a two-byte CRC at the end for line-error detection. Modbus TCP wraps the same function code + data payload in a small TCP header and drops the CRC — TCP already handles error detection. The two transports speak the same language; only the envelope changes.
The response carries the same server address and function code echoed back, a byte count, and the requested values back-to-back. Two-byte starting address and quantity in the request, multi-byte payload in the response — that's most of what a Modbus request looks like on the wire.
When something goes wrong — exception responses
When the server can't or won't honor a request, it returns an exception
response instead of the data. The shape is deliberately simple: it
echoes the function code back with the high bit set (FC 0x03
comes back as 0x83), followed by a one-byte exception code that
names what went wrong.
The exception codes that show up most often in building controls:
0x01Illegal Function. The server doesn't implement this function code at all. A read-only sensor slave with no write support, hit with FC06 (Write Single Register), returns this — the function itself isn't in its table. Contrast0x02below, where the function is supported and only the address is wrong.0x02Illegal Data Address. The function code is fine; the address is outside the device's documented range. A vendor manual that documents holding registers 0–127 and a client asking for register 200 produces this.0x03Illegal Data Value. Address is fine, the value being written is not. Writing 200 to a 0–100 % setpoint register is the canonical example.0x04Server Failure. An internal device error — a sensor fault, an internal comms problem on a multi-board unit, a coprocessor that didn't answer. The device is telling you it can't produce a meaningful answer.
Higher codes (0x05 through 0x0B) cover acknowledge-
and-retry, target-device-busy, and gateway-routing variants. Most BMS techs
will see 0x02 and 0x03 more than the rest, and
0x01 whenever a function code is mistyped or a register has
been mapped to the wrong table.
A response that arrives with the function code echoed and no high bit set is a normal response — the bytes after it are the data that was asked for. The high-bit-set echo is the universal "look at the next byte, that's the error" signal.
What this page didn't cover
That's the message shape and the data model — enough to read a vendor's
Modbus integration table, build a request, and recognize an exception when
one comes back. The next question is the one the
Modbus Register Viewer tool
was built around: the response came back successfully, so what do those
sixteen bits actually mean? The 5-digit register numbering convention
(40001 in the manual is holding register 0 on the wire),
signed vs unsigned interpretation, 32-bit values that span two registers
with four possible byte orderings, and the scale factor that turns a raw
integer into engineering units —
Modbus Decoding is the
companion page that walks through each.