Building a reliable, flicker-free lamp for Raspberry Pi requires more than just wiring an LED to a GPIO pin. To handle the current of a functional desk or room lamp without burning out your Pi's logic board, you need a logic-level MOSFET driver circuit and a hardware PWM pin. By targeting GPIO 18 (the only hardware PWM0 pin available on the standard 40-pin header) and using an IRLZ44N MOSFET, you can smoothly dim a 12V LED strip from 0% to 100% without the visible flicker or audio interference that plagues software PWM implementations.

Difficulty Rating: Intermediate (Requires basic soldering, wire stripping, and Python environment setup).
Time to Build: 45 minutes.
Target Board Variants: Raspberry Pi 4 Model B (4GB) and Raspberry Pi 5 (4GB/8GB) running Raspberry Pi OS (Bookworm or newer).

Hardware BOM and Electrical Specifications

Before stripping any wires, verify your components against this bill of materials. Using a standard 5V logic MOSFET like the IRF520 will result in incomplete switching and excessive heat at 3.3V gate drive; the IRLZ44N is mandatory for direct Pi GPIO driving.

Component Exact Model / Variant Key Specification Est. Cost (2026)
Microcontroller Raspberry Pi 4B (4GB) or Pi 5 3.3V Logic, Hardware PWM on GPIO 18 $55 - $80
MOSFET IRLZ44N (Infineon/International Rectifier) Logic-Level, Vgs(th) ≤ 2.0V, Rds(on) 22mΩ $1.50
LED Load 12V 5050 SMD Analog LED Strip 14.4W/meter, Resistive/Capacitive load $15.00 / 5m
Power Supply Mean Well LRS-60-12 12V DC, 5A (60W), Enclosed $18.00
Gate Resistor 100Ω 1/4W Carbon Film Prevents high-frequency gate ringing $0.10
Pull-down Resistor 10kΩ 1/4W Carbon Film Keeps MOSFET off during Pi boot $0.10

Pin Mapping and Wiring Table

This table maps the physical Raspberry Pi header pins to the MOSFET driver circuit. Always double-check pinouts with a multimeter before applying power.

Pi Pin (BCM / Physical) Function Connects To Notes
GPIO 18 / Pin 12 Hardware PWM0 Output 100Ω Resistor → MOSFET Gate Only hardware PWM pin on standard header
GND / Pin 6 Logic Ground MOSFET Source & 10kΩ Pull-down Must share common ground with 12V PSU
N/A (External) 12V DC Positive LED Strip +12V From Mean Well LRS-60-12 V+ terminal
N/A (External) 12V DC Negative MOSFET Drain (via LED Strip) From Mean Well LRS-60-12 V- terminal

Wiring the 12V MOSFET Driver Circuit

The Raspberry Pi GPIO pins can only source about 16mA safely. A 1-meter strip of 12V LEDs draws roughly 1.2A. We use the Pi to switch the gate of the MOSFET, which in turn switches the high-current 12V load.

Safety Note: This guide uses a 12V DC isolated power supply. If you attempt to adapt this circuit to switch 120V/230V AC mains lighting, you must use an appropriately rated Solid State Relay (SSR) with zero-cross detection, enclose all mains connections in a grounded junction box, and follow local electrical codes. Never connect mains AC directly to a MOSFET or Pi GPIO.
  1. Prepare the Gate Drive: Solder the 100Ω resistor to the end of a jumper wire. Connect the other end of the resistor to the Gate (middle pin) of the IRLZ44N. This resistor dampens parasitic oscillation that can fry your Pi's GPIO pin.
  2. Install the Pull-down: Solder the 10kΩ resistor between the Gate and Source (right pin, tab is also Source) of the MOSFET. When the Pi boots, GPIO pins float; this resistor ensures the lamp stays off until your Python script explicitly initializes the pin.
  3. Wire the Logic Ground: Connect a jumper wire from the MOSFET Source to the Raspberry Pi's GND (Pin 6). Critical: The Pi and the 12V power supply must share a common ground reference for the 3.3V logic signal to be recognized by the MOSFET.
  4. Connect the 12V Load: Connect the 12V Positive output from the Mean Well PSU directly to the positive lead of the LED strip. Connect the negative lead of the LED strip to the Drain (left pin) of the MOSFET.
  5. Complete the Power Circuit: Connect the 12V Negative output from the PSU to the MOSFET Source (tying it in with the Pi GND connection made in Step 3).
  6. Connect the Signal: Finally, connect the free end of the 100Ω gate resistor wire to GPIO 18 (Physical Pin 12) on the Pi.

Python Control Code with Error Handling

We use the gpiozero library, which is pre-installed on modern Raspberry Pi OS and abstracts the hardware PWM cleanly. This code targets both the Pi 4 and Pi 5, utilizing the PWMLED class to handle the duty cycle calculations.

from gpiozero import PWMLED
from signal import pause
import sys
import logging

# Configure basic logging for debugging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Hardware PWM pin on Raspberry Pi 4/5 (BCM numbering)
LAMP_PIN = 18 

def initialize_lamp():
    """Initializes the PWM LED with hardware PWM on GPIO 18."""
    try:
        # frequency=1000 ensures smooth dimming without audible whine from the MOSFET
        lamp = PWMLED(LAMP_PIN, frequency=1000, initial_value=0)
        logging.info(f'Successfully initialized PWM lamp on GPIO {LAMP_PIN}')
        return lamp
    except Exception as e:
        logging.critical(f'Failed to initialize GPIO {LAMP_PIN}: {e}')
        sys.exit(1)

def main():
    lamp = initialize_lamp()
    
    try:
        logging.info('Starting smooth fade-in sequence...')
        # Fade in over 2 seconds, stay on, then pulse 3 times
        lamp.fade_in(fade_time=2)
        lamp.brightness = 0.85  # Set to 85% duty cycle for nominal desk lighting
        logging.info('Lamp set to 85% brightness. Press Ctrl+C to exit.')
        
        # Keep the script running to maintain the PWM signal
        pause()
        
    except KeyboardInterrupt:
        logging.info('Shutdown signal received (Ctrl+C).')
    except Exception as e:
        logging.error(f'Unexpected runtime error: {e}')
    finally:
        # Always ensure the hardware is cleaned up and turned off
        logging.info('Turning off lamp and releasing GPIO resources.')
        lamp.off()
        lamp.close()

if __name__ == '__main__':
    main()

Debugging: Fixing "RuntimeError: This module can only be run on a Raspberry Pi!"

If you copy legacy code from older tutorials using the RPi.GPIO library on a Raspberry Pi 5, your script will immediately crash with this exact error string:

RuntimeError: This module can only be run on a Raspberry Pi!

This happens because the Raspberry Pi 5 uses the new RP1 southbridge chip, and the legacy RPi.GPIO library hardcodes a check for the older BCM2711 SoC found on the Pi 4. It fails the hardware check and aborts.

The First Three Things to Check When It Fails

  1. Verify Your Library: Check your import statements. If you see import RPi.GPIO as GPIO, you are using the legacy library. Refactor your code to use gpiozero (as shown above) or install the modern fork rpi-lgpio via sudo apt install python3-rpi-lgpio.
  2. Check User Permissions (gpio group): If you are running a minimal container or a custom OS build, your user might lack hardware access. Run groups in the terminal. If gpio is missing, add your user with sudo usermod -aG gpio $USER and reboot.
  3. Inspect for Pin Conflicts: While GPIO 18 is dedicated to PWM0, ensure you haven't accidentally enabled an overlay in /boot/firmware/config.txt (like dtoverlay=pwm-2chan) that remaps the pin to an audio output, which will cause gpiozero to throw a PinFactoryFallback warning or fail to assert the signal.

Extending and Simplifying the Build

Once you have the baseline lamp working, you can adapt the architecture to fit your specific project constraints.

How to Simplify (No Pi Required)

If you realize you don't need network connectivity or complex scheduling, driving a Pi just to dim an LED is massive overkill. You can simplify this exact MOSFET driver circuit by replacing the Pi with a 555 Timer IC (NE555) wired in astable mode with a diode across the timing resistor. This gives you a physical potentiometer knob for PWM dimming, costs under $2 in parts, and draws microamps of standby current compared to the Pi's 3W-5W idle draw.

How to Extend (Home Assistant / MQTT)

To integrate this lamp into a smart home ecosystem without relying on proprietary cloud APIs, extend the Python script using the paho-mqtt library.

Add an MQTT client that subscribes to a topic like home/office/desk_lamp/set. When a payload of {"brightness": 150} arrives (using an 8-bit 0-255 scale), map that value to the 0.0-1.0 float required by gpiozero (150 / 255.0) and update lamp.brightness. This allows seamless integration with Home Assistant via the MQTT integration, giving you local-only, sub-50ms latency control over your lighting without exposing your network to external cloud servers.

For deeper reading on hardware PWM capabilities and pin multiplexing on the RP1 chip, refer to the official Raspberry Pi Compute Documentation and the gpiozero PWMLED API reference.