Problem: T_COMP Output Never Activates When Scheduled Times Match
Symptom reported on S7-300/S7-400 and S7-1500 controllers programmed with TIA Portal (commonly v12 through v20): a Boolean output driven by the result of T_COMP comparing two DATE_AND_TIME (DT) tags never turns ON, even though a real-time clock value equals the scheduled set-time value visible in the watch table.
A typical implementation uses two T_COMP instances:
- Instance 1:
T_COMP(IN1 := RealTime, IN2 := TimeToSet, OUT => SetBrand) - Instance 2:
T_COMP(IN1 := RealTime, IN2 := TimeToReset, OUT => ResetBrand)
The boolean output SetBrand never asserts, so the scheduled brand (bit, marker, or coil) is never set, and the matching reset likewise never fires. The PLC does not report an error; the comparison result simply never evaluates as TRUE.
Root Cause: Bit-Exact Equality on a Sampled Real-Time Clock
T_COMP returns TRUE only when the two input tags contain bit-identical values. The DT (DATE_AND_TIME) format encodes time down to 100 ms increments in the legacy S7-300/S7-400 format, but the wall-clock source — read from the CPU real-time clock via READ_CLK / RD_SYS_T / IEC timers — is sampled only at the OB1 scan boundary.
Two consequences follow:
- The pre-loaded scheduled value (e.g.,
D#2024-01-15-14:30:00.000) is static; the live real-time value increments every cycle. - The OB1 cycle time (typically 5–50 ms for compact logic, up to 150 ms for complex FBD) determines the alignment window. The chance that the sampled
RealTimetag equals the scheduledTimeToSettag at the exact 100 ms boundary is roughly 1 in (cycle_time / 100 ms).
For a 10 ms OB1, the window is so narrow that T_COMP practically never returns TRUE. For a 100 ms OB1, the value may match once and then drift away before the user sees it in the watch table.
T_COMP Instruction Reference (S7-300/S7-400 and S7-1500)
The T_COMP instruction is documented in the Siemens TIA Portal "Extended Instructions" manual under "Date and Time".
| Parameter | Declaration | Data Type | Description |
|---|---|---|---|
| IN1 | Input | DATE, TIME, LTIME, TOD, DT, DTL, LDTL* | First comparison value |
| IN2 | Input | Same as IN1 | Second comparison value |
| OUT | Return | BOOL | Result of the comparison (TRUE if equal) |
* Allowed types depend on CPU family. S7-1500 supports DTL (DATE_AND_TIME_LONG, ns resolution). S7-300/400 typically uses DT (BCD-encoded, 100 ms resolution).
Official reference documentation:
- T_COMP: Compare time tags (S7-300, S7-400) — TIA Portal v20 manual
- T_COMP: Compare time tags (S7-1500) — Siemens Industry Online Support ID 109747174
Why a Direct Equality Test Is Wrong for Time-of-Day Scheduling
For scheduling actions at a specific wall-clock time, the correct semantic is "the current time has passed the scheduled time", not "the current time equals the scheduled time". T_COMP was designed for testing equality between two static DT values, not for edge detection on a free-running clock.
The recommended engineering pattern is one of the following:
- Difference + epsilon (window compare): Subtract the two times, take the absolute value, and compare against a tolerance (e.g., 10 ms for S7-1500 DTL, 100 ms for S7-300/400 DT).
-
Greater-than-or-equal (level compare): Detect a falling or rising edge when
RealTime >= TimeToSetwhileRealTime < TimeToSet + interval. This is the standard pattern for one-shot schedule events. - Periodic interrupt (OB10 / OB1x): Move the comparison into a time-of-day OB that fires once per second or once per minute, removing scan-cycle jitter.
Solution 1: Difference with Epsilon Window (SCL)
Subtract the two values, take the absolute value, and compare against a tolerance. On S7-1500 use TIME or LTIME (millisecond / nanosecond resolution). On S7-300/400 the same code works with TIME (millisecond resolution only — DT resolution is 100 ms so any epsilon < 100 ms will miss).
// SCL — S7-1500 or S7-300/400
// Tags
// RealTime : DTL (S7-1500) or DT (S7-300/400), read from RD_SYS_T / READ_CLK
// TimeToSet : DTL or DT, scheduled activation time
// TimeToReset : DTL or DT, scheduled reset time
// Tolerance : TIME = T#10ms (S7-1500) or T#100ms (S7-300/400)
// SetBrand : BOOL
// ResetBrand : BOOL
#DiffSet := ABS_TIME(TIME_DIFF(IN1 := #RealTime, IN2 := #TimeToSet));
#DiffReset := ABS_TIME(TIME_DIFF(IN1 := #RealTime, IN2 := #TimeToReset));
IF #DiffSet <= #Tolerance THEN
#SetBrand := TRUE;
END_IF;
IF #DiffReset <= #Tolerance THEN
#ResetBrand := TRUE;
END_IF;
On S7-1500, TIME_DIFF returns LTIME (ns) so a 10 ms tolerance is meaningful. On S7-300/400 use the standard library FC "FC_TIMEDIFF" or compute with DTL-to-TIME conversion.
Solution 2: Edge Detection on Crossing the Scheduled Time
The most robust scheduling pattern on a free-running clock. Use the previous scan's value to detect when the clock crosses the scheduled threshold.
// SCL — one-shot pulse when the clock crosses TimeToSet
// Tags
// RealTime : DTL / DT
// RealTimePrev : DTL / DT, latched from last cycle
// TimeToSet : DTL / DT
// TimeToReset : DTL / DT
// SetPulse : BOOL (TRUE for one cycle when crossing TimeToSet)
// ResetPulse : BOOL (TRUE for one cycle when crossing TimeToReset)
IF (#RealTimePrev < #TimeToSet) AND (#RealTime >= #TimeToSet) THEN
#SetPulse := TRUE;
END_IF;
IF (#RealTimePrev < #TimeToReset) AND (#RealTime >= #TimeToReset) THEN
#ResetPulse := TRUE;
END_IF;
#RealTimePrev := #RealTime;
This pattern is immune to scan-cycle jitter and to TIA Portal v12 / v15 / v16 / v17 / v18 / v19 / v20 differences. It fires exactly once per crossing.
Solution 3: Move the Comparison into a Time-of-Day OB
If the schedule resolution is in whole seconds or minutes, configure a Time-of-Day interrupt OB on the CPU:
- In the device configuration, enable Time-of-day interrupts and assign OB10.
- Set the start time and the period (e.g., every 60 s).
- In OB10, perform the comparison against the scheduled time. Because OB10 runs once per configured period, the comparison window is deterministic and T_COMP / greater-than-equal both work reliably.
- From OB10, set the brand flag; reset it from OB1 once the action is acknowledged.
Ladder / FBD Equivalent for Time Difference
If you must stay in LAD or FBD (no SCL compiler available on the target CPU), implement the same logic with the IEC standard arithmetic:
| Network | Operation | Note |
|---|---|---|
| 1 | SUB_TIME / T_SUB: diff := RealTime - TimeToSet | Sign matters; branch to ABS |
| 2 | ABS / ABS_TIME: |diff| | S7-1500 only; for S7-300/400 use conditional swap |
| 3 | LE_TIME / <= DTL: |diff| <= Tolerance | Drives SetBrand |
| 4 | Repeat for Reset with TimeToReset | Drives ResetBrand |
Compatible Data Types per CPU Family
| CPU | Time Data Type | Resolution | Recommended Tolerance | T_COMP Returns TRUE? |
|---|---|---|---|---|
| S7-300 / S7-400 (DT) | DATE_AND_TIME (DT) | 100 ms (BCD-encoded seconds field) | ≥ T#100ms (T#200ms recommended) | Rarely — depends on OB1 alignment |
| S7-1500 (DTL) | DTL (DATE_AND_TIME_LONG) | 1 ns (nanosecond field) | ≥ T#10ms for scheduling | Essentially never for scheduled values |
| S7-1200 | DTL | 1 ms | ≥ T#10ms | Same caveat as S7-1500 |
Step-by-Step Procedure to Fix a T_COMP Time-Match Application
- Open the affected FB/FC in TIA Portal.
- Identify the two
T_COMPinstances drivingSetBrandandResetBrand. - Replace
T_COMPwith a time-difference calculation:diff := abs(realTime - scheduledTime). - Insert a constant
Tolerance(TIME tag, default value T#10ms for S7-1500 or T#200ms for S7-300/400). - Replace the equality test with
diff <= Tolerance. - If using LAD/FBD without SCL, use the SUB / ABS / LE instructions on TIME-tag operands. Note that S7-300/400
DTarithmetic requires conversion via the IEC library functionsFC_TIMEDIFFor manual BCD subtraction. - If you must keep the boolean equality semantic, drive the comparison from a Time-of-Day OB (OB10) instead of OB1.
- Compile and download to the CPU.
Verification Procedure
After applying the fix, confirm operation with the following checks:
- Add
RealTime,TimeToSet, andDiffSetto a watch table with cycle-time monitoring enabled. - Set
TimeToSettoRealTime + 30susing the modify function (with watch table in non-productive mode during commissioning). - Observe
SetBrandasserting when the difference crosses into the tolerance window. - Force
RealTimefrom the watch table and step through several scheduled times to confirm one-shot behavior. - If using OB10, confirm in the diagnostic buffer (Online > Diagnostics > Diagnostic buffer) that OB10 is being called at the configured period and not being aborted by higher-priority OBs.
- Run a 24-hour soak test with multiple scheduled events to verify no spurious matches and no missed events.
Troubleshooting Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
| T_COMP output never TRUE despite matching watch table values | Equality test against a sampled clock | Switch to difference + epsilon or edge-detect |
| T_COMP output flickers for one cycle then drops | Equality holds briefly during scan jitter | Use >= edge detection; latch the pulse for one cycle |
| Compiler error: "T_COMP does not support operand type DTL" | Old library version on S7-1500 | Upgrade TIA Portal to v16 or later; reload the "Date and Time" extended instructions |
| Time difference shows as negative on S7-300/400 | DT subtraction overflow at midnight | Convert DT to TIME first, or wrap-around the comparison across midnight |
| SetBrand fires but ResetBrand never does | OB1 cycle exceeds the scheduled reset interval | Use a Time-of-Day OB or increase the tolerance to match the cycle time |
| SetBrand fires on every cycle once the time has passed | No edge detection; comparison is now a level | Add the previous-cycle latch as in Solution 2 |
| OB10 never executes | OB10 not configured or disabled by RUN-to-STOP transition | Re-arm OB10 in OB100 / via SET_TINT and verify with diagnostic buffer |
Edge Cases and Field-Proven Caveats
-
Day boundary: Comparing
DTvalues across midnight produces an apparent "negative" difference because the underlying BCD encoding wraps. The standard remedy is to split the schedule into a date and a time-of-day and compare each component. - PLC clock drift: If the CPU is unsynchronized, the scheduled time may drift several seconds per day. Enable NTP or SIMATIC time-of-day synchronization via Siemens Industry Online Support knowledge base entry on time synchronization.
- OB1 priority inversion: If OB1 is interrupted by a higher-priority OB (OB35 cyclic, OB82 diagnostic, OB121 error), the cycle can stretch. Use a tolerance greater than the worst-case OB1 cycle.
-
TIA Portal version differences:
T_COMPbehavior is consistent from TIA Portal v12 through v20 for both S7-300/400 and S7-1500. Differences appear only in supported operand types (S7-1500 gained LTIME / LDTL support in v16). - Watch-table sampling: Watch tables refresh at 500 ms by default; a transient match can be missed entirely in the watch table even though the PLC fired the output. Cross-check with a latch in the PLC rather than relying on watch-table observation alone.
FAQ
Why does T_COMP never return TRUE for matching scheduled times?
T_COMP performs bit-exact equality. The PLC real-time clock value is sampled once per OB1 cycle, so it almost never equals the scheduled static value at the exact instant of evaluation. Use a difference-with-tolerance comparison (e.g., |diff| ≤ T#10ms) or an edge-detect on RealTime crossing TimeToSet.
What tolerance should I use when comparing DTL or DT values?
On S7-1500 (DTL, ns resolution) use T#10ms or larger. On S7-300/400 (DT, 100 ms resolution) use T#200ms or larger. The tolerance must exceed the worst-case OB1 cycle time of your program.
Can I keep using T_COMP if I move the comparison into OB10?
Yes. OB10 fires once per configured period (typically 1 s, 10 s, or 60 s), so the comparison window is deterministic and T_COMP reliably returns TRUE when the values match. This is the cleanest pattern when you do not need sub-second scheduling precision.
Does T_COMP work on S7-1500 with DTL operands in TIA Portal v12?
The DTL data type was introduced with S7-1500 and TIA Portal v12 SP1. T_COMP supports DTL operands from that release onward. If you are on a pre-SP1 v12 install, upgrade to the latest service pack or use DT (DATE_AND_TIME) instead.
How do I detect a one-shot schedule event without missing it or firing it twice?
Latch the previous cycle's RealTime value and assert the pulse only when RealTimePrev < TimeToSet AND RealTime >= TimeToSet. This edge-detect pattern fires exactly once per crossing and is immune to scan-cycle jitter. Reset the latch from a separate network after the action is acknowledged.