Embedded & IoT No. 464

Raspberry Pi Projects skill for Claude Code

Headless Pi builds that survive a pulled plug. Claude sets up a Pi headless with Imager customisation and NetworkManager, writes GPIO code with gpiozero so it runs on Pi 5 where RPi.GPIO fails, checks 3.3 V and per-pin current limits before wiring advice, and ships the app as a systemd service under a dedicated user. It checks undervoltage with vcgencmd and limits SD card writes so the device survives power cuts.

  • nameraspberry-pi-projects
  • size202 lines · ≈2,050 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Builds Raspberry Pi hardware projects that run unattended — headless setup with Imager customisation and NetworkManager, GPIO with gpiozero and libgpiod on Pi 4/Pi 5, I2C/SPI enablement, camera capture with rpicam-apps and Picamera2, Python virtual environments under PEP 668, systemd services with restart and watchdog, and SD-card-friendly logging. Use when wiring sensors, buttons, relays or LEDs to a Pi, when RPi.GPIO fails on a Pi 5, when a script must start on boot, when the camera is not detected, or when a Pi throttles, corrupts its SD card, or drops off Wi-Fi.

Try asking

  • RPi.GPIO crashes on my new Pi 5 — port this button and relay script
  • Make my Pi sensor logger start on boot and restart if it crashes
  • The Pi camera says no cameras available after I upgraded the OS

Use it when

  • Wiring sensors, buttons, LEDs or relays to Pi GPIO
  • Porting RPi.GPIO or legacy camera code to current Raspberry Pi OS
  • Running a Python app on boot as a reliable service
  • A Pi throttles, reboots randomly, or corrupts its SD card

Skip it when

How to check the result

vcgencmd get_throttled reads 0x0 under load, the service is enabled and running after a reboot with no login, and the device recovers from a pulled power plug.

The common mistake it prevents

Feeding a 5 V sensor output straight into a GPIO pin, which can permanently damage the SoC even if it seems to work at first.

~/.claude/skills/raspberry-pi-projects/SKILL.md

--- name: raspberry-pi-projects description: Builds Raspberry Pi hardware projects that run unattended — headless setup with Imager customisation and NetworkManager, GPIO with gpiozero… license: MIT ---

Done well means: the Pi boots headless into a known state, the application runs as a systemd service under its own user, GPIO code uses a library that works on every Pi model the project targets, no pin exceeds its electrical limits, and power-cut or reboot does not need anyone to touch the device.

Electrical rules (check before any wiring advice)

  • GPIO is 3.3 V only. Any 5 V signal into a GPIO pin can destroy the SoC. Use a level shifter (e.g. a bidirectional MOSFET shifter for I2C, 74AHCT125 for fast outputs to 5 V devices) or a resistor divider for slow inputs.
  • Keep each pin at or below ~16 mA and the whole header’s GPIO total around 50 mA. An LED gets a resistor: (3.3 − 2.0) V / 5 mA ≈ 270 Ω, use 330 Ω.
  • The 3.3 V header pins have a limited budget (a few hundred mA shared with the board); power hungry sensors and displays from a separate regulator.
  • Motors, relays, solenoids, LED strips: transistor/MOSFET or driver board, separate supply, common ground, flyback diode across coils. Relay modules sold “for Arduino” often need 5 V on the input side — check they trigger reliably at 3.3 V.
  • Use the official PSU: Pi 4 needs 5.1 V 3 A; Pi 5 needs 5 V 5 A USB-C PD for full USB power (a 3 A supply limits USB peripherals). Undervoltage causes SD corruption and random crashes, not just a warning icon.
  • Never hot-plug HATs or wiring on a powered Pi.

Workflow

- [ ] 1. Identify Pi model, OS release (cat /etc/os-release), and every attached part
- [ ] 2. Flash with Imager OS customisation: hostname, user, SSH key, Wi-Fi, locale
- [ ] 3. First boot: update, enable interfaces, check power (vcgencmd get_throttled)
- [ ] 4. Pin map with voltage/current per pin; verify against the rules above
- [ ] 5. Write the app in a venv; GPIO via gpiozero (lgpio backend)
- [ ] 6. Run it as a systemd service with restart policy
- [ ] 7. Reduce SD writes; set up logs and remote access
- [ ] 8. Pull-the-plug test and 24 h soak

2–3. Headless setup

  • Raspberry Pi OS has no default pi user or password. Set user, SSH public key and Wi-Fi in Imager’s customisation before writing the card.
  • Bookworm and later use NetworkManager; wpa_supplicant.conf in the boot partition is no longer the supported path. Manage networks with nmcli:
sudo nmcli dev wifi connect "SSID" password "secret" ifname wlan0
nmcli connection show
sudo nmcli connection modify "SSID" connection.autoconnect-priority 10
  • Boot config lives at /boot/firmware/config.txt (Bookworm and later), not /boot/config.txt.
  • Enable interfaces non-interactively:
sudo raspi-config nonint do_i2c 0     # 0 = enable
sudo raspi-config nonint do_spi 0
sudo raspi-config nonint do_serial_hw 0 && sudo raspi-config nonint do_serial_cons 1   # UART on, console off
i2cdetect -y 1                         # from i2c-tools; shows device addresses
vcgencmd get_throttled                 # 0x0 = never undervolted or throttled
vcgencmd measure_temp

get_throttled bits: 0x1 undervoltage now, 0x4 throttled now, 0x10000 undervoltage has occurred since boot. Any non-zero value means fix power or cooling first.

5. GPIO library choice

SituationUse
Buttons, LEDs, PWM, motors, sensors (default)gpiozero (uses the lgpio backend on current OS)
Low-level line control, C or Python, any Linux boardlibgpiod v2 (gpiod Python bindings)
Legacy code using RPi.GPIO on a Pi 5rpi-lgpio drop-in, then migrate to gpiozero
Precise PWM / servo timingHardware PWM (dtoverlay=pwm-2chan) or a PCA9685 board over I2C
Hard real-time (µs) timingOffload to a microcontroller (Pico) over UART/I2C

RPi.GPIO and pigpio do not work on the Pi 5 (GPIO moved to the RP1 chip). Write new code with gpiozero.

from signal import pause
from gpiozero import Button, LED

led = LED(17)                         # BCM numbering
button = Button(27, pull_up=True, bounce_time=0.05)   # button to GND
button.when_pressed = led.toggle
pause()
  • gpiozero uses BCM numbers by default; "BOARD11" selects physical pin numbering.
  • pinout prints the header map for the board you are on.
  • pinctrl get 17 (current OS) shows a pin’s mode and level for debugging.
  • With libgpiod CLI tools, find the right chip with gpiodetect/gpioinfo rather than assuming gpiochip0 or gpiochip4; the numbering of the header chip has differed between Pi models and kernel versions.

Python environments

Current Raspberry Pi OS marks system Python as externally managed (PEP 668); pip install outside a venv fails by design.

sudo apt install -y python3-gpiozero python3-picamera2 python3-lgpio
python3 -m venv --system-site-packages ~/app/.venv     # sees apt-installed hardware libs
~/app/.venv/bin/pip install -r ~/app/requirements.txt

Prefer apt for hardware libraries (picamera2, lgpio, libcamera bindings are tied to the OS build); pip for pure-Python dependencies.

Camera

rpicam-hello --list-cameras           # detection check (rpicam-apps; formerly libcamera-*)
rpicam-still -o test.jpg
rpicam-vid -t 10000 -o test.h264
from picamera2 import Picamera2
cam = Picamera2()
cam.configure(cam.create_still_configuration())
cam.start()
cam.capture_file("frame.jpg")
cam.stop()
  • raspistill/raspivid and the legacy camera stack are gone; old tutorials do not apply.
  • “No cameras available”: reseat the ribbon (contacts orientation), and on Pi 5 use the narrower 22-pin cable. Third-party sensors may need a dtoverlay= line in config.txt.
  • Only one process can own the camera at a time.

6. systemd service

# /etc/systemd/system/sensor-logger.service
[Unit]
Description=Sensor logger
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=sensor
Group=sensor
SupplementaryGroups=gpio i2c spi video
WorkingDirectory=/opt/sensor-logger
ExecStart=/opt/sensor-logger/.venv/bin/python -u main.py
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now sensor-logger
journalctl -u sensor-logger -f
  • Do not use rc.local, @reboot cron, or .bashrc for services.
  • Run as a dedicated user in the gpio, i2c, spi, video groups, never root.
  • For hang detection, add WatchdogSec=30 and notify from the app (sd_notify), or enable the hardware watchdog via systemd’s RuntimeWatchdogSec in system.conf.
  • For timers, dependencies and hardening options use systemd-services.

7. SD card longevity

  • Send logs to journald with Storage=volatile or cap them (SystemMaxUse=50M) in /etc/systemd/journald.conf.
  • Write data in batches; avoid per-second small writes. Put scratch files in /tmp (tmpfs).
  • For kiosk/appliance builds, enable the read-only overlay filesystem via raspi-config (Performance Options → Overlay File System), keeping data on a separate writable partition or USB/NVMe drive.
  • Pi 5 and Pi 4 boot from USB SSD or (Pi 5) NVMe; prefer that for write-heavy loads.

Debugging

SymptomCheck
RuntimeError: Cannot determine SOC peripheral base addressRPi.GPIO on Pi 5; switch to gpiozero / rpi-lgpio
i2cdetect shows nothingInterface not enabled, wiring, missing pull-ups, wrong bus number
Random reboots / SD corruptionvcgencmd get_throttled; PSU and cable
Service works manually, not on bootPaths, user groups, network not up; read journalctl -u
error: externally-managed-environmentUse a venv as shown
Wi-Fi dropsDisable power save: iw wlan0 set power_save off (persist via NetworkManager config)

Verification

  1. vcgencmd get_throttled returns 0x0 after a full-load run.
  2. Every pin in the pin map is within 3.3 V and ~16 mA; 5 V devices go through shifters.
  3. systemctl is-enabled and is-active both succeed; after sudo reboot the app is running within the expected time with no login.
  4. Pull the power plug mid-run, restore it: the Pi boots and the service recovers.
  5. 24 h soak: memory flat (systemctl status memory line), no throttling, logs bounded.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc