Skip to content

Instantly share code, notes, and snippets.

@ds17f
Created October 25, 2025 23:47
Show Gist options
  • Select an option

  • Save ds17f/546bcc98a480d3ace5254d61b57011b9 to your computer and use it in GitHub Desktop.

Select an option

Save ds17f/546bcc98a480d3ace5254d61b57011b9 to your computer and use it in GitHub Desktop.
SafeShutdown.py Script for Retroflag Nespi4 on Debian Bookworm using gpiod instead of the legacy RPi.GPIO
## This script was written by Claude Code when I was trying to set up Safe Shutdown on Bookworm
## Claude told me not to do this but I told it "Make It So!"
## The script worked on the first run, but I don't make any guarantees about it
## Hopefully if you're reading this it will help you too.
## Please link this gist to any threads out there that need it.
import gpiod
import os
import time
import signal
import sys
from multiprocessing import Process
# GPIO pin definitions
POWER_PIN = 3 # pin 5
LED_PIN = 14 # TXD
RESET_PIN = 2 # pin 13
POWEREN_PIN = 4 # pin 5
# Open GPIO chip
chip = gpiod.Chip('gpiochip4') # RPi4 uses gpiochip4
# Define lines
power_line = chip.get_line(POWER_PIN)
reset_line = chip.get_line(RESET_PIN)
led_line = chip.get_line(LED_PIN)
poweren_line = chip.get_line(POWEREN_PIN)
def init():
"""Initialize GPIO pins"""
try:
# Configure power and reset as inputs with pull-up
power_line.request(
consumer="power",
type=gpiod.LINE_REQ_DIR_IN,
flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP
)
reset_line.request(
consumer="reset",
type=gpiod.LINE_REQ_DIR_IN,
flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP
)
# Configure LED and power enable as outputs
led_line.request(
consumer="led",
type=gpiod.LINE_REQ_DIR_OUT,
default_val=1 # HIGH
)
poweren_line.request(
consumer="poweren",
type=gpiod.LINE_REQ_DIR_OUT,
default_val=1 # HIGH
)
except Exception as e:
print(f"GPIO initialization error: {e}")
sys.exit(1)
def listen():
"""Listen for button presses"""
print("SafeShutdown listening for power button...")
power_pressed = False
reset_pressed = False
try:
while True:
# Read power button
power_state = power_line.get_value()
if power_state == 0 and not power_pressed: # Button pressed (LOW)
power_pressed = True
print("Power button pressed, initiating shutdown...")
os.system("sudo shutdown -h now")
elif power_state == 1: # Button released (HIGH)
power_pressed = False
# Read reset button
reset_state = reset_line.get_value()
if reset_state == 0 and not reset_pressed: # Button pressed (LOW)
reset_pressed = True
print("Reset button pressed, initiating reboot...")
os.system("sudo shutdown -r now")
elif reset_state == 1: # Button released (HIGH)
reset_pressed = False
time.sleep(0.1) # Poll every 100ms
except KeyboardInterrupt:
print("\nShutdown listener stopped")
finally:
# Release GPIO
power_line.release()
reset_line.release()
led_line.release()
poweren_line.release()
def signal_handler(sig, frame):
"""Handle termination signals gracefully"""
print("Received signal, cleaning up...")
try:
power_line.release()
reset_line.release()
led_line.release()
poweren_line.release()
except:
pass
sys.exit(0)
if __name__ == "__main__":
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
init()
listen()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment