Building a Momentary HTML Push Button for the S7-1200/1500 Web Server
Standard HTML form submissions cannot deliver a true "press-and-hold" (momentary) behavior on a Siemens SIMATIC S7 CPU web server because every <input type="submit"> triggers a full HTTP POST, page reload, and a single value write that the browser retains until the next submit. This reference shows how to replace the static AWP form with an HTML5 event-driven button that writes 1 on mousedown and 0 on mouseup using JavaScript and XMLHttpRequest (AJAX) against the S7 web server's CGI write interface. The article covers AWP variable declaration, TIA Portal web server configuration, working HTML5/JavaScript code, browser-side debounce, S7-1200 versus S7-1500 firmware differences, and the GOT/PLCSim alternatives that engineers use when the web server is unavailable.
1. Why Standard AWP Forms Cannot Be Momentary
The Siemens S7 web server exposes the Automation Web Programming (AWP) command set for user-defined pages. A typical "Motor ON / Motor OFF" pair uses the following pattern from the SIMATIC S7-1200 Programmable Controller Web Server Function Manual:
<!-- AWP_In_Variable Name='"webdata".Motor_Start' -->
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Motor Start</title>
</head>
<body>
<form method="post" action="">
<input type="submit" value="Motor ON" class="inputbutton">
<input type="hidden" name='"webdata".Motor_Start' value="1">
</form>
<form method="post" action="">
<input type="submit" value="Motor OFF" class="inputbutton">
<input type="hidden" name='"webdata".Motor_Start' value="0">
</form>
</body>
</html>
Each form posts a single value and reloads the page. The CPU never receives a "release" event because the browser is stateless between submits, and the page that comes back already contains the value just written. This is the architectural constraint that prevents a true momentary control using vanilla HTML forms. A "toggle" attempt with two hidden inputs in the same form is even worse - HTML forms submit all named fields, but the order is undefined and the page only reflects the last parsed assignment.
2. Prerequisites
| Item | Requirement |
|---|---|
| CPU | S7-1200 (any) or S7-1500 (any), firmware as listed below |
| S7-1200 firmware | V4.0 or later (AWP user-defined pages supported from V4.0). V4.4+ recommended for TLS and improved HTML handling |
| S7-1500 firmware | V1.8 or later; V2.9+ recommended for full JavaScript / AJAX tolerance |
| Engineering tool | TIA Portal V16 or later (V17/V18 recommended) |
| Web server | Enabled in Device Configuration > Web server; "Enable user-defined web pages" must be checked |
| Access level | The PLC tag used as the button state must permit write access from the web server (tag must be in DB "webdata" or marked accessible from HMI/OPC UA with write permission) |
| User authorization | A user with "Write to PLC" right in the web server user list; the default "admin" account or a custom user with write privilege |
| Browser | Any HTML5-compliant browser (Chrome 90+, Firefox 90+, Edge 90+). Touch devices work via pointerdown / pointerup
|
| Network | CPU and browser on same subnet, or routed through VPN; port 80 (HTTP) or 443 (HTTPS) reachable |
3. Enabling the Web Server and Declaring AWP Variables
- In TIA Portal, open the CPU device configuration and select Web server from the navigation tree.
- Check Enable Web server. Check Enable user-defined web pages and enter the HTML application name (for example
MotorControl). The HTML source must be placed in the project under CPU > Web application > [name] > htm. - Add a data block named
webdata(case-sensitive - the AWP engine references the DB by this name). Inside it declare a static tag of BOOL type namedMotor_Startwith the default valuefalse. - In the user-defined web page source, declare the variable as an AWP_In_Variable at the very top of the file (this statement must appear before any HTML body content):
<!-- AWP_In_Variable Name='"webdata".Motor_Start' --> - Compile the project and download hardware and software to the CPU. The web server then becomes available at
http://<cpu-ip>/awp/MotorControl/<pagename>.html.
Name= must match the symbolic PLC name, including the DB name in double quotes. TIA Portal normally formats the reference as "webdata".Motor_Start. If the AWP declaration is missing, the page loads but the form fields are not bound to the PLC tag.4. Static Reference: AWP Forms for Latched Control
For applications where a latched start/stop is sufficient, the two-button form posted earlier is the canonical Siemens example and is documented in section 11.3 of the S7-1200 Web Server Function Manual. A variant that toggles a single bit uses a checkbox:
<!-- AWP_In_Variable Name='"webdata".Motor_Start' -->
<form method="post" action="">
<input type="checkbox" name='"webdata".Motor_Start' value="1"> Start
<input type="submit" value="Apply">
</form>
When the user checks the box and clicks Apply, the form POSTs "webdata".Motor_Start=1. Unchecking sends =0 after the next Apply. None of these produce press-and-hold semantics - they always require a second user action (clicking Apply) to send the second value.
5. Momentary Control Using HTML5, JavaScript and AJAX
To write 1 while a button is depressed and 0 on release, the page must issue two POSTs without a page reload. The mechanism is straightforward: bind pointerdown (or mousedown) and pointerup (or mouseup) handlers to a <button> element and use the fetch() API or XMLHttpRequest to POST a application/x-www-form-urlencoded body to the current URL with the variable name/value pair. The S7 web server accepts the same body format that an HTML form would produce.
5.1 Complete Working HTML5 Page
<!-- AWP_In_Variable Name='"webdata".Motor_Start' -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Motor Start (Momentary)</title>
<style>
body { font-family: Segoe UI, sans-serif; background: #1e1e1e; color: #eee; }
.pb { width: 160px; height: 80px; font-size: 18px;
background: #444; color: #fff; border: 2px solid #888;
border-radius: 8px; user-select: none; cursor: pointer; }
.pb.pressed { background: #c33; border-color: #f55; }
.status { margin-top: 16px; font-size: 14px; }
</style>
</head>
<body>
<h2>Motor Start - Press and Hold</h2>
<button id="pb" class="pb" type="button">MOTOR</button>
<div class="status" id="status">Tag: ?</div>
<script>
const TAG = '"webdata".Motor_Start';
const URL = window.location.href.split('?')[0]; // strip any query string
const pb = document.getElementById('pb');
const stat = document.getElementById('status');
function writeTag(val) {
// Use sendBeacon on release so the request is sent even if the page
// is being closed; use fetch on press for full control.
const body = encodeURIComponent(TAG) + '=' + val;
if (val === '0' && navigator.sendBeacon) {
navigator.sendBeacon(URL, new Blob([body],
{type: 'application/x-www-form-urlencoded'}));
} else {
fetch(URL, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: body,
credentials: 'include'
}).then(r => {
stat.textContent = 'Tag write HTTP ' + r.status;
}).catch(err => {
stat.textContent = 'Error: ' + err;
});
}
}
pb.addEventListener('pointerdown', e => { e.preventDefault();
pb.classList.add('pressed');
writeTag('1'); });
pb.addEventListener('pointerup', e => { e.preventDefault();
pb.classList.remove('pressed');
writeTag('0'); });
pb.addEventListener('pointercancel', () => {
pb.classList.remove('pressed');
writeTag('0'); });
pb.addEventListener('pointerleave', () => {
pb.classList.remove('pressed');
writeTag('0'); });
// Keyboard support: SPACE while focused = momentary
pb.addEventListener('keydown', e => {
if ((e.key === ' ' || e.code === 'Space') && !e.repeat) {
pb.classList.add('pressed'); writeTag('1');
}
});
pb.addEventListener('keyup', e => {
if (e.key === ' ' || e.code === 'Space') {
pb.classList.remove('pressed'); writeTag('0');
}
});
</script>
</body>
</html>
5.2 Why This Works
- The
pointerdown/pointerupevents cover mouse, touch, and pen input uniformly.mousedown/mouseupare acceptable for desktop-only deployments. - The POST body uses the same
name=valueencoding as a real HTML form, so the S7 web server's AWP handler binds the value to the declaredwebdata.Motor_Starttag. - Using the same URL as the page (no separate endpoint) means the page re-renders after the POST, which is the natural AWP behavior; on
pointerupthe page is replaced and the user can press again. -
navigator.sendBeaconon release ensures the final0write is delivered even if the user is closing the tab or the connection is slow. - Including
credentials: 'include'makes the browser send the basic-auth cookies of the web server session, which is required for the write to be authorized.
6. S7-1200 vs S7-1500: JavaScript Tolerance Differences
| Feature | S7-1200 (FW 4.x) | S7-1500 (FW 1.8+) |
|---|---|---|
| User-defined web pages | Yes (from V4.0) | Yes |
| AWP_In_Variable write | Yes via form POST | Yes |
| HTML5 / JS / AJAX scripts referenced in page | Limited - the S7-1200 only serves the page; JavaScript runs in the browser, not in the CPU. AJAX requests to the same page work, but the S7-1200 web server can be slow to answer a second POST if the first is still being processed. | Fully supported - S7-1500 web server (FW 2.0+) answers concurrent POSTs from AJAX more reliably and has higher connection limits |
| TLS / HTTPS | Available V4.4+ | Available all firmware |
| Concurrent HTTP connections | ~4 | ~8 (FW 2.9: 16+) |
| Recommended for momentary button | Acceptable, expect ~150-300 ms round-trip per write | Preferred; round-trip typically <80 ms on LAN |
7. PLC-Side Handling of the Momentary Tag
The web server writes the tag in DB webdata exactly as an HMI would. In the OB1 or a cyclic OB, your application logic should treat the rising edge of webdata.Motor_Start as the start command and the falling edge as the stop command, the same way you would treat a physical push button wired to a digital input. A safe pattern is:
// SCL (TIA Portal) - momentary start/stop with edge detection
IF "webdata".Motor_Start" THEN // tag is 1 while button is held
"Motor".StartCmd := TRUE;
ELSE
"Motor".StartCmd := FALSE;
END_IF;
// Optional: heartbeat watchdog - clear StartCmd if webdata is stuck at 1
// for more than e.g. 5 seconds (operator may have walked away)
"webdata".Motor_Start_Watchdog(IN := "webdata".Motor_Start",
PT := T#5s);
IF "webdata".Motor_Start_Watchdog.Q THEN
"Motor".StartCmd := FALSE;
END_IF;
For a jog-style output (drive runs only while button is held), wire the tag directly to the drive enable input and add a hardware/software interlock in the safety circuit. The web page must not bypass the safety chain.
8. Security Considerations
- Enable HTTPS and disable HTTP in the web server configuration. The S7-1200 supports self-signed certificates; load a CA-signed certificate on production systems to suppress browser warnings.
- Create a dedicated web user with the minimum required right (write to
webdataonly). Do not reuse the PLC's "admin" account. - Add IP-based access restrictions in the CPU's firewall (S7-1500) or in the network infrastructure. See the SIMATIC Security Concept.
- Add CSRF mitigation: the AWP POST is authenticated by the basic-auth cookie, which is sent automatically. For higher assurance, include a per-session token in a hidden field and validate it in the PLC.
- Confirm the operator's expectation: a momentary web button that controls a hazardous actuator is generally not acceptable under functional-safety standards. For SIL-rated controls, use a hardwired E-stop and a safety PLC.
9. Verification and Test Procedure
- Open the user-defined page in a browser, e.g.
http://<cpu-ip>/awp/MotorControl/motor.html. Log in with a user that has write rights. - Open the TIA Portal online watch table for DB
webdataand observe theMotor_Starttag. - Click and hold the button. The tag should change to
TRUEwithin ~100 ms on an S7-1500 (LAN) and ~250 ms on an S7-1200. The button CSS class should change to.pressedfor visual feedback. - Release the button. The tag must drop to
FALSE. The page may visibly refresh, but the release POST should be sent viasendBeaconimmediately before navigation. - Run a network capture (Wireshark) on the CPU port to confirm exactly two POSTs per click: one with body
"webdata".Motor_Start=1and one with=0. - Test on a touch device. Tap and hold; release. The
pointer*events handle touch identically to mouse. - Test keyboard operation: TAB to focus the button, then hold SPACE. The tag should toggle
1/0with the key state. - Stress test: rapid click sequence (10 presses in 2 seconds). The CPU should not drop a release POST; if it does, increase the S7-1500 maximum web connections in the device configuration or move to a dedicated HMI panel.
10. Troubleshooting Matrix
| Symptom | Likely Cause | Corrective Action |
|---|---|---|
| Tag never goes to 1 when button pressed | AWP_In_Variable declaration missing or tag name typo | Verify the <!-- AWP_In_Variable ... --> is the first line of the page and that the name exactly matches the symbolic PLC name |
| Tag sticks at 1 after release | Release POST never sent (page navigation interrupts fetch) |
Use navigator.sendBeacon for the release write, as in the sample above |
| HTTP 401 on every POST | User lacks write rights, or session cookie not sent | Confirm the user has "Write to PLC" in the web server user list; add credentials: 'include' in fetch options |
| HTTP 403 / 302 to login | CSRF or session timeout | Re-login; for production, extend session in web server config or implement a token |
| No visible response in browser | Browser blocks mixed content (HTTPS page calling HTTP POST or vice versa) | Use a single protocol end-to-end |
| Tag toggles randomly | Multiple event handlers bound; pointerdown fires twice on touch |
Call e.preventDefault() in handlers; ensure handlers are bound only once |
| S7-1200 extremely slow to respond | CPU under heavy load or too many simultaneous connections | Reduce polling; use S7-1500 for high-frequency control |
| Touch device never sees pointerup | Finger drifts off button; pointerleave never fires |
Add pointercancel handler and a global document.pointerup that always writes 0 |
11. Alternative Platforms and When to Use Them
The S7 web server is appropriate for low-frequency, local operator panels and for prototypes. For real production HMI, Siemens recommends either a SIMATIC HMI Panel (Unified Comfort or Basic) programmed in TIA Portal with WinCC, or a SCADA system such as WinCC Professional / WinCC OA. Both deliver robust, deterministic momentary controls without requiring the user to write JavaScript. A custom HTML page should be used only when:
- The CPU is the only device available (no panel budget).
- The control is non-safety and rate-limited (operator acknowledgements, slow jogging).
- The deployment is a local maintenance interface, not a primary operator panel.
For development without a physical CPU, use S7-PLCSIM (V16+) which emulates the web server and accepts the same AJAX POSTs, allowing the entire HTML5 page to be tested in a browser before hardware is available.
12. Reference: AWP Commands Used in This Page
| Command | Syntax | Purpose |
|---|---|---|
| AWP_In_Variable | <!-- AWP_In_Variable Name='"db".tag' --> |
Declare a variable whose value can be written from the web page via HTTP POST |
| AWP_Out_Variable | <!-- AWP_Out_Variable Name='"db".tag' --> |
Declare a variable that can be read into the page (substituted into the HTML) |
| AWP_Enum_Def | <!-- AWP_Enum_Def Name="State" Values="0:OFF,1:ON" --> |
Map integer values to display strings for combo boxes |
| AWP_Start_Form / AWP_End_Form | HTML form block | Mark the form whose fields are bound to the declared variables |
Complete reference: SIMATIC S7-1200 Programmable Controller - Web Server Function Manual (entry ID 59192925) and S7-1500 Web Server Function Manual (entry ID 59193592).
Can the S7-1200 web server run JavaScript?
JavaScript always runs in the browser, never inside the S7-1200. The CPU only serves the HTML file and answers POSTs. The S7-1200 supports HTML5 pages with AJAX, but it is slower than the S7-1500 and limits concurrent connections to about 4, so for high-frequency momentary control prefer the S7-1500.
Why does the tag stay at 1 after I release the button?
The release POST is lost because the browser begins navigating to the new page (returned by the press POST) and cancels the pending fetch. Use navigator.sendBeacon for the release write, or use two distinct page endpoints so the press and release POSTs do not collide with a navigation.
What HTTP status does the S7 web server return for a successful tag write?
A successful write returns HTTP 200 and the rendered page. A failed authentication returns 401, a missing or wrong tag returns 404, and an AWP parse error returns 500. Watch the network tab in the browser to confirm a 200 on every press and release.
Do I need a specific firmware version for AJAX support?
No firmware requirement is unique to AJAX because the S7 web server has always accepted standard application/x-www-form-urlencoded POSTs. However, S7-1200 firmware V4.4 and S7-1500 firmware V2.9 add connection-handling and TLS improvements that improve reliability of high-frequency AJAX calls.
Can I bind the same momentary button to a safety function?
No. The S7 web server is not a safety-rated interface, has no deterministic response time, and is exposed to the same IT attack surface as any other web service. SIL-rated emergency stop and enable controls must remain hardwired to a safety input module (F-CPU) and a certified E-stop device.