S7-300 Weekly Timer: Day-of-Week Fan Scheduling in STEP 7

David Krause11 min read
HMI ProgrammingSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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.

Applies to: CPU 31x family with firmware V2.x and later, STEP 7 V5.5 / V5.6, and TIA Portal V13+ with S7-300 add-on. For S7-1200/1500 users, Siemens' Library of General Functions (LGF) already includes a ready-to-use 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
Critical detail: The weekday is encoded in the low nibble of byte 7 (bits 0–3) and uses the US convention: 1 = Sunday, 2 = Monday, …, 7 = Saturday. Many European engineers expect Monday = 1, so verify the mapping before writing the comparisons. The S7-300 has no built-in helper to remap this; it is a manual offset in your FB.

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:

  1. Index into DB20 using (iWeekday - 1) * 8 as the base offset.
  2. Read the two ON/OFF pairs (DBWbase+0, +2, +4, +6) into local tags.
  3. Evaluate the two window comparisons and OR them together.
  4. 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.

Tip: Create a separate instance DB (DB21, DB22, DB23 …) for each fan. The instance DB holds the FB's static memory (e.g. last output state for edge detection, run-time counters). Open the FB for the first time with "Instance DB = Multiple Instances" off, so the maintenance team can clearly see which DB drives which fan.

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

  1. Open the project in STEP 7 and download hardware + software to the CPU in RUN-P.
  2. Force the clock to a value just before the first ON time (e.g. 07:59 on Monday) and observe the watch table VAT_Schedule in online mode.
  3. Confirm iWeekday, iHour, and iMinute match the forced clock and that Fan1_Run transitions high at the programmed minute.
  4. Step the clock through 24 hours, checking each on/off transition in the watch table.
  5. 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 DB20 as retentive in the CPU properties (Retain → Data Blocks).
  6. 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.

Back to blog