Fix a CODESYS toggle flip-flop that sets and resets

6 min read · Published November 3, 2026

Your two schematics both write u1 twice in one scan: the set branch writes 1, the branch below reads the new value in the same pass and writes 0, and the scan stores the last write. That is why every pulse ends at 0 in one drawing and at 1 in the other. Detect the rising edge of in1 with R_TRIG and XOR it into u1 in one assignment.

u1 is written twice in one scan. The set branch writes 1, the branch below reads the new value in the same pass and its reset coil writes 0, and the scan stores the last write. Every pulse on in1 therefore ends with u1 at 0 in your upper schematic. Your lower schematic runs the same two writes with the branch order swapped, so every pulse ends at 1. The two drawings that look equivalent are one race, mirrored. Fix it by detecting the rising edge of in1 and XORing that one-cycle event into u1 in a single assignment, with no other write to u1 anywhere in the program.

Why the second write wins

Ladder executes top to bottom, and a coil write takes effect at once: branches later in the same network read the operand as already changed. Follow one press with u1 at 0:

  1. The upper branch sees in1 true and u1 false, so its set coil writes u1 = 1.
  2. The lower branch, still in the same pass, sees in1 true and u1 now 1, so its reset coil writes u1 = 0.
  3. The scan ends. The stored value is 0, the second write.

Preset u1 to 1 and press: only the reset branch is active, so the pulse again leaves 0. The lower drawing executes the reset first and the set last, so it always leaves 1. One operand with two writers in one scan is the whole fault; the toggle logic itself never got a chance to act.

Symptoms and what each points to

  • The pulse never sets u1, and a u1 you force to 1 falls back to 0 on the next pulse: a reset write executes after the set write in the same scan. Your upper drawing.
  • The pulse never resets u1, and a forced 0 returns to 1: the same two writes with the set executing last. Your lower drawing.
  • u1 flickers while the button is held, or its final value changes with how long you hold it: the network toggles on the input level, once per scan, with no edge detection.
  • The state variable changes but the lamp or the screen line does not: a separate problem in the output path. Trace the assignment from u1 to the physical output or the visualization element.

Checks that pin it down

Watch in1, the internal marker and u1 in one online table so the order of the writes is visible. To hold signals still for a single scan, forcing is the tool; read how CoDeSys forcing works in the PLC cycle before you use it on a running machine.

The false-contact test proves the override in one minute: insert a normally open contact tied to FALSE in series with the lower branch and press. If u1 now sets and holds, the branch below was the second writer. Remove the contact afterwards; it disables the reset path, and a disabled branch is a diagnosis, not a repair.

The hold test finds the second fault: press and hold for 2 s. If u1 keeps changing while held, the network also lacks edge detection, and fixing the write order alone will leave it unstable.

If nothing changes at all, confirm the controller is running the new code and the input channel actually toggles; see checking whether a CODESYS runtime is running or stopped.

The fix: one edge, one write

The rule: one button transition produces exactly one state change, and exactly one assignment in the application writes the state. In Structured Text:

trigIn1(CLK := in1);         // R_TRIG instance, declared once
u1     := u1 XOR trigIn1.Q;  // the only write to u1

R_TRIG ships with the IEC 61131-3 Standard library that comes with the runtime. Its Q output is true for the single cycle after each false-to-true transition of the clock input and false again on every following scan, so the XOR inverts u1 once per press and then holds, however long the press lasts.

In ladder, replace both branches with one path: a rising-edge contact, the P contact, of in1 into an XOR box with u1, driving one coil. Two rungs also work: the R_TRIG box into a pulse marker, then the marker XOR u1 into u1.

Then search the whole application for assignments to u1 and delete every other one. A second writer from any network restarts the race. If several stations need the same toggle, wrap the edge and the XOR in one function block and give each station its own instance; when the station count is decided at runtime, creating CODESYS function block instances dynamically covers the pattern.

Example: a 300 ms press on a 20 ms scan

Inputs: a 300 ms press, a typical mechanical button press; a 20 ms task cycle, a typical figure for a mid-size program, so read the real value in the task configuration; u1 begins at 0.

The rule: a toggle driven by the input level inverts once per scan in which the input is true. A toggle driven by the rising edge inverts once per false-to-true transition.

The arithmetic: 300 ms ÷ 20 ms = 15 scans with in1 true. Level logic inverts 15 times, and an odd count starting from 0 ends at 1. A 320 ms press gives 320 ÷ 20 = 16 scans and ends at 0. Edge logic sees one transition and inverts once, so u1 ends at 1 for both press lengths.

The decision: any press longer than one task cycle defeats a level-driven toggle, and every real press is longer. If the input can be true for two scans or more, and it always can be, the toggle must be edge-driven.

When you want a latch, not a toggle

One button alternating a state is a toggle. Two separate commands, one to switch on and one to switch off, is a latch, and the IEC 61131-3 Standard library ships SR and RS for it. SR is set dominant and RS is reset dominant:

Set, reset SR output RS output
1, 0 1 1
0, 1 0 0
1, 1 1, set wins 0, reset wins
0, 0 holds holds

A latch will not alternate. Feed the same edge into set and reset and SR sits at 1, RS sits at 0. For the wider sealing-in and self-holding family, this PLC latch and flip-flop reference walks the circuits.

One safety point: if u1 energizes motion or heat, choose RS so a stuck set command cannot hold the output on, and keep the emergency stop out of this logic. The stop function belongs in the safety chain, wired and rated for it.

Prove the fix on the machine

  1. Go online with in1, the edge output and u1 in one view.
  2. From u1 = 0, press once: one edge cycle and exactly one change of u1.
  3. Keep the button down across several scans: no further change.
  4. Release, press again: u1 returns to 0. Ten presses, ten changes.
  5. One press that gives two changes means a bouncing contact: raise the input filter to the 20 to 50 ms typical for a mechanical button and retest.

Recommendation: replace both branches of the toggle network with R_TRIG on in1 and a single XOR write into u1 whenever one button must alternate a state, and leave exactly one assignment writing u1 in the application. Use SR or RS only when set and reset are two different commands, and pick RS when the output must go to off with both commands present.

Questions

Does the ladder edge contact replace R_TRIG?

Yes. The rising-edge contact, the P contact, does the same detection inside the network, and R_TRIG is the box and Structured Text form. Both must remember the previous state between scans, so give each button its own instance or its own storage bit. Never share one instance between two different inputs, or edges will be missed.

Can one button press toggle several outputs?

Yes. Run the edge once into one pulse marker, then XOR that marker into each state variable, one assignment per variable. Every state keeps exactly one writer, which is the rule that keeps the race out of the program. Do not add a second write to any state from another network.

The toggle works in simulation but skips states on the machine. Why?

The real button bounces: a few mechanical transitions inside a couple of milliseconds give several rising edges, and each edge flips the state. Raise the input filter to the 20 to 50 ms typical for a mechanical contact and retest. Also check that the input returns false between presses; a jammed contact gives one edge and then nothing more.

Should the toggle state be retained?

If the output must come back in its last state after a power cut, declare the state variable retained and XOR into it. The edge detector needs no retention; after a restart it simply waits for the next press.

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