WS2812B LED strip glowing warm on a desk edge, wired to PIR sensor and ESP32-C3 on a breadboard

Beginner

Build a Motion-Activated Desk Light

SensorsAutomationDisplays

Use a motion sensor and controller to switch a low-voltage light automatically, introducing digital inputs, timing and simple automation logic.

7 real parts included

Total for this kit: 1,640.00 ฿

Doesn't include any order discount you may get at checkout.

What you'll need

Project Goal

This project introduces automation logic using the simplest possible physical action: a light that turns on when it detects movement and off again shortly after. It's a natural second project after a sensor-reading build, since it adds a digital input and a decision (should the light be on right now?) rather than just displaying a value.

Motion Sensing

A common motion sensor for this kind of project detects changes in infrared radiation in its field of view and outputs a simple digital signal -- high when motion is detected, low when it isn't. There's no complex data to parse, which makes it a good first sensor for learning digital inputs specifically.

Digital Input Basics

Unlike an analog sensor, a digital input only ever reports one of two states. Your controller reads that pin on each loop and simply checks: is it currently high, or low? Every decision in this project builds from that one check.

Safe Low-Voltage Switching

This project is intentionally scoped to low-voltage lighting only -- a light powered the same way as your controller, switched through a low-voltage-rated driver or transistor circuit appropriate for that specific light. Do not attempt to adapt this project to switch mains-voltage household lighting; that requires different components, different safety knowledge, and is outside the scope of this guide entirely.

Timing Logic

Rather than turning the light off the instant motion stops being detected, a workable version keeps the light on for a short fixed period after the last detected motion, refreshing that timer every time new motion is seen. This is what makes the light feel like it's staying on for a person moving around, rather than flickering off the moment they briefly stand still.

Avoiding Repeated Triggering

Without any timing logic at all, a motion sensor firing rapidly can make a light flicker on and off many times a second. The timed stay-on window described above is the simplest fix, and is worth implementing before doing anything more elaborate.

Basic Debugging

  • Light never turns on -- confirm the sensor's output pin state directly (a simple readout, before adding any light logic) to isolate whether the sensor or the light-switching side is at fault.
  • Light stays on permanently -- check whether your sensor has its own onboard sensitivity/time delay adjustment that might be interfering with your own timing logic.
  • Light flickers rapidly -- this is the repeated-triggering problem above; add or lengthen the stay-on timer.

Optional Upgrade: Ambient-Light Brightness

Once basic motion switching works reliably, a natural extension is adding a simple light-level sensor so the light only activates when the room is actually dim -- avoiding pointless daytime triggering.

Wiring

  • PIR module VCC → ESP32-C3 SuperMini 5V (VBUS) — most PIR modules want 5V; check your specific module's rated range
  • PIR module GND → ESP32-C3 SuperMini GND
  • PIR module OUT → ESP32-C3 SuperMini GPIO4 — digital input, goes HIGH on motion
  • External DC supply (e.g. 9–12V) → LM2596 IN+ / IN− (skip this if powering everything from 5V already — see notes)
  • LM2596 OUT+ → WS2812B strip 5V input
  • LM2596 OUT− → WS2812B strip GND, tied to ESP32-C3 GND (common ground across all supplies)
  • WS2812B DIN → ESP32-C3 SuperMini GPIO5, through a ~330–470Ω resistor in series — reduces reflections/protects the first LED
  • (Optional) large electrolytic capacitor (≥100–1000µF) across strip 5V/GND right at the strip — absorbs the strip's power-on current surge

Before ever connecting the LED strip, power the LM2596 alone and trim its onboard potentiometer while measuring the output with a multimeter until it reads a clean 5.0V — feeding the strip an untrimmed higher voltage will destroy it. Mind the current budget: 30 WS2812B LEDs at full white can draw up to ~1.8A, well beyond what the ESP32-C3's own 5V/USB pin can safely supply, so use the LM2596 (or an external 5V supply) for the strip and never power the strip from the SuperMini's regulator. The ESP32's 3.3V data signal driving 5V-logic WS2812B works reliably on short strips in most builds; if you see flicker or wrong colors on the first pixel, add a 74HCT125/level shifter between GPIO5 and DIN.

Wiring Diagram

VCC → 5V GND OUT → GPIO4 IN− OUT+ → input OUT− → GND DIN → GPIO5 ESP32-C3 SuperMini PIR module WS2812B LM2596 External DC supply

Starter Code

#include <Adafruit_NeoPixel.h>

#define PIR_PIN 4
#define LED_DATA_PIN 5
#define NUM_LEDS 30

// How long to keep the light on after the last motion pulse
const unsigned long HOLD_TIME_MS = 30000;

Adafruit_NeoPixel strip(NUM_LEDS, LED_DATA_PIN, NEO_GRB + NEO_KHZ800);

bool lightsOn = false;
unsigned long lastMotionTime = 0;

void setLights(bool on) {
  uint32_t color = on ? strip.Color(255, 214, 170) : strip.Color(0, 0, 0); // warm white / off
  for (int i = 0; i < NUM_LEDS; i++) {
    strip.setPixelColor(i, color);
  }
  strip.show();
  lightsOn = on;
}

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);

  strip.begin();
  strip.setBrightness(180);
  setLights(false);

  // Many PIR modules need 30-60s to calibrate ambient IR after power-up
  Serial.println("Warming up PIR...");
  delay(30000);
  Serial.println("Ready.");
}

void loop() {
  if (digitalRead(PIR_PIN) == HIGH) {
    lastMotionTime = millis();
    if (!lightsOn) {
      Serial.println("Motion detected — lights on");
      setLights(true);
    }
  }

  if (lightsOn && (millis() - lastMotionTime > HOLD_TIME_MS)) {
    Serial.println("Timeout — lights off");
    setLights(false);
  }
}

Not sure about a step? Ask us