This workshop walks you through using the NIG stack — Node-RED, InfluxDB and Grafana — to collect, store and visualise data. By the end you will have published numbers to your own MQTT broker, subscribed to them, cleaned them up, stored them in a time series database, and plotted them on a dashboard.
You don't need any hardware. We'll generate test values inside Node-RED itself. Once the pattern is clear, swap the test source for a real sensor and the rest of the flow stays the same.
Your instructor has already started an instance for you and will hand you the URLs and login. This page is only about using the three tools together.
This combination — sometimes called the NIG stack — is one of the standard ways to do IoT data processing.
A complete data pipeline:
[inject node]──►[mqtt out]──┐
│ test/value
[aedes broker]◄─────────────┤
│ test/value
[mqtt in]──►[clean payload]─┴──►[influxdb out]──►[ Grafana panel ]
This round-trip (publish → subscribe → store) is artificial for the workshop, but it's exactly the shape of a real IoT pipeline: a sensor publishes to MQTT, Node-RED subscribes, transforms and stores, Grafana visualises.
Your instructor will give you:
Open the Node-RED URL and log in. You'll see:
The message object is called msg. The actual data usually lives on msg.payload. Other useful fields appear as you go (msg.topic for MQTT, msg.error for errors, …), but msg.payload is the one you care about 90 % of the time.
Try it before continuing: drag an inject node and a debug node onto the canvas, wire them together, hit Deploy, and click the inject node's button. You should see a timestamp appear in the debug sidebar. That's a complete Node-RED flow.
The base Node-RED installation doesn't include MQTT broker or InfluxDB support, so we install them from the palette manager.
node-red-contrib-aedes → click Install. This adds an embedded MQTT broker.node-red-contrib-influxdb → click Install. This adds the InfluxDB client nodes.You should now see new nodes in the palette: an aedes broker node (under “network”) and influxdb in / influxdb out / influxdb batch nodes (under “storage”).
Aedes is a full MQTT broker that runs inside Node-RED itself. No separate Mosquitto installation, no external service — your Node-RED is the broker.
1881Your broker is now reachable in two ways:
mqtt in and mqtt out nodes we'll add next) — at localhost:1881.1881 on the inside; just use what you were given.Since the broker has no authentication, treat it as untrusted if you ever wire up a real device.
We'll generate fake “sensor” readings using an inject node and publish them to a topic on your broker.
23.5 (any number you like).5 seconds (so it auto-publishes; uncheck to publish only when you click the button).localhost1881test/value0
Click the inject node's button (or wait for the 5-second interval). The mqtt out node should show “connected” underneath it. Your broker is now receiving messages on test/value.
Publishing without subscribing is shouting into the void. Let's listen.
localhost:1881).test/value0msg.payload in the sidebar).mqtt in → debug.23.5 appear, over and over.Congratulations — you've just sent a message through your own broker and received it back. The MQTT layer works.
If nothing shows up, check (in this order): (1) is the debug node enabled? (the small green dot next to it should be lit), (2) is the mqtt in node “connected”? (status underneath), (3) is the topic in mqtt in exactly the same as in mqtt out — including upper/lowercase? (4) did you Deploy?
This step looks fussy but it's important. InfluxDB cares about types: a value stored as the string “23.5” cannot be averaged, charted on a numeric axis, or compared to thresholds. It needs to be a number.
When the mqtt in node is set to auto-detect, a payload that looks like a number is already parsed as one — but only if it looks numeric. Sensors that send “23.5°C” or “value: 23.5” would land as strings. To be safe and explicit, convert it yourself.
msg.payload to the value $number(payload)mqtt in → change → debug (re-route through the change node).number, not string.
The $number() expression is a JSONata function that coerces whatever it's given into a number, throwing an error if it can't. This catches malformed data at the right place: the flow stops with a visible error instead of silently writing garbage to the database.
For real sensor data with multiple fields — e.g. a payload like {“temperature”: 23.5, “humidity”: 60} — keep msg.payload as an object instead of a single number. Each key becomes its own InfluxDB field automatically (see the next step). The cleaning step then becomes “make sure each value is a number”, which you can do with a small function node:
msg.payload = { temperature: Number(msg.payload.temperature), humidity: Number(msg.payload.humidity) }; return msg;
The node-red-contrib-influxdb package added an influxdb out node that takes msg.payload and writes it to the database.
You're connecting to InfluxDB 1.x, which is much simpler to configure than 2.x or 3.x — no tokens, no organisations, just a database name and a username/password.
1.xinfluxdb8086dbworkshop (a measurement is roughly what a table is in SQL — pick any name; you'll use it again in Grafana).change → influxdb out. Your flow should now read:[mqtt in]──►[change: $number]──┬──►[debug]
└──►[influxdb out]
The influxdb out node should show “connected” underneath. After a few seconds (and a few inject firings) your data is in the database.
Why is the host influxdb and not localhost?
InfluxDB doesn't run inside Node-RED — it's a separate service that Node-RED reaches over the network by its name, influxdb. From inside your Node-RED, localhost means “Node-RED itself”, which is why MQTT in/out earlier used localhost (the Aedes broker is inside Node-RED). For the database, use influxdb. The same hostname works in Grafana, in the next section.
You won't interact with InfluxDB's own UI in this workshop — InfluxDB 1.x doesn't ship a friendly one, and you'd query through Grafana anyway. Just know that:
value or temperature) and optional tags (string labels for filtering, e.g. location=lab1). Every point has a timestamp, which InfluxDB assigns automatically if you don't.value in the workshop measurement. If you'd sent an object like {temperature: 23.5, humidity: 60}, each key would be its own field.
InfluxDB 1.x is end-of-life. We use it here because it's the simplest version to configure for a teaching environment. For anything you build after this workshop, use TimescaleDB or InfluxDB 3.x instead.
TimescaleDB is PostgreSQL with a time-series extension — if you already know SQL, you already know it. InfluxDB 3.x is a complete rewrite with a different query model again (SQL via Flight SQL); it's faster and actively maintained, but migrating 1.x dashboards/queries to it is non-trivial.
Open your Grafana URL and log in with the same credentials as Node-RED.
A data source tells Grafana where to read data from. Yours may already be set up — if so, walk through this section anyway, because adding a data source is a skill you'll use on every Grafana instance you ever touch.
InfluxQL (because we run InfluxDB 1.x; the Flux option is for 2.x).http://influxdb:8086dbGETYou're now in the panel editor. The graph fills the top; the bottom half has tabs for the query, with panel options on the right.
For InfluxQL the query builder is point-and-click:
workshop (the measurement you wrote to in Node-RED).field(value) — keep the default mean() aggregator, or change it to last() if you'd rather see the raw last reading per time bucket.time($__interval) fill(null) (the default).The graph should immediately show your injected values, one point every few seconds.
If you used multiple fields (the {temperature, humidity} object from the tip earlier), click + next to SELECT to add a second field, or add a second query (the + Query button below) so each line is independent.
On the right side panel, useful starting points:
Click Apply (top right) to drop the panel onto the dashboard, then Save dashboard to persist it. Give it a name.
Top right of the dashboard, you can change the visible range (Last 5 minutes, Last 24 hours, …) and the auto-refresh interval. For a live demo of inject → MQTT → InfluxDB, “Last 5 minutes” with a 5-second refresh is satisfying.
You now have a working end-to-end pipeline. The natural next steps:
lab1/room2/temperature). MQTT wildcards (lab1/#) let one mqtt in subscribe to a whole subtree.$number() — converting raw ADC counts to engineering units, smoothing, alerting on out-of-range values.Here you can find more information: Details on the used Cloud Infrastructure