lsh-bridge is the ESP32 bridge runtime for the Labo Smart Home controller path. It
talks to an lsh-core controller over UART, exposes the controller model over MQTT and
Homie, and keeps the network-facing runtime synchronized with the physical panel.
The reference path documented here is one bridge per controller: a Controllino-style AVR
device running lsh-core, an ESP32 running lsh-bridge, an MQTT broker, and the LSH
coordinator or Node-RED logic layer above it.
If you are new to LSH as a whole, start with the
labo-smart-home documentation map
before tuning bridge settings.
For a first end-to-end installation, prefer the stack-generated path over copying capacity, topic and codec flags by hand:
lsh-stack new my-lsh-installation
cd my-lsh-installation
lsh-stack setupThat command creates the consumer PlatformIO projects and writes the bridge
platformio-bridge.ini, coordinator config, Node-RED settings and deploy plan from the
same controller TOML.
The main branch can move ahead of tagged releases. For downstream projects, prefer
released tags unless you are intentionally testing coordinated unreleased work across
the LSH repos.
lsh-bridge is intentionally narrow:
- it reads and writes the controller serial protocol
- it rebuilds the MQTT and Homie model from controller
DEVICE_DETAILS - it publishes controller state, events and bridge diagnostics
- it coalesces actuator command bursts before sending
SET_STATEto the controller - it keeps a validated controller topology cache in ESP32 NVS
- it asks the controller to resync after boot, MQTT recovery or topology changes
The bridge does not own physical I/O. Buttons, relays, indicators and local fallback remain controller responsibilities.
For the documented bridge path:
- PlatformIO
- an ESP32 board supported by
pioarduino/platform-espressif32 - a controller running
lsh-core - a hardware UART between controller and ESP32
- a 5 V / 3.3 V level shifter when the controller UART is 5 V
- an MQTT broker; the bridge publishes Homie v5 metadata from the controller topology
The bridge is optimized for a static controller topology. It can recover from a changed topology, but topology changes are expected to come from controller firmware updates or reboots rather than from devices appearing dynamically at runtime.
The practical starting reference is the bundled PlatformIO example:
platformio run -d examples/basic-homie-bridge -e releaseThe example also keeps CI-backed variants for codec and optimization coverage:
release: conservative first buildrelease_aggressive: more aggressive optimization profilerelease_json_serial: JSON on the controller UARTrelease_msgpack_mqtt: MessagePack on serial and MQTTrelease_json_serial_msgpack_mqtt: JSON on serial, MessagePack on MQTT
Keep the release profile for the first bring-up. Change codecs, topic names or
capacity limits after the controller, bridge and MQTT path have worked once together.
Install from the PlatformIO Registry:
lib_deps =
labodj/lsh-bridge@^1.6.0The embedding firmware owns board choice, serial pins, topic names, firmware identity and deployment policy. A minimal Arduino entry point looks like this:
#include <Arduino.h>
#include <lsh_bridge.hpp>
namespace {
lsh::bridge::BridgeOptions makeBridgeOptions() {
lsh::bridge::BridgeOptions options;
options.serial = &Serial2;
options.disableLedFeedback = true;
return options;
}
lsh::bridge::LSHBridge bridge(makeBridgeOptions());
} // namespace
void setup() {
bridge.begin();
}
void loop() {
bridge.loop();
}Leaving BridgeOptions::serial unset resolves to Serial2. By default, the bridge also
disables Homie LED feedback, keeps ESP32 Wi-Fi modem sleep disabled and uses
build-driven logging. Set the options explicitly when you want the firmware entry point
to document those choices.
The full example lives in examples/basic-homie-bridge.
In the public panel pattern, the ESP32 bridge is paired one-to-one with the controller:
12/24 VDC supply
|
+-------------------------------> Controllino / lsh-core
|
+--> 5 V buck converter -------> ESP32 / lsh-bridge
Controller TTL UART
|
+--> 5 V / 3.3 V level shifter --> ESP32 UART
Common ground shared by controller, buck converter, level shifter and ESP32.
The bridge usually talks to the controller over a hardware UART rather than USB. On Controllino-style panels, the controller side is 5 V logic and the ESP32 side is 3.3 V logic, so the UART path needs a proper level shifter.
For panel-level power and wiring context, read the Labo Smart Home hardware overview.
+-------------+ serial +------------+ MQTT +--------------+
| lsh-core | <----------> | lsh-bridge | <--------> | coordinator |
| controller | | ESP32 | | or Node-RED |
+-------------+ +------------+ +--------------+
Runtime rules:
- the controller remains authoritative for actuator state
- the bridge caches only validated topology, never runtime actuator state
- startup uses cached topology when available, then asks the controller for fresh details and state
- device-topic
PINGanswers controller reachability only when the runtime is synchronized - service-topic
PINGanswers bridge reachability on the bridge-local topic - topology changes are saved to NVS and followed by one controlled reboot
- retained, fragmented, oversize or malformed MQTT commands are rejected and diagnosed
For the detailed runtime story, read docs/runtime-behavior.md.
Most bridge choices are compile-time PlatformIO flags because they affect fixed buffers, topic sizes, protocol codecs or Homie identity. The most common groups are:
- capacities:
CONFIG_MAX_ACTUATORS,CONFIG_MAX_BUTTONS,CONFIG_MAX_NAME_LENGTH - serial:
CONFIG_ARDCOM_SERIAL_RX_PIN,CONFIG_ARDCOM_SERIAL_TX_PIN,CONFIG_ARDCOM_SERIAL_BAUD - MQTT topics:
CONFIG_MQTT_TOPIC_BASE,CONFIG_MQTT_TOPIC_EVENTS,CONFIG_MQTT_TOPIC_BRIDGE,CONFIG_MQTT_TOPIC_SERVICE - Homie:
HOMIE_CONVENTION_VERSION=5,CONFIG_HOMIE_FIRMWARE_NAME,CONFIG_HOMIE_FIRMWARE_VERSION,CONFIG_HOMIE_BRAND - runtime policy: queue capacity, command coalescing windows, topology reboot timing and reset-trigger policy
- codecs:
CONFIG_MSG_PACK_ARDUINOandCONFIG_MSG_PACK_MQTT
Use the bundled example as the starting point. The complete reference lives in docs/compile-time-configuration.md.
For full-stack installations, prefer generating these flags from the controller contract
with the labo-smart-home stack composer. The generated platformio-bridge.ini creates
one wide bridge firmware environment per profile. Device names are upload targets, not
firmware variants:
[platformio]
extra_configs = generated/platformio-bridge.iniplatformio run -e bridge_littlefs
platformio run -e bridge_littlefs_usb_j1 -t uploadIn the one-project workflow, lsh-bridge stays a normal PlatformIO dependency. The
generated environments remove the need to copy capacity, topic and codec flags by hand,
and persistent local extensions can live outside generated/. Firmware OTA for this
bridge is MQTT/Homie-based: use the stack-generated LSH OTA <device> custom targets or
the generated deploy-plan.json command for your MQTT OTA helper.
- DOCS.md: repository documentation map
- docs/runtime-behavior.md: startup, synchronization, diagnostics and MQTT behavior
- docs/compile-time-configuration.md: build flags and capacity policy
- LSH reference stack: cross-repo runtime model
Validated baseline:
- ESP32
- Arduino framework
pioarduino/platform-espressif32- Homie convention v5 through
labodj/homie-v5
Recommended PlatformIO platform:
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
board = esp32devstable is a moving platform alias. Validate every lsh-bridge release against the
current platform before deployment.
OTA updates only the application image (firmware.bin). It does not update bootloader,
partition table or other full-flash artifacts. If the ESP32 platform update changes
those artifacts, validate them explicitly and use a full USB/serial flash when required.
This repository targets ESP32. ESP8266 support would need separate validation and support work.
Maintainer workflow notes live in
DOCS.md.
Package smoke and release publishing use platformio pkg pack, so exported Markdown
uses absolute links rather than repository-relative links.