Control RaspberryPi On-Board LEDs and GPIO LED

Control RaspberryPi 5 On-Board LEDs

OS: Raspberry Pi OS
The Raspberry Pi 5 has two primary on-board LEDs:
  1. ACT (Green): Indicates Activity (default: SD card read/write).
  2. PWR (Red): Indicates Power status (default: Always On).
These are controlled via the Linux sysfs interface located at /sys/class/leds/.

1. Green LED (ACT) - Status

Default Behavior: Flashes during SD card (mmc0) activity.

Because this LED is managed by a kernel trigger (mmc0), you must disable the trigger before you can manually control it.

Step 1: Disable the Trigger

# Set trigger to 'none' to take manual control
$echo none | sudo tee /sys/class/leds/ACT/trigger

Step 2: Manual Control

# Turn ON
$echo 0 |sudo tee /sys/class/leds/ACT/brightness
# Turn OFF
$echo 1 |sudo tee /sys/class/leds/ACT/brightness

Step 3: Restore Default Behavior

$echo mmc0 |sudo tee /sys/class/leds/ACT/trigger

2. Red LED (PWR) - Power

Default Behavior: Always On (default-on).

Manual Control (Note: The kernel driver handles the polarity here, so 1 means "Active/On")

# Turn ON
$echo 1 |sudo tee /sys/class/leds/PWR/brightness
# Turn OFF
$echo 0 |sudo tee /sys/class/leds/PWR/brightness

3. Under the Hood: Why does this work?

The sysfs Interface

When you list the LED directory, you can see the available LEDs:
$ls /sys/class/leds
ACT  PWR  mmc0::  mmc1:: 

Understanding Triggers

A "trigger" is a kernel function that automatically controls the LED. You can see all available triggers by reading the file. The currently selected trigger is enclosed in [brackets].
$cat /sys/class/leds/ACT/trigger
none usb-gadget usb-host rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot disk-activity disk-read disk-write mtd nand-disk heartbeat backlight cpu cpu0 cpu1 cpu2 cpu3 default-on panic mmc1 [mmc0] rfkill-any rfkill-none bluetooth-power hci0-power rfkill0 rfkill1 1f00100000.ethernet-ffffffff:01:link 1f00100000.ethernet-ffffffff:01:1Gbps 1f00100000.ethernet-ffffffff:01:100Mbps 1f00100000.ethernet-ffffffff:01:10Mbps

Change the brightness

$echo 0 |sudo tee /sys/class/leds/PWR/brightness 
$echo 1 |sudo tee /sys/class/leds/PWR/brightness 

Low Active 

LED ON

echo 1 | sudo tee /sys/class/leds/PWR/brightness

1

$ sudo pinctrl  get 44

44: op dl pd | lo // RP1_STAT_LED/GPIO44 = output
(dl = drive low) --> low active so LED ON

LED OFF

$  echo 0 | sudo tee /sys/class/leds/PWR/brightness

0

$ sudo pinctrl  get 44

44: op dh pd | lo // RP1_STAT_LED/GPIO44 = output
(dh = drive high) --> low active so LED OFF

Where did set to active low

under linux Raspberry pi kernel source code, the Pi 5 device tree file  : 
linux/arch/arm/boot/dts/broadcom/bcm2712-rpi-5-b.dts 

4. The "Mystery": Why are the echo values different?

The Question: "PWR (Red) and ACT (Green) LEDs are both hardware Active Low. Why do I echo 1 to turn on Red, but echo 0 to turn on Green?"
The Answer: This discrepancy happens because of how the Device Tree (.dts) is configured for each pin versus how the sysfs brightness interface interprets raw values.
  • Red LED (Managed Logic): The Red LED is configured with the active-low flag in the Device Tree.
    • The Kernel says: "I know this is inverted."
    • You say: echo 1 (I want "ON").
    • The Kernel translates: "Okay, since it's inverted, I will send 0 (Low) to the hardware."
    • Result: Intuitive control (1=On).
  • Green LED (Raw/Unmanaged Discrepancy): The Green LED (ACT) is often bound to the mmc0 trigger. When you force echo 0 or echo 1 to brightness while the trigger is transitioning or if the active-low property isn't fully respected in "manual mode" for this specific GPIO block (BCM2712 AON vs RP1), you are hitting the hardware raw logic.
    • Hardware Reality: It is physically wired Active Low.
    • You say: echo 0 (Raw value 0).
    • The Hardware gets: 0 (Low).
    • Result: LED turns ON.
This is why the Red LED feels "logical" (software handled) and the Green LED feels "backwards" (raw hardware logic).

Debug commands

cat /sys/kernel/debug/gpio
sudo pinctrl -c bcm2712d0_aon
gpiofind 2712_STAT_LED
gpiofind RP1_STAT_LED
pinctrl -l  //list all gpio chips
gpioinfo  //list all gpio information
cat /sys/kernel/debug/gpio  // can see the internal number




Comments