The Raspberry Pi 3 Model B and B+ feature a 40-pin GPIO header operating at 3.3V logic. Understanding the gpio pin layout raspberry pi 3 requires distinguishing between Physical pin numbers (1-40) and Broadcom (BCM) SOC channel numbers. Physical Pin 1 is the 3.3V power rail, located nearest to the SD card slot and HDMI ports, while Pin 2 is 5V. Feeding 5V directly into any BCM GPIO pin will instantly destroy the SoC. Always use a multimeter to verify physical pin 1 (3.3V) before wiring active components.
The GPIO Pin Layout Raspberry Pi 3: 40-Pin Header Reference
Below is the data-dense reference table for the most critical pins on the Pi 3 header. While the header contains 40 pins, many are duplicated grounds or reserved for specific hardware interfaces (I2C, SPI, UART). This table maps the Physical Pin, BCM GPIO, and primary function.
| Physical Pin | BCM GPIO | Name / Function | Notes & Hardware Constraints |
|---|---|---|---|
| 1 | - | 3.3V Power | Max draw ~50mA combined across all 3.3V pins |
| 2 | - | 5V Power | Tied directly to USB 5V input; max draw depends on PSU |
| 3 | 2 | GPIO2 (SDA1) | I2C Data; includes 1.8kΩ onboard pull-up to 3.3V |
| 5 | 3 | GPIO3 (SCL1) | I2C Clock; includes 1.8kΩ onboard pull-up to 3.3V |
| 6 | - | Ground | Primary ground reference for 3.3V logic |
| 8 | 14 | GPIO14 (TXD) | Primary UART Transmit; defaults to serial console on boot |
| 10 | 15 | GPIO15 (RXD) | Primary UART Receive; defaults to serial console on boot |
| 11 | 17 | GPIO17 | General Purpose; safe for PWM and digital I/O |
| 12 | 18 | GPIO18 (PWM0) | Hardware PWM0; ideal for motor/fan speed control |
| 14 | - | Ground | Located adjacent to GPIO15 and GPIO18 |
| 16 | 23 | GPIO23 | General Purpose; defaults to input with pull-down |
| 19 | 10 | GPIO10 (MOSI) | SPI0 Master Out Slave In |
| 21 | 9 | GPIO9 (MISO) | SPI0 Master In Slave Out |
| 23 | 11 | GPIO11 (SCLK) | SPI0 Serial Clock |
| 25 | - | Ground | Located adjacent to GPIO24 and GPIO8 |
| 27 | 0 | GPIO0 (ID_SD) | I2C ID EEPROM Data; reserved for HAT identification |
For a complete visual map of all 40 pins, the community-standard Pinout.xyz remains the definitive interactive reference. Always cross-reference physical pinouts with the official Raspberry Pi documentation when working with HATs.
Project Build: 5V PWM Fan Controller with Tachometer Feedback
Difficulty: Intermediate | Time: 45 Minutes | Target Board: Raspberry Pi 3 Model B+ (1.4GHz, 1GB LPDDR2)
The Pi 3 runs hot under sustained load. This project uses a 3.3V GPIO pin to drive a 5V PWM fan via a logic-level MOSFET, while reading the fan's tachometer signal back to the Pi to verify RPM.
Parts List
- Microcontroller: Raspberry Pi 3 Model B+ (Running Raspberry Pi OS Bullseye 32-bit or 64-bit Legacy)
- Fan: Noctua NF-A4x20 5V PWM (4-pin variant: VCC, GND, PWM, Tach)
- Switching Component: 2N7000 N-Channel Logic-Level MOSFET (Vgs threshold ~2.0V, fully on at 3.3V)
- Resistors: 1x 1kΩ (Gate resistor), 1x 10kΩ (Gate pull-down), 1x 10kΩ (Tach pull-up)
- Wiring: 22 AWG solid core hookup wire, half-size breadboard
Project Pin Mapping
| Pi 3 Physical Pin | BCM GPIO | Component Target | Purpose |
|---|---|---|---|
| 2 | - | Fan VCC (Pin 2) | 5V Power to Fan |
| 6 | - | Fan GND (Pin 1) & MOSFET Source | Common Ground |
| 11 | 17 | MOSFET Gate (via 1kΩ) | PWM Signal Output |
| 13 | 27 | Fan Tach (Pin 4) | RPM Pulse Input |
Wiring Steps and Physical Connections
SAFETY CALL: De-energize the Pi (unplug the USB-C/Micro-USB power) before wiring. The Pi 3 GPIO pins are strictly 3.3V tolerant. Accidentally routing the 5V fan VCC line into BCM 17 or 27 will permanently short the SoC.
- Establish Common Ground: Connect Physical Pin 6 (GND) to the breadboard's negative rail. Connect the Noctua fan's black wire (GND) and the 2N7000 MOSFET's Source pin to this same rail.
- Wire the 5V Power: Connect Physical Pin 2 (5V) directly to the Noctua fan's yellow wire (VCC). Do not route 5V through the breadboard's main power rails if you are also using 3.3V logic elsewhere; keep 5V isolated to prevent accidental cross-wiring.
- Build the MOSFET Gate Drive: Connect BCM 17 (Physical 11) through a 1kΩ resistor to the 2N7000 Gate. This resistor limits inrush current from the GPIO pin's parasitic capacitance.
- Add the Gate Pull-Down: Connect a 10kΩ resistor between the MOSFET Gate and GND. Critical E-E-A-T tip: During Pi boot, GPIO pins float. Without this 10kΩ pull-down, the fan will spin at 100% for 10-15 seconds during OS load, causing unnecessary wear and noise.
- Wire the Tachometer: Connect the fan's green wire (Tach) to BCM 27 (Physical 13). Because the Noctua tach output is open-collector, you must add a 10kΩ pull-up resistor between BCM 27 and the 3.3V rail (Physical Pin 1).
- Verify: Use a multimeter in continuity mode to ensure the 5V line does not short to the 3.3V line or any GPIO pins before applying power.
Complete Python Control Code (RPi.GPIO)
The following code targets the RPi.GPIO library, which is natively supported on Raspberry Pi OS Bullseye. (Note: If you upgrade the Pi 3 to Bookworm, RPi.GPIO is deprecated in favor of lgpio or gpiozero, but Bullseye remains the stable baseline for Pi 3 legacy deployments).
import RPi.GPIO as GPIO
import time
import sys
# --- PIN DEFINITIONS (BCM Numbering) ---
PWM_PIN = 17
TACH_PIN = 27
# --- GLOBAL VARIABLES ---
pulse_count = 0
pulses_per_rev = 2 # Standard for most PC/Noctua fans
def tach_callback(channel):
global pulse_count
pulse_count += 1
def calculate_rpm(elapsed_seconds):
global pulse_count
if elapsed_seconds == 0:
return 0
# (Pulses / Pulses per Rev) / (Time in minutes)
revs = pulse_count / pulses_per_rev
minutes = elapsed_seconds / 60.0
rpm = revs / minutes
pulse_count = 0 # Reset for next interval
return int(rpm)
def main():
global pulse_count
# Explicitly set BCM mode and disable warnings for clean restarts
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
try:
# Setup PWM Output
GPIO.setup(PWM_PIN, GPIO.OUT)
pwm = GPIO.PWM(PWM_PIN, 25000) # 25kHz frequency (Intel 4-wire spec)
pwm.start(0)
# Setup Tachometer Input with Pull-Up
GPIO.setup(TACH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(TACH_PIN, GPIO.FALLING, callback=tach_callback)
print("Fan controller initialized. Press Ctrl+C to exit.")
# Ramp up to 70% duty cycle
target_duty = 70
pwm.ChangeDutyCycle(target_duty)
print(f"Set PWM to {target_duty}%")
while True:
time.sleep(2) # Measure RPM over a 2-second window
rpm = calculate_rpm(2)
print(f"Current RPM: {rpm}")
except RuntimeError as e:
# Catch specific RPi.GPIO hardware conflicts
print(f"[FATAL GPIO ERROR] {e}")
sys.exit(1)
except KeyboardInterrupt:
print("\nInterrupt received. Cleaning up...")
finally:
# CRITICAL: Always clean up to release hardware locks
GPIO.cleanup()
print("GPIO pins reset to safe input state.")
if __name__ == '__main__':
main()
Debugging: Fatal GPIO Errors and the "First Three" Checklist
When working with the Pi 3 GPIO header, hardware-level exceptions are common. If your script crashes, look for these exact error strings in your terminal output.
Ranked Error Causes
- Exact Error:
RuntimeError: Channel 17 is already in use- Cause: A previous execution of your script crashed before reaching
GPIO.cleanup(), leaving the kernel's sysfs lock engaged on that pin. Alternatively, a background daemon (like a pre-installed fan shim service) is holding the pin. - Fix: Run
sudo killall python3or reboot the Pi. In code, useGPIO.setwarnings(False)and force setup, or ensure yourfinally:block always executes.
- Cause: A previous execution of your script crashed before reaching
- Exact Error:
RuntimeError: No access to /dev/mem. Try running as root!- Cause: You are running the script as a standard user on an older OS build, or the
gpiogroup permissions are misconfigured in/etc/udev/rules.d/. - Fix: Execute with
sudo python3 fan_control.pyor add your user to the gpio group:sudo usermod -aG gpio $USER.
- Cause: You are running the script as a standard user on an older OS build, or the
- Exact Error:
RuntimeError: Conflicting edge detection already enabled for this GPIO channel- Cause: You called
GPIO.add_event_detect()twice on the same pin without callingGPIO.remove_event_detect()orGPIO.cleanup()in between.
- Cause: You called
The "First Three" Debugging Checklist:
When the fan won't spin or the RPM reads zero, check these three things immediately:
1. Numbering Mode: Did you use GPIO.setmode(GPIO.BCM) but wire to Physical Pin 17 instead of BCM 17 (Physical 11)? This is the #1 beginner mistake.
2. The Pull-Down: Is the 10kΩ gate pull-down resistor physically seated? If the gate is floating, the MOSFET may be partially conducting, stalling the fan motor.
3. Tach Pull-Up: Measure BCM 27 with a multimeter. It must read ~3.3V when the fan is disconnected. If it reads 0V, your pull-up resistor is missing or the 3.3V rail is dead.
Extending and Simplifying the Build
How to Simplify
If you do not need RPM feedback and want to avoid the RPi.GPIO deprecation warnings on newer OS versions, strip the tachometer wiring and use the gpiozero library. The PWMOutputDevice class handles cleanup automatically on exit and abstracts the BCM/BOARD numbering confusion entirely. You will lose the ability to detect a stalled fan, but the code footprint drops to under 10 lines.
How to Extend
To turn this into a closed-loop thermal management system, add an I2C temperature sensor like the BME280 or TMP102. Wire the sensor's SDA/SCL to Physical Pins 3 and 5 (BCM 2 and 3). Implement a PID (Proportional-Integral-Derivative) control loop in Python: read the SoC temperature via vcgencmd measure_temp, calculate the error from your target setpoint (e.g., 55°C), and dynamically adjust the pwm.ChangeDutyCycle() value. This prevents the fan from hunting (rapidly speeding up and slowing down) and keeps the Pi 3 whisper-quiet during light workloads.






