Overview
Scheduling routine start/stop events on a Siemens SIMATIC S7-300 is one of the most common application tasks for HVAC, ventilation, irrigation, and process-cooling systems. The S7-300 family — including the widely deployed CPU 315-2N/DP (6ES7 315-2AH14-0AB0) and similar variants — does not expose a "Clock Alarm" tile in the same way an S7-1200/1500 does in TIA Portal, so the engineer must build the weekly scheduler from the system clock using standard SFCs and a few lines of comparison logic.
This guide shows how to read the real-time clock with SFC 1 (READ_CLK), convert the BCD-encoded date/time bytes into usable integers, and then build a ladder-logic decision tree that drives multiple fan outputs on a per-day, per-time basis. The result is a compact, reusable function block that the maintenance team can reconfigure by editing a data block (DB) — no re-programming required.
LGF_TimerSwitch FB and a different approach should be considered.Prerequisites
| Item | Specification |
|---|---|
| Controller | SIMATIC S7-300 CPU 315-2N/DP (or any CPU 31x with integrated RTC) |
| Firmware | V2.6 or later recommended for SFC 0/1/100 compatibility |
| Engineering tool | STEP 7 V5.5+ or TIA Portal with S7-300 add-on package |
| Hardware | 1 free digital output byte (e.g. SM 322 DO8) per fan group; SM 321 DI for any hand/auto feedback |
| Buffer battery | Installed and healthy (RTC retention > 1 year; alarm output only with battery) |
| Time sync | Optional: SICLOCK or NTP/SNTP via CP for sub-second accuracy |
Confirm that the CPU has a real-time clock. Every S7-300 CPU 31x shipped after 2002 has one; the buffer capacitor or backup battery retains it through power loss. The status LED BATF must be OFF, otherwise the clock will reset every time the rack powers down.
S7-300 Date/Time Data Structure
SFC 1 READ_CLK returns the current date and time as eight contiguous bytes in BCD format. The structure is fixed across the entire S7-300/400 family and is the key to all schedule comparisons:
| Byte offset | Content | Range (BCD) | Example (Mon 10:30:00, 15 Jul 2024) |
|---|---|---|---|
| 0 | Year (00–99) | 0–99 | 0x24 |
| 1 | Month (1–12) | 1–12 | 0x07 |
| 2 | Day (1–31) | 1–31 | 0x15 |
| 3 | Hour (0–23) | 0–23 | 0x10 |
| 4 | Minute (0–59) | 0–59 | 0x30 |
| 5 | Second (0–59) | 0–59 | 0x00 |
| 6 | Reserved (ms + weekday high) | — | 0x00 |
| 7 | Weekday (1=Sun … 7=Sat) + ms high | 1–7 | 0x02 |
Reading the Real-Time Clock with SFC 1
Call SFC 1 "READ_CLK" in OB1 (or in a low-priority cyclic OB such as OB35 at 1 s) to refresh a shared date/time buffer. The function has no error return path that matters for non-redundant CPUs, but the BOOL RET_VAL can still be evaluated.
LAD / FBD symbol call (STEP 7 V5.x):
SFC1 DB_RTC
|---| READ_CLK |---ENO
| | |
| EN | RET_VAL MW200
| | CDT DB10.DBD0 // 8 bytes starting at DBB0
O---| |---
After this call, DB10.DBB0 through DB10.DBB7 hold the live date/time. In TIA Portal the same SFC is found under Instructions → Extended Instructions → Date and Time as RD_SYS_T; the call is identical in behaviour.
Extracting Day-of-Week and Hour
To compare a BCD byte against an integer constant (e.g. "Monday = 2"), use FC 38 (BCD_I) on the low byte and then mask the weekday nibble. The cleanest pattern is to place the conversion inside a dedicated FC so the rest of the program sees clean INT variables:
| Tag | Type | Source | Description |
|---|---|---|---|
| iWeekday | INT | BCD_I( DB10.DBB7 AND 0x0F ) | 1 = Sun … 7 = Sat |
| iHour | INT | BCD_I( DB10.DBB3 ) | 0–23 |
| iMinute | INT | BCD_I( DB10.DBB4 ) | 0–59 |
| iDay | INT | BCD_I( DB10.DBB2 ) | 1–31 |
Mask the weekday nibble with W#16#000F to remove the milliseconds bits:
// STL excerpt for STEP 7 V5.x
L DB10.DBB7 // Load BCD weekday
AW W#16#0F // Mask to 0..15
BTI // Convert BCD to INT (16-bit)
T MW 210 // iWeekday
L DB10.DBB3
BTI
T MW 212 // iHour
L DB10.DBB4
BTI
T MW 214 // iMinute
From here, every subsequent comparison is a plain INT == INT test that fits in a single ladder rung.
Defining the Weekly Schedule Data Block
Put the on/off times in a shared DB (here DB20 "Weekly Schedule") so the maintenance team can edit them online. A two-window-per-day model gives the example requested in the original brief (e.g. Monday 08:00–10:00 and 15:00–19:00):
| Offset | Symbol | Type | Meaning |
|---|---|---|---|
| DBW0 | Mon_On1 | INT | Minutes since midnight for first ON |
| DBW2 | Mon_Off1 | INT | Minutes since midnight for first OFF |
| DBW4 | Mon_On2 | INT | Minutes since midnight for second ON |
| DBW6 | Mon_Off2 | INT | Minutes since midnight for second OFF |
| DBW8 | Tue_On1 | INT | …etc… |
| … | … | … | Repeat to DBW54 for Sunday |
Storing time as "minutes since midnight" (0–1439) collapses hour and minute into a single integer, so the comparison is one rung per window:
iNowMin = (iHour * 60) + iMinute // computed in FC
Comparison Logic in Ladder
Build a single rung that decides whether the fans should run right now. Only one rung is needed per fan group; the rung references the current day's two windows in DB20.
Example for Monday (weekday = 2):
--| |--|---------|/|------------------------------------( )--
iWeekday = 2 AND Fan1_On
( iNowMin >= DB20.DBW0
AND iNowMin < DB20.DBW2 )
OR
( iNowMin >= DB20.DBW4
AND iNowMin < DB20.DBW6 )
Repeat the same structure for Tuesday, Wednesday, etc., with a 7-way OR on the weekday input. In practice, a small FC/FB scales better:
- Index into DB20 using
(iWeekday - 1) * 8as the base offset. - Read the two ON/OFF pairs (DBWbase+0, +2, +4, +6) into local tags.
- Evaluate the two window comparisons and OR them together.
- Assign the result to the fan output tag (e.g.
Q 4.0).
Reusable Function Block "FB_Scheduler"
To avoid duplicating the comparison tree for every fan group, package it as an FB with an instance DB per fan. The same block can drive 1 fan or 32 fans simply by calling it multiple times.
| FB input | Type | Description |
|---|---|---|
| iWeekday | INT | 1–7 from the RTC extraction |
| iNowMin | INT | Minutes since midnight |
| wScheduleBase | WORD | Pointer to the day's block in DB20 (computed as 16*weekday) |
| bHandAuto | BOOL | 0 = auto schedule, 1 = manual ON |
| bManOverride | BOOL | Optional: forces OFF for maintenance |
FB interface (LAD view):
FB_Scheduler
iWeekday : INT
iNowMin : INT
wScheduleBase: WORD
bHandAuto : BOOL
bManOverride : BOOL
bRun : BOOL // output, wired to Q area or coil
Inside the FB, use OPN DB20 to open the schedule DB, then L DBW[ wScheduleBase + 0 ] through +6 to pull the four minute-of-day values. Two ANDs, two ANDs, one OR, and a final OR with bHandAuto AND NOT bManOverride produces the run command.
Wiring the Fan Outputs
Each fan is wired through a digital output module (e.g. SM 322 DO16 DC24V/0.5A). For motor loads, always use an interposing relay or contactor coil — never drive a motor starter directly from the SM card. A typical output assignment is:
| Tag | Address | Device |
|---|---|---|
| Fan1_Run | Q 4.0 | Supply fan SF-01 contactor |
| Fan2_Run | Q 4.1 | Extract fan EF-01 contactor |
| Fan3_Run | Q 4.2 | Extract fan EF-02 contactor |
| Schedule_OK | Q 4.7 | 24 V green "Schedule active" lamp |
For a S7-300 315-2N/DP, slot 4 is a convenient location for the DO module, but consult the hardware configuration in HW Config for the actual slot assigned in your rack.
Setting the Clock
If the S7-300's RTC drifts or after a battery replacement, the time must be set. The cleanest method from HMI is to write to SFC 0 "SET_CLK" using a faceplate that mirrors the same 8-byte BCD layout. An alternative is to enable time-of-day interrupts (OB10–OB17) from HW Config so the CPU can call a service routine once per minute. Combined with the LGF Library's LGF_SetRTC block (if a CP is installed), the controller can keep itself synchronised to an NTP server.
Verification and Commissioning
- Open the project in STEP 7 and download hardware + software to the CPU in RUN-P.
- Force the clock to a value just before the first ON time (e.g. 07:59 on Monday) and observe the watch table
VAT_Schedulein online mode. - Confirm
iWeekday,iHour, andiMinutematch the forced clock and thatFan1_Runtransitions high at the programmed minute. - Step the clock through 24 hours, checking each on/off transition in the watch table.
- Power-cycle the rack and verify the schedule resumes after the buffer capacitor has discharged — this confirms the RTC and DB retentive settings are correct. Mark
DB20as retentive in the CPU properties (Retain → Data Blocks). - Switch a fan to manual mode using the operator panel and verify the schedule is bypassed without affecting the other fans.
Troubleshooting Matrix
| Symptom | Likely cause | Diagnostic step | Fix |
|---|---|---|---|
| All fans stuck OFF even at 08:00 | BCD→INT conversion failed, iWeekday=0 |
Watch table on DB10.DBB7 and MW210
|
Check the BTI instruction runs every scan; verify AW mask is 0x0F |
| Fans ON at the wrong weekday (Mon = Sun) | Weekday mapping not adjusted | Compare MW210 with the calendar |
Add an offset (1–7) or remap DB index manually |
| Clock resets at every restart | Backup battery dead or Retain not set | Check BATF LED; check CPU diag buffer |
Replace battery, set DB20 to retentive |
| Schedule runs but only one window per day | DB20 second window overwritten or +6 offset wrong |
Open DB20 online, inspect all 56 WORDs | Re-enter the four WORDs per day; check FB index math |
| Output chatters near boundary minute | Hourly comparison logic missing minute resolution | Trace iNowMin across a transition |
Use minutes-of-day (not hour) to give 1-min granularity |
| CPU STOP with SF after download | FB expects instance DB that is not present | Open CPU diag buffer | Generate the instance DB for the FB, recompile |
Alternative: Time-of-Day Interrupt OB10
For an S7-300 315-2N/DP you can also configure OB10 in HW Config to call a routine once per minute. The OB10 start time is set with a BCD DT constant; the runtime can then update internal flags that feed the same comparison logic. This reduces CPU scan load and gives 1-second resolution. Combined with the LGF library's LGF_TimerSwitch (S7-1200/1500), the two platforms share an identical schedule XML, simplifying multi-PLC projects.
Sample STL Listing (OB1 excerpt)
// --- Read RTC once per second (call in OB35 if available) ---
CALL SFC 1
RET_VAL := MW200
CDT := DB10.DBD0
// --- Extract weekday (low nibble of byte 7) ---
L DB10.DBB7
AW W#16#0F
BTI
T MW210 // iWeekday 1..7
// --- Extract hour and minute ---
L DB10.DBB3
BTI
T MW212 // iHour
L DB10.DBB4
BTI
T MW214 // iMinute
// --- Compute minutes since midnight ---
L MW212
L 60
*I
L MW214
+I
T MW216 // iNowMin 0..1439
// --- Drive three fans via FB_Scheduler ---
CALL FB 100, DB21
iWeekday := MW210
iNowMin := MW216
wScheduleBase := 0 // fan 1 base = Monday offset 0
bHandAuto := DB30.DBX0.0
bManOverride := DB30.DBX0.1
bRun := Q 4.0
Field-Commissioning Checklist
- CPU diagnostic buffer clear of "Time-of-day interrupt not started" entries.
- Battery voltage > 2.7 V at the BAT holder.
- DB20 online view shows expected ON/OFF minute values for each day.
- Watch table confirms transition at scheduled minute within ±1 s.
- Manual mode override works independently of the schedule.
- Power-down test: schedule resumes correctly after at least 30 minutes offline.
- Operator HMI has a faceplate to force-edit the schedule DB online.
FAQ
Why does the S7-300 "Clock Alarm" block not appear in STEP 7?
The S7-300/400 family uses SFC 0/1 for clock read/write and the OB10–OB17 time-of-day interrupt OBs instead of the TIA Portal "Date_And_Time" alarm tile. The S7-1200/1500 "Clock Alarm" instruction is not part of the S7-300 instruction set, so you must build the comparison from SFC 1 output bytes.
How is the weekday encoded in the SFC 1 return value?
The weekday is in the low nibble of byte 7 of the CDT output, in BCD, with the US convention 1 = Sunday … 7 = Saturday. Mask with W#16#0F and run BTI to convert to an integer before comparing.
Can I store the on/off times in a data block the operators can edit?
Yes — define a shared DB (e.g. DB20) with two INT words per window (four windows per day = 8 WORDs × 7 days = 56 WORDs total) and mark the DB as retentive. Operators can then change the schedule online from an HMI faceplate or directly from STEP 7 without recompiling.
What resolution does the S7-300 clock provide?
The hardware RTC has 1-second resolution. SFC 1 returns the time to the nearest second, so the smallest controllable window is 1 minute when comparing "minutes since midnight". For second-level scheduling, switch to OB10 time-of-day interrupt and drive the comparison on the second field.
How do I adapt this code to an S7-1200 or S7-1500?
Use TIA Portal's RD_SYS_T instruction and the LGF_TimerSwitch FB from the Library of General Functions (LGF) for STEP 7 (TIA Portal) and S7-1200/S7-1500. The LGF block handles BCD conversion and weekday mapping internally and reads a structured "schedule" DB that you can configure from a single HMI screen.