A smart toilet control interface using an ESP32-C6 with touchscreen display, integrated with Home Assistant. This project monitors a toilet's status LED and provides touch-based flush controls.
This project transforms a standard electric toilet into a smart device by:
- Monitoring the toilet's status LED using an LDR (Light Dependent Resistor) sensor
- Controlling flush modes through a touchscreen interface (Heavy, Full, Normal, Extended)
- Integrating with Home Assistant for automation and remote control
-
Waveshare ESP32-C6 1.47" Touch Display Development Board
- ESP32-C6 chip with WiFi 6 and Bluetooth 5 (LE)
- 172x320 rectangular display (ST7789V driver)
- AXS5106 capacitive touch controller
-
LDR Light Sensor Module (DIY-LIGHT-MODULE-DO-2)
- Digital output (DO) for simple on/off detection
- Adjustable sensitivity via potentiometer
-
Solderless Breadboard (400 point) - for prototyping
-
USB-C Power Module (3.3V/5V switchable, 500mA protection) - optional
- VCC → ESP32 3.3V pin
- GND → ESP32 GND pin
- DO (Digital Out) → GPIO4
- Display: GPIO1 (CLK), GPIO2 (MOSI), GPIO15 (DC), GPIO14 (CS), GPIO22 (RESET)
- Backlight: GPIO23
- Touch: GPIO18 (SDA), GPIO19 (SCL), GPIO21 (Interrupt), GPIO20 (Reset)
Connect USB-C directly to the ESP32-C6 board. The board provides sufficient power for the LDR module.
- Detects when the toilet's green status LED is ON or OFF
- Uses inverted logic for reliable detection
- Display shows current LED state in status bar
Four large touch buttons for flush control:
- HEAVY (Red) - Top left
- FULL (Orange) - Top right
- NORMAL (Blue) - Bottom left
- EXTENDED (Green) - Bottom right
- Binary Sensor: Toilet Status LED (on/off)
- Text Sensor: Last Button Pressed (for automation triggers)
- Sensors: WiFi signal strength, uptime, last button timestamp
- Buttons: All four flush controls accessible in HA
- Status LED: Onboard status indicator
- Connects to your home WiFi network
- Over-the-Air (OTA) firmware updates - no USB cable needed after initial flash
- Fallback WiFi hotspot if connection fails
- Go to Settings → Add-ons
- Install ESPHome add-on
- Start the add-on and enable "Show in sidebar"
- Connect ESP32-C6 to your computer via USB-C
- In ESPHome dashboard, create new device:
toilet-control - Copy the configuration from
esp32.yml - Click Install → Plug into this computer
- Select the USB serial port (usually "USB JTAG/serial debug unit")
Update the secrets.yaml file or configuration:
wifi_ssid: "Your_WiFi_Name"
wifi_password: "Your_WiFi_Password"- Position the LDR sensor directly over the toilet's green status LED
- Tape over the sensor to block ambient light for better detection
- Adjust the potentiometer on the LDR module until it reliably detects LED on/off
- Mount the display where it's easily accessible
You have two options for handling button presses:
Option A: Simple Automations (for direct control) Create automations for each button:
- Trigger: State →
sensor.toilet_control_last_button_pressedchanges to"eco" - Action: Your toilet control action (call a service, trigger a relay, etc.)
Option B: Node-RED Flow (recommended - full featured) See Node-RED Integration section below for complete flush control logic with:
- Tuya finger bot control
- Multi-cycle flush sequences
- Motion-based BLE polling
- Emergency kill switch
- Timeout protection
The project uses a text sensor approach rather than button events because:
- Template buttons in ESPHome don't reliably trigger HA automations
- Text sensor state changes are detected immediately by HA
- The sensor resets to
"none"after 100ms to allow repeated presses
The display can be flipped 180° by changing the transform parameters:
transform:
mirror_x: false
mirror_y: trueTouch coordinates match display dimensions (172x320). If buttons don't respond correctly, adjust the touchscreen transform to match the display orientation.
- Verify pin connections match your specific board revision
- Check that
invert_colors: falseis correct for your display - Ensure backlight is controlled by GPIO23
- Check connections - Make sure DO wire is firmly connected to GPIO4
- Adjust potentiometer - Turn the blue/white screw on the LDR module while testing
- Check inverted setting - Should be
inverted: truefor most LDR modules - Test without toilet LED - Cover sensor with your finger to see if it changes state
- Tape over sensor - Block ambient light for reliable detection
- Use the text sensor (
sensor.toilet_control_last_button_pressed) as trigger, not button entities - Check Developer Tools → States to see if sensor is changing
- Verify the sensor resets to
"none"between presses
This is fixed by the 100ms reset delay in the button configuration. Each button publishes its state, waits 100ms, then resets to "none".
- Verify I2C pins (GPIO18/GPIO19) are correctly configured
- Check that external component
axs5106is loading from GitHub - Monitor logs for touch coordinates when pressing
- Dupont Connectors (Recommended) - Crimp female Dupont connectors onto wires, plug onto header pins
- Direct Soldering - Solder wires to header pins with heat shrink tubing
- Pin Header Sockets - Solder female header strip to wires
- JST Connectors - Use pre-made JST pigtails for modular connections
Pro Tip: Use color-coded wires (Red=3.3V, Black=GND, Yellow=Signal)
- 3D Printed - Search Thingiverse/Printables for "ESP32-C6 1.47" cases
- Project Box - Use ~80mm x 50mm x 30mm generic enclosures with display cutout
- DIY Container - Modified mint tin or small food container
- Wall Mount - Mount openly with the LDR sensor positioned remotely via wires
Started as a simple "LED Light Monitor" to detect when an indicator LED turns on/off.
Expanded to include:
- Four touch buttons for different flush modes
- Full-screen button layout
- Status indicators for LED, WiFi, and button presses
- Robust Home Assistant integration
-
Display Type: The Waveshare 1.47" board uses a rectangular 172x320 display (ST7789V), not the round GC9A01A initially assumed
-
Button Events: ESPHome template buttons don't generate reliable HA automation triggers - use text sensors instead
-
Pin Conflicts: Display/touch uses many pins; LDR had to move from GPIO2 → GPIO4 to avoid conflicts
-
Color Syntax: ESPHome uses
Color(R, G, B)format, notColor::GREENconstants -
Inverted Logic: LDR module requires
inverted: truefor correct on/off detection -
Repeated Presses: Text sensor needs reset delay (100ms) to detect same button pressed multiple times
-
Ambient Light: Taping over the LDR sensor dramatically improves detection reliability
esp32.yml- Complete ESPHome configurationelectrictoilet.json- Node-RED flow for flush control logicREADME.md- This documentation (overview and getting started)NODE-RED-INTEGRATION.md- Detailed Node-RED integration guideARCHITECTURE.md- System architecture and data flow diagramsDEVELOPMENT_NOTES.md- Troubleshooting history and lessons learned.gitignore- Excludes sensitive files
This project includes a complete Node-RED flow for intelligent flush control using Tuya finger bot switches.
The Node-RED flow provides sophisticated flush control with:
- 4 Flush Modes: Normal, Heavy, Full, Extended (each with custom timing and cycles)
- Smart Polling: Motion sensor triggers BLE keep-alive polling (solves Tuya sleep delays)
- Safety Features: Emergency kill switch (leak sensor), timeout protection, online checking
- Notifications: Pushover integration for status and errors
- Cycle Management: Automatically sequences fill/empty cycles based on LED status
-
Install Node-RED (if not already installed):
- Home Assistant: Install "Node-RED" add-on
- Standalone: Follow Node-RED installation guide
-
Import Flow:
Node-RED Menu → Import → Clipboard → Paste contents of electrictoilet.json → Click Import → Deploy -
Configure Entities:
- Ensure Home Assistant integration is connected
- Verify Tuya entities exist:
switch.finger_robot_2_switch(Fill button)switch.finger_robot_switch(Empty button)
- Optional: Configure Pushover for notifications
Button Press → Node-RED Detects Change → Selects Mode → Sets Delays
↓
Fill Cycle → Wait for LED ON → Wait for LED OFF
↓
Empty Cycle → Wait for LED ON → Wait for LED OFF
↓
Repeat (N cycles) → Flush Complete → Notification
Flush Modes:
- Normal: 3 cycles, 4s fill / 3s empty (quick flush)
- Heavy: 3 cycles, 7s fill / 6s empty (powerful flush)
- Full: 3 cycles, 4s fill / 3s empty (complete flush)
- Extended: 12 cycles, 7s fill / 6s empty (thorough cleaning)
Motion-Based Polling (solves Tuya BLE sleep delays):
- When motion detected in bathroom → Start polling every 15 seconds
- Polls both finger bots to keep them awake
- Continues for 10 polls (2.5 minutes)
- Result: Near-instant response when bathroom is in use
For complete details, see:
- NODE-RED-INTEGRATION.md - Detailed setup, configuration, and troubleshooting
- ARCHITECTURE.md - System architecture, data flows, and diagrams
| ESP32 Entities | Tuya Entities | YoLink Entities (Optional) |
|---|---|---|
sensor.toilet_control_last_button_timestamp |
switch.finger_robot_2_switch |
binary_sensor.yolink_motionsensor_*_motion |
sensor.toilet_control_last_button_pressed |
switch.finger_robot_switch |
sensor.yolink_leaksensor_*_leak_switch |
binary_sensor.toilet_control_toilet_status_led |
number.finger_robot_2_down_delay |
|
binary_sensor.toilet_control_toilet_control_online |
number.finger_robot_down_delay |
Potential additions:
- Usage statistics and tracking
- Scheduled automatic cleaning cycles
- Water usage monitoring (if toilet provides data)
- Voice control via Home Assistant
- Push notifications on status changes
- Battery backup for power outages
- Multiple language support
- Multi-toilet support with flow replication
- InfluxDB logging and Grafana dashboards
This project configuration is provided as-is for personal use.
Developed through iterative troubleshooting and collaboration. Special thanks to the ESPHome and Home Assistant communities for their excellent documentation and support.