Node-RED Flows and Dashboard for ESP32 MQTT Sensors and Relays

Node-RED is a flow-based programming tool that runs on Node.js. Instead of writing a full server application, you drag nodes onto a canvas and wire them together: an MQTT input, a bit of JavaScript to reshape the message, a gauge on a web dashboard. It runs well on a Raspberry Pi next to Mosquitto, which together make a complete self-hosted IoT backend.

In this tutorial you will connect Node-RED to the Mosquitto broker, receive the JSON telemetry from the ESP32 MQTT publisher tutorial, show it on gauges and a chart, add a switch that sends relay commands back to the ESP32, and raise an alert when the temperature crosses a limit.

Installing Node-RED

Install a current LTS release of Node.js first. Then install Node-RED globally with npm and start it. On a Raspberry Pi, the Node-RED project also provides an install script that sets it up as a system service; see the Getting Started section of nodered.org.

Install and start Node-RED with npm
# Linux / Raspberry Pi (omit sudo on Windows)
sudo npm install -g --unsafe-perm node-red

# start it; the editor opens at http://<host-ip>:1880
node-red

For the dashboard, open the editor menu, choose Manage palette > Install, search for @flowfuse/node-red-dashboard and install it. This is Dashboard 2.0, the actively maintained successor to the older node-red-dashboard package, which is deprecated. Its widgets appear in the palette under the dashboard 2 category.

The Flow We Will Build

[mqtt in: home/node1/telemetry] -> [function: split] --out1--> [ui-gauge Temperature]
                                                   |                -> [ui-chart Temp history]
                                                   +--out2--> [ui-gauge Humidity]
                                                   +--out3--> [switch > 35] -> [delay 1 msg/5min] -> [ui-notification]

[ui-switch Fan] -> [mqtt out: home/relay1/set]
[mqtt in: home/relay1/state] -> [ui-switch Fan]   (keeps switch in sync)
NodePalette categoryKey settings
mqtt innetworkServer: broker IP, port 1883, username and password; Topic: home/node1/telemetry; Output: a parsed JSON object
functionfunctionOutputs: 3; code shown below
ui-gaugedashboard 2Range 0 to 50 for temperature, 0 to 100 for humidity; units °C and %
ui-chartdashboard 2Type: line; X-axis: timestamp; keep data for a time window you choose
switchfunctionProperty msg.payload, rule > 35
delayfunctionAction: rate limit, 1 msg per 5 minutes, drop intermediate messages
ui-notificationdashboard 2Shows a pop-up on the dashboard page
ui-switchdashboard 2On payload string ON, off payload string OFF
mqtt outnetworkTopic: home/relay1/set, QoS 1, retain false

Function Node Code

The MQTT input already parses the JSON, so msg.payload is an object like {temp_c: 28.4, hum_pct: 61}. A function node with three outputs returns an array; each element goes to the matching output, and null sends nothing on that output.

Function node 'split': route temperature, humidity and alert messages
// msg.payload = { temp_c: 28.4, hum_pct: 61.2, rssi: -58 }
const p = msg.payload;

// Ignore anything that is not a valid reading
if (typeof p !== 'object' || typeof p.temp_c !== 'number') {
    node.warn('Bad payload: ' + JSON.stringify(p));
    return null;
}

// Output 1: temperature for the gauge and the chart
const temp = { payload: p.temp_c, topic: 'Temperature' };

// Output 2: humidity for its gauge
const hum = { payload: p.hum_pct, topic: 'Humidity' };

// Output 3: raw value for the alert switch node
const alert = { payload: p.temp_c,
                topic: 'High temperature: ' + p.temp_c + ' °C' };

// Remember the last value so other flows can use it
flow.set('lastTemp', p.temp_c);

node.status({ fill: 'green', shape: 'dot', text: p.temp_c + ' °C' });
return [temp, hum, alert];

The topic of each message becomes the series name in the chart, so you can feed several sensors into one chart and get a separate line for each. node.status() shows a small label under the node in the editor, which is very handy for debugging without opening the debug panel.

Before the notification node, add a change node that sets msg.payload to msg.topic, so the pop-up shows the readable text rather than just the number.

Sending Commands Back to the ESP32

The ui-switch node emits ON or OFF when the user taps it, and the mqtt out node publishes that to home/relay1/set. The ESP32 subscribes to that topic, switches the relay, and publishes its real state to home/relay1/state as a retained message. Wiring that state topic back into the switch's input keeps the dashboard honest: if the ESP32 is offline, the switch does not falsely show ON.

In the ui-switch properties, disable the option that passes input messages straight through to the output. Otherwise every state message from the ESP32 would be republished as a command, creating a loop.

Deploy and view

  1. Create a ui-base, a ui-page (path /home) and a ui-group named "Living Room" when the first widget asks for a group.
  2. Click Deploy. Both MQTT nodes should show a green "connected" status.
  3. Open http://<host-ip>:1880/dashboard on a PC or phone on the same network.
  4. Use a debug node on any wire while building; its output appears in the debug sidebar.

Troubleshooting

  • MQTT node stays "connecting": wrong broker IP or credentials. Remember that inside Node-RED on the same Pi, localhost works as the broker address.
  • Gauge shows NaN or nothing: the payload is still a string. Set the mqtt in output to "a parsed JSON object" or add a json node.
  • Chart stays empty: the payload is not a number, or the X-axis type does not match the data. Keep the X-axis as timestamp and send numeric payloads only.
  • Alert floods the screen: the rate-limit delay node is missing or set to queue rather than drop intermediate messages.
  • Editor is open to the whole network: enable adminAuth in Node-RED's settings.js and set a password before exposing port 1880 beyond your LAN.

Summary

Node-RED connects to Mosquitto with an mqtt in node, reshapes data with short JavaScript function nodes, and presents it on a Dashboard 2.0 page with gauges, charts, switches and notifications. With state fed back from the device and rate-limited alerts, you have a practical, self-hosted control panel for any number of ESP32 nodes.