Overview
Generating a multi-tone audible alarm from a single digital output on a Siemens SIMATIC S7-300 requires more than a simple on/off coil. The horn must be pulsed at varying mark/space ratios (for example 1 s on, 1 s off, 0.5 s on, 0.75 s off) in a deterministic sequence. The most reliable and reusable method is to model the melody as a Finite State Machine (FSM) – specifically a Moore machine in which every state is active for a fixed dwell time, the output depends only on the active state, and transitions are driven by IEC on-delay (TON) timers.
This reference details a complete Step 7 v5.5 implementation: multi-instance Function Block architecture, IEC TON timer declaration, state-transition logic, output mapping, and CPU clock memory fallback for shorter tones. The result is a single FB that you can drop into any S7-300 program, drive from a digital input or network bit, and scale to N tones without duplicating code.
Prerequisites
| Item | Requirement |
|---|---|
| Controller | SIMATIC S7-300 CPU 312, 313, 314, 315, 315-2 DP/PN, 317 or 319 |
| Firmware | CPU firmware that supports the standard IEC library; CPU 3xx with FW ≥ V2.x recommended |
| Engineering tool | STEP 7 V5.5 + SP2 (or later SP) with optional S7 Technology / S7-SCL packages |
| Library blocks | Standard Library > System Function Blocks (SFB 3 TP, SFB 4 TON, SFB 5 TOF) and the IEC Timers (FB 186 TON / IEC_TON, etc.) shipped in the Standard Library |
| Hardware output | One SM 322 (or SM 323 / SM 327) DO channel capable of sourcing the horn's inrush current; 24 V DC typical |
| Horn | 24 V DC piezo or electromechanical alarm rated ≤ 2 A continuous (use an interposing relay if higher) |
| Programming language | LAD/FBD or SCL (FBD shown in screenshots; SCL equivalents provided) |
Finite State Machine Theory Applied to a Melody
A melody is a sequence of tones. Each tone is characterised by:
- An ON duration (the time the horn output is energised – perceived as pitch/length)
- An OFF duration (the silent gap – perceived as rhythm)
- A terminal flag (last note of the melody) used to release the FSM and revert to idle
Modelling this as a Moore machine gives a state per (ON, OFF, next-state) triplet. The output function is the OR of all ON states. Transitions are governed by a per-state TON that, when it elapses, requests the next state. Because each state has its own static timer instance, the same code pattern repeats N times without copy/paste – the multi-instance FB does the bookkeeping.
State Bit vs State Word
For fewer than 16 notes, a single BOOL per state (boStateA, boStateB, …) is the most readable approach. For larger melodies, use an INT state index and a CASE statement in SCL – the principles are identical. The BOOL approach is documented below because it surfaces directly in LAD/FBD contact logic, which is the natural language for an S7-300 maintenance team.
Project Structure in STEP 7 V5.5
- Open SIMATIC Manager and create a new S7-300 station (or open an existing project).
- Insert a new FB in the Blocks container named
FB_Melodywith Multi-instance capable enabled (this is the default; STEP 7 sets theMC7attribute automatically for any FB you create from the FB wizard). - Declare the following STATIC variables inside
FB_Melody:
tonStateA : FB 186; // IEC_TON, multi-instance
tonStateB : FB 186;
tonStateC : FB 186;
tonStateD : FB 186;
boStateA : BOOL := FALSE;
boStateB : BOOL := FALSE;
boStateC : BOOL := FALSE;
boStateD : BOOL := FALSE;
rDwellA : REAL := 1.0; // seconds
rDwellB : REAL := 1.0;
rDwellC : REAL := 0.75;
rDwellD : REAL := 0.5; - Declare INPUT:
i_bStart : BOOL;– edge-triggered start command (e.g. from a network bit, a pushbutton, or a fault condition). - Declare OUTPUT:
q_bHorn : BOOL;– the actual horn drive signal. - Declare STATIC constants for the four target outputs of the IEC timer calls (e.g.
tDwellA : TIME;– aTIMEin STEP 7, fed by multiplying theREALdwell byT#1s, or simply declaretDwellA : TIME := T#1s;directly).
Implementing One State in FBD
The pattern for a single state (boStateA) is shown below. Repeat it three more times, renaming the variables, to cover four notes.
-
Set state on entry: a
SRflip-flop setsboStateAwhen the previous state (boStateDwrapped, or Start for the first state) pulses. TheSinput is wired to the start command or the previous state's transition bit; theRinput is wired totonStateA.Q(the timer's elapsed flag) so the state self-clears the moment the dwell expires. -
Run the dwell timer: place an empty box, type
tonStateA, and STEP 7 will instantiate the multi-instance block. WireIN := boStateA,PT := tDwellA, and readQ. -
Drive the next state: the trailing edge of
tonStateA.Q(or simply the rising edge of the next call to a TOF / RS latch) is what enablesboStateB. The simplest implementation is to set the next state from the timer's elapsed bit – but to avoid races, use a one-shot edge flag derived fromtonStateA.Qin the previous scan.
Pattern (FBD, expressed in pseudo-code)
// ---- State A ----
A_R_TRIG(CLK := tonStateA.Q, Q => boStateBStep); // pulse when A elapses
SR(S := boStateA_start OR boStateD_step, R := tonStateA.Q, Q1 => boStateA);
TON(CD := boStateA, PT := tDwellA, Q => tonStateA.Q, ET => tonStateA.ET);
// ---- State B ----
B_R_TRIG(CLK := tonStateB.Q, Q => boStateBStep);
SR(S := boStateA_step, R := tonStateB.Q, Q1 => boStateB);
TON(CD := boStateB, PT := tDwellB, Q => tonStateB.Q, ET => tonStateB.ET);
// ---- State C ----
C_R_TRIG(CLK := tonStateC.Q, Q => boStateCStep);
SR(S := boStateB_step, R := tonStateC.Q, Q1 => boStateC);
TON(CD := boStateC, PT := tDwellC, Q => tonStateC.Q, ET => tonStateC.ET);
// ---- State D (final) ----
SR(S := boStateC_step, R := tonStateD.Q, Q1 => boStateD);
TON(CD := boStateD, PT := tDwellD, Q => tonStateD.Q, ET => tonStateD.ET);
Pattern (SCL equivalent)
tonStateA(IN := boStateA, PT := tDwellA);
IF tonStateA.Q AND NOT boStateA_elapsed THEN
boStateA_elapsed := TRUE;
boStateB := TRUE;
boStateA := FALSE;
END_IF;
// repeat for B, C, D
q_bHorn := boStateA OR boStateC; // example: A and C are "on" states
Multi-Instance Background
Every FB in STEP 7 requires a Instance DB (or be embedded as a multi-instance inside another FB) to host its STATIC variables. The "tonStateA : FB 186;" declaration tells the compiler to allocate FB 186's instance data inside the DB that owns FB_Melody's static area. The advantages are:
- One instance DB per melody block – no DB explosion when you instantiate the melody several times for different horns.
-
One call interface – you can call
FB_Melodyfrom OB1, OB35 or an alarm OB without re-declaring timers in the symbol table. - Re-entrancy – the same FB can drive three different horns with three different dwell tables by simply changing the instance DB.
tonStateA) – the box will then bind to the multi-instance declared in the FB's STATIC section.Output Function
In a Moore machine the output is a function of the state only, not of the inputs. A typical mapping for a four-note melody with alternating long/short tones is:
| State | Dwell (s) | Horn output |
|---|---|---|
| boStateA | 1.0 | 1 (energise) |
| boStateB | 1.0 | 0 (silent) |
| boStateC | 0.75 | 1 (energise) |
| boStateD | 0.5 | 0 (silent – terminal) |
The FBD line therefore reduces to a single OR coil:
q_bHorn := boStateA OR boStateC;
To make the mapping data-driven, replace the OR with a lookup against a constant ARRAY of BOOLs. For a four-note melody the hard-wired OR is faster and easier to commission – the array approach is only worth the effort above ~8 notes.
Initialising the FSM
The very first scan of the user program must place the FSM in a known state. Two options are commonly used:
-
Startup OB (OB 100) – set
boStateA := TRUEand clear B, C, D. This guarantees the melody always begins at the first tone on a warm/cold restart. -
Run-time command – a positive edge on
i_bStartfrom OB1 resets all four states to FALSE and pre-setsboStateA. This is preferred when the melody is one-shot (e.g. a fault acknowledgement chirp).
// In OB 100 (warm restart)
SET;
S boStateA;
R boStateB;
R boStateC;
R boStateD;
// --- end OB 100 ---
If the melody must repeat (e.g. an alarm that chirps every 30 s), the last state (boStateD) should transition back to boStateA when its timer elapses. Add a counter or a flag such as boLoop to break the loop on operator acknowledgement.
CPU Clock Memory Fallback for Short Tones
For sub-second tones below ~100 ms the IEC TON scheduler jitter (1 OB1 cycle, typically 10–20 ms on an S7-300) can become audible as a chirpy timbre. Two mitigations are commonly used:
-
Clock memory bit (German: Taktmerker): in HW Config > CPU Properties > Cycle/Clock Memory, enable a clock byte and assign it to e.g.
MB 10. Each bit toggles at a fixed ratio:
M10.0= 10 Hz,M10.1= 5 Hz,M10.2= 2.5 Hz,M10.3= 2 Hz,M10.4= 1.25 Hz,M10.5= 1 Hz,M10.6= 0.625 Hz,M10.7= 0.5 Hz. - Time-tick OB: use a hardware interrupt OB (OB 40) on a digital input wired to a 100 Hz / 1 kHz reference pulse and decrement a counter. This gives sub-millisecond resolution and is immune to OB1 scan-time drift.
Step 7 v5.5 Commissioning Steps
- Insert the FB and a single Instance DB (e.g.
DB 50– "Horn1 melody"). - In OB1, call
CALL FB_Melody, DB 50; wirei_bStartto a Merker or a process input; wireq_bHornto a DO address, e.g.A 4.0. - Download HW Config + the S7 program to the CPU. Use Online > Monitor/Modify on the Instance DB to watch
tonStateA.ETcount up. - Force
i_bStart := TRUEfor one scan, then FALSE. The melody plays once. - Use the VAT table to step through the state bits and verify transitions. A useful trick is to monitor
DB50.DBD 0(the timer's elapsed time) in engineering units (ms). - To change the tempo, edit
tDwellA–tDwellDin the Instance DB initial values; the change is online-effective on the next cold restart of the FB.
Verification Checklist
| Check | Method | Pass criterion |
|---|---|---|
| FSM initialises on restart | Power-cycle CPU; watch boStateA on first scan |
TRUE in OB 100 execution, FALSE on scan 2 until a Start pulse |
| Tone A is exactly 1.0 s | Oscilloscope on output; trigger on rising edge | High time = 1.00 s ± 1 OB1 cycle |
| State order is A → B → C → D | Monitor all four boState* bits in a VAT |
Bits pulse in alphabetical order with no overlap |
| Terminal state stops the horn | Watch q_bHorn after final timer elapses |
Goes FALSE and stays FALSE until next Start |
| Multi-instance compiles | Check the Reference Data > Program Structure | Only one Instance DB per FB call; no DB 18xx stubs |
| Clock memory present | Watch MB 10 in VAT |
Bits toggle at the configured ratios |
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
"Type conflict" when typing tonStateA
|
Timer dragged from the library, not declared as a multi-instance | Add tonStateA : FB 186; to STATIC, then type the name in an empty box |
| DB exploded – DB 18xx, DB 18xy appear after compile | Library FB used in a network without a STATIC instance | Delete the network, re-create the timer from the empty box referencing the multi-instance |
| Horn chirps continuously | Output OR line missing parentheses – BOOL OR wiring shorts state to output | Check that q_bHorn is the coil of a single network; the OR must precede the assignment |
| Tone durations are wrong by a constant offset |
PT typed in milliseconds but value treated as a 16-bit ms constant |
Use the T# prefix, e.g. T#1s, T#750ms, T#500ms
|
| FSM never advances from state A | Timer IN wired to the wrong state bit |
Each timer's IN must be its own boStateX, not the previous one |
| DB initial values are not what you saved | Downloaded the FB but not the Instance DB | In SIMATIC Manager: PLC > Download > Selected blocks, tick both |
| Tone sounds different on the same FB called twice | Multi-instance attribute cleared during edit | Re-create the FB and tick Multi-instance capable in the FB wizard |
| Clock memory bits are all zero | Clock memory not enabled in HW Config | Open CPU properties → Cycle/Clock Memory → enable and set the byte number |
| Startup OB does not clear B/C/D | OB 100 not present in the offline program | Insert OB 100 from the Standard Library and write the reset code shown above |
Performance, Memory and Scan-Time Notes
On a CPU 315-2 PN/DP at typical 10 ms OB1 scan, the four-state melody consumes:
- Instance DB footprint: 4 × 32 bytes for FB 186 + 4 × 4 bytes for the BOOL states ≈ 144 bytes per call.
- OB1 execution time: ~120 µs for the four TONs, plus the SR logic – well below the 10 ms budget even on a CPU 312.
- Watchdog: with default 150 ms / 300 ms watchdog settings the additional load is negligible. On a CPU 312 set to 100 ms OB1 you still have > 90 % margin.
If you scale the melody to 32 notes, expect ~1 ms additional scan time – consider moving the FB to OB 35 (cyclic interrupt, e.g. 50 ms) so the horn timing is decoupled from OB1 jitter.
Field-Proven Variations
-
Data-driven version: replace the four hard-wired
boStateXbits with anINTindex (0–3) and aCASEstatement in SCL; dwell times are loaded from a DB that the operator can edit online. -
Repeat mode: add an
i_iRepeatinput and wrap state D back to A using a SFB 0 / CTU counter that decrements once per cycle. -
Priority mute: add a
i_bMuteinput that resets the FSM and forcesq_bHorn := FALSE. This is the safest pattern for SIL applications where the alarm is not the only output device on the panel. -
Networked start: drive
i_bStartfrom a Profibus / Profinet / Modbus bit. The FSM is otherwise platform-agnostic.
Standards and Safety Caveats
An audible alarm is a function-relevant output on many machines. If the application falls under ISO 13849-1 / IEC 62061, the horn circuit must be evaluated against the required Performance Level (PL) or Safety Integrity Level (SIL). The melody sequencer shown here is not a safety function by itself – the actual alarm-evaluation path (sensor → logic → horn driver) must be implemented in a Safety CPU (CPU 315F / 317F) or a separate safety relay. Use a multi-channel output stage (e.g. an ET 200S 4F-DO or a Sirius 3SK safety relay) for the final horn switch.
FAQ
Why does STEP 7 show "Type conflict" when I drag a TON block from the library?
You are creating a stand-alone instance, but the variable you typed is declared as a multi-instance inside the FB's STATIC section. Drag the block from the library only when you want STEP 7 to allocate a separate instance DB; otherwise, type the instance name (e.g. tonStateA) into an empty LAD/FBD box and the multi-instance binding is created automatically.
Can I use SFB 4 (system TON) instead of FB 186 (IEC TON) inside the same multi-instance FB?
Yes. SFB 4 is multi-instance capable on S7-300/400 and is actually preferred on older firmware because it is smaller (~18 B of instance data vs ~32 B for FB 186). The trade-off is that SFB 4 uses the CPU's internal SFB instance table; mixing the two families in one FB is allowed but increases the maintenance burden.
How do I play a melody repeatedly until the operator acknowledges it?
Add a SFB 0 / CTU counter that increments each time the FSM reaches state D. The terminal transition resets state A only if the counter has not reached the configured number of repeats. An i_bAck input resets the counter and forces the FSM to its idle state.
What is the smallest tone duration I can achieve with this architecture?
Two OB1 scans (≈ 20 ms on a CPU 315 with 10 ms cycle) is the practical floor for a state-based approach. For shorter tones, replace the IEC TON for the OFF half of the cycle with a clock memory bit (M10.0 toggles at 50 ms, M10.1 at 100 ms, etc.) and you can drop to 100 ms resolution without OB1 jitter.
Can I call the same FB_Melody twice for two different horns on one CPU?
Yes – create one Instance DB per horn (e.g. DB 50 for "Horn1" and DB 51 for "Horn2") and call FB_Melody, DB 50 / FB_Melody, DB 51 in OB1. Each Instance DB carries its own timer instances and dwell values, so the two horns are fully independent.
My CPU is an S7-1500 – will the same code work?
The FSM pattern is portable, but in TIA Portal you should use the native IEC timers (TP, TON, TOF) from the Instructions task card. Multi-instance FBs in TIA Portal are even simpler because every FB is multi-instance capable by default; the manual "drag from library" pitfall that caused the type conflict on v5.5 no longer exists.