Raspberry Pi PINAS LEDs

40-pin header (Pi 1 B+ / 2 / 3 / 4 / 5 / Zero). All GPIO pins are 3.3 V logic. Per-pin source/sink limit 16 mA, total across the header 50 mA. Target 10 mA per LED to stay well inside those budgets.

Pinout map

              3V3  (1) (2)  5V
   GPIO2  SDA  (3) (4)  5V
   GPIO3  SCL  (5) (6)  GND
   GPIO4       (7) (8)  GPIO14  TXD
          GND  (9)(10)  GPIO15  RXD
   GPIO17     (11)(12)  GPIO18  PWM0
   GPIO27     (13)(14)  GND
   GPIO22     (15)(16)  GPIO23
          3V3 (17)(18)  GPIO24
   GPIO10 MOSI(19)(20)  GND
   GPIO9  MISO(21)(22)  GPIO25
   GPIO11 SCLK(23)(24)  GPIO8   CE0
          GND (25)(26)  GPIO7   CE1
   ID_SD      (27)(28)  ID_SC          <- reserved (HAT EEPROM)
   GPIO5      (29)(30)  GND
   GPIO6      (31)(32)  GPIO12  PWM0
   GPIO13 PWM1(33)(34)  GND
   GPIO19 PWM1(35)(36)  GPIO16
   GPIO26     (37)(38)  GPIO20
          GND (39)(40)  GPIO21

Pin map for PiNAS for LEDs - Disk Activity Extra LEDS

GPIO Header pin Notes Led Use
27 13 X1009 Hard Disk 1 sda
18 29 X1009 Hard Disk 2 sdb
19 35 X1009 Hard Disk 3 sdc
20 38 - Disk Space
21 40 Led 05 - Disk Space
22 15 Led 06 - Disk Space

Wiring

  GPIOxx ──┬── [ R ] ──▶|── GND
           │           LED

Resistor on either side of the LED is fine. Anode ( +ve ) is the long leg, connect it to GPIO. The cathode ( -ve, short leg ) connects toward GND.

Resistor values (3.3 V supply)

R = (3.3 − Vf) / I. Picking the next-larger standard E12 value.

LED colour Vf (typ.) 10 mA → calc Use 5 mA → calc Use
Red 2.0 V 130 Ω 150 Ω 260 Ω 330 Ω
Yellow 2.1 V 120 Ω 150 Ω 240 Ω 270 Ω
Orange 2.0 V 130 Ω 150 Ω 260 Ω 330 Ω
Green (std) 2.1 V 120 Ω 150 Ω 240 Ω 270 Ω
Green (hi-b) 3.0 V 30 Ω 47 Ω 60 Ω 100 Ω
Blue 3.0 V 30 Ω 47 Ω 60 Ω 100 Ω
White 3.0 V 30 Ω 47 Ω 60 Ω 100 Ω

A 220 Ω resistor is a safe universal default for any colour from a 3.3 V GPIO — gives roughly 6 mA on a red, ~1.5 mA on a blue (dim but visible). Use 330 Ω if you only need an indicator.

Quick test (Python, gpiozero)

from gpiozero import LED
from time import sleep

led = LED(17)          # BCM numbering — header pin 11
while True:
    led.on();  sleep(0.5)
    led.off(); sleep(0.5)

For PWM brightness use PWMLED(12) etc. on one of the hardware-PWM-capable pins.

All the pins available

On the standard 40-pin Raspberry Pi header, the GPIOs generally available for LEDs are: Physical Pin GPIO 3 GPIO2 5 GPIO3 7 GPIO4 8 GPIO14 10 GPIO15 11 GPIO17 12 GPIO18 13 GPIO27 15 GPIO22 16 GPIO23 18 GPIO24 19 GPIO10 21 GPIO9 22 GPIO25 23 GPIO11 24 GPIO8 26 GPIO7 29 GPIO5 31 GPIO6 32 GPIO12 33 GPIO13 35 GPIO19 36 GPIO16 37 GPIO26 38 GPIO20 40 GPIO21

That's 26 GPIO pins in total.

Since you're using 12, you have 14 more available if you're happy to repurpose the interface pins:

Pin 3 – GPIO2 (I²C SDA)
Pin 5 – GPIO3 (I²C SCL)
Pin 8 – GPIO14 (UART TX)
Pin 10 – GPIO15 (UART RX)
Pin 12 – GPIO18
Pin 13 – GPIO27
Pin 19 – GPIO10 (SPI MOSI)
Pin 21 – GPIO9 (SPI MISO)
Pin 23 – GPIO11 (SPI SCLK)
Pin 24 – GPIO8 (SPI CE0)
Pin 26 – GPIO7 (SPI CE1)
Pin 32 – GPIO12
Pin 33 – GPIO13
Pin 35 – GPIO19

Current limits

The limiting factor is usually current, not the number of GPIOs.

Aim for 2–5 mA per LED—modern LEDs are plenty bright at that current.

Don't exceed 16 mA per GPIO (absolute maximum).

Keep the total GPIO current below about 50 mA across the entire Pi for reliable operation.

If you're using 330 Ω resistors on a 3.3 V GPIO with a typical red LED, you're drawing roughly 3–5 mA per LED, so 26 LEDs is generally achievable if not all are driven at maximum current simultaneously.

Yep, if you're using the X1009, I'd leave the I²C pins alone.

That means you should reserve:

Pin 3 – GPIO2 (SDA)

Pin 5 – GPIO3 (SCL)

Assuming you're not using SPI or the UART, that leaves these 12 additional GPIOs beyond the ones you're already using: Physical Pin GPIO Notes 8 GPIO14 UART TX (available if serial console disabled) 10 GPIO15 UART RX 12 GPIO18 Good general-purpose GPIO 13 GPIO27 Good general-purpose GPIO 19 GPIO10 SPI MOSI 21 GPIO9 SPI MISO 23 GPIO11 SPI SCLK 24 GPIO8 SPI CE0 26 GPIO7 SPI CE1 32 GPIO12 Good general-purpose GPIO 33 GPIO13 Good general-purpose GPIO 35 GPIO19 Good general-purpose GPIO

So with the X1009 using I²C:

12 LEDs already connected
12 more GPIOs potentially available
24 LEDs total on the 40-pin header without adding expanders.

If you don't need SPI or the UART either, those pins are perfectly fine for LEDs. If you do need more than 24 indicators in the future, an I²C GPIO expander like an MCP23017 would normally be the next step—but since your X1009 is already on I²C, it can happily share the same bus with additional I²C devices.

And then is there a disk activity for the sda-sde on the X1009 to get an activity led ?

Yes, but not via a dedicated GPIO signal from the X1009.

The X1009 is essentially a SATA HAT. The SATA drives (/dev/sda–/dev/sde) appear as normal Linux block devices, but the HAT doesn't expose individual hardware activity LED pins for each drive.

You have a few options:

Software activity LEDs (recommended) – This is probably the best fit for what you're already building. Your daemon can monitor disk I/O statistics and drive a GPIO LED for each drive.

    Read /sys/block/sdX/stat or /proc/diskstats.

    If the read/write sector counts change between polls, flash the corresponding LED.

    Polling every 100–250 ms gives a responsive activity LED.

Use the Linux LED trigger – The kernel provides a disk-activity LED trigger, but it's a single global activity indicator, not per-drive.

Tap SATA hardware signals – SATA activity signaling exists electrically, but the X1009 doesn't bring those signals out to headers. Modifying the hardware to extract them would be quite involved.

Since you've already written a GPIO LED daemon (we've worked on it over several conversations), I'd go with option 1. It integrates nicely with your existing framework and lets you have:

GPIO17 → sda

GPIO18 → sdb

GPIO27 → sdc

GPIO22 → sdd

GPIO23 → sde

or any mapping you prefer.

The implementation is straightforward: cache the previous values from /sys/block/sdX/stat (or /proc/diskstats), compare them each polling interval, and pulse the LED whenever the counters increase. This works regardless of the filesystem or RAID configuration because it monitors block-device activity directly.

Lets turn this into use GPIO17 for sda import sys from gpiozero import LED from time import sleep

IFACE = sys.argv[1] LED_PIN = int(sys.argv[2]) if len(sys.argv) > 2 else 5 MIN_BYTES = int(sys.argv[3]) if len(sys.argv) > 3 else 0 POLL = 0.05 # seconds between counter reads BLINK = 0.03 # LED on-time per detected activity tick

led = LED(LED_PIN)

def iface_bytes(name): with open("/proc/net/dev") as f: for line in f: n, _, rest = line.partition(":") if n.strip() == name: cols = rest.split() return int(cols[0]) + int(cols[8]) # rx_bytes + tx_bytes return 0

prev = iface_bytes(IFACE) while True: sleep(POLL) now = iface_bytes(IFACE) if now - prev > MIN_BYTES: led.on() sleep(BLINK) led.off() prev = now

Yep. For a disk, the simplest thing is to read /sys/block/sda/stat. The first 11 fields are documented by the kernel, and activity is indicated by the read/write counters increasing.

Here's a version hardcoded to GPIO17 and /dev/sda:

from gpiozero import LED from time import sleep

LED_PIN = 17 DEVICE = "sda"

POLL = 0.05 # seconds between reads BLINK = 0.03 # LED on-time

led = LED(LED_PIN)

def disk_activity(dev): with open(f"/sys/block/{dev}/stat") as f: fields = f.read().split()

# reads completed + writes completed
return int(fields[0]) + int(fields[4])

prev = disk_activity(DEVICE)

while True: sleep(POLL) now = disk_activity(DEVICE)

if now > prev:
    led.on()
    sleep(BLINK)
    led.off()

prev = now

Or, if you want to keep it flexible like your network version:

import sys from gpiozero import LED from time import sleep

DEVICE = sys.argv[1] if len(sys.argv) > 1 else "sda" LED_PIN = int(sys.argv[2]) if len(sys.argv) > 2 else 17

POLL = 0.05 BLINK = 0.03

led = LED(LED_PIN)

def disk_activity(dev): with open(f"/sys/block/{dev}/stat") as f: fields = f.read().split() return int(fields[0]) + int(fields[4])

prev = disk_activity(DEVICE)

while True: sleep(POLL) now = disk_activity(DEVICE)

if now > prev:
    led.on()
    sleep(BLINK)
    led.off()

prev = now

That lets you start it like:

python3 disk-led.py sda 17 python3 disk-led.py sdb 18 python3 disk-led.py sdc 27 python3 disk-led.py sdd 22 python3 disk-led.py sde 23

This is very lightweight—the kernel updates /sys/block/*/stat in memory, so even polling every 50 ms for five drives has negligible CPU overhead.

Did I not say GPIO17 Pin 11 was in use ?

You did. My mistake.

From your earlier list, you're already using:

Pin 11 → GPIO17 ✅

Pin 7 → GPIO4

Pin 29 → GPIO5

Pin 31 → GPIO6

Pin 36 → GPIO16

Pin 38 → GPIO20

Pin 40 → GPIO21

Pin 15 → GPIO22

Pin 16 → GPIO23

Pin 18 → GPIO24

Pin 22 → GPIO25

Pin 37 → GPIO26

And you've ruled out:

GPIO2/GPIO3 (Pins 3 & 5) for the X1009's I²C.

So the next free GPIOs I'd use are: Disk GPIO Physical Pin sda GPIO27 Pin 13 sdb GPIO18 Pin 12 sdc GPIO12 Pin 32 sdd GPIO13 Pin 33 sde GPIO19 Pin 35

Just change:

LED_PIN = 27 DEVICE = "sda"

or, if using the parameterised version:

python3 disk-led.py sda 27 python3 disk-led.py sdb 18 python3 disk-led.py sdc 12 python3 disk-led.py sdd 13 python3 disk-led.py sde 19

That keeps all of your existing LEDs intact and avoids the I²C pins.