The classic magic mirror raspberry pi project is a rite of passage for embedded hobbyists, but most online tutorials share a critical hardware flaw: they use PIR motion sensors that cannot see through glass. When you seal your monitor behind a two-way mirror, the display stays on 24/7, wasting power and causing severe LCD burn-in.

In this guide, we will build a motion-activated smart mirror using a Raspberry Pi 4 Model B (4GB) running Raspberry Pi OS Bookworm (64-bit). Instead of a standard PIR sensor, we are integrating an RCWL-0516 microwave radar module, which easily penetrates glass and acrylic to detect your presence.

Difficulty: Intermediate (3/5) | Time: 3-4 Hours | Target Board: Raspberry Pi 4 Model B (4GB) or Pi 5 (4GB)

Hardware Spec Sheet & Parts List

To ensure smooth 60fps rendering of web-based modules without thermal throttling inside a sealed wooden frame, component selection matters. Here is the exact bill of materials with 2026 pricing.

ComponentExact Model / VariantEst. CostWhy This Variant?
Compute BoardRaspberry Pi 4 Model B (4GB)$554GB handles Electron/Chromium smoothly. Pi 5 runs hotter and requires active cooling in sealed frames.
Display24-inch 1080p IPS Monitor (e.g., Dell P2425H)$110IPS panels offer wide viewing angles, crucial when looking at a mirror from the side.
Mirror GlassTwo-Way Acrylic Mirror (1/8 inch thick)$45Acrylic is lighter and safer than glass; 1/8 inch allows radar penetration.
Motion SensorRCWL-0516 Microwave Radar$4Emits 5.8GHz microwaves that pass through non-metallic solids (unlike PIR infrared).
Power SupplyOfficial 27W USB-C PD Power Supply$12Provides stable 5.1V/3A to prevent brownouts when the monitor back-light draws surge current.

Wiring the Radar Sensor (The Glass Penetration Trick)

Pro-Tip: Ditch the HC-SR501 PIR Sensor. Standard PIR sensors detect infrared heat signatures. Glass and acrylic block infrared radiation, rendering PIR useless behind a two-way mirror. The RCWL-0516 uses Doppler radar, which passes right through the mirror substrate.

The RCWL-0516 operates on 3.3V to 5V and outputs a simple HIGH/LOW digital signal. We will wire it directly to the Pi's GPIO header.

RCWL-0516 PinRaspberry Pi 4 PinFunction
VCCPin 2 (5V Power)Provides power to the microwave oscillator.
GNDPin 6 (Ground)Common ground reference.
OUTPin 11 (GPIO 17)Digital HIGH when motion is detected.

Note: Mount the radar sensor flat against the back of the two-way mirror using double-sided foam tape. The detection range is roughly 5-7 meters in a 120-degree cone. If it triggers through walls, place a small piece of copper tape on the back of the sensor PCB to shield the rear lobe.

Motion-Activated Display Control (Python Code)

Raspberry Pi OS Bookworm uses the Wayland display server by default, which breaks legacy xset screen-blanking commands. The most reliable, hardware-level method to toggle the HDMI output on Pi 4 and Pi 5 is using the vcgencmd utility via the official Raspberry Pi configuration tools.

Save the following script as mirror_wake.py. It uses the gpiozero library to monitor the radar pin and includes robust error handling and a cooldown timer to prevent rapid HDMI flickering.

import time
import subprocess
import logging
from gpiozero import MotionSensor

# --- Pin & Timing Definitions ---
RADAR_PIN = 17
TIMEOUT_SECONDS = 60  # Keep display on for 60s after last motion

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

# Initialize sensor (queue_len smooths out radar jitter)
radar = MotionSensor(RADAR_PIN, queue_len=5, threshold=0.5)
last_motion_time = time.time()
display_on = True

def set_display_power(state: int):
    """Toggles HDMI power via vcgencmd. 0=Off, 1=On."""
    global display_on
    try:
        subprocess.run(
            ['vcgencmd', 'display_power', str(state)],
            check=True,
            stdout=subprocess.DEVNULL
        )
        logging.info(f"Display powered {'ON' if state else 'OFF'}")
        display_on = bool(state)
    except subprocess.CalledProcessError as e:
        logging.error(f"vcgencmd failed with exit code {e.returncode}")
    except FileNotFoundError:
        logging.critical("vcgencmd not found. Ensure you are running on Raspberry Pi OS.")

def motion_callback():
    """Triggered on rising edge of radar OUT pin."""
    global last_motion_time
    last_motion_time = time.time()
    if not display_on:
        set_display_power(1)

# Bind callback
radar.when_motion = motion_callback

try:
    logging.info(f"Radar active on GPIO {RADAR_PIN}. Waiting for motion...")
    set_display_power(1)  # Ensure display is on at boot
    
    while True:
        # Check if timeout has elapsed without new motion
        if display_on and (time.time() - last_motion_time > TIMEOUT_SECONDS):
            set_display_power(0)
        time.sleep(5)  # Polling interval for timeout check
        
except KeyboardInterrupt:
    logging.info("Shutdown requested. Restoring display power.")
    set_display_power(1)
except Exception as e:
    logging.critical(f"Unhandled exception: {e}")
    set_display_power(1)

Run this script on boot by adding /usr/bin/python3 /home/pi/mirror_wake.py & to your /etc/rc.local file, or set it up as a systemd service for better lifecycle management.

Debugging Boot & Display Failures

When your mirror fails to wake or the MagicMirror² software crashes, don't guess. Check these exact error strings and their ranked causes.

The First Three Things to Check When It Fails:

  1. Power Delivery: Is the Pi throttling? Check vcgencmd get_throttled. If it returns anything other than 0x0, your power supply or cable is inadequate.
  2. Display Server Mismatch: Are you trying to run X11 commands on Wayland? Bookworm defaults to Wayland; adjust your launch scripts accordingly.
  3. HDMI CEC Backfeed: Is the monitor turning the Pi off? Disable CEC in your Pi config.

Error: throttled=0x50005

What it means: Under-voltage has occurred, and the Pi is currently thermally throttling.

Ranked Causes:

  1. Using a third-party phone charger instead of the official 27W USB-C PD supply.
  2. Poor ventilation inside the mirror enclosure causing the Pi 4's CPU to hit 85°C.
  3. A low-quality USB-C cable with high resistance (voltage drop).

Fix: Use the official power supply and install low-profile copper heatsinks on the Pi's SoC. Cut a passive ventilation grille into the top and bottom of your wooden frame to allow convection airflow.

Error: Failed to connect to server (when running npm start)

What it means: The Electron app cannot find the X11 display server because Raspberry Pi OS Bookworm is running Wayland.

Ranked Causes:

  1. Running the default npm start script which relies on X11.
  2. Missing Wayland-specific Electron flags in the MagicMirror² start.sh script.

Fix: Open /boot/firmware/config.txt and ensure Wayland is disabled if you want the legacy X11 experience, OR update your start.sh to use DISPLAY=:0 npm start -- --enable-features=UseOzonePlatform --ozone-platform=wayland as detailed in the official MagicMirror² documentation.

Error: CEC: Received standby command (in dmesg)

What it means: The monitor's internal logic board is sending a standby signal back through the HDMI cable, putting the Pi to sleep.

Ranked Causes:

  1. Monitor's auto-sleep feature is enabled in its physical OSD menu.
  2. HDMI CEC (Consumer Electronics Control) is active on the Pi.

Fix: Add hdmi_ignore_cec=1 to /boot/firmware/config.txt to blind the Pi to CEC commands, and disable all 'Eco' or 'Auto Power Off' settings in the monitor's physical button menu.

Extending and Simplifying Your Build

Not every space requires a full-featured 24-inch smart mirror. Here is how to scale the project to your exact needs.

How to Simplify the Build

  • Downsize the Compute: If you only plan to display a clock, date, and basic weather, swap the Pi 4 for a Raspberry Pi Zero 2 W ($15). It draws less than 2W, eliminating the need for heatsinks or ventilation grilles in small frames.
  • Ditch the Sensor: If the mirror is in a low-traffic guest bathroom, skip the radar entirely. Use the MMM-Remote-Control module to toggle the display via your smartphone when you enter the room.

How to Extend the Build

  • Add Proximity Swapping: Wire a VL53L1X Time-of-Flight (ToF) sensor via I2C. Configure MagicMirror² to show only the time when you are 2 meters away, but swap to a detailed calendar and news feed when you step within 50cm to brush your teeth.
  • Local Voice Control: Integrate an INMP441 I2S MEMS microphone and run Rhasspy locally. This allows you to say 'Turn on the bathroom lights' without sending audio to the cloud.

Magic Mirror Raspberry Pi Project FAQ

Can I use a Raspberry Pi Zero 2 W for a magic mirror raspberry pi project?

Yes, but with strict limitations. The Pi Zero 2 W has 512MB of RAM. It will handle basic text modules (clock, weather, calendar) perfectly. However, if you add heavy modules like live security camera feeds, complex CSS animations, or Spotify album art, the Electron wrapper will exhaust RAM and crash. Stick to the Pi 4 (4GB) or Pi 5 for feature-rich builds.

Why does my two-way mirror look tinted or dark?

This is a lighting physics issue, not a Pi issue. A two-way mirror requires the room behind it (where the Pi is) to be darker than the room in front of it. If your bathroom has bright daylight hitting the mirror, the reflection will wash out. Furthermore, you must set your monitor's brightness to 100% in its OSD menu and use a pure black background (#000000) in your MagicMirror CSS to maximize light transmission.

How do I prevent screen burn-in on the monitor behind the mirror?

LCD burn-in (image retention) happens when static white text is left on screen for thousands of hours. The radar-triggered Python script provided above is your primary defense, ensuring the screen is off when the room is empty. Additionally, install the MMM-Pages module to rotate your layout every 30 seconds, shifting the position of the text widgets so no single pixel remains illuminated continuously.

Do I need to strip the monitor's plastic bezel?

Yes, in almost all cases. You need the LCD panel to sit flush against the back of the two-way mirror to prevent a 'double image' ghosting effect caused by the air gap. Carefully remove the plastic front bezel using a spudger tool, leaving the metal chassis and internal driver boards intact. Secure the bare panel to the back of the mirror using VHB (Very High Bond) tape or custom 3D-printed brackets.