- C++ 100%
| src | ||
| .gitignore | ||
| platformio.ini | ||
| README.md | ||
ESP32 Smart Mirror Controller
Reverse-engineered ESP32 controller for an OEM smart mirror with heater and LED lighting control over MQTT.
This project connects an ESP32 directly to the signal pins of the original smart mirror controller. It keeps the mirror’s original capacitive touch buttons working, while adding smart-home control through MQTT, making it easy to integrate the mirror with OpenHAB, Home Assistant, Node-RED, or any other MQTT-based automation system.
What It Does
The ESP32 reads the original capacitive button signal lines from the mirror controller and mirrors their state into GPIO outputs that control:
- Mirror heater
- LED lighting
- LED brightness
The firmware preserves the original physical button behavior and adds remote control through MQTT topics.
The main goal is to make an existing OEM smart mirror smart without fully replacing its original controller.
Features
- Reverse-engineered OEM smart mirror button signal handling
- MQTT control for heater and LED lighting
- OpenHAB-compatible MQTT topic structure
- Physical capacitive buttons remain fully functional
- Offline-first operation
- Non-blocking WiFi and MQTT reconnect logic
- Automatic startup state synchronization
- LED brightness control using PWM
- Retained MQTT state publishing
- Compatible with Arduino-ESP32 v3+ LEDC API
Hardware Overview
The ESP32 is connected to the mirror controller’s internal signal lines.
Inputs
| ESP32 Pin | Function | Signal Meaning |
|---|---|---|
| GPIO32 | Heater button signal | Detects OFF / ON / HELD |
| GPIO33 | LED button signal | Detects OFF / ON / HELD |
The input signals are inverted and level-shifted before reaching the ESP32.
Signal interpretation:
| Signal | Meaning |
|---|---|
| PWM, about 330 Hz, about 33% duty | Device OFF |
| HIGH, 3.3 V | Device ON |
| LOW, 0 V | Button HELD |
For the heater, the HELD state is ignored.
For the LED, the HELD state is used for brightness adjustment.
Outputs
| ESP32 Pin | Function |
|---|---|
| GPIO13 | Heater control output |
| GPIO12 | LED PWM output |
The LED output uses inverted PWM:
| PWM Duty | LED State |
|---|---|
| 255 | OFF |
| 0 | Maximum brightness |
Note: GPIO12 is a strapping pin on the ESP32. Make sure your hardware does not pull it into an invalid boot state.
MQTT Topics
Commands
| Topic | Payload | Description |
|---|---|---|
smartmirror/heater/set |
ON / OFF |
Turn heater on or off |
smartmirror/led/set |
ON / OFF |
Turn LED lighting on or off |
smartmirror/led/brightness/set |
0 - 100 |
Set LED brightness percentage |
States
| Topic | Payload | Retained | Description |
|---|---|---|---|
smartmirror/heater/state |
ON / OFF |
Yes | Current heater state |
smartmirror/led/state |
ON / OFF |
Yes | Current LED state |
smartmirror/led/brightness |
0 - 100 |
Yes | Current LED brightness percentage |
OpenHAB Integration Example
Example MQTT Things/items can be created around these topics:
smartmirror/heater/set
smartmirror/heater/state
smartmirror/led/set
smartmirror/led/state
smartmirror/led/brightness/set
smartmirror/led/brightness
Typical OpenHAB items:
Switch Mirror_Heater
Switch Mirror_LED
Dimmer Mirror_LED_Brightness
The firmware publishes retained state messages, so OpenHAB can restore the last known state after restart.
Offline-First Behavior
The mirror remains usable even when WiFi or MQTT is unavailable.
Physical buttons are always handled locally by the ESP32. Network reconnect attempts happen in the background and do not block button handling.
This means:
- The heater button still works without WiFi
- The LED button still works without WiFi
- Brightness adjustment still works without WiFi
- MQTT state is republished after reconnection
Startup State Sync
On boot, the ESP32 samples the original mirror controller signal pins and synchronizes its outputs to the detected state.
This avoids the common issue where the ESP32 starts with the wrong output state and requires an extra physical tap to sync.
Startup behavior:
- Sample heater and LED signal pins
- Detect whether each device is currently ON or OFF
- Apply the matching ESP32 output state
- Publish MQTT state when MQTT becomes available
If a signal appears to be in HELD state during boot, the firmware waits briefly for it to resolve. If it cannot resolve the state, it defaults to OFF for safety.
Button Signal Logic
The original controller signal is decoded like this:
- PWM signal means the device is OFF
- Constant HIGH means the device is ON
- Constant LOW means the button is being held
To avoid false triggers, the firmware only reacts to stable OFF-to-ON or ON-to-OFF transitions.
This prevents double-press behavior and avoids unwanted toggles caused by noisy signal edges.
LED Brightness Logic
When the LED button is held:
- Brightness changes gradually
- Direction alternates after each hold-release cycle
- One hold increases brightness
- Next hold decreases brightness
Brightness is stored internally on a 0..255 scale and published over MQTT as 0..100.
Configuration
Edit these values in the source code before flashing:
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_SERVER = "192.168.0.100";
const int MQTT_PORT = 1883;
const char* MQTT_CLIENT_ID = "ESP32_SmartMirror";
You can also change the MQTT topic names:
const char* TOPIC_HEATER_CMD = "smartmirror/heater/set";
const char* TOPIC_LED_CMD = "smartmirror/led/set";
const char* TOPIC_HEATER_STATE = "smartmirror/heater/state";
const char* TOPIC_LED_STATE = "smartmirror/led/state";
const char* TOPIC_LED_BRIGHTNESS_CMD = "smartmirror/led/brightness/set";
const char* TOPIC_LED_BRIGHTNESS_STATE = "smartmirror/led/brightness";
Required Libraries
Install these Arduino libraries:
WiFiPubSubClient
The firmware is written for Arduino-ESP32 v3+ and uses the newer LEDC API:
ledcAttach(pin, frequency, resolution);
ledcWrite(pin, duty);
Safety Notes
This project involves modifying an OEM smart mirror controller.
Depending on the mirror design, the original controller may be connected near mains voltage. Be careful when opening or modifying the mirror.
Recommended precautions:
- Disconnect mains power before working on the mirror
- Verify signal levels before connecting them to the ESP32
- Use proper level shifting if needed
- Do not connect 5 V logic directly to ESP32 GPIO pins
- Keep low-voltage ESP32 wiring isolated from mains wiring
- Add fuses or protection where appropriate
- Do not work on live mains-powered hardware
Project Status
This firmware is a working reverse-engineered controller for a specific smart mirror setup.
It may need adjustment for other mirror models because OEM controller signals can differ between manufacturers.
Possible Future Improvements
- Move WiFi and MQTT configuration to a separate config file
- Add Home Assistant MQTT discovery
- Add OpenHAB example configuration files
- Add web-based configuration portal
- Add OTA firmware updates
- Add persistent brightness storage in flash
- Add watchdog recovery
- Add optional failsafe timeout for heater control
Disclaimer
This project is provided for educational and personal use.
Modifying mains-powered devices can be dangerous. You are responsible for verifying the safety of your own hardware, wiring, and installation.