A production line monitoring dashboard turns raw machine signals into one screen that tells supervisors whether the line is running, how many good parts it has made and where it is losing time. In this tutorial you will publish station data over MQTT, store it with Telegraf and InfluxDB, and build the dashboard in Grafana, with a lighter Node-RED alternative for small lines. Everything runs against a real broker, so you can follow along from a laptop.
What a Good Production Line Dashboard Shows
Before wiring anything, decide what the screen must answer. Most line dashboards fail because they display every tag the PLC exposes instead of the handful of questions people actually ask during a shift.
| Question on the floor | Signal you need | Typical widget |
|---|---|---|
| Is each station running right now? | state (RUNNING, IDLE, DOWN, SETUP) |
Colour-coded status tiles |
| Are we on plan for the shift? | Good count, plan count | Big number plus progress bar |
| Where is scrap coming from? | Reject count per station | Bar chart by station |
| Which station is the bottleneck? | Actual rate vs ideal rate | Table sorted by rate gap |
| How healthy is the line overall? | Availability, performance, quality | OEE gauge with three sub-bars |
| What stopped us last hour? | State changes with timestamps | State timeline |
Keep the first version to these six. Supervisors will ask for more once they trust the numbers, and trust comes from the counts matching what they see on the floor.
Architecture: From Station to Screen
The data path is short and every piece is replaceable:
- Each station (PLC gateway, edge PC or microcontroller) publishes a small JSON status message to an MQTT broker whenever its state changes and at least every few seconds.
- Telegraf subscribes to the line's topics and writes every message into InfluxDB as a time series.
- Grafana queries InfluxDB and renders the panels.
- Optionally, a Node-RED flow subscribes to the same topics and drives a lightweight dashboard on a TV beside the line.
The broker is the hub that decouples stations from consumers. If you are new to that role, this explainer on what an MQTT broker does covers the basics. Because every consumer subscribes independently, you can add a historian, an andon board or an OEE calculator later without touching the stations.
Step 1: Design the Topic Structure
A predictable topic tree saves hours later. Use an ISA-95 style hierarchy so the dashboard can filter by line and station with wildcards:
connectmq/demo/<your-id>/line1/<station>/status
For example connectmq/demo/a7f3k9/line1/filler/status. The payload is a single JSON object:
{
"state": "RUNNING",
"good": 1842,
"reject": 17,
"rate": 58.5,
"ideal_rate": 60,
"ts": 1792137600
}
Counts are cumulative for the shift, which makes the data robust: if a message is lost, the next one still carries the correct total. Rates are parts per minute. Add an enterprise and site level in front of the line when you roll out to more than one plant.
Step 2: Simulate the Stations
To build the dashboard before the real line is connected, run this Python simulator. It uses paho-mqtt 2.x and the free public MQTT broker at broker.connectmq.com. Replace a7f3k9 with your own random ID, because the public area is readable by everyone.
import json
import random
import time
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/a7f3k9/line1"
STATIONS = {"filler": 60, "capper": 62, "labeler": 65, "packer": 58}
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="line1-sim-a7f3k9")
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()
counters = {name: {"good": 0, "reject": 0, "state": "RUNNING"} for name in STATIONS}
try:
while True:
for name, ideal in STATIONS.items():
c = counters[name]
# Occasionally change state to make the dashboard interesting
roll = random.random()
if c["state"] == "RUNNING" and roll < 0.03:
c["state"] = random.choice(["DOWN", "IDLE"])
elif c["state"] != "RUNNING" and roll < 0.25:
c["state"] = "RUNNING"
rate = 0.0
if c["state"] == "RUNNING":
rate = round(ideal * random.uniform(0.85, 1.0), 1)
made = int(rate / 12) # roughly 5 seconds of production
rejects = 1 if random.random() < 0.04 else 0
c["good"] += made - rejects
c["reject"] += rejects
payload = {
"state": c["state"],
"good": c["good"],
"reject": c["reject"],
"rate": rate,
"ideal_rate": ideal,
"ts": int(time.time()),
}
client.publish(f"{PREFIX}/{name}/status", json.dumps(payload), qos=1, retain=True)
time.sleep(5)
except KeyboardInterrupt:
client.loop_stop()
client.disconnect()
Four stations publishing every five seconds stays well inside the public broker limit of 10 messages per second per client. The retain flag means a dashboard that connects mid-shift immediately receives the last known state of every station. Verify the stream from a terminal:
mosquitto_sub -h broker.connectmq.com -p 1883 -i viewer-a7f3k9 -t "connectmq/demo/a7f3k9/line1/+/status" -v
Step 3: Store the Data with Telegraf and InfluxDB
Telegraf's MQTT consumer turns each message into an InfluxDB point, and topic parsing extracts the line and station as tags. Save this as telegraf.conf:
[agent]
interval = "5s"
flush_interval = "5s"
[[inputs.mqtt_consumer]]
servers = ["tcp://broker.connectmq.com:1883"]
topics = ["connectmq/demo/a7f3k9/line1/+/status"]
client_id = "telegraf-a7f3k9"
qos = 1
data_format = "json"
json_string_fields = ["state"]
json_time_key = "ts"
json_time_format = "unix"
[[inputs.mqtt_consumer.topic_parsing]]
topic = "connectmq/demo/+/+/+/status"
measurement = "_/_/_/_/_/measurement"
tags = "_/_/_/line/station/_"
[[outputs.influxdb_v2]]
urls = ["http://localhost:8086"]
token = "YOUR_INFLUX_TOKEN"
organization = "plant"
bucket = "line"
Each point lands in the status measurement with tags line=line1 and station=filler, plus fields state, good, reject, rate and ideal_rate. For production, switch the server to ssl://broker.connectmq.com:8883 and add username = "YOUR_USERNAME", password = "YOUR_PASSWORD" and your issued client_id. More options are covered in the Telegraf MQTT consumer guide.
Step 4: Build the Production Line Monitoring Dashboard in Grafana
Add InfluxDB as a data source in Grafana (Flux query language) and create these panels.
Station status tiles. Use a Stat panel with value mappings: RUNNING to green, IDLE to yellow, DOWN to red, SETUP to blue.
from(bucket: "line")
|> range(start: -5m)
|> filter(fn: (r) => r._measurement == "status" and r._field == "state")
|> last()
|> group(columns: ["station"])
Good parts this shift. Because counts are cumulative, the last value is the total:
from(bucket: "line")
|> range(start: today())
|> filter(fn: (r) => r._measurement == "status" and r._field == "good" and r.station == "packer")
|> last()
Use the last station on the line (here the packer) as the line output; counting at every station double-counts.
Rate vs ideal. A Time series panel with rate and ideal_rate per station shows the bottleneck at a glance: the station whose actual rate sits furthest below its ideal while running.
State timeline. Grafana's State timeline panel fed by the state field gives a bar per station coloured by state across the shift, which is the fastest way to see when and where the line stopped.
OEE gauge. For a first version, compute OEE in a separate subscriber and publish the result to line1/oee, then chart that topic. The maths is explained in how to calculate OEE, and the real-time OEE monitoring tutorial contains a ready subscriber. Doing the calculation once, outside Grafana, keeps every screen showing the same number.
Node-RED Alternative for a Single Line
If you only need a TV beside one line and do not want a time-series database, Node-RED is quicker. Configure an mqtt-broker config node with server broker.connectmq.com and port 1883 (or 8883 with TLS and your credentials in production), then add:
- An
mqtt innode subscribed toconnectmq/demo/a7f3k9/line1/+/status, output set to a parsed JSON object. - A
functionnode that extracts the station name frommsg.topic.split("/")[4]and routes it to the right widget. - Dashboard 2.0
ui-textwidgets for state,ui-gaugewidgets for rate and aui-chartfor good counts.
The trade-off is history: Node-RED shows the present well but is not a historian. Many plants run both, with Node-RED on the line display and Grafana for the shift review. The Node-RED OEE dashboard article goes further with this approach.
Common Pitfalls and How to Fix Them
- Counts reset to zero mid-shift. A PLC or gateway restart clears its counter. Store the shift baseline at the edge, or have the dashboard use
difference()withnonNegative: trueso resets do not create negative production. - Tiles show stale RUNNING. If a station goes offline, its retained message still says RUNNING. Add the
tsfield to the tile and colour it grey when older than 30 seconds, or use an MQTT Last Will message that publishes OFFLINE. - Duplicate client IDs. Two Telegraf instances with the same client ID will keep kicking each other off. Every client needs a unique ID.
- Too many panels. If the screen needs scrolling on the floor TV, it is too busy. Move detail to a drill-down dashboard.
Moving from Demo to Production
The public broker is perfect for building, but its topics are visible to anyone. For a real line, move to a private broker: a managed MQTT broker removes the job of patching and scaling a server, and a cloud MQTT broker lets remote supervisors and the head office view the same dashboard. With ConnectMQ you create the broker in the portal, get a username, password and client ID, and change only the connection settings in the simulator, Telegraf and Node-RED. The portal's live topic explorer is handy for confirming that every station is publishing before you debug a blank Grafana panel.
The free Serverless plan supports up to 25 connections, which covers a pilot line with its gateway, Telegraf and a couple of dashboards. Larger plants typically move to the paid Serverless plan or a Dedicated cluster as more lines come online. For the wider strategy, see the smart manufacturing guide.
Get Started with ConnectMQ
Create a free broker at app.connectmq.com on the Serverless plan with no credit card, point your stations and Telegraf at it, and have a production line dashboard running on private infrastructure today.
Frequently Asked Questions
Should I use Grafana or Node-RED for a production line dashboard?
Use Grafana when you need history, shift comparisons and many users; use Node-RED for a quick live display on one line. Many teams run both from the same MQTT topics.
How often should stations publish status?
Publish immediately on every state change and send a heartbeat every 5 to 10 seconds. That keeps tiles current without flooding the broker.
Can I connect PLCs directly?
Most PLCs need an edge gateway (Node-RED, a protocol converter or the PLC's own MQTT client) that reads tags and publishes JSON to the broker. Once the data is on MQTT, the dashboard does not care where it came from.
Is the public broker safe for real production data?
No. The public area of broker.connectmq.com is readable by anyone, so use it only for tests and move production data to a private ConnectMQ broker with credentials and TLS.



