1. Overview: Time-of-Day Interrupts in SIMATIC S7
Time-of-Day (TOD) interrupts are hardware-independent, time-driven events that fire an assigned Organization Block (OB10–OB17 on S7-300/400; OB10–OB17 on S7-1200/1500) at a specific date and time. They are configured through the CPU hardware properties (time-of-day interrupt tab) and parameterized from the user program using SFC28 SET_TINT (S7-300/400) or the SET_TINT instruction (S7-1200/1500).
Three failure modes drive the majority of field calls:
- The SDT (start date/time) is already in the past when
SET_TINTexecutes, producing RET_VAL = W#16#80A1 (only whenPERIOD = W#16#0000). - The assigned OB has not been generated or downloaded, producing a runtime "OB not loaded" event.
- The OB fires earlier than expected, usually because of how the PERIOD base/multiplier is encoded or how the SDT timestamp is normalized against the CPU clock base.
This reference documents the parameter interface, the PERIOD encoding, error-code mapping, and the field-proven procedures to recover from each failure mode. Official documentation is anchored at the TIA Portal SET_TINT reference and the STEP 7 V5 system software manuals.
2. SET_TINT (SFC28) Parameter Interface
SFC28 "SET_TINT" sets the start date/time and period of a TOD interrupt OB. The block has the following parameter interface (call from STL/SCL):
| Parameter | Type | Direction | Description |
|---|---|---|---|
| OB_NR | INT | INPUT | OB number; permitted values 10–17 on S7-300, 10–17 on S7-400 (additional OBs 18–23 may be available depending on CPU type). |
| SDT | DATE_AND_TIME | INPUT | Start date and time in BCD-coded 8-byte DT format (DT#1990-01-01-00:00:00.0 to DT#2089-12-31-23:59:59.999). |
| PERIOD | WORD | INPUT | Period encoded as W#16#xx00 base unit and W#16#00yy multiplier (see §3). |
| RET_VAL | INT | OUTPUT | Error code; W#16#0000 if execution succeeded. |
Typical call pattern in SCL (S7-300/400):
// Define TOD interrupt start time
tod_start := DT#2024-03-15-08:30:00.0;
// Call SFC28 - run every 60 seconds starting at 08:30:00
ret := SET_TINT(OB_NR := 10,
SDT := tod_start,
PERIOD := W#16#0201);
IF ret <> W#16#0000 THEN
// handle error
END_IF;
// Activate the OB
ret_act := ACT_TINT(OB_NR := 10);
The output RET_VAL must be evaluated in the same OB cycle that called SET_TINT. If the call is made in OB1, the next scan evaluates the result after the next cycle.
3. PERIOD Encoding Reference
The PERIOD parameter uses a two-part encoding. The high byte (bits 15–8) specifies the time unit; the low byte (bits 7–0) specifies the multiplier. W#16#0000 is a special case indicating a single execution at SDT.
| PERIOD Value | Base Unit | Multiplier | Effective Period |
|---|---|---|---|
| W#16#0000 | — | — | Single execution at SDT (one-shot) |
| W#16#0201 | Minutes (02h) | 1 | Every 1 minute |
| W#16#0205 | Minutes (02h) | 5 | Every 5 minutes |
| W#16#0210 | Minutes (02h) | 16 | Every 16 minutes |
| W#16#0401 | Hours (04h) | 1 | Every 1 hour |
| W#16#0402 | Hours (04h) | 2 | Every 2 hours |
| W#16#0801 | Days (08h) | 1 | Every day at SDT time-of-day |
| W#16#0C01 | Weeks (0Ch) | 1 | Every week at SDT day-of-week/time |
| W#16#1001 | Months (10h) | 1 | Every month at SDT day-of-month/time |
| W#16#1801 | Years (18h) | 1 | Every year at SDT month/day/time |
The exact moment of each firing is computed by the CPU firmware as:
Next_fire_time = SDT + (k × PERIOD_base × PERIOD_multiplier) for k = 0, 1, 2 ...
For weekly OBs the day-of-week is preserved from SDT; for monthly OBs the day-of-month is preserved (clamped to the last day if the target month is shorter).
4. Error Code 80A1: Start Time in the Past
4.1 Problem Description
After executing SET_TINT with PERIOD = W#16#0000 (one-shot), the RET_VAL output reports W#16#80A1. The CPU's diagnostic buffer records "Time-of-day interrupt OBxx: Set start time is in the past" with event ID 4911 or similar. The OB never fires.
4.2 Root Cause
SFC28 compares the SDT input against the CPU real-time clock at the moment of execution. If SDT < CPU_RTC at execution time, the firmware rejects the one-shot configuration. The error is generated only when PERIOD = W#16#0000; periodic configurations with PERIOD > 0 silently wrap the start time to the next valid period boundary, so error 80A1 cannot occur in that mode.
Common upstream causes:
- The SDT was computed in seconds-from-epoch and converted to DT with an off-by-one error or wrong time zone offset.
- The CPU real-time clock drifted or was never synchronized after a power-up and reads a default value (1990-01-01).
- The DT literal in SCL/ST was interpreted as local time but the CPU clock is in UTC (or vice versa) and the difference pushes SDT into the past.
- The block was called inside an OB that itself ran at a low priority, and several seconds elapsed between read of SDT and execution of the SFC.
4.3 Solution Procedure
- Verify the CPU clock: open the online diagnostic in STEP 7 → "Set Time of Day" dialog and confirm the displayed date/time matches the wall clock. If not, click "Set" to synchronize from the PG/PC.
- Read back the SDT you intend to use and the current RTC inside the same OB cycle, before calling SFC28. Add an evaluation that compares the two and bypasses the SET_TINT call if SDT < RTC by more than the OB cycle time. Example:
IF tod_start > dt_now THEN ret := SET_TINT(OB_NR := 10, SDT := tod_start, PERIOD := W#16#0000); ELSE // shift start to next minute boundary tod_start := dt_now + t#1m; ret := SET_TINT(OB_NR := 10, SDT := tod_start, PERIOD := W#16#0000); END_IF; - If a single absolute time is required (e.g., shutdown at 23:00), recompute the target time as "next occurrence of 23:00:00" by date arithmetic against the current RTC. A one-shot that has already passed today should be deferred to tomorrow.
- For repeated tasks (every shift, every batch), use a periodic PERIOD value (e.g.,
W#16#0801for daily at the SDT time-of-day) so the firmware auto-rolls the next fire time. - Reload the changed block and re-evaluate RET_VAL on the online watch table.
4.4 Verification
- Online watch table: pin
RET_VALon the SFC28 call. Expected value:W#16#0000. - Diagnostic buffer: confirm no event ID 4911 / 4912 entry referencing OB10 (or whichever OB_NR you configured).
- Force a test fire: temporarily set SDT to
DT#<today>-<now+30s>with PERIOD = 0; confirm OB10 enters run state by observing a flag you set as the first statement in OB10.
5. Activating TOD OBs Without ACT_TINT (SFC30)
5.1 Two-Step Configuration Requirement
The TOD interrupt is dormant after SET_TINT until it is explicitly activated. Until activation, the configured start time sits in the CPU's interrupt queue but does not generate OB calls. Activation can be performed in three ways:
- By calling SFC30 ACT_TINT with the same OB_NR from the user program.
- By enabling the "Time-of-day interrupt" checkbox in the CPU's hardware properties under the "Time-of-Day Interrupts" tab in HW Config. The activation flag is then retained as part of the project download and is automatically applied on every CPU restart (warm/cold).
- By setting the activation bit via SFC30 in OB100 (startup OB) so that the OB is armed on every restart without needing a manual call from OB1.
5.2 STEP 7 Hardware Configuration Method
To activate the OB from STEP 7 without using ACT_TINT in the program:
- Open HW Config and double-click the CPU.
- Select the "Time-of-Day Interrupts" tab.
- Choose the OB (e.g., OB10), set the active checkbox, set the execution list, and either set "once at" or a periodic interval. The values entered here are the initial parameters; SET_TINT overwrites them at runtime.
- Save, compile, and download the hardware configuration. The activation flag is part of the system data.
- On every restart the OB will be active with the parameters from HW Config; the user program can still override SDT/PERIOD with SET_TINT before the first scheduled fire.
5.3 Programmatic Activation in OB100
For a fully software-controlled approach (no HW Config checkbox), insert SFC30 in OB100 so the OB is armed automatically on restart:
// OB100 - Startup
ACT_TINT(OB_NR := 10);
This guarantees that the activation flag survives power-cycle, since OB100 runs after every restart. Pair this with a SET_TINT call in OB1 (or wherever the SDT is recomputed) so the firing time is always aligned with the desired schedule.
6. PERIOD = W#16#0201: OB Activates Earlier Than Expected
6.1 Symptom
With PERIOD = W#16#0201 (every minute), the user observes that OB10 enters run state roughly two minutes earlier than the SDT value passed to SET_TINT. The same effect is reported when PERIOD = W#16#0401 (every hour) fires at HH-1 or HH-2 of the configured hour.
6.2 Root Cause Analysis
Three mechanisms combine to produce early firing:
-
PERIOD rollover arithmetic with a non-zero SDT remainder. The CPU firmware computes
Next_fire = floor((RTC - SDT_epoch) / period_seconds) × period_seconds + SDT_epoch. If SDT was specified with sub-second precision that the firmware truncates, the first fire is calculated from the truncated SDT, not the full DT value. For a PERIOD of 60 s, a 0.999 s truncation shifts the base by almost 1 minute; with another rounding on the periodic reschedule, a 2-minute skew can accumulate. - CPU clock granularity and OB priority handling. TOD interrupts are dispatched at the OB priority configured for the OB number. If the OB has lower priority than OB1 and OB1 is currently executing a long FB, the interrupt is queued and fires the moment OB1 yields — which can be tens of seconds later, and the diagnostic logs record the request time, not the dispatch time. From the operator's perspective this looks like "fired 2 minutes late," but the math is correct.
-
Hourly TOD with second-precision SDT and minute-aligned CPU base. The CPU tick for TOD comparison is at second boundaries; an SDT of
DT#...-HH:MM:30combined with PERIOD = W#16#0401 produces fires at HH:30, HH+1:30, HH+2:30 — not at the user-expected HH:00 boundaries. Users who select "round" hour boundaries naturally choose SDT at minute :00, but PERIOD base arithmetic with multiplier 1 keeps the offset, so the next firing is HH+1:00 — which the operator can mistake for HH-2 if they were counting from a different starting reference.
6.3 Resolution Procedure
- Normalize SDT to the period boundary: when PERIOD = W#16#0201 (every minute), set SDT seconds to 00 and fractional seconds to 0. When PERIOD = W#16#0401 (every hour), set SDT minutes and seconds to 00. This eliminates the carry-over skew.
- Use DATE_AND_TIME literals aligned to the base unit:
// For every-minute TOD with second-aligned fire tod_start := DT#2024-03-15-08:30:00.000; ret := SET_TINT(OB_NR := 10, SDT := tod_start, PERIOD := W#16#0201); - Check the OB priority: in HW Config → CPU Properties → "Interrupts", the OB priority must be higher than OB1's priority if deterministic dispatch matters. Typical values: OB1 = 1 (OB26 cycle), TOD OB = 2–24.
- Verify with the online watch table: read the variable
OB10_DATE_TIMEif your application writes it on entry; or instrument OB10 to captureSFC1 READ_CLKoutput at the top of the OB and log it to a DB. - If the firing is still skewed by a multiple of the period after step 1, suspect a CPU clock that is not synchronized to the plant master clock. Enable time synchronization via NTP (S7-1500) or via SFC48 / SFC58 in S7-300/400.
7. SET_TINT vs SET_TINTL: S7-1500 Differences
On S7-1200/1500 the instruction is named SET_TINT in TIA Portal but the parameter set is different from SFC28:
| Parameter | S7-300/400 SFC28 | S7-1500 SET_TINT |
|---|---|---|
| OBnr | OB_NR : INT | OBnr : OB_TOD |
| Start time | SDT : DATE_AND_TIME (BCD) | SDT : DTL |
| Period | PERIOD : WORD (encoded) | PERIOD : LTime (e.g., LTime#1m, LTime#1h) |
| RET_VAL | Output | Error code on instruction instance / status output |
| One-shot | PERIOD = W#16#0000 | PERIOD := LTime#0ns |
Error code 80A1 has an equivalent on S7-1500: status output reports DW#16#80A1 or the symbolic identifier ERR_TINT_TIME if the SDT is in the past with PERIOD = 0ns. The diagnosis logic is identical to S7-300/400.
The S7-1200/1500 variant adds an extra parameter Operation in some instruction versions to allow reading status without modifying the configuration. The user-programmatic activation is via QRY_TINT / ACT_TINT / CAN_TINT in the same family.
For STEP 7 V5 projects migrated to TIA Portal, an automatic conversion transforms SFC28 calls into SET_TINT instruction calls; the WORD PERIOD is rewritten to an LTime literal. After migration, verify the converted PERIOD value in the migrated block — common conversion errors substitute W#16#0201 → LTime#1m, which is correct, but W#16#0210 (16 min) is sometimes misread as 16 min × 16 = 256 min.
8. Complete Error Code Reference Table
| RET_VAL | Symbolic Name (S7-1500) | Meaning | Remediation |
|---|---|---|---|
| W#16#0000 | STATUS_NO_ERROR | No error | None required |
| W#16#80A0 | ERR_TINT_ALARM_IN_PAST | Start time already passed (periodic mode) | Recompute SDT or accept auto-rollover |
| W#16#80A1 | ERR_TINT_TIME | Set start time is in the past (PERIOD = 0) | See §4 of this article |
| W#16#80A2 | ERR_TINT_INVALID_OB | OB_NR is invalid or OB not configured | Generate the OB in project, download, retry |
| W#16#80A3 | ERR_TINT_NOT_CONFIGURED | Time-of-day interrupt not configured in HW Config | Enable the OB in CPU properties |
| W#16#80A4 | ERR_TINT_INVALID_FORMAT | SDT format invalid (e.g., wrong year, BCD error) | Validate DT literal; year must be 1990–2089 |
| W#16#80A6 | ERR_TINT_INVALID_PERIOD | PERIOD value out of range | Use only permitted base unit/multiplier |
| W#16#80A7 | ERR_TINT_INTERNAL | Internal CPU error | Power-cycle CPU; check firmware version |
| W#16#80B0 | ERR_TINT_NO_CONFIG | No TOD interrupt OB configured at all | Add OB10 (or required OB) to project |
| W#16#80B1 | — | OB_NR does not match any configured TOD OB | Verify OB number vs HW Config assignment |
| W#16#80B2 | — | Configuration inconsistency between SDT and PERIOD | Re-evaluate encoding |
Codes above W#16#80FF are CPU-firmware-specific and may vary by firmware version. Reference your CPU's manual for the exact list. For S7-1500 the symbolic names above are defined in the TIA Portal SET_TINT documentation.
9. Companion System Functions
| SFC | Name | Function | Typical Use |
|---|---|---|---|
| SFC28 | SET_TINT | Set start date/time and period | Programmatic scheduling of TOD OB |
| SFC29 | CAN_TINT | Cancel (deactivate) TOD interrupt | Disarm OB before maintenance |
| SFC30 | ACT_TINT | Activate TOD interrupt | Arm OB after SET_TINT |
| SFC31 | QRY_TINT | Query TOD interrupt status | Read RET_VAL, status, configured SDT |
| SFC1 | READ_CLK | Read CPU clock | Diagnose time skew |
| SFC0 | SET_CLK | Set CPU clock | Time synchronization from master |
| SFC48 | SNR_RTCB | Synchronize slaves via RTC | Time sync in distributed I/O |
| SFC58 | WR_REC | Write record to slave | Send time parameters to slaves |
| SFC59 | RD_REC | Read record from slave | Read time status from slaves |
Always pair SFC28 with SFC30 to ensure the OB is armed. Pair SFC28 with SFC31 immediately afterward if your application needs to confirm the configuration was actually accepted.
10. Diagnostic and Verification Procedures
10.1 Online Watch Table Method
- In STEP 7, open the online view of the block that contains the SET_TINT call.
- Right-click → Monitor/Modify; add
RET_VALof the SFC28 instance and the configured SDT/PERIOD tag if exposed. - Trigger the call (e.g., manual M-bit). Confirm RET_VAL = W#16#0000.
- Use "Monitor All" with update cycle 500 ms; expect the OB to enter run state at the configured time, visible by an OB-specific debug flag.
10.2 Diagnostic Buffer Method
- CPU → Online → Diagnostic Buffer.
- Filter for event class "OB" and event ID 4910–4920.
- Confirm presence of an "OB started" entry for the target OB. Absence after the expected fire time indicates SET_TINT failed or activation flag is missing.
- For S7-1500 the equivalent path is TIA Portal → Online & Diagnostics → Diagnostic buffer.
10.3 Force-Fire Method
- Set SDT to
DT#<today>-<now+30s>:00.000and PERIOD = W#16#0000. - Download, place CPU in RUN, observe OB entry. If OB fires, configuration logic is correct; the issue is in the production SDT computation. If OB does not fire, the OB is not activated or the OB number does not exist.
11. Commissioning Checklist
- Confirm OB10 (or target OB number) is generated in the project and downloaded to the CPU.
- Open CPU Properties → Time-of-Day Interrupts → tick the Active checkbox for the OB; this provides the initial activation that survives restart.
- Set the initial SDT and PERIOD in HW Config to reasonable values for first power-up.
- Insert OB100 startup code that calls SFC30 ACT_TINT for every TOD OB in use — this guarantees re-arming after cold restart even if the HW Config tickbox is later cleared.
- In the cyclic block (OB1) call SFC28 SET_TINT with the desired production SDT and PERIOD; evaluate RET_VAL and escalate to HMI alarm if not W#16#0000.
- Implement SFC31 QRY_TINT polling at a low rate (e.g., every 60 s) to detect de-activation by a CAN_TINT call elsewhere in the program.
- For S7-1500: enable NTP or PTP time synchronization so all TOD OB firings reference a single wall-clock source across the plant.
- Add a watchdog inside the TOD OB: at the first line of the OB, write a monotonically incrementing counter to a DB; alarm if the counter does not advance within 2 × PERIOD seconds.
- Document the PERIOD encoding used (e.g., W#16#0402 = every 2 hours) in the function header of the block containing SET_TINT so future maintainers do not have to reverse-engineer the encoding.
- Lock the CPU clock from unauthorized SFC0 SET_CLK calls by isolating SFC0 behind an authorization check, or disable the PG/PC write-time-of-day privilege.
12. Edge Cases and Field-Proven Caveats
- CPU in STOP/RUN transitions: A TOD interrupt scheduled during STOP is lost. On RUN return the next fire is calculated from the new SDT only if the OB is reactivated. If your application depends on TOD firings across a planned stop, use SFC30 in OB100 and re-issue SET_TINT before the scheduled time.
- Daylight saving time: TOD OBs do not automatically adjust for DST. A daily OB scheduled at 02:30 will fire twice on the "spring forward" night if the hour 02:00–03:00 exists twice in the local time zone. Use UTC SDT and convert at the application layer, or use a CPU that operates exclusively in UTC and apply DST offsets in software.
- Year 2090 boundary: DATE_AND_TIME on S7-300/400 supports up to 31-Dec-2089. SDT values beyond 2089 will return W#16#80A4 (invalid format). Plan migration to S7-1500 (which uses DTL with no 2090 boundary) well in advance.
- Multiple TOD OBs at the same second: The CPU serializes simultaneous TOD OB firings according to OB priority; lower-priority OBs are delayed by the higher-priority OB's run time. If your process logic depends on exact same-second firing of two OBs, assign them the same priority and rely on the CPU to interleave (or merge them into one OB and dispatch internally).
- PERIOD = 0 after a previous periodic call: Once a periodic TOD has been configured, calling SET_TINT again with PERIOD = W#16#0000 re-arms the OB for a single execution but does not cancel the periodic schedule; the next firing after the one-shot will revert to the prior periodic pattern. To fully switch to one-shot, call CAN_TINT first, then SET_TINT, then ACT_TINT.
- Firmware-version-specific behavior: S7-300 CPUs with firmware < V2.x had bugs in PERIOD rollover arithmetic for weekly/monthly periods. Always cross-check the firmware version against the CPU manual's known-issue list before commissioning a long-period TOD.
What does Siemens SET_TINT error 80A1 mean and how do I fix it?
Error W#16#80A1 means the SDT (start date/time) passed to SFC28 is in the past relative to the CPU real-time clock. It only occurs when PERIOD = W#16#0000 (one-shot). Fix it by reading the CPU clock with SFC1, comparing against your target SDT, and if SDT is in the past, shift it forward by the desired interval before re-issuing SET_TINT. For repeating tasks, use a non-zero PERIOD (e.g., W#16#0801 for daily) so the firmware auto-rolls the next fire time.
How do I activate a time-of-day interrupt OB without calling ACT_TINT in the program?
Enable the OB in HW Config → CPU Properties → Time-of-Day Interrupts → Active checkbox. Save, compile, and download. The activation flag is stored in the system data and is automatically applied on every restart. For belt-and-braces reliability also call ACT_TINT from OB100 so the OB re-arms after cold restart regardless of HW Config state.
Why does my OB fire earlier than expected with PERIOD = W#16#0201?
The CPU computes the next fire from a normalized SDT truncated to the period base. If SDT has sub-second precision or a non-zero seconds component while PERIOD is minute-based, the carry-over shifts the firing reference. Normalize SDT to second boundary (e.g., DT#...:00.000) and ensure the OB priority is higher than OB1 for deterministic dispatch.
What is the difference between SET_TINT (SFC28) and SET_TINT (S7-1500)?
SFC28 uses DATE_AND_TIME (BCD 8 bytes) and WORD PERIOD (encoded). The S7-1500 SET_TINT instruction uses DTL and LTime (e.g., LTime#1m) for PERIOD, and supports a one-shot via PERIOD := LTime#0ns. Error codes are equivalent (W#16#80A1 on both) but symbolic names are exposed on S7-1500.
How can I confirm the TOD OB was actually scheduled?
Call SFC31 QRY_TINT immediately after SFC28 SET_TINT and SFC30 ACT_TINT. Inspect the status output (active flag, configured SDT, configured period). Additionally, the CPU diagnostic buffer records event ID 4911 for successful scheduling and 4912 for missed firings; check both for the target OB number.