No description
Find a file
2026-05-02 15:09:57 +02:00
src remove wifi credentials 2026-05-02 15:09:57 +02:00
.gitignore add README.md 2026-05-02 14:59:01 +02:00
platformio.ini add missed depency 2026-05-02 14:49:23 +02:00
README.md update README.md 2026-05-02 15:08:39 +02:00

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 mirrors 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 controllers 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:

  1. Sample heater and LED signal pins
  2. Detect whether each device is currently ON or OFF
  3. Apply the matching ESP32 output state
  4. 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:

  • WiFi
  • PubSubClient

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.