Overview
The SIMATIC S7-1200 user-defined web page feature (AWP — Automation Web Programming) lets engineers publish an HMI-style view directly from the CPU without a separate panel. By default, however, AWP forms that read or write tags cause the browser to re-fetch and re-render the entire HTML body. That full-page reload is colloquially called a "postback": inputs lose focus, scroll position resets to the top, the cursor disappears, and any JavaScript state inside the page is wiped.
This reference documents the canonical fix: split each tag into its own HTML fragment file, poll those fragments from JavaScript with AJAX, and overwrite DOM text nodes in place. The technique works on S7-1200 CPUs firmware V4.0 and newer and on S7-1500 CPUs without modifying the PLC program beyond declaring the AWP variables.
Prerequisites
- SIMATIC S7-1200 CPU, firmware V4.0 minimum; V4.2 or later recommended for HTTPS,
AWP_Decimal_Places, and stability fixes. - STEP 7 (TIA Portal) V13 SP1 minimum; V15.1 or later for the modern HTML5 AWP syntax.
- CPU web server enabled in Device Configuration > Web server, with the Permit access with PUT/POST option turned on if you intend to write tags.
- User-defined web pages activated; this enables the AWP pre-processor and the
WebDBaccess mechanism. - An SD card installed if the custom pages exceed the internal file-system limit (about 2 MB on firmware V4.x).
- Basic familiarity with jQuery 2.x or vanilla
fetch().
How a Postback Happens on the S7-1200
When the AWP pre-processor renders HTML it wraps each writable block in a hidden <form method="POST"> whose action attribute points back at the same page. Any submit inside an ... block causes the browser to re-request the full URL, the CPU re-renders the entire HTML, and the page is replaced wholesale. Inputs lose focus, scroll offset resets to zero, and any image or chart that is regenerated every cycle visibly flickers.
The fix is to never let the browser navigate to the full page after the initial load. Two architectural changes accomplish that:
- Move every tag reference that needs live updating into a separate fragment HTM file containing only the AWP variable substitution — nothing else.
- Use JavaScript
XMLHttpRequest(or jQuery$.get) to fetch those fragments at a fixed interval, then overwrite the matching<span>or<label>text node.
AWP Command Reference
The AWP pre-processor parses a small set of HTML-comment directives embedded in user pages. The most relevant ones for AJAX-style dashboards:
| AWP Command | Syntax | Purpose |
|---|---|---|
| Read tag | :=variableName: |
Replaced with current tag value when page is served. |
| Declare input tag | |
Allows POST to write the tag from a form or AJAX. |
| Declare output tag | |
Allows the tag to be referenced with := ... :. |
| Form start | |
Begins a form block; submits cause a postback. |
| Form action URL | |
Target of the generated <form action="">. |
| Form end | |
Closes the form block. |
| Enum encoding | |
Maps integer tag values to text for display. |
| Decimal places | |
Forces a fixed dot decimal on REAL substitutions. |
| Byte order (16-bit) | |
For INT/WORD tags served as hexadecimal strings. |
Refer to the Siemens Industry Online Support S7-1200 Web Server function manual (entry ID 109751614 in the Siemens support database) for the complete grammar.
Architecture: The Fragment Polling Pattern
The fragment pattern treats each polled variable as a tiny independent document. The CPU serves each fragment with a single AWP substitution — a few bytes of HTML — so the HTTP request and parse time are minimal. The browser never re-renders the page chrome (headers, buttons, images); only the text node inside the target element changes.
Browser S7-1200 Web Server
| |
|--- GET /index.htm -------------------------->| (one time)
|<- 200 OK (full HTML) -------------------------|
| |
|--- GET /IOtriangleWave.htm (every 1 s) ------->|
|<- 200 OK (":=triangleWave:" rendered) --------|
| |
|--- GET /IOlevel.htm (every 1 s) -------------->|
|<- 200 OK (":=level:" rendered) ---------------|
| |
|--- GET /IOsetpoint.htm (every 1 s) ----------->|
|<- 200 OK (":=setpoint:" rendered) ------------|
| |
|--- POST /SetSetpoint.htm (on Apply click) ---->|
|<- 204 No Content (no body, no flicker) -------|
Each IO<tagname>.htm file is independent. Adding a new variable means adding one fragment file and one polling line in JavaScript. The main index.htm is never re-fetched after the initial load, so no scroll reset, no input loss, no visible flicker.
SVG Diagram: Browser-Side Polling State Machine
Step-by-Step: Adding Multiple Read Variables
This procedure extends a single-variable web page (for example triangleWave) into a dashboard with N independent tags.
1. Declare the tags in the PLC DB
Open the webdata data block and add the additional tags as REAL, INT, or BOOL as appropriate. Example additions:
| Symbolic name | Type | Initial value | Use |
|---|---|---|---|
triangleWave |
REAL | 0.0 | Existing analog example |
level |
INT | 0 | Tank level, percent |
setpoint |
REAL | 50.0 | Writable setpoint |
pumpRunning |
BOOL | FALSE | Digital status |
alarmActive |
BOOL | FALSE | Alarm flag |
2. Create one fragment file per tag
Inside the user-defined pages folder (default UserFiles\) create the following files. Each contains only the AWP substitution so the response body is the literal value.
IOtriangleWave.htm:
:="webdata".triangleWave:
IOlevel.htm:
:="webdata".level:
IOpumpRunning.htm:
:="webdata".pumpRunning:
IOalarmActive.htm:
:="webdata".alarmActive:
REAL tags serialize with locale-dependent decimal separators (comma on German CPUs). To force a fixed dot decimal, add to the fragment or convert to STRING in the PLC code with a fixed format string.3. Declare the variables on the main page
The main index.htm must still declare each tag at the top using AWP comments — otherwise the substitution engine will not recognize :="webdata".xxx: when it serves the fragment.
<!-- AWP_In_Variable Name='"webdata".setpoint' -->
<!-- AWP_Out_Variable Name='"webdata".triangleWave' -->
<!-- AWP_Out_Variable Name='"webdata".level' -->
<!-- AWP_Out_Variable Name='"webdata".pumpRunning' -->
<!-- AWP_Out_Variable Name='"webdata".alarmActive' -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>S7-1200 Dashboard</title>
<script src="jquery-2.0.2.min.js"></script>
<style>
.tag { font-family: monospace; font-size: 14pt; }
.alarm { color: red; font-weight: bold; }
.ok { color: #2a7; }
</style>
</head>
<body>
<h1>Process Dashboard</h1>
<div>Triangle wave: <span class="tag" id="triangleWave">---</span></div>
<div>Level: <span class="tag" id="level">---</span> %</div>
<div>Pump running: <span class="tag" id="pumpRunning">---</span></div>
<div>Alarm: <span class="tag" id="alarmActive">---</span></div>
<h2>Setpoint</h2>
<input id="setpointInput" type="text" value="50" />
<button id="btnApply">Apply</button>
<script type="text/javascript">
$(document).ready(function(){
$.ajaxSetup({ cache: false });
function poll(fragment, targetId, intervalMs) {
return setInterval(function(){
$.get(fragment, function(result){
$('#' + targetId).text(result);
}).fail(function(){
$('#' + targetId).text('ERR');
});
}, intervalMs);
}
poll('IOtriangleWave.htm', 'triangleWave', 1000);
poll('IOlevel.htm', 'level', 1000);
poll('IOpumpRunning.htm', 'pumpRunning', 500);
poll('IOalarmActive.htm', 'alarmActive', 500);
// visual flash when alarm is true
setInterval(function(){
$.get('IOalarmActive.htm', function(v){
$('#alarmActive').toggleClass('alarm', $.trim(v) === 'true');
});
}, 500);
$('#btnApply').on('click', function(){
var v = parseFloat($('#setpointInput').val());
if (!isNaN(v)) writeSetpoint(v);
});
function writeSetpoint(value) {
$.ajax({
url: 'SetSetpoint.htm',
type: 'POST',
data: { '"webdata".setpoint': value },
success: function(){ console.log('Setpoint written:', value); },
error: function(x){ console.error('Write failed', x.status); }
});
}
});
</script>
</body>
</html>
4. Upload the pages to the CPU
In TIA Portal, drag the user-defined pages folder onto the project tree node for the CPU, or use Online > Download user-defined web pages. After download, navigate to http://<cpu-ip>/index.htm and confirm in Chrome DevTools > Network that only the initial index.htm request appears; all subsequent traffic is fragment GETs.
Step-by-Step: Writing Variables Without Postback
Reading avoids postback because no form is submitted. Writing, however, requires an HTTP POST. The pattern keeps the postback from happening by sending the POST through JavaScript and never letting the browser navigate to the response.
1. Declare the tag as writable
<!-- AWP_In_Variable Name='"webdata".setpoint' -->
2. Create a write handler fragment
The handler does not need its own visual HTM file — the browser POSTs to a target page with a form field name matching the AWP declaration. To keep the design clean, create a dedicated SetSetpoint.htm:
<!-- AWP_In_Variable Name='"webdata".setpoint' --> <!-- AWP_Start_Form --> <!-- AWP_End_Form -->
This file, when POSTed to with a field named "webdata".setpoint, will write the value. The response body is empty (no := substitution), so the browser ignores the body and the user sees no flicker.
3. JavaScript submit without postback
function writeSetpoint(value) {
$.ajax({
url: 'SetSetpoint.htm',
type: 'POST',
data: { '"webdata".setpoint': value },
success: function(){ console.log('Setpoint written:', value); },
error: function(x){ console.error('Write failed', x.status); }
});
}
"webdata".setpoint. The exact string the browser must send is the value between Name=' and ' in the AWP_In_Variable declaration.Working with BOOL and Structured Tags
BOOL tags serialize as the literal text true or false. To convert them to a human label, do the substitution in JavaScript:
function renderBool(v) { return $.trim(v) === 'true' ? 'ON' : 'OFF'; }
$.get('IOpumpRunning.htm', function(v){
$('#pumpRunning').text(renderBool(v));
});
For arrays or UDT members, use a CSV-style fragment and parse in JS. Example fragment for a small struct webdata.drive.speed, webdata.drive.current, webdata.drive.torque:
:="webdata".drive.speed:,:="webdata".drive.current:,:="webdata".drive.torque:
$.get('IOdrive.htm', function(csv){
var parts = csv.split(',');
$('#speed').text(parts[0]);
$('#current').text(parts[1]);
$('#torque').text(parts[2]);
});
setInterval for dashboards with up to 50 tags.Polling Rate and Performance
The S7-1200 web server is single-threaded and runs at low priority relative to the OB1 cycle. Each HTTP request adds 5–20 ms of CPU load depending on payload size. Practical polling rates:
| Variables | Recommended poll interval | Network load |
|---|---|---|
| 1–5 | 250–500 ms | ~5 req/s, < 1 KB/s |
| 5–20 | 1000 ms | ~20 req/s, < 4 KB/s |
| 20–50 | 2000 ms or batch via single fragment | ~25 req/s |
| > 50 | Use OPC UA, S7-1500 Web API, or a dedicated HMI | n/a |
The web server is suitable for diagnostic dashboards and small operator panels but is not intended as a substitute for a production HMI, alarm system, or historian.
Security Hardening
User-defined web pages are served by the same web server that exposes diagnostics and tag access. Recommended hardening:
- Enable HTTPS on the CPU. Firmware V4.2+ supports self-signed certificates; production deployments should use a CA-signed certificate.
- Configure at least the read protection level on the CPU so that diagnostics pages require a password.
- Use the user-management features added in TIA Portal V15+ to assign write access to specific roles only.
- Place the PLC on a dedicated VLAN; do not expose port 80/443 to the corporate network without a firewall.
- Enforce setpoint min/max limits in PLC code so a malicious POST cannot drive the process outside safe bounds. Never rely solely on JavaScript-side validation.
- For external (Internet) access, terminate at a VPN or reverse proxy with mutual TLS — do not publish the CPU directly.
Verification Checklist
- Open Chrome DevTools > Network. Reload
index.htm. Confirm only one request forindex.htm; subsequent activity is fragment GETs every interval. - Toggle the alarm flag in the PLC (force
webdata.alarmActive := TRUEvia watch table). The alarm label turns red within one polling interval. - Click Apply for the setpoint. Confirm
webdata.setpointupdates in the PLC online monitor, and the page does not reload (noindex.htmrequest appears in DevTools). - Disable the web server briefly. The dashboard should show
ERRin place of each value without losing page chrome. - Disconnect network for 10 s; reconnect. Polling should resume automatically with no manual refresh.
- Verify a
POST /SetSetpoint.htmin DevTools shows status 204 or 200 with no body change toindex.htm.
Troubleshooting Matrix
| Symptom | Likely cause | Remedy |
|---|---|---|
Fragment returns the literal text :="webdata".x:
|
AWP command not recognized — missing declaration on index.htm
|
Add AWP_Out_Variable declaration at top of main page. |
| Page flickers or scrolls to top on update | JavaScript is updating the wrong element, or page is being reloaded | Check Network tab; ensure no index.htm request after initial load. |
| Write succeeds but value reverts immediately | PLC code overwrites the tag (assignment from HMI tag in OB1) | Trace the tag in the PLC; ensure user code does not unconditionally write the tag. |
| HTTP 404 on fragment | File not uploaded, or filename case mismatch | Confirm case-sensitive filename via the CPU's web server file browser. |
| Values appear with comma decimal (German locale) | No AWP_Decimal_Places directive |
Add directive or convert to STRING in PLC. |
| HTTP 500 on POST | Form field name typo | Verify field name matches AWP_In_Variable Name='...' exactly. |
| Polling stops after several minutes | Browser tab throttled, fragment file too large, or web server overload | Reduce payload; check console for fetch errors; increase interval. |
| CPU diagnostic buffer reports "Web server: too many requests" | Polling too aggressive | Increase interval; batch variables into one fragment. |
JS sees true for both on and off |
Wrong comparison type or whitespace in response | Use $.trim(v) === 'true' instead of truthy check. |
| HTTPS works but AJAX fails | Mixed-content blocking | Either serve all assets via HTTPS or downgrade the page to HTTP-only. |
Frequently Asked Questions
What S7-1200 firmware versions support user-defined web pages?
Firmware V4.0 introduced user-defined web pages. V4.2 added HTTPS, AWP_Decimal_Places, and improved stability. The S7-1500 supports the same AWP syntax from firmware V1.5 onward and additionally offers a JSON-based Web API.
Can I avoid jQuery and use vanilla JavaScript?
Yes. Replace $.get(url, cb) with fetch(url).then(r => r.text()).then(cb) and keep the same setInterval pattern. jQuery 2.x is used historically because it supported older browsers; modern code rarely needs it.
How do I update more than one tag without postback?
Create one fragment HTM file per tag containing only :=...:, declare each tag with AWP_Out_Variable on the main page, and use setInterval plus AJAX to fetch each fragment into a DOM element. The pattern scales linearly; for large tag counts, batch several tags into one CSV fragment.
Is the user-defined web server a replacement for an HMI?
No. Siemens explicitly positions it as a diagnostic and visualization supplement. For production screens, alarm handling, user management, recipes, and audit logging, use a SIMATIC HMI panel or WinCC Runtime on a PC.
How do I prevent unsafe writes from the page?
Enforce limits in PLC code: clamp the incoming value inside the OB that writes the tag, or use AWP_Enum_Def with a fixed list. Never rely on JavaScript-side validation alone — a crafted POST bypasses the browser entirely.