The Arduino Opta is an excellent Home Assistant controller when you want something more industrial than a Wi-Fi development board.
Every Opta variant includes:
|
1 2 3 4 5 6 7 8 9 |
10/100 Ethernet STM32H747 dual-core MCU 8 × digital / 0-10 V analogue inputs 4 × relay outputs secure element DIN-rail enclosure |
This means even Opta Lite can become a wired MQTT node for Home Assistant without using Wi-Fi.
A practical architecture is:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Opta → Ethernet → MQTT broker → Home Assistant I1-I8 → publish state / analogue values Relay 1-4 ← receive MQTT commands Opta → publish confirmed relay state → publish availability |
Why Use Ethernet Instead of Wi-Fi?
For fixed automation equipment, wired Ethernet normally gives:
- more predictable connectivity;
- less RF interference;
- easier troubleshooting;
- simpler network segmentation;
- no Wi-Fi credential dependency;
- more stable operation in metal control cabinets.
All Opta models include Ethernet, so this guide works with:
|
1 2 3 4 5 6 |
Opta Lite Opta RS485 Opta WiFi |
Hardware Architecture
The Opta communicates with an MQTT broker over its onboard:
|
1 2 3 4 5 |
10/100BASE-T RJ45 Ethernet interface |
Home Assistant then interacts with the broker rather than connecting directly to the PLC.
Typical topology:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Opta 192.168.1.50 │ │ Ethernet ▼ MQTT Broker 192.168.1.10 │ ▼ Home Assistant |
MQTT Broker
Home Assistant needs access to an MQTT broker.
A common installation uses Mosquitto, but any broker compatible with the Home Assistant MQTT integration can work.
The broker should normally require:
- username/password authentication;
- network access control;
- appropriate firewall rules;
- TLS where traffic leaves a trusted local network.
Suggested Topic Structure
Do not create random topic names for every variable.
A simple hierarchy makes the system easier to maintain:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
opta01/status opta01/input/1/state opta01/input/2/state ... opta01/input/8/state opta01/relay/1/state opta01/relay/1/set opta01/relay/2/state opta01/relay/2/set ... |
The distinction between:
|
1 2 3 4 5 6 |
/set and /state |
is important.
Command Topic vs State Topic
Home Assistant sends a requested change to:
|
1 2 3 4 |
opta01/relay/1/set |
The Opta then operates the physical relay and publishes the resulting state to:
|
1 2 3 4 |
opta01/relay/1/state |
This gives Home Assistant confirmation from the device instead of assuming the command succeeded.
Avoid Optimistic Relay Control
Home Assistant can operate MQTT switches without a state topic, but then it works optimistically.
For an industrial controller, a better pattern is:
|
1 2 3 4 5 6 7 8 |
command → Opta receives command → validate command → drive relay → publish actual state |
This keeps the user interface aligned with the controller.
Required Arduino Libraries
The main libraries are:
|
1 2 3 4 5 |
Ethernet ArduinoMqttClient |
The Opta Mbed core includes an Ethernet library designed for its onboard PHY.
Install ArduinoMqttClient through the Arduino Library Manager if it is not already installed.
Starting Ethernet with DHCP
On Opta, the simplest Ethernet initialisation is:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <Ethernet.h> void setup() { Serial.begin(115200); if (Ethernet.begin() == 0) { Serial.println("DHCP failed"); while (true) { delay(1000); } } Serial.print("Opta IP: "); Serial.println(Ethernet.localIP()); } |
The Opta core can use the board’s factory Ethernet information, so you do not need to invent the old shield-style MAC address manually.
Check the Ethernet Link
During commissioning, check:
|
1 2 3 4 |
Ethernet.linkStatus() |
before blaming MQTT.
If there is no link:
- check the RJ45 cable;
- check the switch port;
- check VLAN configuration;
- check that the Opta is powered from its normal 12-24 V supply.
Static IP Address
For fixed automation equipment, a static lease in the DHCP server is often preferable to hard-coding network settings in firmware.
If you do need a static address in the sketch, the Opta Ethernet API supports:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
IPAddress ip(192, 168, 1, 50); IPAddress dns(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0); Ethernet.begin( ip, dns, gateway, subnet ); |
Create the MQTT Client
|
1 2 3 4 5 6 7 8 9 10 11 |
#include <Ethernet.h> #include <ArduinoMqttClient.h> EthernetClient ethernetClient; MqttClient mqttClient(ethernetClient); const char broker[] = "192.168.1.10"; const int mqttPort = 1883; |
MQTT Authentication
Configure a dedicated MQTT account for the Opta:
|
1 2 3 4 5 6 7 8 9 |
mqttClient.setId("opta01"); mqttClient.setUsernamePassword( "opta01", "your-mqtt-password" ); |
Do not reuse the Home Assistant administrator password.
Availability Topic
A useful availability topic is:
|
1 2 3 4 |
opta01/status |
with:
|
1 2 3 4 5 |
online offline |
payloads.
MQTT Last Will and Testament
The MQTT broker can automatically publish:
|
1 2 3 4 |
offline |
if the Opta disappears unexpectedly.
Configure the Last Will before connecting:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const char availabilityTopic[] = "opta01/status"; mqttClient.beginWill( availabilityTopic, true, 1 ); mqttClient.print("offline"); mqttClient.endWill(); |
The:
|
1 2 3 4 |
true |
argument retains the offline state at the broker.
Publish Online After Connection
After MQTT connects successfully:
|
1 2 3 4 5 6 7 8 9 10 11 |
mqttClient.beginMessage( availabilityTopic, true, 1 ); mqttClient.print("online"); mqttClient.endMessage(); |
Home Assistant can now show entities as unavailable when the Opta loses its broker connection.
MQTT Connection Function
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
void connectMqtt() { mqttClient.setId("opta01"); mqttClient.setUsernamePassword( "opta01", "your-mqtt-password" ); mqttClient.beginWill( "opta01/status", true, 1 ); mqttClient.print("offline"); mqttClient.endWill(); while (!mqttClient.connect( broker, mqttPort)) { Serial.print( "MQTT error: " ); Serial.println( mqttClient.connectError() ); delay(5000); } mqttClient.beginMessage( "opta01/status", true, 1 ); mqttClient.print("online"); mqttClient.endMessage(); } |
Always Call mqttClient.poll()
The ArduinoMqttClient library expects:
|
1 2 3 4 |
mqttClient.poll(); |
to run regularly.
This processes incoming packets and MQTT keep-alive traffic.
Do not put long blocking delays in the main loop.
Relay Pin Mapping
On Opta:
|
1 2 3 4 5 6 7 |
Relay 1 → D0 Relay 2 → D1 Relay 3 → D2 Relay 4 → D3 |
The relays are physical normally-open electromechanical contacts.
Set Up Relay Outputs
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void setupRelays() { pinMode(D0, OUTPUT); pinMode(D1, OUTPUT); pinMode(D2, OUTPUT); pinMode(D3, OUTPUT); digitalWrite(D0, LOW); digitalWrite(D1, LOW); digitalWrite(D2, LOW); digitalWrite(D3, LOW); } |
Starting in the OFF state is usually the safest default unless the machine risk assessment requires different behaviour.
Subscribe to Relay Commands
|
1 2 3 4 5 6 7 8 9 10 |
mqttClient.onMessage( onMqttMessage ); mqttClient.subscribe( "opta01/relay/1/set" ); |
Process the Command
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void onMqttMessage( int messageSize ) { String topic = mqttClient.messageTopic(); String payload; while (mqttClient.available()) { payload += (char)mqttClient.read(); } if ( topic == "opta01/relay/1/set" ) { if (payload == "ON") { setRelay1(true); } if (payload == "OFF") { setRelay1(false); } } } |
Publish Confirmed Relay State
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
bool relay1State = false; void setRelay1(bool state) { relay1State = state; digitalWrite( D0, state ? HIGH : LOW ); mqttClient.beginMessage( "opta01/relay/1/state", true, 1 ); mqttClient.print( state ? "ON" : "OFF" ); mqttClient.endMessage(); } |
The state is retained so Home Assistant immediately knows the last reported relay state after reconnecting.
Should Command Topics Be Retained?
For real actuator commands, usually:
|
1 2 3 4 5 |
do not retain command topics |
A retained:
|
1 2 3 4 |
ON |
command could be replayed when the Opta reconnects after a restart.
That may be undesirable or unsafe.
A safer pattern is:
|
1 2 3 4 5 6 7 8 |
command topic → not retained state topic → retained |
Publish Input States
Opta inputs map as:
|
1 2 3 4 5 6 7 8 9 10 11 |
I1 → A0 I2 → A1 I3 → A2 I4 → A3 I5 → A4 I6 → A5 I7 → A6 I8 → A7 |
For a digital input:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool input1 = digitalRead(A0); mqttClient.beginMessage( "opta01/input/1/state", true, 0 ); mqttClient.print( input1 ? "ON" : "OFF" ); mqttClient.endMessage(); |
Do Not Flood MQTT
A PLC loop can execute thousands of times per second.
Do not publish a message every loop iteration.
Instead publish:
- when a digital state changes;
- when an analogue value changes significantly;
- at a sensible periodic heartbeat;
- when Home Assistant restarts and asks for discovery/state again.
Publish on Change
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
bool lastInput1 = false; void updateInput1() { bool input1 = digitalRead(A0); if (input1 != lastInput1) { lastInput1 = input1; mqttClient.beginMessage( "opta01/input/1/state", true, 0 ); mqttClient.print( input1 ? "ON" : "OFF" ); mqttClient.endMessage(); } } |
Publishing Analogue Inputs
Opta I1-I8 can also be used for:
|
1 2 3 4 |
0-10 V analogue measurement |
with user-configurable ADC resolution.
A Home Assistant topic might be:
|
1 2 3 4 |
opta01/input/1/voltage |
with payload:
|
1 2 3 4 |
7.42 |
Filter Analogue Values Before Publishing
A noisy analogue signal may vary by a few ADC counts continuously.
Use:
- moving average;
- deadband;
- minimum change threshold;
- maximum publish interval.
For example:
|
1 2 3 4 5 6 7 |
publish if change > 0.05 V or 10 seconds elapsed |
Home Assistant Manual MQTT Switch
A relay can be configured manually in Home Assistant:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
mqtt: - switch: name: "Opta Relay 1" unique_id: "opta01_relay1" command_topic: "opta01/relay/1/set" state_topic: "opta01/relay/1/state" availability_topic: "opta01/status" payload_on: "ON" payload_off: "OFF" payload_available: "online" payload_not_available: "offline" optimistic: false |
Why state_topic Matters
When a:
|
1 2 3 4 |
state_topic |
is configured, Home Assistant waits for the Opta to publish the actual state instead of simply assuming a command succeeded.
Home Assistant MQTT Discovery
Home Assistant supports automatic MQTT discovery.
The default discovery prefix is:
|
1 2 3 4 |
homeassistant |
A single switch can be announced on:
|
1 2 3 4 |
homeassistant/switch/opta01_relay1/config |
Example Discovery Payload
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "name":"Relay 1", "unique_id":"opta01_relay1", "command_topic":"opta01/relay/1/set", "state_topic":"opta01/relay/1/state", "availability_topic":"opta01/status", "payload_on":"ON", "payload_off":"OFF", "device":{ "identifiers":["opta01"], "name":"Arduino Opta" } } |
Publish the discovery message with the:
|
1 2 3 4 |
retain |
flag so Home Assistant receives it after restarting.
ArduinoMqttClient Discovery Message
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const char discoveryTopic[] = "homeassistant/switch/" "opta01_relay1/config"; const char discoveryPayload[] = "{\"name\":\"Relay 1\"," "\"unique_id\":\"opta01_relay1\"," "\"command_topic\":" "\"opta01/relay/1/set\"," "\"state_topic\":" "\"opta01/relay/1/state\"," "\"availability_topic\":" "\"opta01/status\"," "\"payload_on\":\"ON\"," "\"payload_off\":\"OFF\"," "\"device\":{" "\"identifiers\":[\"opta01\"]," "\"name\":\"Arduino Opta\"}}"; mqttClient.beginMessage( discoveryTopic, true, 1 ); mqttClient.print( discoveryPayload ); mqttClient.endMessage(); |
Increase MQTT Payload Buffer If Required
Discovery JSON can become large when you define many entities.
ArduinoMqttClient allows the transmit payload size to be increased:
|
1 2 3 4 5 6 |
mqttClient.setTxPayloadSize( 1024 ); |
Do this before publishing large discovery payloads.
Home Assistant Device Discovery
Current Home Assistant versions also support:
|
1 2 3 4 |
MQTT device discovery |
where several components are included in one device discovery message.
For a device such as Opta with:
- eight inputs;
- four relays;
- analogue values;
- availability;
device discovery can reduce the number of separate discovery messages.
Retained Discovery vs Home Assistant Birth Message
There are two common approaches.
Simple embedded approach:
|
1 2 3 4 |
retain discovery messages |
so the broker replays them whenever Home Assistant subscribes.
More dynamic approach:
|
1 2 3 4 5 |
subscribe to homeassistant/status |
and republish discovery when Home Assistant publishes:
|
1 2 3 4 |
online |
after startup.
Home Assistant Status Topic
By default Home Assistant publishes its own birth/status messages to:
|
1 2 3 4 |
homeassistant/status |
This can be useful for causing the Opta to:
- republish discovery;
- republish relay states;
- republish current input states.
Reconnect Logic
Networks fail.
The sketch must recover without requiring a PLC restart.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void ensureMqtt() { if (!mqttClient.connected()) { connectMqtt(); mqttClient.onMessage( onMqttMessage ); mqttClient.subscribe( "opta01/relay/1/set" ); publishAllStates(); } } |
Do Not Reconnect in a Tight Loop
If the broker is offline, an immediate reconnect loop can monopolise the CPU and flood the network.
Use a retry interval such as:
|
1 2 3 4 5 6 |
5 seconds 10 seconds 30 seconds |
or exponential back-off for longer outages.
Complete Main Loop Pattern
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void loop() { ensureEthernet(); ensureMqtt(); mqttClient.poll(); updateDigitalInputs(); updateAnalogueInputs(); runLocalControlLogic(); } |
MQTT should be a communications layer around the controller, not the controller’s only source of logic.
Local Control Should Continue Without Home Assistant
For serious automation, design the Opta so that:
|
1 2 3 4 5 6 |
Home Assistant offline does not mean machine logic stops |
Critical logic should remain local on the Opta.
Home Assistant should typically provide:
- supervision;
- setpoints;
- status;
- non-critical commands;
- logging;
- notifications.
Home Assistant Is Not a Safety PLC
Do not use MQTT/Home Assistant as the sole safety layer for:
- emergency stops;
- machine guarding;
- over-temperature shutdown;
- over-pressure protection;
- personnel safety;
- critical motor interlocks.
Those functions require appropriate local hardware and safety architecture.
Relay Commands Should Be Validated Locally
Instead of:
|
1 2 3 4 5 |
MQTT ON → relay ON |
a better industrial pattern may be:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
MQTT ON request │ ▼ Opta checks: - permissive? - local mode? - fault active? - interlock satisfied? - output allowed? │ ▼ relay ON │ ▼ publish confirmed state |
Retained State Needs Care
Retaining:
|
1 2 3 4 5 6 7 |
relay state sensor state availability discovery configuration |
can be useful because Home Assistant receives a state immediately after reconnecting.
But a retained message represents the:
|
1 2 3 4 |
last reported state |
not proof that the physical system is still in that state now.
Republish Physical State After Restart
When the Opta boots:
|
1 2 3 4 5 6 7 |
read actual local state → initialise outputs safely → connect MQTT → publish current state |
Do not blindly restore a potentially dangerous actuator from an old retained command.
MQTT QoS
Typical choices are:
|
1 2 3 4 5 6 7 8 9 10 11 |
QoS 0 → high-rate sensors → acceptable to lose occasional packet QoS 1 → relay state → commands → important telemetry |
QoS alone does not create application-level safety or exactly-once physical operation.
MQTT Client IDs Must Be Unique
If two Opta controllers both connect using:
|
1 2 3 4 |
opta01 |
as the MQTT client ID, the broker may repeatedly disconnect one when the other connects.
Use unique IDs such as:
|
1 2 3 4 5 6 |
opta-boiler-room opta-pump-house opta-ahU-01 |
MQTT Credentials Per Controller
For multiple Optas, use separate credentials where practical:
|
1 2 3 4 5 6 |
opta01 opta02 opta03 |
This allows broker ACL rules such as:
|
1 2 3 4 5 6 7 8 9 10 |
opta01 → publish opta01/# → subscribe opta01/+/+/set opta02 → publish opta02/# → subscribe opta02/+/+/set |
Network Segmentation
In an industrial installation, consider placing automation devices on a dedicated:
|
1 2 3 4 5 6 |
VLAN or OT network |
with firewall rules that allow only required traffic to:
- MQTT broker;
- Home Assistant;
- DNS;
- NTP;
- management stations.
MQTT Broker Outside the LAN
If the broker is accessed over an untrusted network:
|
1 2 3 4 5 6 |
do not send plain MQTT credentials over port 1883 |
Use TLS, VPN or another secure network architecture.
Home Assistant Sensor Example
A manually configured analogue sensor could look like:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
mqtt: - sensor: name: "Opta Input 1 Voltage" unique_id: "opta01_i1_voltage" state_topic: "opta01/input/1/voltage" availability_topic: "opta01/status" unit_of_measurement: "V" |
Home Assistant Binary Sensor Example
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mqtt: - binary_sensor: name: "Opta Input 2" unique_id: "opta01_i2" state_topic: "opta01/input/2/state" payload_on: "ON" payload_off: "OFF" availability_topic: "opta01/status" |
Recommended Topic Layout for a Full Opta
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
opta01/status opta01/input/1/state opta01/input/2/state opta01/input/3/state opta01/input/4/state opta01/input/5/state opta01/input/6/state opta01/input/7/state opta01/input/8/state opta01/input/1/voltage ... opta01/input/8/voltage opta01/relay/1/set opta01/relay/1/state opta01/relay/2/set opta01/relay/2/state opta01/relay/3/set opta01/relay/3/state opta01/relay/4/set opta01/relay/4/state |
Common Mistake 1: Publishing Every Loop
This can create thousands of MQTT messages per second.
Publish on change and use sensible periodic updates.
Common Mistake 2: Retaining Relay Commands
A stale retained ON command may be replayed after reconnect.
Retain state, not actuator requests, unless the application has been deliberately designed around retained commands.
Common Mistake 3: No Availability Topic
Without availability, Home Assistant may continue displaying the last state even when the Opta is physically offline.
Common Mistake 4: No State Confirmation
A Home Assistant switch should preferably have both:
|
1 2 3 4 5 6 |
command_topic and state_topic |
so it is not operating purely optimistically.
Common Mistake 5: Blocking delay()
Long delays interfere with:
- MQTT keep-alives;
- incoming commands;
- input scanning;
- reconnect logic;
- local PLC tasks.
Use millis()-based timing instead.
Common Mistake 6: Making Home Assistant the Interlock
Do not place a safety condition only in a Home Assistant automation.
The Opta should enforce important permissives locally.
Common Mistake 7: No Reconnect State Republish
After reconnecting:
|
1 2 3 4 5 6 7 |
resubscribe commands republish online republish discovery republish actual states |
so Home Assistant and the PLC become synchronised again.
Quick Architecture Reference
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Arduino Opta 10/100 Ethernet │ ▼ MQTT Broker │ ▼ Home Assistant Opta publishes: opta01/status opta01/input/+/state opta01/input/+/voltage opta01/relay/+/state Opta subscribes: opta01/relay/+/set Recommended: availability + LWT retained discovery retained state non-retained commands local interlocks local autonomous control |
Final Thoughts
Opta and Home Assistant work particularly well together when the roles are kept clear.
Opta should remain responsible for:
|
1 2 3 4 5 6 7 8 9 |
physical I/O real-time logic relay state interlocks local fallback machine control |
Home Assistant should provide:
|
1 2 3 4 5 6 7 8 9 10 |
dashboard history notifications scheduling setpoints non-critical remote commands automation across systems |
MQTT becomes the clean boundary between the two.
Using Ethernet, a confirmed state topic, Last Will availability and sensible reconnect handling produces a substantially more robust integration than simply publishing relay commands over Wi-Fi.
For the underlying terminal mapping, see our Arduino Opta pinout and I/O guide. For serial field devices on Opta RS485/WiFi, see our Arduino Opta Modbus RS485 guide.