An operator panel with a Modbus RTU master mode talks to a bare microcontroller; no PLC is required. Set both ends to the same serial settings, 19200 baud 8E1, keep 3.5 character times of silence between frames, and implement functions 03, 06 and 16 in C on the microcontroller. Bench-test one register before connecting machine control.
A touch operator panel with a Modbus RTU master mode connects to a bare microcontroller, an 8051-class part included, with no PLC in between. The panel sends every request; the microcontroller answers as an RTU slave. Three numbers decide whether the link comes up: the same serial rate and 11-bit format on both ends (8 data bits, even parity, 1 stop bit); a silent gap of at least 3.5 character times, which is 2.0 ms at the rate set below, between frames; and matching slave addressing. This page walks the role check, the serial settings, the register map, the C code and a bench test.
First check who starts the conversation
Modbus RTU allows exactly one master on the wire. The panel is the master: it starts every transaction. The microcontroller is the slave, at one address in the 1 to 247 range the specification defines. Two devices configured as masters sit waiting for a request that never arrives; two slaves never transmit first. Before writing code, open the panel project and confirm that the serial connection you plan to use is set to the RTU protocol in the master role, and note whether that port is RS-485 or RS-232. The panel-side settings are the same ones you enter when you connect a PC HMI to a PLC over a serial protocol or Ethernet.
Serial settings both ends must share
The RTU default in the Modbus over Serial Line specification is 19200 baud, 8 data bits, even parity, 1 stop bit. A character is 11 bits long: 1 start bit, 8 data bits, 1 parity bit, 1 stop bit. Program the microcontroller's UART for that same 11-bit format and set its slave address to match the panel project.
On RS-485, wire A to A and B to B, terminate both cable ends with 120 Ω resistors (the characteristic impedance of twisted-pair data cable) and fit bias resistors so the idle line rests at a defined level. RS-232 suits a point-to-point link at the common planning distance of about 15 m. If the panel reports no reply or address errors once wiring is done, the fault table in Modbus RTU master no reply and address errors lists the causes in the order to check them.
Frame timing: silence separates the frames
The Modbus over Serial Line specification separates RTU frames with silence: at least 3.5 character times of idle line between frames, and at most 1.5 character times between characters inside one frame. At the 19200 baud line rate one character takes 11 / 19200 = 0.573 ms, so the frame gap is 3.5 × 0.573 = 2.0 ms and the inter-character limit is 0.86 ms. Above that rate the specification fixes the two timers at 750 µs and 1.75 ms.
/* Illustrative constants at the line rate; pick values at or above the spec minima. */
static const uint32_t t1_5_us = 900u; /* spec minimum 859 us */
static const uint32_t t3_5_us = 3000u; /* spec minimum 2005 us */
Restart the idle timer on every received byte and treat its expiry as the frame boundary; the margin above the 2005 µs minimum absorbs timer jitter. A frequent firmware bug is restarting the timer once per frame or servicing it in a slow main loop, which merges two frames into one; the notes on the t3.5 silent-interval timer restart issue describe that failure and the fix. Assemble the reply only after the gap confirms the request is complete.
Map the registers before writing code
Give every panel object one holding register and write down access and range. That table is the contract both ends follow:
| Protocol address | Size | Access | Contents |
|---|---|---|---|
| 0 | 1 word | write | speed setpoint, 0 to 3000 |
| 1 | 1 word | read | measured speed |
| 2 | 1 word | read | status bits |
| 3 to 4 | 2 words | read | run-hour counter, 32-bit across two words |
Expose read-only registers first and check them on the bench; add writable registers only afterwards. Gate every write through the same validity and interlock checks the machine firmware applies locally; a panel write must never bypass them. Keep the table with the panel project so a later change updates both ends at once.
Implement the RTU slave in C
- Configure the UART for the agreed 8E1 11-bit format at the line rate, and enable an idle-line interrupt or timestamp every received byte.
- Collect bytes into a frame buffer of 256 bytes, the maximum frame the specification allows. If the buffer fills mid-frame, discard the whole frame, return to idle and wait for the next 3.5-character gap; never parse a partial frame.
- When the idle gap fires, check the address byte against yours, verify the CRC-16 that RTU framing uses, then dispatch: function 03 reads holding registers, 06 writes one, 16 (hex 10) writes several.
- Answer every valid request, and reject invalid ones with an exception frame: your address, the function code with the high bit set, an exception code such as 02 for an address outside the map, and a fresh CRC. A silent slave is exactly what the panel reports as a timeout.
- Start from an existing open-source RTU slave library for framing and CRC and port its transport to your UART; the state machine is where most bench hours go.
- Set the panel's response timeout explicitly; with a 100 ms poll cycle, a 500 ms timeout gives the microcontroller room.
Worked example: one poll cycle
Scenario: the panel reads measured speed and status bits (function 03, start address 1, quantity 2) from slave 1 every 100 ms, at the line rate with 8E1.
- Request frame: 1 address byte + 1 function byte + 2 start-address bytes + 2 quantity bytes + 2 CRC bytes = 8 bytes. At 11 bits per character: 8 × 11 = 88 bits; 88 / 19200 bps = 4.6 ms on the wire.
- Response frame: 1 address + 1 function + 1 byte-count byte (04 hex for 4 data bytes) + 2 × 2 data bytes + 2 CRC = 10 bytes; 110 bits; 110 / 19200 = 5.7 ms.
- Silence: 2.0 ms before the request and 2.0 ms after the response, since every frame needs the 3.5-character gap on both sides.
- Minimum cycle: 4.6 + 5.7 + 4.0 = 14.3 ms.
A 100 ms poll therefore occupies 14.3 / 100 = about 14 percent of the bus and leaves room for other slaves. For counters that move slowly, poll every 1000 ms and the load drops to 14.3 / 1000 = about 1.4 percent.
How to check it worked
- Add holding register 5 as a test value, hold 12345 in it in the firmware, and confirm the panel screen shows 12345.
- Write 7 to the speed setpoint from the panel keypad; confirm the firmware variable becomes 7 and reads back through register 0.
- Set the panel's slave address to 2 and confirm it reports no reply; restore address 1.
- Pull one RS-485 wire and confirm the panel raises a communication error instead of presenting stale numbers as current; reconnect.
- Power-cycle the microcontroller and confirm the panel starts polling again on its own; the panel side of that recovery is the subject of make a Modbus master reconnect after a slave outage.
- Only now connect real machine values: reads first, writes last, with the interlocks in place.
When a panel and controller both have a network port, the same slave pattern carries over on Ethernet; the register side of that arrangement is shown in Arduino Mega as a Modbus TCP slave with analog and PWM I/O.
Recommendation: run the panel as the only RTU master at the specification default rate of 19200 baud 8E1 over RS-485 with 120 Ω termination at both cable ends; give the microcontroller slave address 1 and implement functions 03, 06 and 16 only; poll every 100 ms while tuning and every 500 to 1000 ms once the values read back steadily. Add writable registers only after the bench test passes, with the firmware's own interlocks behind every write.
Questions
The panel asks for register 40001 but my firmware has no register 40001. What is wrong?
Nothing yet. 4xxxx is the display notation for holding registers, and the protocol address behind it is one lower, so 40001 is protocol address 0. Many panels let you choose whether that offset is added automatically. Read 40001 with function 03 and compare the reply to the variable at index 0 in your map. If every value appears shifted by one register, toggle the offset setting and the map lines up.
Can the microcontroller send data to the panel on its own?
No. RTU is master-driven: only the panel starts a transaction, so the microcontroller never transmits first. Put any value the screen must show in a holding register and let the panel poll for it. If updates need to arrive faster, shorten the poll interval, or move both ends to Modbus TCP over Ethernet, where the same register map and the same slave code pattern apply with a different transport.
Does a microcontroller slave need a PLC anywhere in the setup?
No. The panel talks to the RTU slave directly, which is why the pairing suits a small cabinet: the microcontroller occupies the space a PLC would. The panel only needs a device that speaks RTU at the agreed serial settings and address. Keep the register map documented next to the panel project so a firmware change and a screen change stay in step.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.