8-Channel Relay Control with Node-RED and MQTT State Feedback
An eight-channel relay board is useful for a multi-output automation project, but adding eight switches to a dashboard is only the UI part. The system also needs a clear command format, real device feedback, a predictable restart policy and electrical protection suitable for the load.
This guide describes the architecture and message contract for a Node-RED dashboard that controls a relay controller over MQTT. It is not a mains-wiring tutorial; check the exact relay module, driver, isolation and load rating before connecting real equipment. Develop the topic and state logic with LEDs or low-voltage test loads first.
1. Define commands and reported state separately
Use a topic namespace for a device, such as solderhub/lab/relaybox01. A command topic may be solderhub/lab/relaybox01/command/channel/1, while the reported state is solderhub/lab/relaybox01/state/channel/1. Repeat for channels 1–8, or send a documented JSON object if your firmware and subscriber design support it.
Keep the command payload simple and explicit, such as ON and OFF or a strictly validated 0/1. Do not use TOGGLE as the only command contract: repeating a toggle after a retry or reconnect can reverse the intended state. Make the firmware publish the output it actually applied to the state topic after validating each command.
2. Build the dashboard
For a new UI, use FlowFuse Dashboard 2.0 (@flowfuse/node-red-dashboard), not the deprecated legacy Dashboard package. Create a page with eight clearly labelled switches and a visible relay-controller connection indicator. Keep channel labels descriptive—“Workshop light” is more useful than “Switch 3”—but document their physical mapping.
Wire each switch to a Change/Function path that validates the requested value and chooses the correct topic. Do not let a browser widget construct arbitrary MQTT topics. A fixed allow-list of eight known topics is easier to reason about and prevents accidental publishing to an unintended output.
3. Validate commands before publishing
A Function node can normalize an allowed switch value into a two-state command. Treat the widget's payload type as a contract: inspect it in Debug first. If the widget emits Boolean or numeric state, convert it deliberately rather than accepting every truthy value. Route invalid input to a warning/debug branch and do not publish a command.
After publishing, update the UI from the controller's reported state rather than assuming the command succeeded. The controller may be offline, a channel may be disabled, or hardware feedback may not confirm that a load changed. If your controller only reports its commanded GPIO state, label the indicator accurately; do not call it “load confirmed” without physical feedback.
4. Decide how retained messages affect restart behavior
MQTT retained messages store the last retained value for a topic and may deliver it to a new subscriber. This is useful for reported state. It is dangerous to casually retain one-shot commands: a relay controller can reconnect and receive an old command when an operator expected outputs to remain off.
Choose one documented policy:
- start all channels in a known safe state and publish their reported state;
- restore a desired state only if that is an explicit system requirement;
- keep per-channel state and availability separate from command topics.
Do not blindly publish ON to all outputs during boot. The safe default depends on the application and load. If a power failure could cause an unsafe movement or energize hazardous equipment, add local hardware interlocks and suitable fail-safe design.
5. Test every failure path
Use LEDs or low-voltage loads while testing. For each channel, verify ON, OFF, incorrect payload, broker disconnect, controller reboot, Node-RED redeploy and new dashboard session. Confirm the switch shows the reported state after reconnect. Also test duplicate command publication; ON and OFF should be idempotent rather than toggle-like.
Check that the broker uses authentication and appropriate topic ACLs, and that Node-RED's editor is protected. Never make the dashboard the only safety mechanism for hazardous loads. Keep records of the relay module model, input polarity, power supply and channel-to-load mapping.
Troubleshooting
Switch moves but output does not: inspect the published command topic and payload, then controller logs and wiring.
UI state flips back: verify the feedback topic is mapped to the correct channel and distinguish command from state.
Outputs change after reboot: inspect retained command topics and firmware startup policy.
Channels appear swapped: verify physical output mapping with low-voltage indicators and update the labels/documentation.
Connection drops: show controller availability separately from the last reported channel state and inspect broker/Node-RED logs.
Next steps
- Node-RED Dashboard 2.0: Install Buttons, Switches and Gauges
- Node-RED with MQTT: Connect Mosquitto, Publish & Subscribe
- MQTT Topic Naming Best Practices
- Retained Messages and Last Will with ESP32
Design the command/state contract before wiring eight controls to real outputs. That makes a dashboard easier to debug and much harder to misinterpret.