Motion Light Controller
An ESP32, a PIR sensor, and a servo that physically flips your wall light switch. No mains wiring.
This build lets a motion sensor control an existing wall switch. The servo moves the toggle itself, so the device never touches 120V. The light stays on for five minutes after the last detected motion, and a dark web panel gives manual control from anywhere over MQTT.
What is in the build
The whole controller is a small stack of parts sitting on or near a standard wall switch. The servo does the mechanical work, and everything else is low voltage and easy to move around.
ESP32 dev board
Runs the Arduino firmware, handles the servo, reads the PIR, and keeps the MQTT connection alive.
AM312 PIR motion sensor
Small, low-power sensor that detects movement in the room and refreshes the shutoff timer.
MG90S metal-gear servo
Flips the physical wall switch. Enough torque to move a standard toggle without touching the wiring.
Custom servo mount bracket
3D-printed bracket that holds the servo in the right spot above the switch plate.
Status LEDs
Green when the light is on, red blinking when the system is idle and waiting for motion.
USB-C power supply
Powers the whole unit from a normal phone charger or wall adapter.
Why a servo instead of a relay
A relay would have to splice into the wall wiring and switch 120V. That means mains voltage, code mistakes, and the risk of leaving the switch in a weird state. A servo physically moves the existing toggle instead. Nothing in the build touches high voltage. If the device is unplugged or the code crashes, the switch still works exactly like it did before.



Retriggering timer
Every time the PIR sees motion, the shutoff deadline resets. The light only turns off after five minutes of sustained stillness, not on a fixed countdown that could cut out while someone is barely moving.
Slow servo sweeps
The servo moves the switch in 2-degree increments instead of snapping instantly. That limits the current spike at the start of each move and avoids browning out the board when the servo starts under load.
From local WiFi to MQTT over TLS
The first version ran a small web server on the ESP32 itself, so you could control the light while connected to the same network. That worked fine inside the house, but it broke the moment you left WiFi range. The current version uses a HiveMQ Cloud broker and connects over TLS. The ESP32 dials out and keeps the connection open, so it works through NAT without port forwarding or a VPN.
The UI loads with the current switch state already there, even if the device just connected.
If the controller loses power or drops offline, the panel marks it offline immediately.
Because the ESP32 opens the outbound connection, your home router does not need any inbound rules.
Dark web UI
The frontend is a single dark control panel hosted on GitHub Pages. A live animated SVG of the switch plate flips in sync with the real servo, and the plate glows amber whenever the light is on.
The countdown ticks locally and resyncs from the device whenever a new MQTT message arrives, so the timer does not drift if you leave the page open.
Build it or borrow from it.
The full Arduino firmware, 3D servo mount files, and the dark web panel are all in the GitHub repo. Stack is C++ / Arduino, MQTT over TLS, HiveMQ Cloud, HTML/CSS/JS, MQTT.js, and GitHub Pages.