Geotechnical slope monitoringServer (Node-RED)HTTPHMI / Dashboard
A multi-station dashboard with gauges and LEDs in Node-RED
A Node-RED dashboard can replace a small SCADA when what you need is visibility, not control. This example renders four remote ESP32 PLC 14 Ethernet stations on one screen: a gauge per station for the alert level 0-4, text widgets for each input channel, and one LED per station that turns red when data stops arriving. It mirrors a real slope monitoring deployment that runs 60 ui-text widgets, 31 gauges and 4 LEDs on a Raspberry Pi.
One function node, three kinds of widgets
The "preparar dashboard" function has three outputs. Output 1 feeds the ui-gauge with the numeric alert level; output 2 sends an array of messages, one per input channel, so a single node refreshes every ui-text widget at once; output 3 drives the ui-led with
msg.color. Topics like estacion/31/gauge keep routing explicit.The LED that watches the watcher
Each station LED is green, yellow or red by alert level — but it also checks the timestamp of the last reading stored in flow context. More than three minutes of silence and the LED goes red regardless of the last known level. A frozen dashboard that still looks green is the most dangerous failure mode of any HMI.
Scaling tabs, not flows
Each station gets its own dashboard tab plus one summary tab showing the worst level across the fleet. Because widgets filter by topic, adding a station means cloning a tab and adding an entry to the polling list — the function code stays untouched.
A snippet from the implementation
Straight from the example as deployed on the Server (Node-RED) — copy it freely:
const station = msg.station;
const level = msg.alert ?? 0;
const inputs = msg.inputs || {};
const ts = msg.ts || new Date().toISOString();
// --- Output 1: alert level gauge (range 0-4) --------------------------
const msgGauge = {
topic: `station/${station}/gauge`,
payload: level
};
// --- Output 2: one ui-text per input + summary text --------------------
// node.send() accepts arrays: every element of the inner array goes out
// the same output, so we feed several ui-text widgets at once.
const textMsgs = Object.keys(inputs).map(channel => ({
topic: `station/${station}/text/${channel}`,
payload: inputs[channel] ? "ACTIVE" : "idle"
}));
textMsgs.push({
topic: `station/${station}/text/summary`,
payload: `Level ${level} — ${ts.substring(11, 19)}`
});
// --- Output 3: station status LED ---------------------------------------
// Green = normal (0-1), yellow = attention (2-3), red = critical (4).
// If the station has not reported in >3 min, the LED blinks red.
const COLORS = ["green", "green", "yellow", "yellow", "red"];
const states = flow.get("states") || {};The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.
Frequently asked questions
Which dashboard package does this use?
node-red-dashboard for gauges and text plus node-red-contrib-ui-led for the status LEDs. Both install from the palette manager in one click.
Can a Raspberry Pi handle 90+ widgets?
Yes. The widgets only update when a message arrives, so four stations reporting every 60 seconds is a very light load. The browser rendering the dashboard does more work than the Pi serving it.
How do I show that a station is offline rather than just calm?
Store the timestamp of each reading in flow context and compare against the clock when refreshing. In this example the LED turns red and the node status shows SIN DATOS after three minutes without a report.