ET-bus mantiz010

ET-Bus (ElectronicsTech Bus)

ET-Bus is a local-first, real-time device bus designed for ESP32-based automation systems and Home Assistant.

It is built for speed, reliability, and deterministic control on a local LAN — without MQTT brokers, without cloud dependencies, and without retained-message complexity.

ET-Bus is not a replacement for MQTT, Zigbee, or ESPHome.
It is a complementary protocol for always-on, mains-powered, low-latency devices.


Why ET-Bus Exists

Home Assistant users regularly encounter real-world problems such as:

  • MQTT broker failures stopping all automation
  • Retained messages causing state desynchronisation
  • Devices appearing online when physically offline
  • Slow or unpredictable command response
  • Devices failing to recover after Home Assistant restarts

These are architectural issues, not misconfiguration.

ET-Bus was created to eliminate these failure modes by moving to a device-owned, UDP-based state model.


Key Features

  • Ultra-low latency (UDP on LAN)
  • No broker required
  • Device-owned state (no retained lies)
  • Automatic resync after reconnect
  • Multicast discovery
  • Local-only by default
  • Human-readable JSON (Wireshark friendly)
  • Designed specifically for Home Assistant

What ET-Bus Is (and Is Not)

Designed for

  • Relays and switches
  • WS2812 / RGB lighting
  • Fans and motors
  • Pumps and valves
  • Industrial & hydroponics controllers
  • Server rack & homelab automation

Not designed for

  • Battery-powered sensors
  • Sleepy devices
  • Cloud-first IoT
  • Internet-scale messaging

Use Zigbee / LoRa / BLE for battery devices.
Use ET-Bus for fast, deterministic, always-on control.


Architecture Overview

  • Devices communicate with Home Assistant using UDP
  • Multicast is used for discovery and health checks
  • Unicast is used for commands once the device IP is known
  • Devices send periodic pong heartbeats
  • Home Assistant tracks true online/offline state

No broker.
No retained messages.
No single point of failure.


Protocol Summary

All messages are JSON over UDP.

Common message envelope

{ “v”: 1, “type”: “state | command | discover | ping | pong”, “id”: “device_id”, “class”: “switch.relay | light.rgb | fan.speed”, “payload”: {} }

Message Types

Type Purpose
discover Device announces itself
state Device publishes current state
command Home Assistant sends a command
ping Health check from Home Assistant
pong Device heartbeat

Device Lifecycle

  1. Device boots
  2. Sends discover (multicast)
  3. Home Assistant registers device
  4. Device sends initial state
  5. Device sends periodic pong
  6. Home Assistant marks device online/offline

If Home Assistant restarts:

  • Devices re-announce automatically
  • State is rebuilt from live devices
  • No stale or ghost entities remain

Command Flow

Home Assistant UI
        ↓
     command
        ↓
     ESP32 device
        ↓
   apply action
        ↓
   state confirmation
        ↓
 Home Assistant updates entity

State is never assumed — only confirmed.


Reliability Model

ET-Bus follows an industrial reliability model:

  • No heartbeat → offline
  • No device → no state
  • No confirmation → no assumption

Explicit failure is preferred over silent failure.


Why ET-Bus Avoids Retained Messages

MQTT retained failure

  • Broker retains relay = ON
  • Device loses power
  • Home Assistant still shows ON
  • Automations behave incorrectly

ET-Bus behaviour

  • Device loses power
  • Heartbeats stop
  • Device marked offline
  • State becomes unknown

ET-Bus refuses to lie about physical reality.


Failure Scenarios

MQTT Broker Restart

  • Broker goes down
  • Devices disconnect
  • Home Assistant shows old retained state
  • Automations misfire

ET-Bus Controller Restart

  • Home Assistant restarts
  • Devices re-announce
  • State rebuilt automatically
  • System stabilises cleanly

Security Model (current state – important)

ET-Bus currently operates as plaintext UDP and is intended for use on trusted local networks only.

Current reality

  • No protocol-level encryption
  • No authentication
  • No cryptographic integrity checking
  • Any device on the same network could observe or inject packets

This is intentional and documented, not accidental.

ET-Bus prioritises deterministic latency, simple failure modes, broker-less operation, easy packet inspection, and fast recovery after Home Assistant restarts over protocol-level security.

Intended deployment model

Trusted LAN or VLAN → ET-Bus
Remote access → VPN (WireGuard / OpenVPN) → LAN → ET-Bus

Security is enforced at the network layer, not the protocol layer.


ESP32 Example Behaviour

Relay

void onCommand(JsonObject payload) { if (payload.containsKey(“on”)) { bool on = payload[“on”]; digitalWrite(RELAY_PIN, on ? HIGH : LOW); sendState(on); } }

WS2812 / RGB Light

void onCommand(JsonObject payload) { setColor(payload[“r”], payload[“g”], payload[“b”]); setBrightness(payload[“brightness”]); sendState(); }

Fan Speed Control

void onCommand(JsonObject payload) { setFanSpeed(payload[“speed”]); // 0–100 sendState(); }


ESPHome vs MQTT vs ET-Bus

ESPHome

Strengths

  • YAML-based configuration
  • Native Home Assistant API
  • OTA updates

Limitations

  • Firmware-centric
  • Less suitable for complex distributed systems

MQTT

Strengths

  • Huge ecosystem
  • Flexible pub/sub model
  • Cross-platform support

Limitations

  • Requires a broker
  • Retained message state issues
  • Single point of failure

ET-Bus

Strengths

  • No broker
  • No retained state
  • Deterministic latency
  • True device presence tracking
  • Automatic recovery after restarts

Trade-offs

  • LAN-only by design
  • Always-on devices only

Comparison Table

Feature ESPHome MQTT ET-Bus
Broker required No Yes No
Local-first Yes Optional Yes
Deterministic latency Medium Low High
Retained state risk No Yes No
Device-owned state Partial No Yes
Best for battery devices Sometimes Sometimes No
Best for always-on control Yes Yes Yes (best)

Networking Notes

  • UDP must be allowed through firewalls
  • Multicast must be permitted on the LAN
  • VLANs must pass multicast or use unicast mode

ET-Bus is intentionally LAN-first.


Troubleshooting

Devices appear then disappear

  • Heartbeat interval too long
  • Home Assistant timeout too short
  • Multicast blocked

Fix

  • Reduce heartbeat interval
  • Increase HA timeout
  • Verify multicast routing

Commands sent but nothing happens

  • Device does not parse command
  • Device does not send state confirmation
  • ID or class mismatch

Fix

  • Log received packets on device
  • Confirm state is sent after applying command

Roadmap

  • Protocol versioning rules
  • Device profile definitions
  • WS2812 effect schema
  • Protocol-level security (future work)
  • Robust reconnect logic
  • Rate limiting
  • Stable device registry
  • HACS support
  • Example ESP32 firmwares

Contributing

Contributions should prioritise:

  • Correctness over convenience
  • Explicit failure over hidden failure
  • Simple, inspectable protocols

License

MIT License


Author

ElectronicsTech
Built for real-world automation — not demos.

You better make that security a very high priority issue for release version 1.1.

/ -----------------------------
// HARD-CODED WIFI CREDENTIALS
// -----------------------------
static const char* WIFI_SSID = “home”;
static const char* WIFI_PASS = “test”;

Anybody else using 5555 UDP packets going to cause you grief?

Is packet size going to be a concern?

IPv6 support?

Did I understand well: You’re basically flooding your network with keep alive packets to verify connectivity? As you add devices, the traffic volume increases?

I’d make the version at the front of your packets, so that subsequent data can be interpreted correctly in future versions, rather than at the tail, where checksums traditionally go.

How am I going to address multi-relay boards, etc?

Are dropped packets an issue?

Thanks — all valid points. Here’s how I’m handling these for ET-Bus v1.1 (aiming for “commercial-grade”, not hobby-grade):

:closed_lock_with_key: Security (highest priority for v1.1)

  • ChaCha20-Poly1305 (AEAD) for commands (and optionally state later)
  • 32-byte master secret configured in Home Assistant, not in firmware
  • Per-device key derived from SHA256(master_secret || device_mac) so one secret in HA generates unique keys per device
  • AAD binds header fields to the ciphertext so packets can’t be tampered with without failing auth:
    v, type, device_id, class, key_id, ctr
  • Monotonic counter (ctr) included to block replay attacks
  • key_id is fixed = 1 in v1.1 (NOT user configurable) to avoid mismatch/decrypt-fail foot-guns
    Discovery/ping can remain plaintext so devices can always learn the hub IP during provisioning.

:key: Hard-coded Wi-Fi credentials

Agree — hard-coded SSID/pass is a release blocker.

  • For release builds: Wi-Fi Manager / provisioning
  • Hard-coded credentials only remain as a clearly labeled dev mode

:electric_plug: UDP port 5555

Default stays 5555, but:

  • Port is configurable in HA options (and device uses the same)
  • If a user changes it, devices learn the port via hub ping payload ({"port": ...})
    So 5555 is a sane default, but not a hard dependency.

:package: Packet size / MTU concerns

Yes — I’m keeping packets small to avoid fragmentation:

  • Target < ~1200 bytes per message (compact JSON)
  • Larger data is split across multiple messages
  • Commands are unicast once IP is known (multicast is mainly discovery)

:globe_with_meridians: IPv6

v1.1 is IPv4 multicast + IPv4 unicast only.
IPv6 is on the roadmap, but I’m keeping v1.1 tight and reliable first.

:satellite: “Flooding” / keep-alives scaling

Not flooding — traffic is tiny even at scale:

  • Hub ping: every 10s
  • Device pong: every 30s, with jitter so devices don’t all pong at once
    Example: 50 devices → ~1–2 UDP packets/sec total on LAN.

:white_check_mark: Versioning placement

Already done: packets start with {"v": 1, ...}
Version is included in the authenticated data too, so future versions can coexist safely.

:repeat: Multi-relay boards / more complex devices

Planned model is clean HA mapping:

  • One device ID (e.g. relay_board_01)
  • Multiple entities via class naming:
    switch.relay1, switch.relay2, etc. (or per-channel classes)
    This avoids ambiguity and maps naturally to HA entities.

:chart_with_downwards_trend: Dropped packets / reliability

UDP drops are expected — design handles it:

  • Commands are unicast to last-known device IP
  • Device always sends a state update after applying a command
  • Devices send full state on boot
  • HA can request a resync state dump after restart/reconnect
    So HA doesn’t “assume” state; it learns it.

If there’s anything you think I should change before calling this v1.1-ready, I’m all ears — especially around HA entity modeling for multi-channel devices and best practices for resync on HA restar
:repeat: Multi-relay boards

Multi-relay boards are handled as multiple standard switch entities, not a single “multi-relay” switch.
Each relay channel is exposed as its own entity (e.g. switch.relay1, switch.relay2, etc.) under the same device ID. This maps cleanly to Home Assistant’s entity model, avoids channel indexing bugs, and makes automations, dashboards, and resync after restarts much more reliable.

On boot or resync, the device simply re-publishes state for each relay channel, allowing HA to fully rebuild state without guessing.

:bulb: Light sync / effects syncing (extra feature)

For lights (including RGB/WS2812 effects), I’m treating the device as the state authority and focusing on drift-free sync rather than “fire-and-forget” effects:

  • Light entities report full state (on/off, brightness, RGB, effect) back to HA after any change.
  • After HA restart or device reconnect, the device can re-announce current light state so HA rebuilds correctly.
  • HA can optionally request a resync to force the device to re-send the full light state (useful after outages).
  • Commands are unicast and the device replies with a state update, so HA doesn’t need to assume the light/effect applied.

Goal is that LED effects never drift or get “stuck wrong” after restarts — HA always ends up reflecting what the device is actually doing.

:zap: Speed / latency (explicit design goal)
ET-Bus is designed to be fast by default on a local network:

  • Uses direct UDP (no broker, no TCP handshakes)
  • Single-packet unicast commands once device IP is known
  • No topic parsing, retain state, or QoS overhead
  • Compact JSON, minimal processing on both HA and device
  • Typical command → state round-trip is single-digit milliseconds on Wi-Fi

This makes a noticeable difference for:

  • Rapid relay toggling
  • Multi-relay boards
  • RGB / WS2812 effect syncing
  • UI responsiveness (HA toggle feels instant)

The goal is deterministic, near-instant response, not eventual consistency.