Smart Manufacturing

Real-Time SPC with MQTT: Live Control Charts and Out-of-Control Alerts

Stream measurements over MQTT, compute X-bar and R control limits in Python, apply Western Electric rules to every new subgroup and show out-of-control points on a live dashboard within seconds.

Real-time SPC control chart with upper and lower control limits and highlighted out-of-control points streamed over MQTT
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

Real-time SPC moves statistical process control from a weekly spreadsheet exercise to a live signal on the shop floor. In this tutorial you will stream measurements over MQTT, compute X-bar and R control limits in Python, check every new subgroup against the Western Electric rules and display out-of-control points on a live dashboard within seconds of the measurement.

Why SPC Should Be Real Time

Statistical process control separates normal, common-cause variation from special causes such as a worn tool, a new material batch or a temperature change. Its purpose is to react before parts go out of specification. That purpose is defeated if charts are updated at the end of the shift from manually typed values: by then the process may have drifted for hours.

When measurements already flow from in-line gauges, as described in IoT quality control in manufacturing, there is no reason to wait. A small service can maintain the control chart continuously and alert the operator on the first rule violation.

SPC Basics in Five Minutes

This tutorial uses the classic X-bar and R chart, suited to measurements taken in small subgroups (here five consecutive parts).

For each subgroup you calculate:

  • X-bar: the mean of the five values. It tracks the process centre.
  • R: the range (max minus min). It tracks short-term variation.

From an initial set of in-control subgroups (commonly 20 to 25) you calculate the grand mean (X-double-bar) and average range (R-bar), then the control limits using standard constants for subgroup size 5:

Chart Centre line Upper control limit Lower control limit
X-bar X-double-bar X-double-bar + A2 x R-bar X-double-bar - A2 x R-bar
R R-bar D4 x R-bar D3 x R-bar

For n = 5 the constants are A2 = 0.577, D3 = 0 and D4 = 2.114.

Control limits come from the process itself, not from the specification. A process can be in control and still produce out-of-spec parts if it is not capable, and it can be out of control while every part is still within tolerance. That second case is exactly where real-time SPC pays off.

Rules for out-of-control signals

Rule Signal Typical cause
1 One point beyond a control limit Sudden special cause: wrong setting, broken tool
2 Two of three consecutive points beyond 2 sigma on the same side Shift starting
3 Four of five consecutive points beyond 1 sigma on the same side Smaller sustained shift
4 Eight consecutive points on the same side of the centre line Process mean has moved

These are the Western Electric rules. Applying all four increases sensitivity but also the false alarm rate, so many plants start with rules 1 and 4.

Architecture

gauge / simulator --> .../station40/measurement --> MQTT broker
                                                       |
                                    SPC service (Python) subscribes
                                                       |
             .../station40/spc/limits (retained)  +  .../station40/spc/point
                                                       |
                                       live dashboard subscribes (WSS)

Everything runs on the public MQTT test broker at broker.connectmq.com for the tutorial. Pick a random ID and use it in every topic so your data does not mix with anyone else's.

Step 1: Simulate a Measuring Station

If you do not yet have a connected gauge, this simulator publishes a bore diameter twice per second. After 200 parts it introduces a slow upward drift, the kind caused by tool wear or thermal growth.

import json
import random
import time
from datetime import datetime, timezone
import paho.mqtt.client as mqtt

TOPIC = "connectmq/demo/3d9c57/line3/station40/measurement"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="spc-sim-3d9c57")
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()

part = 0
while True:
    part += 1
    drift = max(0, part - 200) * 0.00004
    value = random.gauss(22.000 + drift, 0.003)
    msg = {"ts": datetime.now(timezone.utc).isoformat(timespec="milliseconds"),
           "serial": f"A3-{part:06d}", "value": round(value, 4)}
    client.publish(TOPIC, json.dumps(msg), qos=1)
    time.sleep(0.5)

Step 2: The Real-Time SPC Service

The service collects subgroups of five, uses the first 25 subgroups to calculate control limits (Phase I), publishes the limits as a retained message and then evaluates every new subgroup against the rules (Phase II). Each evaluated subgroup is published as a point with any violations attached.

import json
import statistics
from collections import deque
import paho.mqtt.client as mqtt

BASE = "connectmq/demo/3d9c57/line3/station40"
N = 5
PHASE1_SUBGROUPS = 25
A2, D3, D4 = 0.577, 0.0, 2.114

buffer = []
phase1 = []
limits = None
history = deque(maxlen=8)  # recent X-bar values

def zone(x):
    sigma = (limits["ucl_x"] - limits["cl_x"]) / 3
    return (x - limits["cl_x"]) / sigma

def check_rules(xbar, r):
    v = []
    if not limits["lcl_x"] <= xbar <= limits["ucl_x"]:
        v.append("rule1_xbar")
    if r > limits["ucl_r"]:
        v.append("rule1_range")
    z = [zone(x) for x in history]
    for side in (1, -1):
        if len(z) >= 3 and sum(1 for s in z[-3:] if s * side > 2) >= 2:
            v.append("rule2")
        if len(z) >= 5 and sum(1 for s in z[-5:] if s * side > 1) >= 4:
            v.append("rule3")
        if len(z) == 8 and all(s * side > 0 for s in z):
            v.append("rule4")
    return sorted(set(v))

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(f"{BASE}/measurement", qos=1)

def on_message(client, userdata, msg):
    global limits
    buffer.append(json.loads(msg.payload))
    if len(buffer) < N:
        return
    values = [m["value"] for m in buffer]
    serials = [buffer[0]["serial"], buffer[-1]["serial"]]
    buffer.clear()
    xbar, r = statistics.fmean(values), max(values) - min(values)

    if limits is None:
        phase1.append((xbar, r))
        if len(phase1) == PHASE1_SUBGROUPS:
            xbb = statistics.fmean(x for x, _ in phase1)
            rbar = statistics.fmean(rr for _, rr in phase1)
            limits = {"cl_x": xbb, "ucl_x": xbb + A2 * rbar, "lcl_x": xbb - A2 * rbar,
                      "cl_r": rbar, "ucl_r": D4 * rbar, "lcl_r": D3 * rbar, "n": N}
            client.publish(f"{BASE}/spc/limits", json.dumps(limits), qos=1, retain=True)
            print("limits", limits)
        return

    history.append(xbar)
    violations = check_rules(xbar, r)
    point = {"xbar": round(xbar, 5), "range": round(r, 5),
             "serials": serials, "violations": violations,
             "in_control": not violations}
    client.publish(f"{BASE}/spc/point", json.dumps(point), qos=1)
    if violations:
        client.publish(f"{BASE}/spc/alarm", json.dumps(point), qos=1, retain=True)
        print("OUT OF CONTROL", point)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="spc-service-3d9c57")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()

Run the simulator and the service in two terminals. After 125 parts (about a minute) the limits appear. Shortly after part 200 the drift starts, and within roughly a hundred parts you will typically see rule 4, rule 3 or rule 2 violations, while every individual part is still well within a typical specification. That is the early warning SPC is designed to give.

Step 3: A Live SPC Dashboard

Any MQTT-capable dashboard can subscribe to spc/limits and spc/point. The MQTT.js script below is a minimal live view that connects over Secure WebSocket, exactly as a browser dashboard would, and prints each subgroup as a position between the control limits, marking violations. Install it with npm install mqtt and run it with Node.js 18 or later.

const mqtt = require("mqtt");

const BASE = "connectmq/demo/3d9c57/line3/station40/spc";
const client = mqtt.connect("wss://broker.connectmq.com:8084/mqtt", {
  clientId: "spc-dash-3d9c57",
  protocolVersion: 5,
});

let limits = null;

client.on("connect", () => {
  client.subscribe([`${BASE}/limits`, `${BASE}/point`], { qos: 1 });
  console.log("connected, waiting for limits...");
});

client.on("message", (topic, payload) => {
  const data = JSON.parse(payload.toString());
  if (topic.endsWith("/limits")) {
    limits = data;
    console.log(`LCL ${data.lcl_x.toFixed(4)}  CL ${data.cl_x.toFixed(4)}  UCL ${data.ucl_x.toFixed(4)}`);
    return;
  }
  if (!limits) return;
  const width = 41;
  const span = limits.ucl_x - limits.lcl_x;
  let pos = Math.round(((data.xbar - limits.lcl_x) / span) * (width - 1));
  const outside = pos < 0 || pos > width - 1;
  pos = Math.min(Math.max(pos, 0), width - 1);
  const line = Array(width).fill(" ");
  line[Math.floor(width / 2)] = "|";
  line[pos] = outside ? "X" : data.in_control ? "o" : "!";
  const flags = data.violations.length ? data.violations.join(",") : "";
  console.log(`[${line.join("")}] ${data.xbar.toFixed(4)} R=${data.range.toFixed(4)} ${flags}`);
});

Because the limits are retained, the dashboard draws correctly even if it starts long after the SPC service. In a web page, the same code runs with the browser build of MQTT.js and feeds a charting library instead of the console. The ConnectMQ portal's live topic explorer is also handy for watching spc/point and spc/alarm while you tune rules.

Reacting to Real-Time SPC Signals

A chart that nobody acts on is decoration. Before switching alarms on, agree with the line team what each signal means and what happens next. A rule 1 violation on the X-bar chart usually calls for an immediate check of the last parts and the machine setting. A rule 4 run of points on one side of the centre line points to a gradual shift, such as tool wear or thermal growth, so the reaction might be a tool offset adjustment or an earlier tool change. A rule 1 violation on the R chart means variation has increased, which often indicates a loose fixture, a damaged tool edge or inconsistent material.

Write these reactions into a short out-of-control action plan per characteristic and show it next to the chart. Correlating SPC signals with spindle data from tool wear monitoring is a good way to confirm whether a shift is tool related before anyone touches the machine. Over time, the recorded causes and reactions become the most valuable part of the system, because they show which special causes recur and which process improvements would remove them permanently.

Making It Production-Ready

  1. Persist limits. Store calculated limits in a database or configuration file, and recalculate only deliberately after a process change, never automatically during a drift.
  2. Exclude known special causes from Phase I data. If a tool broke during the baseline period, remove those subgroups before computing limits.
  3. One chart per characteristic and recipe. Different products need different limits; include the recipe in the topic or payload.
  4. Capture the reaction. When an alarm fires, record what the operator found and did. SPC without a reaction plan is just a chart.
  5. Use a private broker. Quality data is confidential. Move to a hosted MQTT broker with credentials from the portal and TLS on port 8883 or WSS on 8084.
  6. Use QoS 1 for measurements so subgroups are not formed from incomplete data after a network glitch.

Real-time SPC also links naturally to traceability: because every point carries the serial range of its subgroup, you know exactly which parts were produced while the process was out of control. The manufacturing traceability system guide shows how to store that relationship. For a wider view of plant data architecture, see the smart manufacturing guide.

Stream Your SPC Data Through ConnectMQ

ConnectMQ gives your gauges, SPC service and dashboards one secure MQTT 5 backbone with TLS, WebSocket access for browser dashboards, retained messages and a live topic explorer. Start on the free Serverless plan at app.connectmq.com, no credit card required.

Frequently Asked Questions

What is real-time SPC?

It is statistical process control where control charts and rule checks are updated automatically as each measurement arrives, so out-of-control conditions are flagged within seconds instead of at the end of a shift.

How many subgroups do I need to calculate control limits?

20 to 25 subgroups from a stable process is the common guideline. Remove subgroups affected by known special causes before calculating.

Are control limits the same as specification limits?

No. Control limits describe what the process actually does, while specification limits describe what the customer requires. They are calculated and used differently.

Which Western Electric rules should I enable first?

Start with one point beyond the limits and eight points on one side of the centre line. Add the zone rules once operators trust the alarms.

Related articles

OEE vs TEEP vs OOE: Which Metric Should You Use?

OEE, OOE and TEEP share the same performance and quality factors but measure against different time bases. This comparison works through one example and lists the MQTT signals each metric needs.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.