Log PLC data on V-memory change with a compare-and-save bit

9 min read · Published November 10, 2026

Set a 1-second sample pulse and compare each logged V-memory word with a saved copy of its last sample. The not-equal result drives the trend's trigger bit, so the panel writes a row only when the value changed. The same four rungs repeat for every word, and a MOD-formula sheet turns each logged decimal back into its 16 I/O bits.

A trend that logs on a timer writes a row whether or not anything happened. On a line where the I/O word changes a few times a minute, one sample per second turns a handful of real events into tens of thousands of rows per shift. The fix is a compare-and-save bit in the PLC. Each second, compare the logged word against a saved copy of its last sample, and let the not-equal result drive the trend's trigger. This article builds the four rungs with the example addresses V40400, V400, C0 and C10, picks between the panel's edge and Start/Stop trigger modes, walks one word through a second-by-second example, and decodes the exported decimal values back into their 16 input bits in a spreadsheet.

Why does logging every second bury the changes you want?

At one sample per second, an eight-hour shift writes 28,800 rows per word, and nearly all of them repeat the same I/O pattern; a compare-and-save trigger inverts that, so the file holds roughly one row per real change.

The arithmetic is fixed: 3,600 samples per hour times 8 hours is 28,800 rows, whether the input moved twice or two hundred times. Setting up an event for every value the word can take grows just as fast, because a 16-bit word has 65,536 combinations and the list has to cover every direction of change. The compare-and-save pattern needs one pair of rungs per word and catches up and down moves with the same not-equal instruction. The duplicates cost more than clutter: trend history that fills panel memory and stalls the screen is its own failure, covered in why an HMI panel freezes when trend history fills panel memory, and the exported file gets harder to use at the same rate. Where the rows land, and how long the panel keeps them, follows the same storage rules as storing PLC data on an HMI panel for later retrieval.

How does a compare-and-save bit detect a change in V-memory?

The logic keeps two values per word: the live register, for example V40400, and a saved copy in a spare register such as V400; every second, a not-equal compare between them means the word changed, and that result drives the trend's trigger bit.

The not-equal instruction is what makes the pattern cheap. One rung pair answers whether anything changed, in either direction, so there is no list of value-specific events to maintain and no way to miss a downward move. Bit-pattern words like an I/O status word need no dead band around the sample, because any bit difference is a real change. After the compare, the live value is copied into the saved register, so the baseline matches the present again and one difference fires exactly one event. Give each word its own saved register: a baseline shared between V40400 and V40500 compares two unrelated words and reports changes that never happened.

What do you need before you build the logic?

You need a 1-second pulse bit, one spare V register as the saved copy for each logged word, one spare discrete bit as the trend trigger, and a trend object on the panel that accepts a PLC tag as its trigger.

The pulse comes from the PLC's own 1-second system bit, or from a self-resetting timer you drive a coil with, exactly as the 1-second SP does in the example logic. For the three words in the worked case, V40400, V40500 and V40501, the example uses V400 as V40400's saved copy; pick your own spare register for each of the other two and never reuse one. The trigger bit C10 is an ordinary discrete coil the panel reads as a tag. If the compare instruction's operands are unfamiliar, a compare-instruction and V-memory addressing reference works through them against V-memory addresses.

How do you build the compare-and-save rungs step by step?

Build four rungs: make the 1-second pulse, reset the trigger when the words are equal, set it when they differ, and copy the live word into the saved register on the same pulse after both compares have run.

  1. Drive C0 from the 1-second pulse bit, so C0 turns on for one scan every second.
  2. On C0's rising edge, reset C10 when the equal compare is true: V40400 = V400.
  3. On C0's rising edge, set C10 when the not-equal compare is true: V40400 <> V400.
  4. On C0's rising edge, after rungs 2 and 3, copy the live value into the baseline: LD V40400, OUT V400.
  5. On the first scan after power-up, copy V40400 into V400 and reset C10, so the first pulse sees a matched baseline and no startup event fires.
  6. Repeat rungs 2 through 4 for V40500 and V40501, each against its own saved register and its own trigger bit.
Rung 1   [1-second pulse bit]-----------------------( OUT C0 )
Rung 2   [C0 rising edge]--[V40400 =  V400]---------( RST C10 )
Rung 3   [C0 rising edge]--[V40400 <> V400]---------( SET C10 )
Rung 4   [C0 rising edge]----------------( LD V40400 )( OUT V400 )
Startup  [first scan bit]----------------( LD V40400 )( OUT V400 )---( RST C10 )

Contact and coil names differ between PLC families; the order is the pattern. The compares must read the baseline before the copy overwrites it. Copy first and the two words match from that scan on, the not-equal rung never fires again, and every change is lost.

Should the trend trigger on a pulse edge or a Start/Stop bit?

Use Start/Stop: the trend logs on its own 1-second interval while the trigger bit is ON and stops when the bit goes OFF, so a compare result held for one full second is read by the panel's next poll and written as rows.

An edge trigger set to log when ON behaves differently: the panel writes only when it actually sees the bit go OFF and then ON. That makes pulse width matter. With C10 set and reset from pulse edges, a word changing fast enough leaves the bit ON for as little as half a second and OFF for half a second, and a pulse shorter than the gap between panel polls is never seen. Start/Stop removes the race: the bit is a state, the poll reads it, and the trend's own interval paces the rows.

Trigger mode What the panel does What your logic must do
Edge, log when ON Writes when it sees the bit go OFF, then ON Hold C10 ON at least one poll cycle and return it OFF before the next change
Start/Stop Writes on the trend's own interval while C10 is ON, stops when OFF Set C10 on not-equal, reset on equal, trend interval 1 second

If you shorten the trend interval toward half a second, check the panel's update limit first; why an HMI trend graph will not update every half second shows what sets that floor.

Recommendation: Configure the trend for Start/Stop with C10 as its bit and a 1-second log interval, and keep the compare-and-save rungs as built above so C10 drops after one steady second. Take the edge trigger only when the panel has no Start/Stop mode, stretch C10's ON time to a full poll cycle, and confirm on the exported rows that each change you made produced one row.

How does the worked example run on one word?

Take V40400 holding the I/O pattern 13, with V400 also at 13: at the next pulse the compare is equal, C10 stays OFF and nothing logs; the word then becomes 15, the next pulse sees 13 <> 15, C10 sets and the trend logs 15.

Pulse V40400 V400 before Compare C10 after Rows added
t = 0 s 13 13 equal OFF (reset) 0
t = 1 s 15 13 13 <> 15 ON (set) 1: the value 15
t = 2 s 15 15 equal OFF (reset) 0
t = 3 s 13 15 15 <> 13 ON (set) 1: the value 13

Both directions get caught: the move up to 15 and the drop back to 13 each produce a row, and the two steady seconds produce none. In Start/Stop mode the panel paces the rows on its own 1-second ticks while C10 is high, so each change lands as one or two rows with a timestamp. Decoding the numbers: 13 = 1 + 4 + 8, so inputs X0, X2 and X3 were on; 15 = 1 + 2 + 4 + 8, so X0 through X3 were on. One case gets no row on purpose: a word that flips 13 to 15 and back to 13 inside the same 1-second window matches its baseline at the next pulse, and the compare records the net change across each window.

How do you check the trigger before you trust the file?

Watch all four values together in the PLC status view, change the word once, and require three things: C10 sets on the pulse after the change, V400 takes the new value on that same pulse, and three steady pulses later the trigger is still OFF.

Then confirm the file side:

  1. Hold the word steady through 3 or 4 pulses: C10 stays OFF and no rows appear.
  2. Make one change: exactly one ON stretch and one or two rows.
  3. Change the word at the fastest rate the process produces, export, and count rows against the changes you saw.
  4. Repeat step 3 under normal communications load and compare timestamps.

Three misbehaviors cover most of it. Only the first change ever logs: the equal-compare reset rung is missing or sits after the copy. Rows are fewer than the changes you made: the panel is watching an edge it never sees, so move to Start/Stop. A row appears at power-up: the first-scan initialization is missing and a stale baseline fired the first pulse.

How do you turn each logged decimal back into its 16 bits?

The log stores one decimal number per sample, so parse it in the spreadsheet: put the bit weights 1, 2, 4 up to 32,768 across row 2, and each MOD formula divides by the next weight after subtracting the bits already decoded.

Set the sheet up as in the example template: row 1 holds DATE & TIME in column A, LOG DEC in column B, and the input labels X0 through X17 across columns C to R; the first data row is row 3. Row 2 holds the weights 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 and 32768 in C2:R2, with 65536 in S2 as the next weight past the word.

  • C3 (X0): =MOD(B3,D2)/C2
  • D3 (X1): =(MOD(B3,E2)-C3*C2)/D2
  • E3 (X2): =(MOD(B3,F2)-(C3*C2+D3*D2))/E2
  • Continue the same way through R3 (X17), whose next weight is in S2; each column subtracts the contribution of every lower bit already decoded, then divides by its own weight. Fill each formula down after entering it in row 3.

Run the numbers for a logged 13: C3 = MOD(13,2)/1 = 1; D3 = (MOD(13,4) - 1x1)/2 = 0; E3 = (MOD(13,8) - (1 + 0))/4 = 1; F3 = (MOD(13,16) - (1 + 0 + 4))/8 = 1; G3 = (MOD(13,32) - 13)/16 = 0; H3 through R3 all return 0. Thirteen is 1 + 4 + 8, so the sheet shows X0, X2 and X3 on, matching the example above. Put =SUMPRODUCT(C3:R3,C2:R2) in a check column and require it to equal the logged value; a mismatch means one formula was dragged wrong.

Negative values have one wrinkle. When the top bit is on, a panel that logs signed 16-bit integers writes the word as a negative number: a pattern with only X17 on arrives as -32768. The MOD formulas still decode it, because the spreadsheet's MOD wraps a negative into its unsigned range, but the check column fails, since it rebuilds a positive sum against a negative log value. Add 65536 once in a helper column, point the sheet at that column, and both read clean: -32768 + 65536 = 32768, which parses as X17 on and every other bit off.

When the log reads cleanly and the pattern is worth watching at the line, a separate shop-floor display carrying a status board as offline slides does that job without touching the HMI project; our shop-floor display range is built for it.

Questions

Does the compare-and-save trick work for analog values too?

It does, with one change: a raw analog word drifts a few counts with sensor noise, so a plain not-equal compare would log on every pulse. Give the analog a dead band. Treat the word as changed only when it moves more than your band size, for example 10 counts, away from the saved sample, and save the new sample only when the band is exceeded. Bit-pattern words like the I/O examples here need no band, because any bit difference is a real change.

What happens if the value changes twice inside one sample interval?

The compare sees only the net result at the next pulse. A word that goes 13 to 15 and back to 13 inside one second matches its baseline at the pulse, so that excursion gets no row. If the process can flip that fast, shorten the pulse: a 250 ms pulse samples four times per second and catches any excursion lasting at least 250 ms, at the cost of more rows when changes do happen.

How much smaller does the log file get?

It scales with the change rate. A word that changes 6 times per minute produces 360 rows per hour under change-only logging, against 3,600 rows per hour at a 1-second interval, a 90 percent cut. That is roughly ten times more history in the same log before it fills or wraps, and the export shrinks by the same factor. The exact saving depends only on how often the word actually changes.

Can one trigger bit serve all three logged words?

Yes. Build one compare-and-save pair per word, each with its own saved-copy register, and OR the three not-equal results into one trigger bit, so any word's change starts the log. The trade is that the trigger alone no longer tells you which word changed. If you need that, give each word its own Start/Stop bit on its own trend object, or identify the word from the decoded bits after export.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote