Raspberry Pi GPIO - Python

Python examples for accessing Raspberry Pi GPIO, I2C, SPI, I2S, and PWM.


GPIO (Digital I/O)

Installation

1# Install RPi.GPIO
2sudo apt install python3-rpi.gpio
3
4# Or use pip
5pip3 install RPi.GPIO
6
7# Alternative: gpiozero (higher-level)
8pip3 install gpiozero

Basic GPIO (RPi.GPIO)

 1import RPi.GPIO as GPIO
 2import time
 3
 4# Set mode (BCM or BOARD)
 5GPIO.setmode(GPIO.BCM)  # Use BCM pin numbering
 6
 7# Setup pins
 8LED_PIN = 18
 9BUTTON_PIN = 23
10
11GPIO.setup(LED_PIN, GPIO.OUT)  # Output
12GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)  # Input with pull-up
13
14# Digital output
15GPIO.output(LED_PIN, GPIO.HIGH)
16time.sleep(1)
17GPIO.output(LED_PIN, GPIO.LOW)
18
19# Digital input
20button_state = GPIO.input(BUTTON_PIN)
21if button_state == GPIO.LOW:
22    print("Button pressed!")
23
24# Cleanup
25GPIO.cleanup()
 1from gpiozero import LED, Button
 2from signal import pause
 3
 4# LED
 5led = LED(18)
 6led.on()
 7led.off()
 8led.toggle()
 9led.blink(on_time=1, off_time=1)  # Blink every second
10
11# Button
12button = Button(23)
13
14def button_pressed():
15    print("Button pressed!")
16    led.toggle()
17
18button.when_pressed = button_pressed
19
20# Keep program running
21pause()

PWM (Pulse Width Modulation)

Software PWM

 1import RPi.GPIO as GPIO
 2import time
 3
 4GPIO.setmode(GPIO.BCM)
 5LED_PIN = 18
 6GPIO.setup(LED_PIN, GPIO.OUT)
 7
 8# Create PWM instance (pin, frequency in Hz)
 9pwm = GPIO.PWM(LED_PIN, 1000)
10
11# Start PWM (duty cycle 0-100%)
12pwm.start(0)
13
14# Fade in
15for duty in range(0, 101, 5):
16    pwm.ChangeDutyCycle(duty)
17    time.sleep(0.1)
18
19# Fade out
20for duty in range(100, -1, -5):
21    pwm.ChangeDutyCycle(duty)
22    time.sleep(0.1)
23
24pwm.stop()
25GPIO.cleanup()

Hardware PWM (pigpio)

 1import pigpio
 2import time
 3
 4# Connect to pigpio daemon
 5pi = pigpio.pi()
 6
 7if not pi.connected:
 8    exit()
 9
10PWM_PIN = 18
11
12# Set PWM frequency (Hz)
13pi.set_PWM_frequency(PWM_PIN, 1000)
14
15# Set PWM duty cycle (0-255)
16pi.set_PWM_dutycycle(PWM_PIN, 128)  # 50%
17
18# Fade
19for i in range(0, 256, 5):
20    pi.set_PWM_dutycycle(PWM_PIN, i)
21    time.sleep(0.01)
22
23pi.set_PWM_dutycycle(PWM_PIN, 0)
24pi.stop()

I2C

Installation

1# Enable I2C
2sudo raspi-config
3# Interface Options > I2C > Enable
4
5# Install tools
6sudo apt install i2c-tools python3-smbus
7
8# Check I2C devices
9i2cdetect -y 1

I2C Communication

 1import smbus2
 2import time
 3
 4# I2C bus (1 for newer Pi, 0 for very old models)
 5bus = smbus2.SMBus(1)
 6
 7# Device address (e.g., 0x48 for ADS1115)
 8DEVICE_ADDR = 0x48
 9
10# Write byte
11bus.write_byte_data(DEVICE_ADDR, 0x01, 0x83)
12
13# Read byte
14data = bus.read_byte_data(DEVICE_ADDR, 0x00)
15print(f"Read: {data}")
16
17# Read block
18block = bus.read_i2c_block_data(DEVICE_ADDR, 0x00, 2)
19print(f"Block: {block}")
20
21# Write block
22bus.write_i2c_block_data(DEVICE_ADDR, 0x01, [0x83, 0x00])
23
24bus.close()

I2C Example: BMP280 Sensor

 1import smbus2
 2import time
 3
 4class BMP280:
 5    def __init__(self, address=0x76):
 6        self.bus = smbus2.SMBus(1)
 7        self.address = address
 8        
 9        # Read calibration data
10        self.dig_T1 = self.read_uint16(0x88)
11        self.dig_T2 = self.read_int16(0x8A)
12        self.dig_T3 = self.read_int16(0x8C)
13        
14        # Configure sensor
15        self.bus.write_byte_data(self.address, 0xF4, 0x27)
16    
17    def read_uint16(self, reg):
18        data = self.bus.read_i2c_block_data(self.address, reg, 2)
19        return data[0] | (data[1] << 8)
20    
21    def read_int16(self, reg):
22        val = self.read_uint16(reg)
23        return val if val < 32768 else val - 65536
24    
25    def read_temperature(self):
26        # Read raw temperature
27        data = self.bus.read_i2c_block_data(self.address, 0xFA, 3)
28        adc_T = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
29        
30        # Compensate
31        var1 = ((adc_T / 16384.0) - (self.dig_T1 / 1024.0)) * self.dig_T2
32        var2 = (((adc_T / 131072.0) - (self.dig_T1 / 8192.0)) ** 2) * self.dig_T3
33        t_fine = var1 + var2
34        temperature = t_fine / 5120.0
35        
36        return temperature
37
38# Usage
39sensor = BMP280()
40temp = sensor.read_temperature()
41print(f"Temperature: {temp:.2f}°C")

SPI

Installation

1# Enable SPI
2sudo raspi-config
3# Interface Options > SPI > Enable
4
5# Install library
6pip3 install spidev

SPI Communication

 1import spidev
 2import time
 3
 4# Open SPI bus
 5spi = spidev.SpiDev()
 6spi.open(0, 0)  # Bus 0, Device 0 (CE0)
 7
 8# Configure
 9spi.max_speed_hz = 1000000  # 1 MHz
10spi.mode = 0  # SPI mode 0
11
12# Transfer data
13to_send = [0x01, 0x02, 0x03]
14received = spi.xfer2(to_send)
15print(f"Received: {received}")
16
17# Read data
18data = spi.readbytes(3)
19print(f"Read: {data}")
20
21# Write data
22spi.writebytes([0x01, 0x02, 0x03])
23
24spi.close()

SPI Example: MCP3008 ADC

 1import spidev
 2import time
 3
 4class MCP3008:
 5    def __init__(self):
 6        self.spi = spidev.SpiDev()
 7        self.spi.open(0, 0)
 8        self.spi.max_speed_hz = 1350000
 9    
10    def read_channel(self, channel):
11        if channel < 0 or channel > 7:
12            return -1
13        
14        # Build command
15        cmd = [1, (8 + channel) << 4, 0]
16        
17        # Send command and read response
18        reply = self.spi.xfer2(cmd)
19        
20        # Extract 10-bit value
21        value = ((reply[1] & 3) << 8) + reply[2]
22        
23        return value
24    
25    def read_voltage(self, channel, vref=3.3):
26        value = self.read_channel(channel)
27        voltage = (value * vref) / 1023.0
28        return voltage
29    
30    def close(self):
31        self.spi.close()
32
33# Usage
34adc = MCP3008()
35
36while True:
37    value = adc.read_channel(0)
38    voltage = adc.read_voltage(0)
39    print(f"Channel 0: {value} ({voltage:.2f}V)")
40    time.sleep(1)

I2S (Audio)

Installation

1# Enable I2S
2sudo raspi-config
3# Interface Options > I2S > Enable
4
5# Install libraries
6pip3 install pyaudio numpy

I2S Audio Playback

 1import pyaudio
 2import wave
 3import numpy as np
 4
 5# Play WAV file
 6def play_wav(filename):
 7    chunk = 1024
 8    
 9    wf = wave.open(filename, 'rb')
10    
11    p = pyaudio.PyAudio()
12    
13    stream = p.open(
14        format=p.get_format_from_width(wf.getsampwidth()),
15        channels=wf.getnchannels(),
16        rate=wf.getframerate(),
17        output=True
18    )
19    
20    data = wf.readframes(chunk)
21    
22    while data:
23        stream.write(data)
24        data = wf.readframes(chunk)
25    
26    stream.stop_stream()
27    stream.close()
28    p.terminate()
29
30# Generate tone
31def generate_tone(frequency=440, duration=1, sample_rate=44100):
32    t = np.linspace(0, duration, int(sample_rate * duration))
33    wave_data = np.sin(2 * np.pi * frequency * t)
34    
35    # Convert to 16-bit PCM
36    wave_data = (wave_data * 32767).astype(np.int16)
37    
38    p = pyaudio.PyAudio()
39    
40    stream = p.open(
41        format=pyaudio.paInt16,
42        channels=1,
43        rate=sample_rate,
44        output=True
45    )
46    
47    stream.write(wave_data.tobytes())
48    
49    stream.stop_stream()
50    stream.close()
51    p.terminate()
52
53# Usage
54play_wav('sound.wav')
55generate_tone(440, 2)  # A4 note for 2 seconds

Complete Example: Weather Station

 1from gpiozero import LED, Button
 2import smbus2
 3import time
 4
 5class WeatherStation:
 6    def __init__(self):
 7        self.led = LED(18)
 8        self.button = Button(23)
 9        self.bus = smbus2.SMBus(1)
10        self.bmp280_addr = 0x76
11        
12        # Setup BMP280
13        self.bus.write_byte_data(self.bmp280_addr, 0xF4, 0x27)
14        
15        # Button callback
16        self.button.when_pressed = self.read_and_display
17    
18    def read_temperature(self):
19        # Simplified BMP280 reading
20        data = self.bus.read_i2c_block_data(self.bmp280_addr, 0xFA, 3)
21        adc_T = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
22        # ... (calibration code omitted for brevity)
23        return adc_T / 1000.0  # Simplified
24    
25    def read_and_display(self):
26        self.led.on()
27        temp = self.read_temperature()
28        print(f"Temperature: {temp:.2f}°C")
29        time.sleep(0.5)
30        self.led.off()
31    
32    def run(self):
33        print("Weather Station Running. Press button to read temperature.")
34        while True:
35            time.sleep(0.1)
36
37# Usage
38station = WeatherStation()
39station.run()

Best Practices

  1. Always cleanup GPIO - Use try/finally or context managers
  2. Use pull-up/pull-down resistors - Prevent floating inputs
  3. Check voltage levels - RPi GPIO is 3.3V, not 5V tolerant!
  4. Use level shifters - For 5V devices
  5. Limit current - Use resistors with LEDs
  6. Enable interfaces - I2C, SPI, I2S must be enabled in raspi-config

Related Snippets