LeBrondell (Part 1)

Fri 19 June 2026

Brondell + Zephyr = LeBrondell

"A throne fit for a king"

TL;DR I decoded a Brondell bidet IR remote and replaced it with a Zephyr module. See the code @ https://github.com/TortugaCarey/brondellir

Part 1

Background

The idea behind this project is to upgrade a bidet, liberating it from the infrared remote control and allow for smart home integration or similar network-based control (such as a wall panel unit). After integrating a custom microcontroller, layering on additional hardware and/or features could be possible. For example, user recognition could be added by proximity detection to a mobile device via Bluetooth, or adding a load sensor to the seat could make it feasible to recognize users and automatically apply custom configurations as soon as they sit.

The bidet used for this project is a Brondell Swash EM617 . The bidets in the Swash line are packed with features such as multiple nozzle positions and water pressures, a water heater, a seat heater, and a drying fan. Some bidet models have an array of buttons directly attached to the seat, while on the EM617 most of the settings are only available on the infrared (IR) remote control with a very limited set of functions available with buttons on the seat itself.

Infrared Remote Control Basics

With five nozzle positions, five water pressures, four water temperatures, four seat temperatures, and three eco modes, the multitude of settings leads to a large number of possible remote control key code combinations for wash cycles alone (5 x 5 x 4 x 4 x 3 = 1200). Also add on the dry cycle with five dry temperatures and a few individual operations like Pulse, Stop, and Nozzle Clean. To deal with all of the combinations of settings, Brondell uses a bitmask approach where certain bits in the remote control code are used to convey the individual settings. Also worth noting is that while most typical IR remotes use 4 byte (32 bit) commands, the Brondell remote control transmits 8 bytes (64 bits) of data on each key press.

Deciphering the Settings and Bits

In order to figure out which bits represent which settings, an IR Remote Receiver was connected to a Raspberry Pi with LIRC based tools. The IR Receiver board was an Adafruit 5939, because it was available same day at Micro Center. Even though products were almost always overpriced, it is a shame that the much more ubiquitous and conveniently located strip mall Radio Shack stores are no longer an option for picking up electronics components on short notice. Wiring up the IR Receiver and Raspberry Pi was trivial, and the LEDs on the IR board came in handy to make sure that it was powered properly and receiving data.

LIRC on Linux is fairly complicated. There are a couple of sets of commands that could have been potentially useful, from the "lirc" ones (lirc-config-tool, lircd-uinput, lirc-lsremotes, lircrcd, lircd, lirc-init-db, lirc-make-devinput, lirc-setup, lircd-setup, lirc-lsplugins, lircmd), to the "ir" ones (ircat, irdb-get, ir-keytable, irpty, irsend, irsimsend, irtext2udp, ir-ctl, irexec, irpipe, irrecord, irsimreceive, irtestcase, irw). Some of those are used for sending/transmitting and in this case only capturing/receiving was of interest. There is no existing configuration or driver for this type of remote and trying to figure out which commands were even useful was a chore in itself. The command that ended up being the most useful for starting to dump codes was ir-ctl. After enabling the gpio-ir overlay on the Raspberry Pi, ir-ctl -r (and the more verbosely formatted ir-ctl -r --mode2) started showing the "pulses and spaces" when the EM617 remote keys were pressed. This was a good indication that the hardware setup was working at a basic level. Converting the output of ir-ctl into binary or hex was yet another chore, but the open source community is amazing and so many people have written and published so much useful code. The pyIR project provided a great starting point to perform IR captures in Python and manipulate the data without having to parse text output from the command line. Shout out to Will Hall for saving a lot of extra work- if we ever meet in person expect a free beer.

With the combination of these tools, it was fairly easy to capture and dump the remote keys in binary and hex, and then also see which settings were conveyed in which bits. An oscilloscope probe on the IR Receiver data pin also made it possible to see the signal, count 1’s and 0’s and visually verify that the Python code was spitting out the correct bits and values.

Oscilloscope full 'stop' command

Full 'stop' command

Oscilloscope zoomed in beginning of 'stop' command

Zoomed in beginning of 'stop' command

As previously mentioned, the Brondell remote control transmits 8 bytes (64 bits) of data on each key press, call them b0 through b7. The 2 least significant bytes, b0 and b1, seem to be static, perhaps indicating a product or model identifier. Bytes b2, b3, and b4 convey the various settings (nozzle position, water pressure, water temperature, seat temperature, etc). Byte b5 also seems to be a static value, not changing for any combination of settings or functions. The 2 most significant bytes, b6 and b7, contain a checksum for the command.

With this information, it was possible to assemble custom remote key values for any combination of settings- Nozzle Position "4" with Water Pressure "3", Water Temperature "1", and Seat Temperature "2"… except for the checksum.

Checksum Detour

For any combination of settings/bits, a valid checksum had to be calculated in order for the the bidet to respond to the command. The easiest way to figure out the checksum calculation was to mash a bunch of remote control buttons while capturing the hex codes, then feed those hex values to Claude and ask how the checksum bytes were calculated. Claude did not disappoint. Within about 5 minutes it had figured out the checksum calculation. It’s neat that Anthropic has spent a gazillion dollars on AI making it possible to customize a toilet.

Validation

After figuring out which settings were conveyed in which data bits and tackling the checksum problem, it was possible to generate a valid data pattern for any combination of settings. Connecting an IR LED to the microcontroller GPIO pin (through a SN74AHCT125N logic level converter to bump the GPIO 3.3V up to 5V) made it easy to verify that the bidet would perform the correct function when the commands were sent. This was basically a reimplementation of the IR remote that came with the unit and as long as the IR LED was sufficiently aimed at the bidet’s IR receiver, commands were functioning as expected. Verification was performed with both a Raspberry Pi Pico 2 W and an ESP32-S3 DevKitC.

Pico2W breadboard

Breadboard assembly with Pico 2 W

Yes, I should have a current limiting series resistor for that IR LED.

See the Zephyr module source code @ https://github.com/TortugaCarey/brondellir

Stay tuned, Part 2 will explore replacing IR with network-based control...

Category: LeBrondell Tagged: embedded zephyr brondellir

Page 1 of 1