Overview
The S7-1200 does not expose a graphical "weekly timer" instruction block the way an LOGO! 8 or certain third-party libraries do. The CPU instead provides the DTL (Date and Time, Long) data type, a 12-byte structured value that mirrors the wall-clock reading, and a set of read/write system-time instructions. Any multi-day, multi-time-window schedule is built by reading the CPU clock into a DTL tag and then comparing the WEEKDAY, HOUR, and MINUTE fields of that tag against constants. The ladder remains simple, the same logic can be expressed in SCL with a single CASE block, and a free OSCAT schedule function block can be dropped in if more than a handful of windows are required.
This article works through a representative three-window application:
- Monday 08:00 – 17:00, Output 1 active
- Tuesday 08:00 – 15:00, Output 1 active
- Thursday 08:00 – 20:00, Output 1 active
All other days and all hours outside the listed windows must keep Output 1 cleared. The same pattern scales to additional days, additional outputs, and additional windows without changing the architecture.
Prerequisites
- S7-1200 CPU, firmware 2.x or newer (CPU 1211C, 1212C, 1214C, 1215C, 1217C, or the 1212FC/1214FC/1215FC fail-safe variants). The DTL structure and the RD_SYS_T / RD_LOC_T instructions are present from firmware V1.0 onward, but the SCL editor and full access to the DTL sub-fields from the comparison instructions require TIA Portal V11 SP2 or later.
- TIA Portal V11 SP2 (recommended) or later, or TIA Portal V13/V14/V15/V16/V17 if the project is also expected to use newer comfort-panel images. The V10.5 environment referenced in some legacy support notes still compiles ladder that calls RD_SYS_T, but its SCL editor is restricted and it does not expose the OSCAT library cleanly. Projects should be migrated to V11 SP2 or newer for anything more than a three-window weekly timer.
- A global DB (or instance DB of a function block) sized to hold a 12-byte DTL tag plus any auxiliary INTs used for the time-of-day conversion.
- An accurate clock source for the CPU. The internal real-time clock drifts 1 to 2 seconds per day at 25 °C, more at higher temperatures. If the application is a school bell, a process oven, or any schedule where five minutes of drift is a problem, attach the CPU to an NTP server or to a comfort panel that forwards time via the time-master function.
- For the HMI-driven variant: a SIMATIC Comfort Panel or a WinCC Runtime PC attached via PROFINET, and the "Time synchronization" option enabled on the HMI.
S7-1200 Time-of-Day Architecture
The S7-1200 maintains the current time internally as a 64-bit value in POSIX-style format (number of seconds since 1970-01-01 00:00:00 UTC) and converts to DTL on read. The two instructions that matter for a weekly timer are:
| Instruction | Folder | Returns | Notes |
|---|---|---|---|
| RD_SYS_T | Basic instructions → Time → Clock | DTL (UTC) | Reads the system time as a DTL value in Coordinated Universal Time. The internal 64-bit value is referenced to UTC; for a plant in a fixed time zone you typically read RD_LOC_T instead and avoid the conversion. |
| RD_LOC_T | Basic instructions → Time → Clock | DTL (local time, DST aware) | Reads the local time including daylight-saving offset as configured in the CPU properties. This is the instruction a weekly-timer application should normally use, because all of the human-readable schedule (08:00, 17:00) is expressed in local time. |
| WR_SYS_T | Basic instructions → Time → Clock | – | Writes a DTL value into the system time. Used by the HMI time-forwarding mechanism and by the NTP client; not used in the timer logic itself. |
DTL is documented in the TIA Portal help under "Data types → Date and time → DTL". The structure occupies 12 bytes and is laid out as follows.
DTL Data Type Reference
| Element | Type | Range / Encoding | Used by weekly timer? |
|---|---|---|---|
| YEAR | UINT | 1970 – 2554 | Optional (used only for year-bound schedules such as seasonal tariffs) |
| MONTH | USINT | 1 – 12 | No |
| DAY | USINT | 1 – 31 | Only for month-bound schedules |
| WEEKDAY | USINT | 1 = Sunday, 2 = Monday, 3 = Tuesday, 4 = Wednesday, 5 = Thursday, 6 = Friday, 7 = Saturday | Yes — primary day discriminator |
| HOUR | USINT | 0 – 23 | Yes |
| MINUTE | USINT | 0 – 59 | Yes for sub-hour precision (08:15, 14:35, etc.) |
| SECOND | USINT | 0 – 59 | Only if the schedule must fire on a particular second |
| NANOSECOND | UDINT | 0 – 999,999,999 | No |
The WEEKDAY field follows the Siemens convention where Sunday is 1. Operators used to ISO 8601 (Monday = 1) need to keep this in mind, otherwise a "Monday only" schedule will silently activate on Sunday.
Step 1: Read the CPU Local Time into a DTL Tag
Create a global DB named DB_Schedule with the following structure:
DATA_BLOCK "DB_Schedule"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
VAR
LocalTime : DTL;
MinuteOfDay : INT;
Output1 : BOOL;
MondayWindow : BOOL;
TuesdayWindow : BOOL;
ThursdayWindow : BOOL;
END_VAR
END_DATA_BLOCK
In OB1, place the RD_LOC_T instruction on rung 1, wired to the LocalTime tag:
| RD_LOC_T |
| EN ENO |
| RET_VAL -> "DB_Schedule".LocalTime |
The instruction returns the local time of day as a DTL. The RET_VAL return code is an INT (0 = no error, 80A1 / 80B5 / 808x codes if the time is invalid). For a weekly timer, the most common reason to see a non-zero RET_VAL is a CPU that has never been synchronized and is reporting 1970-01-01.
Step 2: Extract Day-of-Week and Time-of-Day Fields
For a clean implementation, convert the HOUR and MINUTE fields into a single integer "minutes since midnight". This makes a half-open interval (start inclusive, end exclusive) trivial to express and avoids boundary bugs at the hour transition.
Add a second rung in OB1 that performs the conversion. The function TIME_OF_DAY_TO_INT in the standard library is not what you want here — it converts a TOD value to a millisecond count, not a DTL to a minute count. Compute it explicitly in SCL or in a small ladder network.
SCL approach inside a function block FB_Scheduler:
"MinuteOfDay" := UDINT_TO_INT(
UINT_TO_UDINT("DB_Schedule".LocalTime.HOUR) * 60
+ UINT_TO_UDINT("DB_Schedule".LocalTime.MINUTE));
The result is a number from 0 to 1439. 08:00 = 480, 15:00 = 900, 17:00 = 1020, 20:00 = 1200.
Step 3: Build the Weekly Schedule in Ladder
Each window becomes a single rung that sets the auxiliary BOOL, and a final rung that ORs the three auxiliary BOOLs into the output. Using dedicated rungs per window keeps the comparison readable and lets you commission each day independently by watching the BOOL in a watch table.
Rung 2: Monday window 08:00 – 17:00
| "DB_Schedule".LocalTime.WEEKDAY == 2 |
| "DB_Schedule".MinuteOfDay >= 480 |
| "DB_Schedule".MinuteOfDay < 1020 |---( "DB_Schedule".MondayWindow )
Rung 3: Tuesday window 08:00 – 15:00
| "DB_Schedule".LocalTime.WEEKDAY == 3 |
| "DB_Schedule".MinuteOfDay >= 480 |
| "DB_Schedule".MinuteOfDay < 900 |---( "DB_Schedule".TuesdayWindow )
Rung 4: Thursday window 08:00 – 20:00
| "DB_Schedule".LocalTime.WEEKDAY == 5 |
| "DB_Schedule".MinuteOfDay >= 480 |
| "DB_Schedule".MinuteOfDay < 1200 |---( "DB_Schedule".ThursdayWindow )
Rung 5: OR the three windows onto the physical output
| "DB_Schedule".MondayWindow |
| "DB_Schedule".TuesdayWindow |---( "DB_Schedule".Output1 )
| "DB_Schedule".ThursdayWindow |
Rung 6: Drive Q0.0
| "DB_Schedule".Output1 |---( Q0.0 )
The half-open interval (>= 480 and < 1020) means the output is active at 08:00:00.000 and clears the instant the clock reaches 17:00:00.000. There is no transient period of 60 seconds where the output might be on at 17:00 because the minute counter has ticked over to 1020.
Step 4: SCL Implementation with CASE
For more than four or five windows, the ladder explodes. A single SCL block is more compact and easier to maintain. The same FB_Scheduler can be called from OB1.
FUNCTION_BLOCK "FB_Scheduler"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR
LocalTime : DTL;
MinuteOfDay : INT;
MondayWindow : BOOL;
TuesdayWindow : BOOL;
ThursdayWindow : BOOL;
Output1 : BOOL;
END_VAR
VAR_TEMP
i : INT;
END_VAR
BEGIN
RD_LOC_T(LocalTime := "LocalTime");
"MinuteOfDay" := INT#0;
IF "LocalTime".HOUR <= 23 AND "LocalTime".MINUTE <= 59 THEN
"MinuteOfDay" := "LocalTime".HOUR * 60 + "LocalTime".MINUTE;
END_IF;
"MondayWindow" := FALSE;
"TuesdayWindow" := FALSE;
"ThursdayWindow" := FALSE;
CASE BYTE#1 OF // dummy to keep CASE for weekday below
END_CASE;
CASE "LocalTime".WEEKDAY OF
INT#2: // Monday
"MondayWindow" := ("MinuteOfDay" >= 480) AND ("MinuteOfDay" < 1020);
INT#3: // Tuesday
"TuesdayWindow" := ("MinuteOfDay" >= 480) AND ("MinuteOfDay" < 900);
INT#5: // Thursday
"ThursdayWindow" := ("MinuteOfDay" >= 480) AND ("MinuteOfDay" < 1200);
ELSE
; // other days, no window
END_CASE;
"Output1" := "MondayWindow" OR "TuesdayWindow" OR "ThursdayWindow";
END_FUNCTION_BLOCK
For sub-hour precision such as 08:15 or 14:35, replace the integer "MinuteOfDay" with a DTL comparison that includes MINUTE explicitly. The standard form is:
// "at 08:15 on Monday, set Output1"
IF ("LocalTime".WEEKDAY = 2)
AND ("LocalTime".HOUR = 8)
AND ("LocalTime".MINUTE = 15)
AND ("LocalTime".SECOND = 0) THEN
"Output1" := TRUE;
END_IF;
// "at 14:35 on Monday, reset Output1"
IF ("LocalTime".WEEKDAY = 2)
AND ("LocalTime".HOUR = 14)
AND ("LocalTime".MINUTE = 35)
AND ("LocalTime".SECOND = 0) THEN
"Output1" := FALSE;
END_IF;
Note that the SECOND = 0 guard is required, because the rung will otherwise re-execute on every cycle within the same minute, which is harmless here (the SET/RESET is idempotent) but which becomes expensive once the SCL grows past a few dozen conditions. Scan-time of a 1214C is typically 1 – 2 ms; an OB1 that contains 50 IF-blocks reading DTL sub-fields is still well below the watchdog limit.
OSCAT Library Integration
The OSCAT (Open Source Community for Automation Technology) library is a free, well-tested set of function blocks originally written for STEP 7 and since ported to TIA Portal. It contains SCHEDULE, SCHEDULE2, and several week-day-aware variants. A SCHEDULE block can be dropped next to FB_Scheduler to handle arbitrarily many windows with one instance per output, which removes the CASE-block maintenance burden for applications such as irrigation, lighting, or classroom-bell schedules.
Installation pattern:
- Download the current OSCAT BASIC release for TIA Portal. The library is provided as a .zal15 / .al14 / .al13 master library matching the portal version.
- In TIA Portal, choose Options → Global libraries → Open library, point at the OSCAT archive, and the library opens in the right-hand task card.
- Drag the SCHEDULE FB from the library into the project library, then drag an instance DB into FB_Scheduler.
- Map the inputs of SCHEDULE: i0 (enable), i1 (weekday pattern as a bitmask 1=Sun, 2=Mon, ... 7=Sat), i2 (start time as TOD), i3 (end time as TOD). The output Q is the active state for that window.
OSCAT week-day patterns are bit-encoded: 64 = Sunday, 32 = Monday, 16 = Tuesday, 8 = Wednesday, 4 = Thursday, 2 = Friday, 1 = Saturday. To activate on Monday OR Tuesday OR Thursday, set the pattern to 32 + 16 + 4 = 52. To activate on weekdays, use 32 + 16 + 8 + 4 + 2 = 62.
Time Synchronization Strategy
The weekly-timer logic is correct only if the CPU clock is correct. Three synchronization mechanisms are supported by the S7-1200:
| Mechanism | Setup | Drift | When to use |
|---|---|---|---|
| Internal RTC only | None | ~10 s/week at 25 °C, worse at high temperature | Tolerable only on non-critical schedules (e.g. "turn off at end of day") |
| HMI time-master | On the Comfort Panel, enable "Time synchronization" and select the S7-1200 as the master target | HMI RTC drift, similar to internal | Campus installations where the HMI is itself on a larger network |
| NTP via CP | CPU 1215C / 1217C with PROFINET interface can be configured as an NTP client in the device properties (Properties → Time synchronization → NTP mode) | Typically < 100 ms when LAN is healthy | Recommended for any production schedule; requires a reachable NTP server |
If none of the above is available, at minimum enable the option to "Set time on power-up from HMI" so that a cold start does not leave the CPU at 1980-01-01.
Edge Cases: Power Loss, Day Rollover, DST
Power loss. A S7-1200 retains the internal RTC for ~ 20 days on a fully charged backup capacitor at 25 °C. After that window, the clock reverts to 1980-01-01 00:00:00 and the schedule will not fire on the day of recovery. Mitigation: UPS on the 24 V supply, NTP client, or a startup routine in OB100 that calls RD_SYS_T and, if the year is < 2020, raises a non-fatal error so that an operator is alerted.
Day rollover at midnight. Because the comparison uses WEEKDAY == N and a minute-of-day < 1440, the day rollover is handled implicitly: the second the WEEKDAY field increments, the old day's rung drops out. There is no transient condition where two windows are simultaneously active.
Daylight saving time. Europe and North America spring forward (losing the 02:00 – 03:00 hour on a Sunday in March) and fall back (gaining 01:00 – 02:00 on a Sunday in November). On a spring-forward Sunday the 02:00 – 03:00 window is skipped; on a fall-back Sunday the 01:00 – 02:00 window fires twice. RD_LOC_T is DST-aware, so the DTL reflects local time correctly — the operator's schedule is interpreted exactly as written. If a specific application cannot tolerate the duplicated hour, gate the rung with an RTC minute counter and ignore the second occurrence.
Empty schedule slot. If the operator deletes all three days from the HMI and leaves the input at zero, every rung drops out and Output1 remains cleared. There is no spurious activation on a fully empty schedule.
Verification and Commissioning
Before energising the actuators, validate the logic with the PLC in STOP / RUN-P and a watch table.
- Open an online watch table with the tags
"DB_Schedule".LocalTime,"DB_Schedule".LocalTime.WEEKDAY,"DB_Schedule".LocalTime.HOUR,"DB_Schedule".LocalTime.MINUTE,"DB_Schedule".MinuteOfDay, and the three window BOOLs. - Force the system time to 07:59 on Monday and watch Output1 = FALSE. Advance the clock minute by minute and confirm that Output1 = TRUE the instant the time crosses 08:00 and Output1 = FALSE the instant the time crosses 17:00.
- Repeat for Tuesday (08:00 – 15:00) and Thursday (08:00 – 20:00).
- Force WEEKDAY = 4 (Wednesday) and confirm Output1 stays FALSE for the full 24 hours.
- Disconnect the PROFINET cable for ten minutes to confirm the CPU retains time (it will, because the internal RTC is still ticking). Reconnect and watch the NTP client correct the time within one polling interval.
- Cycle power to the PLC with the UPS disconnected and confirm that the time is back inside 1 s of the network reference before any schedule window can have been missed. If the schedule window is long (e.g. 12 hours) and the power loss is short, the internal RTC will be sufficient; for short windows, the NTP / HMI time-master is mandatory.
Troubleshooting matrix for a weekly timer that does not behave as expected:
| Symptom | Most likely cause | Diagnostic step |
|---|---|---|
| Output never turns on | CPU clock at 1980-01-01, no synchronization | Watch LocalTime.YEAR. If < 2020, configure NTP or HMI time-master |
| Output always on | WEEKDAY comparison uses ISO 8601 (Monday = 1) instead of Siemens (Sunday = 1) | Add a temporary tag "DB_Schedule".LocalTime.WEEKDAY to the watch table and verify its value on the current day |
| Output fires on the wrong day | Off-by-one on weekday constant | Same as above; double-check the constant in the comparator matches the table in section 2 |
| Output flickers at the hour boundary | Program reads HOUR only, MINUTE ignored | Use the MinuteOfDay form, or add the minute-level compare |
| Output 60 seconds late at start of window | Rung uses HOUR >= 8 only, minute < 30 of the previous hour accidentally included |
Switch to MinuteOfDay >= 480 half-open interval |
| Output 60 minutes late after DST | RD_SYS_T (UTC) used instead of RD_LOC_T (local) | Change to RD_LOC_T and ensure CPU time zone is configured |
Inline state machine for the schedule decision (left to right: idle / window active / day rollover):
Topology of the runtime data path:
Notes on Migration from TIA Portal V10.5
Projects created in V10.5 can be opened in V11 SP2 / V13 / V14 / V15 / V16 / V17 with no functional loss; the SCL and ladder compile without change. The opposite direction (V11+ project opened in V10.5) is not supported. If a V10.5 environment is unavoidable, restrict the implementation to the three-rung ladder pattern (section 3) and avoid the OSCAT library, because the OSCAT distribution is built against V11+ and its type definitions are not compatible with V10.5's reduced SCL editor.
FAQ
Why does my S7-1200 weekly timer not fire after a power outage?
The internal RTC is held for ~20 days by the backup capacitor; beyond that, the clock reverts to 1980-01-01 00:00:00 and the WEEKDAY field reads 1 (Sunday), so no configured window will match. Add an NTP client (CPU 1215C / 1217C) or enable HMI time-master, and the schedule will recover on the next poll.
How do I trigger at 08:15 or 14:35 instead of on the hour?
Compare the DTL sub-fields directly: ("LocalTime".WEEKDAY = 2) AND ("LocalTime".HOUR = 8) AND ("LocalTime".MINUTE = 15). Add AND ("LocalTime".SECOND = 0) to fire once per minute boundary, or use a single-cycle pulse flag so the SET/RESET executes only on the second of the boundary.
Should I use RD_SYS_T (UTC) or RD_LOC_T (local time)?
Use RD_LOC_T for any schedule expressed in human local time. RD_SYS_T returns UTC and the comparison with constants such as 480 (08:00) becomes wrong in any time zone other than UTC. RD_LOC_T is also DST-aware, so spring-forward and fall-back are handled automatically.
Why does the output fire on Sunday when my schedule is Monday only?
The Siemens WEEKDAY field uses Sunday = 1, not ISO 8601's Monday = 1. Check the comparator constant: Monday in Siemens is 2, not 1. Adding "DB_Schedule".LocalTime.WEEKDAY to a watch table during commissioning confirms the encoding on the running CPU.
Can I edit the schedule from an HMI at runtime?
Yes. Replace the integer constants 480 / 1020 / 900 / 1200 with HMI tags in DB_Schedule (StartMon, EndMon, StartTue, EndTue, StartThu, EndThu, DayPatternMon, DayPatternTue, DayPatternThu). The same comparator logic reads from the tags, and the HMI can write to them while the PLC is in RUN. Pair this with the OSCAT SCHEDULE block for ten or more windows to avoid hand-coding the comparator.