OpenPLC

OpenPLC

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

05 - Reading and writing

05 - Reading and writing

Three blocks. MODBUS_READ and MODBUS_WRITE act on a rising edge;
MODBUS_WATCH keeps a value fresh on its own and says how much to believe it.

Reading

rd( NODE := node, REQ := go, UNIT := 2,
    FC := MODBUS_FC03_READ_HOLDING_REGISTERS,
    ADDRESS := 40, VALUE := regs );

One block for a single value or a whole range, for bits or registers, and for
values wider than a register. What you passed decides which.

FC says which of the four lists to read, named the way your device's manual
names it:

MODBUS_FC01_READ_COILS on/off, a client may also write them
MODBUS_FC02_READ_DISCRETE_INPUTS on/off, read only
MODBUS_FC03_READ_HOLDING_REGISTERS 16 bits, a client may also write them
MODBUS_FC04_READ_INPUT_REGISTERS 16 bits, read only

A range is one request, not one per address. A hundred registers is one
frame and one round trip. Reading them one at a time is a hundred round trips,
which on a 19200 baud bus is the difference between a scan and a second.

The variable says how many

Leave LENGTH at 0 and the count comes from the variable itself.
ARRAY [0..99] OF WORD reads a hundred registers without you typing 100
anywhere, and it cannot drift when somebody resizes the array a year later.

Getting a length wrong is the commonest mistake anyone makes on a Modbus system
and the one that reads past the end of a buffer, so the block does not ask for
a number it can work out.

LENGTH is an override for one case only — a deliberate partial transfer, some
of a bigger buffer. Everything else is refused with
MODBUS_INVALID_ARGUMENT, before anything reaches the wire:

  • larger than the variable — refused, not read off the end;
  • past what one request may carry — 2000 bits or 125 registers for a read,
    1968 or 123 for a write — refused here rather than found out from an
    exception, or from a device that does not bother to raise one.

COUNT says how many addresses actually moved. It is the pin that makes a
length mistake visible at a glance in the debugger, instead of being inferred
from values that look wrong.

Handling the answer

DONE and ERROR are one-scan pulses, set on the scan the operation
finished and cleared on the next. That is what lets a program count operations
by counting edges — but it also means you have to act on them in the same scan:

IF rd.DONE THEN
    motorSpeed := regs[0];
    faultWord  := regs[1];
END_IF;

IF rd.ERROR THEN
    commsFault := TRUE;
    lastCode   := rd.STATUS;
END_IF;

BUSY is a level, not a pulse: it is TRUE for as long as the request is
outstanding.

Holding REQ high is safe. It reads once on the rising edge, not once a
scan. And a second read of the same address while the first is still
outstanding is refused as MODBUS_DUPLICATE rather than queued twice, so a
mistake there costs you nothing on the bus.

Writing

wr( NODE := node, REQ := go, UNIT := 2, ADDRESS := 40, VALUE := setpoint );

FC defaults to MODBUS_FC_AUTO, which reads what you passed and sends the
function code that matches it:

you passed it sends
a single BOOL FC 05, write single coil
an array of BOOL FC 15, write multiple coils
a single number FC 06, write single register
several numbers FC 16, write multiple registers

That is the choice a person would make, and the one most devices expect.

Name a function code yourself for the devices that disagree — an older
meter that implements FC 06 and not FC 16, a drive whose manual insists on FC
16 even for one register. The manual will name it the same way this pin does.
A named code that does not match what you passed is refused rather than sent as
something else.

A repeated write sends only the value you settled on

Raise REQ again while the first write is still outstanding and the newer value
replaces it before it goes out. The first is concluded as MODBUS_SUPERSEDED,
so nothing is left waiting.

An operator dragging a setpoint slider therefore puts one value on the bus,
not forty. That is worth knowing before you write a rate limiter you do not
need.

Setting one bit of a shared register

MODBUS_FC22_MASK_WRITE_REGISTER is the only atomic read-modify-write Modbus
has, and it is the one to reach for on a shared status or command word:

wr( NODE := node, REQ := go, UNIT := 2,
    FC := MODBUS_FC22_MASK_WRITE_REGISTER, ADDRESS := 10,
    AND_MASK := 16#FFF7, OR_MASK := 16#0008 );

The device keeps (current AND AND_MASK) OR (OR_MASK AND NOT AND_MASK). Above
sets bit 3 and leaves the rest alone.

Without it, two clients setting different bits of one register corrupt each
other: each reads, modifies and writes back, and whichever writes second erases
the first's bit. That fault is intermittent, load-dependent and very hard to
find.

Broadcast

UNIT := 0 means every device on the segment performs the write and none of
them answers
. So DONE means "it was transmitted", not "they did it". Useful
for a synchronised command, and worth knowing that it can never be confirmed.

Values wider than a register

A flow rate, a kilowatt-hour total, a position — none fit in sixteen bits. Pass
a REAL, DINT, UDINT, LREAL, LINT or ULINT and the block reads or
writes the two or four registers and converts them:

flow( NODE := node, REQ := go, UNIT := 2,
      FC := MODBUS_FC04_READ_INPUT_REGISTERS, ADDRESS := 30,
      ORDER := MODBUS_ORDER_CDAB, VALUE := flowRate );

An array works too, and is still one request: eight REAL setpoints are one
frame.

Word order — the thing everyone hits

Nothing on the Modbus wire says which way round a device puts a 32-bit
value.
There is no convention, no negotiation, and plenty of manuals do not
say either. So it is an explicit pin, named the way vendor manuals name it:

MODBUS_ORDER_ABCD big-endian throughout, high word first. What the specification implies and what most devices do
MODBUS_ORDER_CDAB low word first, big-endian within each word. Extremely common — Schneider, many drives, most of the "swapped words" settings in SCADA packages
MODBUS_ORDER_BADC high word first, byte-swapped within each word. Rare
MODBUS_ORDER_DCBA fully reversed. What a device does when it copies a float straight out of a little-endian CPU

How to recognise it. A wrong order does not give you a slightly wrong
number — it gives you an obviously absurd one. A flow rate that should read
1234.5 reads 4.6e-41, or 1.7e38, or a large negative integer. The address
is right; the layout is not.

What to do. Try MODBUS_ORDER_CDAB first; it is the common alternative to
the default. If neither works, read the two registers as plain WORDs and look
at them in the debugger — the bytes are all there, and one of the four
arrangements will be a plausible number.

ORDER is ignored for BOOL and WORD, which have nothing to reorder.

Keeping a value fresh, and knowing whether to trust it

level( NODE := node, UNIT := 1,
       FC := MODBUS_FC04_READ_INPUT_REGISTERS,
       ADDRESS := 30, EVERY := T#2s, VALUE := tankLevel );

No request pin. Every two seconds it decides a read is due and hands it to the
same queue everything else uses. The node transmits it on its next call.

This block never touches the wire, which means it inherits the whole
contract rather than being a special case:

  • a read still outstanding when the next falls due is refused as a duplicate
    rather than stacked, so a watch cannot flood the bus however short its
    interval;
  • a device that stops answering is backed off to as little as one attempt in
    sixteen, so one dead unit does not consume the segment;
  • every read reaches exactly one conclusion, which is what FAILURES counts.

Why quality matters more on Modbus than anywhere

A register holding 1234 tells you nothing about whether 1234 is still true.

On a bus where one device has quietly failed, a stale reading looks exactly
like a steady process
. The number is plausible. It does not flicker. Every
alarm stays clear. A trend of it is a flat line, which is what a well-controlled
process also looks like.

QUALITY is the only thing that separates them:

MODBUS_QUALITY_GOOD read within the interval you asked for. Act on it
MODBUS_QUALITY_UNCERTAIN late, but not by much. One missed read on a busy bus looks like this and is usually nothing
MODBUS_QUALITY_BAD stale past any reasonable allowance, or the last read failed. Do not act on it
MODBUS_QUALITY_UNKNOWN never read at all

Interlock on quality, not on the value:

IF level.QUALITY = MODBUS_QUALITY_GOOD AND tankLevel[0] > 900 THEN
    closeInletValve := TRUE;
END_IF;

UNKNOWN and BAD are different faults

They are worth separate alarms, because they send an engineer to opposite ends
of the site:

  • UNKNOWN — this value has never arrived. An address typed wrong, a unit
    id that is not on the bus, a device never fitted. A commissioning fault,
    found at the desk with a wiring drawing.
  • BAD — it did arrive and has since stopped. A cable, a power supply, a
    device that died. A running fault, found in the plant.

An alarm list that shows both as "no comms" throws that distinction away, and
it is the most useful thing on the pin.

AGE is milliseconds since the value last arrived, or -1 if it never
has
— the same distinction, as a number.

Choosing EVERY

EVERY is what you asked for, and it is also what QUALITY is judged
against
. Ask for 100 ms on a bus that cannot manage it and the value will
read UNCERTAIN for being exactly as late as you should have expected.

Ask for what the process needs:

a nameplate, a serial number read it once, with MODBUS_READ
a tank level, a temperature seconds
a totaliser seconds
a torque or position feedback a hundred milliseconds, if the bus can carry it

Every extra read is bus time a control word cannot have.
10 - Scan rate has the arithmetic.

ENABLE stops and starts a watch in place, without adding a second one, so a
value watched only in one machine state costs nothing the rest of the time.
Starting it reads at once rather than waiting out an interval.

Next: 06 - Diagnostics

Last updated 2 hours ago
Was this helpful?
Thanks!