The prebuilt HCP PCBs carry four screw terminals next to the sensor connectors: In1, In2, Out1 and Out2. They are plain ESP32 GPIOs brought out to terminals — there is no relay, no level shifter and no protection circuitry on the board.
Since firmware v1.0.2 they are supported by the firmware: inputs become Home
Assistant binary_sensor entities (and can trigger a local door/light action),
outputs become Home Assistant switch entities.
The HCP mini does not have these terminals. It runs the same
HCP_Giffordv3firmware, which is why the whole feature is disabled by default and has to be enabled per channel in the Web UI.
| Property | Value |
|---|---|
| Type | Voltage input through a fixed resistive divider (1.2 kΩ series + 2.2 kΩ to GND) |
| Expected signal | ~5 V logic level against board GND (not a potential-free contact) |
| Divider ratio | 2.2 / (1.2 + 2.2) = 0.647 → 5 V in ≈ 3.24 V at the GPIO |
| Minimum HIGH | ≈ 3.9 V at the terminal (ESP32 needs ≥ 0.75 × 3.3 V = 2.48 V at the pin) |
| Maximum input voltage | ≈ 5.1 V — above that the GPIO exceeds 3.3 V |
| Idle level | LOW — the 2.2 kΩ pulls the GPIO to GND when nothing is applied |
| Input current | ≈ 1.5 mA at 5 V (3.4 kΩ total) |
| Pull resistor | already on the PCB (2.2 kΩ pull-down) — leave the firmware pull mode on none |
| Debounce | 50 ms in firmware (IO_DEBOUNCE_MS) |
These are 5 V signal inputs, not dry-contact inputs. The on-board divider is dimensioned for 5 V. Two consequences:
- A potential-free contact across the two In1 terminals does nothing — it would merely bridge the 1.2 kΩ series resistor. A push button has to switch a voltage (≈5 V against board GND) onto the input.
- The internal pull-up cannot be used. The ESP32's internal pull-up is ~45 kΩ; against the on-board 2.2 kΩ pull-down the pin would sit at ~0.15 V and read LOW permanently. Keep Pull resistor on none (external) for the prebuilt PCBs — the pull mode only exists for self-built boards without this divider.
Never feed 12 V or 24 V in (e.g. straight from the motor): the divider would put 7.8 V resp. 15.5 V on the GPIO and destroy it. Use an external optocoupler or an additional divider that brings the signal down to ≈5 V first.
A 3.3 V source is not reliably detected either — it only produces 2.13 V at the pin, below the ESP32's HIGH threshold.
| Property | Value |
|---|---|
| Type | High-side switch — optocoupler-driven N-channel MOSFET (source follower) |
| ON state | OUTx sources voltage: ≈ VCC_SEL − V_GS(th), i.e. roughly 0.7–1.5 V below the jumper level, load dependent |
| OFF state | High impedance (floating) — the gate is pulled to GND by 2.2 kΩ |
| Output voltage | Set by the 5V | – | 3V jumper (VCC_SEL) |
| Reference | Board GND — the load returns to the same GND as the bridge |
| Suitable loads | PLC inputs, relay module inputs, optocoupler inputs, logic inputs |
| Not suitable | Driving loads directly (relays, lamps, motors, coils, mains loads) |
Voltage selection: the 5V | – | 3V jumper selects the rail (VCC_SEL) the
outputs switch through. Because the MOSFET works as a source follower, the
actual output sits about a threshold voltage below that rail — with the jumper
on 3 V you only get roughly 2.3 V, which is marginal for 3.3 V logic. Prefer
the 5 V position unless the connected device explicitly needs 3.3 V.
VCC_SEL (5V | 3V jumper)
│
┌──────────┴─────────┐
│ optocoupler ──┐ │ drain
GPIO ─[360Ω]─ LED └─ gate ─┤ MOSFET (CJ3400)
│ │ source
GND [2.2kΩ]───┘
│ │
GND OUT1 ──► your device (returns to board GND)
- ON → optocoupler pulls the gate up to
VCC_SEL→ MOSFET conducts → OUT1 follows the rail and can source current. - OFF → the 2.2 kΩ pulls the gate to GND → MOSFET blocks → OUT1 floats. If the connected device needs a defined LOW, add an external pull-down.
Do not drive a load directly. For a relay, lamp or motor use a relay or solid-state relay module with its own supply and feed only its input from OUTx.
No galvanic isolation: the optocoupler's LED and its output transistor both reference the same board GND, so it acts as a level-shifting driver stage, not as an isolation barrier. Treat OUT1/OUT2 as GND-referenced to the bridge.
Active low: the Active low checkbox simply inverts the driving GPIO. Leave it off — ON in Home Assistant then means "output active". Only tick it if ON/OFF end up swapped for your particular wiring.
| Terminal | HCP PCB v2 | HCP PCB v3.x | HCP mini | other boards |
|---|---|---|---|---|
| In1 | GPIO12 | GPIO12 | – | free choice |
| In2 | GPIO14 | GPIO14 | – | free choice |
| Out1 | GPIO37 | GPIO25 | – | free choice |
| Out2 | GPIO35 | GPIO22 | – | free choice |
The GPIO number is configurable in the Web UI, so a self-built board can use any
free pin. The firmware rejects pins that cannot work and reports the reason via
the debug MQTT entity (io_error):
0→ treated as "not configured"- SPI flash pins (ESP32: 6–11, ESP32-S3: 26–32)
- GPIO34–39 on the classic ESP32 as an output (those pins are input-only)
Open the web interface → I/O Configuration.
| Field | Meaning |
|---|---|
| Enable Input 1/2 | Activates the channel. Off = pin untouched, no HA entity. |
| Name | Entity name in Home Assistant (e.g. "Garage wall button"). |
| GPIO | Pin number (see table above). |
| Pull resistor | none (external) — the correct setting for the prebuilt PCBs, which already have a 2.2 kΩ pull-down (default). internal pull-up / internal pull-down only for self-built boards without that divider. |
| Active low | ON = pin LOW means "active". Default OFF, i.e. an applied voltage = active. Only turn it on for a self-built board with a button to GND plus internal pull-up. |
| Local action | What the ESP32 does itself on the activating edge — works even when Wi-Fi/MQTT is down. |
Available local actions:
| Action | Effect |
|---|---|
| none (MQTT only) | Only publishes the state — react in Home Assistant |
| impulse / toggle door | Same as the wall button: open ↔ stop ↔ close |
| open door | Drive to open |
| close door | Drive to close |
| stop door | Stop movement |
| toggle light | Toggles the SupraMatic courtesy light |
| vent position | Move to ventilation position |
| half position | Move to half-open position |
| toggle Output 1/2 | Switches one of the local outputs |
| Field | Meaning |
|---|---|
| Enable Output 1/2 | Activates the channel. Off = pin untouched, no HA entity. |
| Name | Entity name in Home Assistant. |
| GPIO | Pin number (see table above). |
| Active low | ON = the pin is driven LOW when the switch is ON (typical relay module). |
| Restore after reboot | ON = the last state is written to NVS and restored on boot. OFF (default) = the output is always OFF after a power loss — the safer choice for anything that moves. |
Saving the form restarts the device (like every configuration change).
This is the setup from issue #147.
Wiring: the input needs a switched voltage, not a dry contact, so the button switches ≈5 V onto In1:
+5V ────o/ o──── In1 signal terminal
push button
(device GND tied to the bridge GND)
The 2.2 kΩ on the board pulls the input to GND while the button is open, so no
extra resistor is needed. If you only have a potential-free contact and no 5 V
nearby, use the 5 V rail of the bridge (see the 5V | – | 3V jumper area) or
any other 4–5 V source that shares GND with the bridge.
Web UI → I/O Configuration → Input 1:
| Field | Value |
|---|---|
| Enable Input 1 | ✅ |
| Name | Garage light button |
| GPIO | 12 |
| Pull resistor | none (external) |
| Active low | ❌ |
| Local action | toggle light |
Save & restart. Pressing the button now toggles the light directly on the bus —
no Home Assistant, no MQTT, no Wi-Fi required. In addition the button state is
published as a binary_sensor, so you can also use the press as a trigger for
Home Assistant automations.
Out1 sources the module's input, so the module has to be an active HIGH
type (energises when IN is pulled high). Set the 5V | – | 3V jumper to
5 V for a 5 V relay board.
Wiring: relay module IN → Out1 signal terminal, relay module GND → the
bridge GND, relay module VCC → its own 5 V supply.
For the common active LOW relay boards this stage is the wrong polarity — those need their input pulled to GND. Drive them through a small inverting transistor stage, or pick an active-high / optocoupler-input module.
Web UI → I/O Configuration → Output 1:
| Field | Value |
|---|---|
| Enable Output 1 | ✅ |
| Name | Garage socket |
| GPIO | 25 |
| Active low | ❌ (only tick this if ON/OFF end up swapped) |
| Restore after reboot | ❌ |
The switch appears in Home Assistant and in the Web UI status area.
State topic (retained, published on every change):
hormann/<device_id>/io {"in1":"true","in2":"false","out1":"false","out2":"true"}
Command topics for the outputs:
hormann/<device_id>/command/out1 true | false | toggle
hormann/<device_id>/command/out2 true | false | toggle
Only enabled channels appear in the payload. Example:
mosquitto_pub -h broker -t 'hormann/hcpbridge/command/out1' -m 'true'
mosquitto_sub -h broker -t 'hormann/hcpbridge/io'Auto-discovery is sent at boot:
binary_sensorfor every enabled input, named after the configured nameswitchfor every enabled output
Disabled channels get their retained discovery topic cleared, so entities disappear from Home Assistant when you turn a channel off.
Example automation using an input as a trigger:
automation:
- alias: "Garage button also turns on the outside light"
trigger:
- platform: state
entity_id: binary_sensor.garage_light_button
to: "on"
action:
- service: light.turn_on
target:
entity_id: light.outside| Route | Purpose |
|---|---|
GET /status |
contains an io object with the state of all enabled channels |
GET /sysinfo |
contains an io object with active / off per channel |
GET /io?output=1&state=on |
switch Out1 on (on, off, toggle) |
GET /io?output=2&state=toggle |
toggle Out2 |
Returns 404 if the addressed output is not enabled.
If you run the ESPHome firmware instead, esphome_hcpbridge.yaml ships
ready-to-uncomment blocks:
binary_sensor:
- platform: gpio
name: "Garage Door Input 1"
pin:
number: GPIO12
mode:
input: true
# no pullup and not inverted: the PCB divider already pulls the pin down
filters:
- delayed_on_off: 50ms
on_press:
- light.toggle: gd_light
switch:
- platform: gpio
name: "Garage Door Output 1"
pin:
number: GPIO25
inverted: false
restore_mode: ALWAYS_OFF| Symptom | Cause / fix |
|---|---|
| Entity does not appear in HA | Channel not enabled, or discovery was only sent at boot — restart the device after enabling. |
| Input permanently ON, never changes | Pull resistor is set to internal pull-up: on the prebuilt PCB the 2.2 kΩ pull-down wins and the pin reads LOW forever, which with Active low looks like "always active". Set pull to none (external) and Active low off. |
| Input never reacts | Signal too low — the divider needs ≈4 V or more at the terminal; a 3.3 V source only produces 2.13 V at the pin. Or wrong GPIO. |
| Input toggles randomly | On a self-built board without the divider the pin is floating — select internal pull-up (or pull-down) there. |
io_error in the debug entity |
The configured pin is unusable on this chip (see section 2). The channel was auto-disabled. |
| Output measures ~1 V below the jumper level | Expected — the MOSFET is a source follower and drops about one gate threshold. |
| Output floats when off | Expected. Add an external pull-down if the connected device needs a defined LOW. |
| Relay never energises | The module is probably active LOW; this stage sources instead of sinking (see section 5). |
| Output is on right after boot | Turn off Restore after reboot, or fix the Active low setting. |
| Output does nothing | On the classic ESP32, GPIO34–39 cannot be outputs. Pick another pin. |