Skip to content

Sonoff RF Bridge

There is a whole world of $3 sensors on 433 MHz — door contacts, PIRs, water leak pucks, four-button keyfobs — that predate WiFi and will outlast it on a coin cell. The RF Bridge is the translator: an ESP8266 and a dedicated RF chip in one small box, talking to each other over a serial link. Flashed with ESPHome, everything it hears becomes a Home Assistant event, and anything Home Assistant wants to say goes back out the same antenna.

It is also the gentlest Sonoff to flash. The bridge runs on 5 V over micro-USB and there is no mains anywhere near the board, so unlike the S20 and S31 there is no step where you have to think about what is live.

Parts

Some links below are Amazon affiliate links. As an Amazon Associate I earn from qualifying purchases — at no extra cost to you. It helps keep these guides free. Outside the US, they should send you to your own Amazon store; if one doesn't, searching the part number there will find it.

And then something for it to talk to. Hearing and transmitting are two different jobs, and they want different hardware:

  • 433 MHz EV1527 wall button — a single button on an adhesive pad, running on a coin cell. EV1527 is a fixed code, which is the kind the bridge can both hear and replay. It is the cheapest way to put a scene button on a wall with no wire behind it.
  • Mini 433 MHz transmitter PCB, 3–12 V — a bare sender. Wire a contact across it and you have built a sensor for whatever the shop-bought ones don’t cover.
  • 433 MHz relay receiver with a momentary remote or the latching version — this is the transmit half of the job. Learn the remote’s code once, and Home Assistant can drive the relay from an automation without any of the receiver’s firmware knowing it happened.
  • 433 MHz door contacts, PIRs and leak sensors — the multipacks sold for cheap alarm panels. Buy one pack of one brand rather than three brands, and see the last item in Troubleshooting before you order a dozen.

If all you want is one remote switching one load, you don’t need a bridge at all. The Sonoff RF switch is an inline WiFi relay with a 433 receiver built into it, so it does that one job on its own. The bridge earns its place when you want the codes in Home Assistant, where they can trigger anything.

Wiring

Four connections to flash it, and the data pair crosses.

Serial adapterBridge headerNote
3V33V3Jumper the adapter to 3.3 V first
TXRXCrossed
RXTXCrossed
GNDGND

The adapter transmits into the board’s receiver. Getting that backwards is the single most common reason a flash never starts.

Flashing

  1. Open the case. Screws first, then work the seam apart with a nylon wedge — the rest of it is clips.

  2. Solder a four-pin header into the programming holes marked 3V3, RX, TX, GND, or press a pogo-pin jig against them. Holding loose jumper wires in the holes works right up until one twitches mid-flash.

  3. Wire the adapter to the table above, with the USB end still unplugged.

  4. Pull GPIO0 low as power arrives. That is what selects flash mode. Which physical thing does it depends on the revision in front of you — some expose a button, others a pad you bridge to ground. Whichever yours has, hold it as you plug the adapter into USB and for a second or two after.

  5. Flash sonoff-rf-bridge.yaml over the serial port. Read the configuration section below first. Everything after this first flash is over WiFi.

How the two halves talk

Unlike the S20 and S31, the interesting pins here are not GPIO you wire anything to. The bridge carries its own RF chip that does the demodulating, and it reports to the ESP8266 over a serial link that is already routed on the PCB. All the config does is name it:

FunctionESP pinIn the config
RF module → ESPGPIO3uart: rx_pin: 3 — decoded codes arrive here
ESP → RF moduleGPIO1uart: tx_pin: 1 — codes to transmit go out here
Link speedbaud_rate: 19200

GPIO1 and GPIO3 are the ESP8266’s hardware UART, which is also where the logger writes by default. That is why the config carries this:

logger:
baud_rate: 0

It only switches off logging over the serial pins. ESPHome’s log stream over WiFi, which is the one you actually read, is untouched. Turn serial logging back on and the bridge stops hearing anything — the log and the RF chip end up shouting over each other on the same two wires.

Configuration

Two substitutions at the top name the device. The file ships with these, and they work as they are:

substitutions:
device_name: "Sonoff RF Bridge"
device_id: "sonoff-rf-bridge"

Change both if you will ever run a second bridge. Everything downstream is built from device_id, including the service names below, so pick it once and leave it alone.

The rest comes from secrets.yamlwifi_ssid, wifi_password and ota_password. There is a secrets.yaml.example in the repo with every key these configs use.

There is no ap: block and no encryption on the API. Both are worth adding: the fallback access point is what catches a mistyped WiFi password without the case coming off again, and secrets.yaml.example already carries an api_key for the encryption: block. Keep the additions small either way — esp01_1m means 1 MB of flash, and an over-the-air update needs room for the new image beside the running one.

What you actually get

Nothing in the entity list. The config declares no sensors, no switches and no binary sensors, and a lot of people flash it and then go looking for the device in Home Assistant wondering what went wrong. What it gives you is two services and one event:

ThingWhat it does
esphome.rf_code_receivedAn event, fired every time the bridge decodes a code. Carries sync, low, high and code
esphome.sonoff_rf_bridge_send_rf_codeA service. Transmits a code — takes the same four values as integers
esphome.sonoff_rf_bridge_learnA service. Runs the RF module’s own learn cycle

The two service names are the node name with its dashes turned into underscores, so they change with device_id if you rename the device.

Learning a code

The bridge reports every code it decodes on its own — you do not have to call anything to make that happen. rf_bridge.learn is there for the module’s explicit learn cycle, and is the fallback rather than the first move.

  1. Open Developer Tools → Events in Home Assistant and listen to esphome.rf_code_received. This is the authoritative view, because it is exactly what the config emits.

  2. Trigger the sensor — open the door, wave at the PIR, press the fob.

  3. An event appears with four hex values. code is the sensor’s identity; write it down. sync, low and high are the protocol’s timings and matter only when you want to transmit the same thing later.

  4. Press it twice more. If code comes back identical every time, it is a fixed-code device and you can both automate on it and replay it. If it changes each press, it is rolling-code and there is nothing to match — that is the remote working as designed, not a fault.

  5. Write the automation against the event. Copy the hex string out of Developer Tools rather than retyping it; the event data are strings, and a string only matches if it matches exactly.

automation:
- alias: "Laundry door opened"
triggers:
- trigger: event
event_type: esphome.rf_code_received
event_data:
code: "5a3c81"
actions:
- action: notify.mobile_app_phone
data:
message: "The laundry door opened"

One automation per sensor. The codes are burned in at the factory, so a sensor keeps its identity across battery changes and you only ever do this once.

Sending a code

The transmit side is where the one real trap in this config lives. The event publishes its four values as hex strings, because the config runs them through format_hex on the way out. The send_rf_code service declares all four as integers. They are the same numbers written two ways, and nothing in between converts for you.

Do it in the call:

actions:
- action: esphome.sonoff_rf_bridge_send_rf_code
data:
sync: "{{ '2a6c' | int(base=16) }}"
low: "{{ '015e' | int(base=16) }}"
high: "{{ '0424' | int(base=16) }}"
code: "{{ '5a3c81' | int(base=16) }}"

The four strings there are placeholders — use the ones your own event handed you. That is the whole recipe for driving a 433 relay receiver from Home Assistant: press its remote once with Developer Tools listening, then have an automation replay those four numbers.

433 MHz sensors of this type are not encrypted or authenticated, and anything in range can transmit the same code — including you, now that you own a bridge that does exactly that. They are fine for “the laundry door opened” and a bad idea as the only thing standing between someone and your alarm being disarmed.

Troubleshooting

No codes ever appear. Usually the wrong band — a 315 MHz sensor against a 433 MHz bridge. After that, check logger: still has baud_rate: 0; if serial logging came back, the RF module cannot get a word in.

Codes appear in the log but the automation never fires. The event data are strings and the comparison is exact. Copy the value out of Developer Tools instead of retyping it, and don’t add an 0x prefix that was never there.

Sending does nothing at all. Hex where the service wants an integer. Convert it in the call — see Sending a code.

The sensor works next to the bridge and not from the far end of the house. The onboard antenna is a trace on the PCB, so the cheapest fix by a wide margin is moving the bridge somewhere central rather than somewhere convenient. If you go looking for the wire-antenna mod afterwards, a quarter wave at 433.92 MHz is 17.3 cm.

Two bridges, and every code looks like it came from both. The event name in the config is a fixed string, so a second bridge fires the same esphome.rf_code_received at the same Home Assistant. Add a line naming the device to the event’s data: block and match on it in the automation.

One sensor triggers another’s automation. Two devices shipped with the same code — common in cheap multipacks programmed in one batch at the factory. Check for it the moment a pack arrives, before anything is mounted: listen to the event and trigger each sensor in turn. Anything duplicated has to go back or go somewhere it cannot matter.

Files & downloads

ESPHome configuration

Copy this into your ESPHome directory and adjust the substitutions at the top. Secrets are referenced by name — see secrets.yaml.example .

sonoff-rf-bridge.yaml 65 lines
sonoff-rf-bridge.yaml
# Sonoff RF Bridge
# ────────────────────────────────────────────────────────────
# 433MHz RF bridge for receiving and transmitting RF codes.
# Allows integration of wireless RF remotes and sensors
# with Home Assistant.
#
# Hardware:
# - Board: ESP8266 (esp01_1m)
# - Protocol: RF 433MHz via RF Bridge module
# ────────────────────────────────────────────────────────────
substitutions:
device_name: "Sonoff RF Bridge"
device_id: "sonoff-rf-bridge"
esphome:
name: ${device_id}
esp8266:
board: esp01_1m
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ota:
password: !secret ota_password
platform: esphome
api:
services:
- service: send_rf_code
variables:
sync: int
low: int
high: int
code: int
then:
- rf_bridge.send_code:
sync: !lambda 'return sync;'
low: !lambda 'return low;'
high: !lambda 'return high;'
code: !lambda 'return code;'
- service: learn
then:
- rf_bridge.learn
uart:
tx_pin: 1
rx_pin: 3
baud_rate: 19200
logger:
baud_rate: 0
rf_bridge:
on_code_received:
then:
- homeassistant.event:
event: esphome.rf_code_received
data:
sync: !lambda 'return format_hex(data.sync);'
low: !lambda 'return format_hex(data.low);'
high: !lambda 'return format_hex(data.high);'
code: !lambda 'return format_hex(data.code);'

View on GitHub · Download raw

Every file for this project on GitHub →