Overview
Siemens SINUMERIK 810D/828D/840D sl controls expose machine data, R-variables, and a set of system variables that make it possible to build a parts counter and a live cycle-time / clock display without writing ladder logic on a PLC. The two building blocks used in this implementation are:
-
R-variables (
R0..R99, extendedR100+where configured) – integer/free numeric work registers that can be loaded, modified, displayed and persisted across program runs. -
NC system variables – read-only variables starting with
$that expose controller state. The relevant ones are$AC_CYCLE_TIME,$A_HOUR,$A_MINUTE,$A_SECOND, and$A_DAY.
The same pattern is frequently requested on SIMATIC S7-1200/S7-1500 CPUs for shop-floor cycle timers; that platform exposes the equivalent through the DTL data type, the clock-memory byte, and a cyclic interrupt OB. The two implementations are shown side by side so a controls engineer familiar with one Siemens line can map it to the other.
R0 through R99 (and many of the extended range) may already be claimed by the machine tool builder (MTB) for tool-changer logic, pallet changer sequencing, or safety interlocks. Do not write to an R-variable until you have confirmed it is unused on your machine. Use a free, MTB-confirmed address and document the assignment in the program header.Prerequisites
- SINUMERIK 810D, 828D, or 840D sl control with SHOPMILL or SHOPTURN option loaded.
- Operator access to the Offsets > R-variables screen (or equivalent on the HMI version installed).
- Program directory write access for subprograms (
_N_*.SPF) under/_N_WKS_DIR/_N_TEMPLATES_WPD/or the manufacturer's template directory. - Knowledge of which R-variables are reserved by the MTB. Cross-check against the manufacturer's PLC ladder and any MTB-supplied template programs before assigning.
SINUMERIK R-Variable Conventions
R-variables on SINUMERIK are 32-bit signed real numbers (REAL/INT compatible). They retain their last value across program executions and across NC resets until the control is powered down or the value is cleared manually. They are the standard scratchpad for user-level counts, timers and flags.
| Variable | Direction | Typical Use | Comment |
|---|---|---|---|
R0 |
RW | Part count accumulator | Often free on basic machines – verify with MTB docs first. |
R1 |
RW | Target parts count (setpoint) | Operator enters this on the R-variables screen. |
R2 |
RW | Running parts count (incremented each cycle) | Must be reset to 0 by the operator before a new run. |
R3–R9
|
RW | User scratchpad | Avoid collision with MTB assignments. |
R10+ |
RW | Frequently MTB-reserved | Do not touch without explicit sign-off. |
5 (the control stores 5.000). Use TRUNC() when you need the integer portion only.Building a Parts Counter Subprogram
The pattern below creates a standalone subprogram called COUNT that can be invoked at the start of every machining cycle. Each call increments R2 and compares it to the target R1; when the target is reached, the control halts with a clearable message and waits for the operator.
Subprogram: COUNT.SPF
;==========================================
; COUNT.SPF -- Parts counter subprogram
; Invoke from main with a bare "COUNT" line.
;
; R1 = target parts (operator sets this)
; R2 = running count (operator resets to 0)
;==========================================
IF R2 == R1 GOTOF JOB_COMPLETE ; if running count has hit target, branch
R2 = R2 + 1 ; otherwise increment the running count
GOTOF END_COUNT ; and exit the subprogram
JOB_COMPLETE:
MSG("PARTS COUNT END") ; first halt message
M00 ; program stop, operator clears
MSG("YOU HAVE REACHED YOUR LIMIT") ; second halt message after cycle-start
M00 ; stop again so operator can acknowledge
END_COUNT:
M17 ; end of subprogram, return to caller
Main program wiring
;MPF - Main machining program
COUNT ; call the counter sub first thing
T1 D1
M06 ; tool change
...machining blocks...
M30 ; program end, rewind
Operator workflow
- Select Offsets > R-variables.
- Set
R1to the desired part quantity. - Set
R2 = 0at the start of a new run. - Run the program. The counter increments once per cycle.
- When
R2 == R1, the control halts with "PARTS COUNT END". - Press CYCLE START to acknowledge. The second message appears and the control halts again.
- To continue a partial run, increment
R1by the remaining quantity and press CYCLE START.
Adding a Cycle Time and Clock Display
SINUMERIK exposes the current cycle time and wall-clock time through system variables. Two useful variables are:
-
$AC_CYCLE_TIME– elapsed time in seconds for the current NC program run. -
$A_HOUR,$A_MINUTE,$A_SECOND– current shift-clock time read from the controller.
The pattern below builds a stats subprogram that formats minutes/seconds from $AC_CYCLE_TIME and concatenates them with the live wall-clock time. It is designed to be called at the end of a cycle so the message line on the HMI updates after each part.
Subprogram: STATS.SPF
;==========================================
; STATS.SPF -- Display part count + cycle time + clock
; R0 = part count
; R1 = minutes (integer portion)
; R2 = seconds (integer portion)
; R5 = working value (minutes with fraction)
;==========================================
PROC STATS DISPLOF
N35 R5 = $AC_CYCLE_TIME / 60 ; convert seconds -> minutes
N40 R1 = TRUNC(R5) ; integer minutes
N45 R2 = R5 MOD 1 ; fractional remainder (0..1)
N50 R2 = TRUNC(R2 * 60) ; convert fraction -> seconds
N55 R0 = R0 + 1 ; increment part count
N20 MSG("PART COUNT= " << R0 << " : CYCLE TIME= " << R1 << " MINS " << R2 << " SECS : TIME= " << $A_HOUR << ":" << $A_MINUTE)
M17 ; return to caller
Triggering the display from the main program
;MPF - last blocks of main program
N230 CALL "/_N_WKS_DIR/_N_TEMPLATES_WPD/_N_STATS_MPF" ;SM;*RO*
F_END(0,1,5);*RO*
F_END: The F_END(0,1,5) line is a Siemens-specific termination block. The third argument controls reset behavior at end of program. Use the value configured by the MTB or copy from an existing working template to avoid changing the machine's reset semantics.The same effect for a basic standalone display (no separate subprogram) is achieved with a single line in the program header:
N145 MSG("TIME= " << $A_HOUR << ":" << $A_MINUTE)
This is sufficient when SHOPMILL/SHOPTURN is the active interface and a richer HMI dashboard is not needed.
Formula Reference
| Quantity | Formula | Source |
|---|---|---|
| Minutes from seconds | R5 = $AC_CYCLE_TIME / 60 |
Constant 60 s/min |
| Integer minutes | R1 = TRUNC(R5) |
TRUNC() drops the fraction |
| Fractional part | R2 = R5 MOD 1 |
MOD returns 0..1 |
| Integer seconds | R2 = TRUNC(R2 * 60) |
Scale fraction to seconds |
| Part count increment | R0 = R0 + 1 |
Direct addition |
Equivalent on SIMATIC S7-1200 / S7-1500
When the same requirement is implemented on a SIMATIC PLC rather than a SINUMERIK, the clock functions are documented in the S7-1200 manual collection. The relevant elements are:
-
DTL (Date and Time Long) – 12-byte structure that holds year, month, day, weekday, hour, minute, second and nanoseconds. Read with
RD_SYS_T, set withWR_SYS_T. - Clock-memory byte – a configured byte in the CPU properties whose individual bits toggle at fixed frequencies (e.g. 10 Hz, 5 Hz, 2 Hz, 1 Hz, 0.5 Hz). The 1 Hz bit is the canonical "seconds tick".
-
Cyclic interrupt OB (e.g.
OB30..OB38) – for deterministic time bases that cannot rely on the main OB1 scan.
Pattern: seconds counter on S7-1200 using the clock-memory byte
// OB1 - count rising edges on MB100 bit 3 (1 Hz, per CPU clock-memory config)
"Counter_DB".SecTickOld := "Counter_DB".SecTick;
"Counter_DB".SecTick := %MB100.3; // configured 1 Hz bit
IF "Counter_DB".SecTick AND NOT "Counter_DB".SecTickOld THEN
"Counter_DB".Seconds := "Counter_DB".Seconds + 1;
IF "Counter_DB".Seconds >= 60 THEN
"Counter_DB".Seconds := 0;
"Counter_DB".Minutes := "Counter_DB".Minutes + 1;
IF "Counter_DB".Minutes >= 60 THEN
"Counter_DB".Minutes := 0;
"Counter_DB".Hours := "Counter_DB".Hours + 1;
END_IF;
END_IF;
END_IF;
Pattern: reading the CPU wall clock via DTL
// Read system clock into a DTL tag
#Now := RD_SYS_T("Now"); // DTL tag of length 12 bytes
// Display fields individually:
#ClockMessage := CONCAT("TIME=", INT_TO_STRING(#Now.HOUR), ":",
INT_TO_STRING(#Now.MINUTE), ":",
INT_TO_STRING(#Now.SECOND));
Both approaches are documented in the Siemens SiePortal knowledge base: see How can I make the seconds counter (Step 7 TIA)? and Hour counter. For cycle-synchronised counting, a cyclic interrupt OB is preferred over polling the clock bit in OB1 – see Clock Tags, counters and cycle time question.
Mapping Between Platforms
| Function | SINUMERIK (810D / 828D / 840D sl) | SIMATIC S7-1200 / S7-1500 |
|---|---|---|
| Wall-clock read |
$A_HOUR, $A_MINUTE, $A_SECOND
|
DTL via RD_SYS_T
|
| Wall-clock write | Operator sets on HMI, or via SETTIME
|
WR_SYS_T |
| Cycle timer |
$AC_CYCLE_TIME (s, REAL) |
Custom timer in OB1 / cyclic OB |
| 1 Hz tick | Implicit in $AC_TIMER family |
Clock-memory byte, bit at 1 Hz |
| Persistent counter | R-variable (volatile across power-cycle) | DB tag (retentive via "Remain" property) |
| HMI message | MSG("..." << var) |
HMI tag / alarm via HMI configuration |
| Program stop | M00 |
Not applicable (PLC continues scan) |
Best Practices and Field-Proven Caveats
- Never assume an R-variable is free. Tool changers, pallet changers, and probing cycles commonly use R10–R70 on machines from multiple builders. Verify with the MTB ladder, the as-shipped template programs, and the machine's PLC project before assigning.
- Prefer defined variables on 840D sl. Modern SINUMERIK programming guides recommend using named GUD (Global User Data) variables or LUD (Local User Data) instead of bare R-variables, because R-numbers are an unstructured flat namespace that is easy to clash with.
-
Persist the count if you need it across power cycles. R-variables clear on power-down. If a count must survive, store it to a non-volatile NV location (
$A_IN[0]–$A_IN[9]as setters, or a settable machine data) or use a PLC counter. -
Keep
MSG()calls short. The HMI message bar truncates around 64 characters on older 810D panels. Concatenate only the essential fields. -
Reset
$AC_CYCLE_TIMEat the right place.$AC_CYCLE_TIMEis reset at program start by default but only if the appropriate machine data is set. If your display always shows the accumulated time of all programs in the NCK, setMD20150 $MC_GCODE_RESET_VALUESfor the relevant G-code or reset$AC_CYCLE_TIMEexplicitly with$AC_CYCLE_TIME = 0in your startup block. - Don't rely on the ISO side if SHOPMILL is loaded. The MSG line for clock display works on both ISO and ShopMill, but parts-counter behaviour (setpoint entry, reset) is significantly smoother on the ShopMill side where soft keys are tailored.
Verification
- Load the program with
COUNTas the first executable block and run it for at least two cycles. After the first cycle,R2should read1.000; after the second,2.000. - Set
R1 = 3andR2 = 0. Run the program. On the third cycle, the message line must read "PARTS COUNT END" and the spindle must stop (M00 active). - Press CYCLE START. The second message "YOU HAVE REACHED YOUR LIMIT" must appear and the control must stop again.
- Change
R1to5and confirm that pressing CYCLE START continues the run from the current count. - With
STATS.SPFwired in, verify that after each cycle the HMI displays a plausibleCYCLE TIMEand a liveTIME= HH:MMmatching the controller clock. - Cross-check the controller's displayed time against an independent reference. If drift exceeds a few seconds per shift, the backup battery on the NCU may need replacement.
Troubleshooting Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
| Counter does not increment | Subprogram not saved under SPF extension or wrong directory |
Save to /_N_WKS_DIR/_N_<workpiece>_WPD/_N_COUNT_SPF and verify with the program manager. |
R value shows garbled characters on HMI |
R-variable overwritten by MTB PLC | Stop the test, switch to a confirmed-free R-number, document. |
| Cycle time always reads zero |
$AC_CYCLE_TIME reset by program start not enabled in MD |
Set MD20150 or reset $AC_CYCLE_TIME = 0 in a startup block. |
| Message line blank after cycle |
MSG() overwritten by another block |
Place the MSG in the last executable block before M30. |
| MSG truncated at 24 chars | Old HMI software version | Reduce displayed fields or upgrade HMI software. |
| Second halt message does not appear | Operator cleared the first MSG too fast | Insert a dwell (G04 F2) or a second explicit M00 before re-issuing MSG. |
| Clock display off by hours | NCK time set to a different time zone | Use SETTIME or adjust via HMI Commissioning > Time/Date. |
FAQ
Which R-variables are safe to use on a SINUMERIK 810D?
There is no universal answer. Start with R0–R9, but always verify against the MTB-supplied PLC project and template programs. If any of those R-numbers is already used, pick the next free number and document the assignment in the program header.
Why does $AC_CYCLE_TIME read zero on the first cycle?
It is reset at the start of each NC program. If your main program calls a subprogram and you read $AC_CYCLE_TIME inside that subprogram before any motion has occurred, the value may briefly read zero. Place the read after the first motion block, or in the cycle-end stats subprogram as shown above.
Can the parts counter survive a power cycle?
Not if it is stored in a plain R-variable – those clear at power-down. Either persist it via a settable machine data, mirror it into a PLC retentive data block, or accept that the count restarts at zero each morning and require the operator to re-enter the remaining quantity.
How do I read the wall-clock time on an S7-1200 instead of a SINUMERIK?
Use the RD_SYS_T instruction in SCL or LAD/FBD to populate a DTL tag, then read .HOUR, .MINUTE, and .SECOND fields directly. See the S7-1200 clock functions reference.
Why use a cyclic interrupt OB for a seconds counter on S7-1200?
The clock-memory byte polls in OB1 only when OB1 finishes its scan. If the main OB scan time exceeds 1 s, you can miss a 1 Hz tick. A cyclic interrupt OB at e.g. 100 ms gives a deterministic read and avoids drift. Details are in the Siemens SiePortal thread Clock Tags, counters and cycle time question.