Log PLC alarms to a circular event buffer with separate pointers

6 min read · Published November 7, 2026

An encode instruction turns one set bit into a number, so a packed alarm word cannot select the message. Detect each alarm's rising edge in the PLC, write its event code and clock stamp into a fixed ring of registers through a write pointer, wrap that pointer with MOD, and keep a second pointer for the operator's arrow keys. A 500-slot ring in 1,500 words covers weeks of shift history; above about 100 events a shift, log outside the PLC.

A packed alarm word fed to the encode instruction is where this design breaks, and it breaks at the instruction: encode reads one set bit and returns that bit's position as a number, and a word carrying several live alarms has several answers. Build the history in the controller instead. Give every alarm an event code, catch the moment the condition switches on, and write the code with a clock stamp into a fixed block of registers through a write pointer. Wrap that pointer at the end of the block so the log never overflows, and keep a second pointer that moves only when the operator presses the arrow keys, so browsing can never shift the write position. This article sizes a 500-event log, works the arithmetic, and lists the checks to run before you trust it.

Why a packed alarm word cannot select the message

The encode instruction answers one question: which single bit is set. It returns that bit's position as a number. Its input is one bit. Feed it a word of alarm bits and the number slides around as alarms come and go, and when the last alarm clears, the word returns to zero with nothing left to show. That loses both things a log exists for. Alarms that overlap collapse into one changing value, and a cleared alarm erases its own trace, because a live word carries the present only. A log stores each occurrence at the moment it happens. Number the alarms, store each code with its time, and let the panel pick the alarm text from the code in the displayed record; configuring operator text in HMI alarms covers that mapping.

What you need

  • Retentive data registers for the ring: 500 slots at 3 words per record, 1,500 words total, placed inside the controller's retentive range.
  • The controller's real-time clock, read at the moment each record is written.
  • An event code for every alarm, 1, 2, 3 and onward, kept in the alarm list that travels with the program.
  • A rising-edge one-shot contact for every alarm condition.
  • Two arrow keys or other scroll inputs on the operator panel, wired so they can move the view pointer only.

Build the log, step by step

  1. Assign the codes. Number every alarm and keep the list with the program; the code is what ties a stored record to its text on the panel.
  2. Edge-detect every alarm. Run each comparison through a rising-edge one-shot so a condition that stays true for 100 scans writes one record.
  3. Stamp the record at the edge. Copy the event code and the clock values into the 3 words of the slot the write pointer selects, reading the clock at that moment. If several controllers write toward one panel, align their clocks first; synchronizing multiple PLC clocks from one master covers the setup.
  4. Advance and wrap the write pointer. Call it iWrite, an integer initialized to 0, with slots numbered 0 to 499. After each complete record, iWrite := (iWrite + 1) MOD 500.
  5. Count the valid records. Keep a count from 0 to 500 that rises with each write and stops at 500. The display reads only slots below the count, so uninitialized registers never reach the screen in the first days.
  6. Keep the view pointer separate. Call it iView, a second integer initialized to 0, moved only by the operator's keys: one key steps toward newer records, one toward older, clamped to the valid range. Once the ring is full, wrap the movement with iView := (iView + 1) MOD 500 for newer and iView := (iView + 499) MOD 500 for older. No key press writes to the ring or touches iWrite.
  7. Map code to text. The panel selects the alarm message from the event code in the record iView points at, one message per code.

Example: a 500-event log in 1,500 words at 20 alarms a shift

The inputs, and where each comes from:

  • 500 slots, the ring size this design uses, sized to hold weeks of shift history in retentive registers.
  • 3 words per record, one word each for event code, date and time, the layout from the build steps.
  • 20 alarms per shift, the working rate for this example; measure your own line and put your number in.
  • 5 shifts per week, one production shift a day.

The rules: memory in words is slots times words per record. Coverage in shifts is slots divided by records per shift. The next slot is the last slot plus 1, wrapped by the slot count.

The arithmetic:

  • Memory: 500 × 3 = 1,500 retentive words.
  • Wrap: the first lap ends when slot 499 is written; (499 + 1) MOD 500 = 0, so record 501 lands in slot 0 and replaces the oldest record.
  • Coverage: 500 ÷ 20 = 25 shifts, which at 5 shifts a week is 5 weeks of history before anything is overwritten.
  • Rate sensitivity: at 100 alarms a shift, 500 ÷ 100 = 5 shifts, one working week.
  • Browsing margin: a record the operator has parked on survives until the writer laps the ring again, 500 events later, 25 shifts at this rate.

Decision: keep 500 slots while a shift produces at most 100 events. Above 100 events per shift, raise the slot count, at 3 words per slot, or send the records outward as they happen.

How to check it worked

  • One condition, one record. Force the first comparison on and confirm exactly one record, with code 1 and the current clock value. Leave it on for ten scans; the record count stays at one.
  • Clear the condition. The record stays on the panel, which is the point of the log.
  • Test the wrap small first. Set the slot count to 5, trigger 6 events, and confirm slot 0 holds event 6 while the oldest record is gone. Then put the count back to 500.
  • Browse under load. Park iView on an old record while new events keep arriving; writes continue, and the parked record survives until the writer reaches it a full lap later.
  • Hide the empty slots. Before the count reaches 500, the arrow keys stop at the last valid record.
  • Power-cycle once. The block keeps its records in the retentive range, and the clock must still read the correct time afterwards.

When a ring is the wrong tool

A ring overwrites by design. When an event has to survive for a quality investigation or an audit, or when the measured rate outruns the coverage in the example, keep the ring as the short-term view and send each record outward as it is written; logging PLC variables to a database from CODESYS shows one route. If the panel's own history feature looked like the store, check what an HMI panel can store for later retrieval before you depend on it: a controller-side ring survives panel swaps and feeds anything that reads the controller.

The same separation applies to a wall board for the crew. A screen across the aisle showing the shift's current status plays its own content, and the shop-floor screens section describes what those units are designed to do.

Recommendation

Recommendation: run the log in the controller as a 500-slot ring of 3-word records, write through iWrite := (iWrite + 1) MOD 500, count the valid records, and let the arrow keys move iView alone. Keep 500 slots while a shift produces at most 100 events; above that, size the ring to your measured rate at 3 words per slot or send the records to a database as they happen.

Questions

What happens when two alarms switch on in the same scan?

Give each comparison its own one-shot and let each write its own record. A ring accepts several records in one scan as long as the write pointer advances after every write, so a three-alarm pile-up becomes three slots. Rung order decides the sequence, so place the safety-related comparisons first in the program and note that order on the code list.

How do I log an alarm together with its return to normal?

Add a fourth word to each record for a set or clear flag, and run the falling edge through its own one-shot that writes the same code with the flag cleared. Each alarm-and-clear pair then takes 2 slots: 500 slots hold 250 pairs, so at 20 alarms a shift the coverage drops to 12.5 shifts, and the ring becomes 500 × 4 = 2,000 words.

What should the log do after the controller loses power?

Check two things separately: the records and the clock. Records survive when the block sits in the controller's retentive range. If the clock resets, every record written after the restart carries the restart time. Write a startup marker record with a reserved code such as 0 at every power-up, so the newest marker shows where the clock discontinuity falls and you can judge the stamps on either side of it.

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