Modbus RTU master over a serial line

You will find the project Qenex_Serial_ModbusMaster_HoldingRegisters.qproj in the Examples folder next to QInsight.exe.

What the example shows

QInsight acts here as a Modbus RTU master: the Serial Port (COM) driver with the Modbus Master protocol in rtu mode reads holding registers from the slave with address 2 once a second. The example shows:

  • the driver and protocol settings exactly as entered in Project Configuration,
  • scalar variables in holding registers (ushort, one register per variable) with a linear conversion and a unit,
  • a Matrix variable transferred as one contiguous register block — axes and data cells of a map in a single request,
  • writing to the device from the Watch Table (scalars) and from the Matrix control (map cells),
  • driver reconnection after the converter is unplugged and the master resuming on its own.

What you need

The counterpart is any Modbus RTU slave with unit id 2 that offers holding registers at addresses 0–12. Without hardware a simulator will do, e.g. pyModSlave: a pair of virtual COM ports (QInsight on one end, the simulator on the other) and, in the simulator, RTU, slave id 2, 9600 Bd, 8N1, holding registers starting at address 0. With a real device a USB/RS-485 converter is enough.

Before starting, adapt in Project Configuration what is specific to your computer and device:

  • port in the driver settings — the project has COM6; enter the port of the converter or your end of the virtual pair,
  • baudRate (and parity, stopBits if needed) to match the device — the project expects 9600 Bd, 8N1,
  • unitId in the protocol settings if the slave is not at address 2.

The project

Drivers and protocols

One driver + protocol pair handles the communication. The settings are stored exactly like this:

Serial Port (COM) driverMeaning
port=COM6COM port name — change it for your computer
baudRate=9600communication speed
dataBits=8number of data bits
parity=noneno parity
stopBits=1one stop bit
handshake=noneno flow control
reconnectDelayMs=2000delay before reopening the port after a failure
reconnectAttempts=5after five consecutive failed attempts the driver stops (the default is -1 = forever)
Modbus Master (RTU/TCP) protocolMeaning
mode=rtuRTU framing (CRC16) for a serial line
unitId=2address of the slave the master talks to
requestTimeoutMs=2000response timeout per request
requestRetries=8request repetitions after a timeout, on top of the first attempt (up to 9 transmissions)

The project also contains two drivers the Modbus example does not need: Data Log Recorder (file=DataLogs;append=true;flushOnWrite=false;reorderBufferMs=500) is enabled and records EngTorque and Torque into a .qilog file in the DataLogs folder next to the project on every run (the matrix is not logged); Data Log Replay (file=DataLogs\values.qilog;mode=realtime;speed=1;loop=false) is disabled. If you do not want logging, disable the Data Log Recorder driver.

Events

The project has a single periodic event Pool500ms with a period of 1000 ms (the name does not match the actual period; the Period value is what counts). All three variables are bound to it, so once a second the master sends three separate FC03 requests — one per variable.

Variables

All variables live in the /Modbus namespace, are of type ushort and share the ModbusSImple presentation: the Pass-through conversion is linear with multiplier=0.01, offset=0 (engineering value = raw × 0.01, so raw 12345 is shown as 123.450), unit Nm, format {0:F3}, range 0–65535.

VariableTypeRegisterAddressDirectionEventPresentation
EngTorque (Engine torque)scalar ushortholding0readWritePool500msModbusSImple (× 0.01, Nm)
Torque (Torque)scalar ushortholding1readPool500msModbusSImple (× 0.01, Nm)
Drivability (Drivability)matrix ushort, 3 × 2holding2readWritePool500msModbusSImple on axes and data

Comm params of the scalars: registerType="holdingRegister";address="0";wordOrder="big";direction="readWrite";eventRef="Pool500ms" (for Torque address="1", direction="read"). wordOrder has no effect on single-register values.

Drivability is a Matrix variable — a map with an X axis Speed [RPM] (3 points), a Y axis Temp. [K] (2 points) and 6 data cells, 11 values = 22 bytes in total, Endianness be. The Modbus Master transfers it as a contiguous block of 11 holding registers starting at address 2 (addresses 2–12); a read is a single FC03 request, a write sends only the changed cells. The ModbusSImple presentation is assigned to both axes as well, so the axis values are displayed with the Nm unit.

Workspace

One workspace (Workspace6904) with five controls:

ControlVariablesNote
GraphEngTorque (red, left axis), Torque (blue, right axis)10 s window, both axes auto-scale
SignalEngTorque250 ms refresh
SignalTorque250 ms refresh
MatrixDrivabilityAutoRead, cell written on Enter (WriteOnEnter)
Watch TableTorque, EngTorquecolumns Name, Value, Unit, Time, Write

No control is saved in write mode — writing is switched on at runtime.

What you will see and what to try

  1. Reading. Start the runtime (Start). The driver opens the COM port and the master starts reading registers 0, 1 and the block 2–12 once a second. Change holding register 0 or 1 in the simulator and watch the value appear within a second in the graph, the Signal control and the Watch Table — divided by one hundred and with Nm.
  2. Writing a scalar. In the Watch Table switch on write mode for the EngTorque row (Write column), enter an engineering value such as 123.45 and confirm. The master writes the raw value 12345 into register 0 and you will see it in the simulator. Torque is read only, so no write is offered for it. Details in Writing values to the device.
  3. Writing a map cell. Switch the Matrix control to write mode, overwrite a cell or an axis point and confirm with Enter — only the affected register of the 2–12 block goes to the slave.
  4. Connection loss. Unplug the USB converter (or close the virtual port) while running. The driver enters the Faulted state, the master pauses its requests and the driver tries to reopen the port every 2 s, at most five times. Plug the converter back within those roughly ten seconds — the driver opens the port, the master resumes reading on its own and the graph only shows a gap. Once the limit is exhausted the driver ends in the Stopped state with the error gave up after 5 reconnect attempt(s) and you restart it with the Start button in its properties (see Driver states and the Start button). Set reconnectAttempts=-1 for endless retries. When it is the slave that does not answer (wrong unitId, simulator closed), the port stays open and only the protocol reports the error through its timeout — with requestTimeoutMs=2000 and requestRetries=8 it waits up to 9 × 2 s for a single request.