#!/usr/bin/env python3 """ GPIO‑monitor.py * Sets every usable BCM pin as an input (pull‑down default) * Saves the last read state of each pin * In an infinite loop it polls the pins (≈10 ms per pass) * When one or more pins change it prints: - Which pins changed and old→new values - The full 32‑bit snapshot as a binary string (MSB = highest pin) Run as root (or a user in the *gpio* group): sudo python3 GPIO‑monitor.py Press Ctrl‑C to stop – the script cleans up the GPIO pins. """ import sys import time import signal import RPi.GPIO as GPIO # ---------------------------------------------------------------------- # 1️⃣ Pin list – all BCM pins that exist on the 40‑pin header # ---------------------------------------------------------------------- GPIO_PINS = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ] # ---------------------------------------------------------------------- # 2️⃣ Initialise GPIO # ---------------------------------------------------------------------- GPIO.setmode(GPIO.BCM) # keep only those pins we can actually configure active_pins = [] failed = [] for pin in GPIO_PINS: try: GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) active_pins.append(pin) except Exception as e: failed.append((pin, str(e))) if failed: sys.stderr.write("⚠️ Could not initialise the following pins (they will be ignored):\n") for p, msg in failed: sys.stderr.write(f" Pin {p}: {msg}\n") # ---------------------------------------------------------------------- # 3️⃣ Helper to read the current snapshot (dict pin→0/1) # ---------------------------------------------------------------------- def read_all(): return {pin: GPIO.input(pin) for pin in active_pins} # ---------------------------------------------------------------------- # 4️⃣ Signal handling – graceful exit on Ctrl‑C / SIGTERM # ---------------------------------------------------------------------- keep_running = True def _handle_exit(signum, frame): global keep_running keep_running = False signal.signal(signal.SIGINT, _handle_exit) # Ctrl‑C signal.signal(signal.SIGTERM, _handle_exit) # ---------------------------------------------------------------------- # 5️⃣ Main monitoring loop # ---------------------------------------------------------------------- prev_state = read_all() # initial snapshot print("🔎 Monitoring {} GPIO pins – press Ctrl‑C to stop".format(len(active_pins))) while keep_running: cur_state = read_all() # Find pins whose value differed from the previous snapshot changed = [(pin, prev_state[pin], cur_state[pin]) for pin in active_pins if prev_state[pin] != cur_state[pin]] if changed: # Build a readable change list e.g. "4:0→1, 17:1→0" changes_txt = ", ".join(f"{pin}:{old}→{new}" for pin, old, new in changed) # Build a compact binary view – 32‑bit with pins sorted ascending # (higher pins become more significant bits) bits = "".join(str(cur_state[p]) for p in sorted(active_pins, reverse=True)) # Pad to a multiple of 4 for readability pad_len = (4 - len(bits) % 4) % 4 bits = "0" * pad_len + bits print(f"[{time.strftime('%H:%M:%S')}] Change detected – {changes_txt}") print(f" Current snapshot: 0b{bits}") # Update reference for the next iteration prev_state = cur_state # Polling interval – adjust if you need faster/slower response time.sleep(0.01) # 10 ms # ---------------------------------------------------------------------- # 6️⃣ Cleanup # ---------------------------------------------------------------------- GPIO.cleanup() print("\n✅ Monitoring stopped – GPIO pins released.")