libgpiod-dev / gpiod 10 Example Python Programs / 10 Signal Handling Examples
10 Example GPIO bit Driving Examples!
We initially started with 10 example c programs here. Since it now only takes a couple minutes to translate it now into python we have had it rewrite it's code in python for your reference and development!
- These applications have not been human verfied. At least we are honest, so you might need to work with them, but they are provided for your benefit and reference.
Here are the 10 examples converted from C (libgpiod v2 API) to Python using the official gpiod Python bindings (available since libgpiod 2.0+).
These examples use the modern Python API, which is cleaner and more Pythonic than the C version.
Installation (one-time)
sudo apt update
sudo apt install python3-libgpiod
All examples assume you are using BCM GPIO numbers (offsets) and gpiochip0.
Example 1: Basic LED Blinker
#!/usr/bin/env python3
"""
Basic LED blinker using libgpiod v2 Python bindings.
Hardware: LED + resistor on BCM 17 (physical pin 11)
"""
import gpiod
import time
CHIP = "gpiochip0"
LED_OFFSET = 17
DELAY = 0.5 # seconds
print(f"Blinking LED on BCM {LED_OFFSET} (Ctrl+C to exit)")
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="blink-example",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
try:
while True:
request.set_value(LED_OFFSET, True)
time.sleep(DELAY)
request.set_value(LED_OFFSET, False)
time.sleep(DELAY)
except KeyboardInterrupt:
print("\nExiting cleanly")
finally:
request.release()
Example 2: Precise LED Blinker (using time.sleep)
#!/usr/bin/env python3
"""
Precise LED blinker with 250 ms period.
"""
import gpiod
import time
CHIP = "gpiochip0"
LED_OFFSET = 17
HALF_PERIOD = 0.250
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="precise-blink",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
print(f"Precise 250 ms blink on BCM {LED_OFFSET}")
try:
while True:
request.set_value(LED_OFFSET, True)
time.sleep(HALF_PERIOD)
request.set_value(LED_OFFSET, False)
time.sleep(HALF_PERIOD)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 3: Button Polling (10 ms loop)
#!/usr/bin/env python3
"""
Button polling example with 10 ms interval.
Hardware: Button to GND on BCM 18 (pull-up enabled)
"""
import gpiod
import time
CHIP = "gpiochip0"
BTN_OFFSET = 18
POLL_INTERVAL = 0.010
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="button-poll",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
last = True
print(f"Polling button on BCM {BTN_OFFSET} every 10 ms")
try:
while True:
val = request.get_value(BTN_OFFSET)
if val != last:
print("Button", "released" if val else "pressed")
last = val
time.sleep(POLL_INTERVAL)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 4: Blocking Edge Detection (Rising Edge)
#!/usr/bin/env python3
"""
Blocking rising-edge detection using event wait.
"""
import gpiod
CHIP = "gpiochip0"
BTN_OFFSET = 18
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="edge-detect",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP,
edge_detection=gpiod.Edge.RISING
)
}
)
print(f"Waiting for rising edges on BCM {BTN_OFFSET}...")
try:
while True:
for event in request.wait_edge_events(-1): # -1 = block forever
ts_sec = event.timestamp_ns // 1_000_000_000
ts_nsec = event.timestamp_ns % 1_000_000_000
print(f"Rising edge! Timestamp: {ts_sec}.{ts_nsec:09d}")
except KeyboardInterrupt:
pass
finally:
request.release()
Example 5: Debounced Button Counter
#!/usr/bin/env python3
"""
Debounced button counter (50 ms debounce)
"""
import gpiod
import time
from datetime import datetime, timedelta
CHIP = "gpiochip0"
BTN_OFFSET = 18
DEBOUNCE_TIME = timedelta(milliseconds=50)
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="debounce",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
count = 0
last_state = True
last_change = datetime.min
print("Debounced button counter started")
try:
while True:
now = datetime.now()
state = request.get_value(BTN_OFFSET)
if state != last_state:
last_change = now
last_state = state
if not state and (now - last_change) > DEBOUNCE_TIME:
count += 1
print(f"Button pressed! Count: {count}")
last_state = True # wait for release
time.sleep(0.01)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 6: Software PWM Dimmer (50 Hz)
#!/usr/bin/env python3
"""
Software PWM dimmer – fades from 0% to 100%
"""
import gpiod
import time
CHIP = "gpiochip0"
LED_OFFSET = 17
PERIOD = 0.020 # 20 ms → 50 Hz
def pwm_cycle(request, duty_percent):
on_time = PERIOD * (duty_percent / 100.0)
off_time = PERIOD - on_time
request.set_value(LED_OFFSET, True)
time.sleep(on_time)
request.set_value(LED_OFFSET, False)
time.sleep(off_time)
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="pwm",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
try:
for duty in range(0, 101, 5):
print(f"Duty cycle: {duty}%")
for _ in range(50): # ~1 second at each level
pwm_cycle(request, duty)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 7: LED Chaser (Multi-line)
#!/usr/bin/env python3
"""
LED chaser using three LEDs
Hardware: LEDs on BCM 17, 18, 27
"""
import gpiod
import time
CHIP = "gpiochip0"
LEDS = [17, 18, 27]
DELAY = 0.2
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="chaser",
config={
offset: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
for offset in LEDS
}
)
try:
while True:
for i, led in enumerate(LEDS):
request.set_value(led, True)
time.sleep(DELAY)
request.set_value(led, False)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 8: One-Shot Precise Pulse (100 µs)
#!/usr/bin/env python3
"""
Generates a single 100 µs pulse
"""
import gpiod
import time
CHIP = "gpiochip0"
OUT_OFFSET = 17
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="pulse",
config={
OUT_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
print(f"Sending 100 µs pulse on BCM {OUT_OFFSET}")
request.set_value(OUT_OFFSET, True)
time.sleep(100e-6) # 100 microseconds
request.set_value(OUT_OFFSET, False)
print("Pulse complete")
Example 9: Input Mirror with Latency Measurement
#!/usr/bin/env python3
"""
Mirrors button state to LED and measures latency
Hardware: Button BCM 18 → LED BCM 17
"""
import gpiod
import time
from datetime import datetime
CHIP = "gpiochip0"
LED_OFFSET = 17
BTN_OFFSET = 18
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="mirror",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
),
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
try:
while True:
t_start = datetime.now()
val = request.get_value(BTN_OFFSET)
request.set_value(LED_OFFSET, val)
t_end = datetime.now()
latency_ns = (t_end - t_start).total_seconds() * 1_000_000_000
if latency_ns > 50000:
print(f"High latency: {latency_ns:.0f} ns")
time.sleep(0.001)
except KeyboardInterrupt:
pass
finally:
request.release()
Example 10: Traffic Light State Machine
#!/usr/bin/env python3
"""
Traffic light simulation state machine
Hardware: Red=17, Yellow=18, Green=27
"""
import gpiod
import time
CHIP = "gpiochip0"
RED, YELLOW, GREEN = 17, 18, 27
LIGHTS = [RED, YELLOW, GREEN]
STATES = {
"RED": (5.0, "GREEN"),
"GREEN": (4.0, "YELLOW"),
"YELLOW": (1.0, "RED")
}
current_state = "RED"
with gpiod.Chip(CHIP) as chip:
request = chip.request_lines(
consumer="traffic",
config={
offset: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
for offset in LIGHTS
}
)
try:
while True:
# Set lights according to current state
request.set_value(RED, current_state == "RED")
request.set_value(YELLOW, current_state == "YELLOW")
request.set_value(GREEN, current_state == "GREEN")
delay, next_state = STATES[current_state]
time.sleep(delay)
current_state = next_state
except KeyboardInterrupt:
pass
finally:
request.release()
These Python versions are more concise, use context managers (with), and handle cleanup automatically on exit or exception.
Run each script with:
python3 exampleN.py
or make them executable:
chmod +x exampleN.py
./exampleN.py
Here is an updated version of the 10 Python examples with proper signal handling added. This ensures graceful cleanup (releasing GPIO lines) when the program receives SIGINT (Ctrl+C) or SIGTERM (e.g., from kill or systemd shutdown).
SIGNAL HANDLING (10 MORE EXAMPLES)
The approach used is consistent across all examples:
- Use
signal.signal()to catch SIGINT and SIGTERM. - Set a global flag (
running = False) when a termination signal is received. - Check this flag in the main loop to exit cleanly.
- Always use
request.release()in afinallyblock or on exit.
Example 1: Basic LED Blinker with signal handling
#!/usr/bin/env python3
"""
Basic LED blinker with graceful shutdown on Ctrl+C or SIGTERM
"""
import gpiod
import time
import signal
import sys
CHIP = "gpiochip0"
LED_OFFSET = 17
DELAY = 0.5
running = True
def signal_handler(sig, frame):
global running
print("\nReceived signal, releasing GPIO lines...")
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
print(f"Blinking LED on BCM {LED_OFFSET} (Ctrl+C to exit cleanly)")
chip = gpiod.Chip(CHIP)
request = None
try:
request = chip.request_lines(
consumer="blink-example",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
while running:
request.set_value(LED_OFFSET, True)
time.sleep(DELAY)
request.set_value(LED_OFFSET, False)
time.sleep(DELAY)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
finally:
if request is not None:
request.release()
chip.close()
print("GPIO lines released. Exiting.")
Example 2: Precise LED Blinker
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
LED_OFFSET = 17
HALF_PERIOD = 0.250
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = None
try:
request = chip.request_lines(
consumer="precise-blink",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
print(f"Precise 250 ms blink on BCM {LED_OFFSET}")
while running:
request.set_value(LED_OFFSET, True)
time.sleep(HALF_PERIOD)
request.set_value(LED_OFFSET, False)
time.sleep(HALF_PERIOD)
finally:
if request:
request.release()
chip.close()
print("Clean exit.")
Example 3: Button Polling
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
BTN_OFFSET = 18
POLL_INTERVAL = 0.010
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="button-poll",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
last = True
print(f"Polling button on BCM {BTN_OFFSET} every 10 ms")
try:
while running:
val = request.get_value(BTN_OFFSET)
if val != last:
print("Button", "released" if val else "pressed")
last = val
time.sleep(POLL_INTERVAL)
finally:
request.release()
chip.close()
print("GPIO released.")
Example 4: Blocking Edge Detection
#!/usr/bin/env python3
import gpiod
import signal
CHIP = "gpiochip0"
BTN_OFFSET = 18
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="edge-detect",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP,
edge_detection=gpiod.Edge.RISING
)
}
)
print(f"Waiting for rising edges on BCM {BTN_OFFSET}...")
try:
while running:
for event in request.wait_edge_events(timeout=0.5):
if not running:
break
ts_sec = event.timestamp_ns // 1_000_000_000
ts_nsec = event.timestamp_ns % 1_000_000_000
print(f"Rising edge! {ts_sec}.{ts_nsec:09d}")
finally:
request.release()
chip.close()
print("Clean shutdown.")
Example 5: Debounced Button Counter
#!/usr/bin/env python3
import gpiod
import time
from datetime import datetime, timedelta
import signal
CHIP = "gpiochip0"
BTN_OFFSET = 18
DEBOUNCE_TIME = timedelta(milliseconds=50)
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="debounce",
config={
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
count = 0
last_state = True
last_change = datetime.min
try:
while running:
now = datetime.now()
state = request.get_value(BTN_OFFSET)
if state != last_state:
last_change = now
last_state = state
if not state and (now - last_change) > DEBOUNCE_TIME:
count += 1
print(f"Button pressed! Count: {count}")
last_state = True
time.sleep(0.01)
finally:
request.release()
chip.close()
print(f"Final count: {count}")
Example 6: Software PWM Dimmer
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
LED_OFFSET = 17
PERIOD = 0.020
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
def pwm_cycle(request, duty):
on_time = PERIOD * (duty / 100.0)
off_time = PERIOD - on_time
request.set_value(LED_OFFSET, True)
time.sleep(on_time)
request.set_value(LED_OFFSET, False)
time.sleep(off_time)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="pwm",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
try:
for duty in range(0, 101, 5):
if not running:
break
print(f"Duty cycle: {duty}%")
for _ in range(50):
if not running:
break
pwm_cycle(request, duty)
finally:
request.release()
chip.close()
print("PWM stopped cleanly.")
Example 7: LED Chaser
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
LEDS = [17, 18, 27]
DELAY = 0.2
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="chaser",
config={
offset: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
for offset in LEDS
}
)
try:
while running:
for led in LEDS:
if not running:
break
request.set_value(led, True)
time.sleep(DELAY)
request.set_value(led, False)
finally:
request.release()
chip.close()
print("Chaser stopped.")
Example 8: One-Shot Precise Pulse
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
OUT_OFFSET = 17
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="pulse",
config={
OUT_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
}
)
print(f"Sending 100 µs pulse on BCM {OUT_OFFSET}")
request.set_value(OUT_OFFSET, True)
time.sleep(100e-6)
request.set_value(OUT_OFFSET, False)
print("Pulse sent.")
# Keep running briefly in case of early interrupt
time.sleep(0.5)
request.release()
chip.close()
Example 9: Input Mirror with Latency Measurement
#!/usr/bin/env python3
import gpiod
import time
from datetime import datetime
import signal
CHIP = "gpiochip0"
LED_OFFSET = 17
BTN_OFFSET = 18
running = True
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="mirror",
config={
LED_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
),
BTN_OFFSET: gpiod.LineSettings(
direction=gpiod.Direction.INPUT,
bias=gpiod.Bias.PULL_UP
)
}
)
try:
while running:
t_start = datetime.now()
val = request.get_value(BTN_OFFSET)
request.set_value(LED_OFFSET, val)
t_end = datetime.now()
latency_ns = (t_end - t_start).total_seconds() * 1_000_000_000
if latency_ns > 50000:
print(f"High latency: {latency_ns:.0f} ns")
time.sleep(0.001)
finally:
request.release()
chip.close()
print("Mirror stopped.")
Example 10: Traffic Light State Machine
#!/usr/bin/env python3
import gpiod
import time
import signal
CHIP = "gpiochip0"
RED, YELLOW, GREEN = 17, 18, 27
LIGHTS = [RED, YELLOW, GREEN]
STATES = {
"RED": (5.0, "GREEN"),
"GREEN": (4.0, "YELLOW"),
"YELLOW": (1.0, "RED")
}
running = True
current_state = "RED"
def signal_handler(sig, frame):
global running
running = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
chip = gpiod.Chip(CHIP)
request = chip.request_lines(
consumer="traffic",
config={
offset: gpiod.LineSettings(
direction=gpiod.Direction.OUTPUT,
output_value=False
)
for offset in LIGHTS
}
)
try:
while running:
request.set_value(RED, current_state == "RED")
request.set_value(YELLOW, current_state == "YELLOW")
request.set_value(GREEN, current_state == "GREEN")
delay, next_state = STATES[current_state]
# Sleep in small steps to allow signal response
for _ in range(int(delay * 10)):
if not running:
break
time.sleep(0.1)
if not running:
break
current_state = next_state
finally:
# Turn all lights off on exit
request.set_value(RED, False)
request.set_value(YELLOW, False)
request.set_value(GREEN, False)
request.release()
chip.close()
print("Traffic light stopped cleanly.")
Summary of Improvements
- Graceful exit on Ctrl+C (SIGINT) and SIGTERM
- GPIO lines are always released
- All lights/outputs are typically reset to safe state (off) when possible
- Minimal added overhead (flag check in loops)
- Compatible with systemd services or background execution
These patterns are suitable for both development scripts and production daemons. Let me know if you would like a version that uses asyncio or threading for non-blocking signal handling instead.