Die Fräse hängt an einem Arduino Uno mit CNC-Shield und Grbl 1.1h, davor sitzt ein Raspberry Pi 3 mit CNCjs. Der Pi macht nur eine Sache an der Elektrik selbst: Er schaltet die Netzteile ein und aus. Für die Schrittmotoren ist das ein umgebautes ATX-Netzteil aus einem alten PC, geschaltet über dessen PS_ON-Leitung.
Nach dem Neuaufbau des Pi (die ganze Geschichte steht im Artikel zum SD-Karten-Defekt) scheiterte die Referenzfahrt. Grbl meldete Alarm bei MPos Z 67,501 – exakt das 1,5-Fache der eingestellten Verfahrstrecke $132=45. Grbl hatte also die volle Suchstrecke abgezählt, nur bewegt hatte sich nichts. Die Motoren hatten keinen Strom.
Das Schaltskript sagte warum, nur verstand ich es erst nicht: „GPIO26 nimmt den Pegel 'lo' nicht an – Leitung liegt fest.“ Und ab und zu gingen die Antriebe von selbst an, wenn jemand gegen die Maschine stieß.
Was PS_ON eigentlich ist
Ein ATX-Netzteil hat neben den Versorgungsleitungen zwei Signalleitungen, die hier zählen:
- PS_ON – Einschaltleitung, aktiv low. Im Netzteil ist sie über einen Widerstand an die 5-V-Standby-Versorgung gezogen. Zieht man sie auf Masse, läuft das Netzteil an.
- PWR_OK – Rückmeldung des Netzteils, dass alle Spannungen stehen.
Entscheidend ist der erste Punkt: Auf PS_ON liegen im ausgeschalteten Zustand rund 5 V. Die GPIOs des Raspberry Pi vertragen 3,3 V – und sind nicht 5-V-tolerant.
Nach ATX-Norm ist PS_ON grün und PWR_OK grau. Bei meinem umgebauten Netzteil war die Einschaltleitung grau. Wer nur nach Farbe geht, verwechselt die beiden.
Sicheres Unterscheidungsmerkmal bei ausgeschaltetem Netzteil (Netzstecker drin, Standby läuft): PS_ON liegt auf etwa 5 V, PWR_OK auf Masse. Gemessen wird Gleichspannung – eine Wechselspannungsmessung zeigt nur die Restwelligkeit. Meine erste Messung ergab genau deshalb nichtssagende 200 mV.
Warum das den GPIO zerstört
Die alte Installation lief seit 2023 (laut Backups) mit PS_ON direkt an GPIO26, in Klipper/Moonraker als pin: !gpio26 eingetragen. Vermutlich drücken die 5 V über den Pull-up im Netzteil dabei Strom über die Schutzdiode des GPIO in die 3,3-V-Schiene des Pi. Der Widerstand im Netzteil begrenzt den Strom, deshalb stirbt der Pin nicht sofort – sondern irgendwann. Gemessen habe ich diesen Mechanismus nicht; sicher ist nur: PS_ON an einem 3,3-V-GPIO hielt den Pin dauerhaft auf High und zerstörte ihn, und die späteren Treiberversuche haben das vermutlich verschärft.
Das Bild am Ende: GPIO26 liegt hart auf High und folgt dem Pull-down nicht – auch mit abgezogenem Kabel. Drei Vergleichspins folgten dabei sauber. Der Pin ist tot.
Das gelegentliche Selbsteinschalten bei Erschütterung hatte übrigens eine harmlosere Ursache: Die Dupont-Buchse auf der Ader saß locker. Ein kurzer Kontakt gegen Masse genügt, und das Netzteil läuft an.
Fehlersuche: erst passiv messen
Die Belegung der GPIOs war mit der SD-Karte verloren. Ein Pin als Ausgang zu treiben, ohne zu wissen, was daran hängt, ist an einer Maschine mit Not-Aus und Netzteilen keine gute Idee. Die Alternative ist eine passive Messung: Jeder Pin wird als Eingang konfiguriert, einmal mit internem Pull-up, einmal mit Pull-down. Ein freier Pin folgt dem Widerstand. Ein Pin, an dem von außen etwas zieht, nicht.
#!/bin/bash
# Welche GPIOs sind von außen beschaltet? Rein passiv, es wird kein Ausgang getrieben.
# Pins als Argumente, z. B. ./pins.sh 5 6 12 13 16 19 20 21 26
# Achtung: Pins bleiben danach als Eingang mit Pull-down stehen.
for p in "$@"; do
pinctrl set "$p" ip pu; sleep 0.05
a=$(pinctrl get "$p" | grep -oE '\| (hi|lo)' | cut -c3-)
pinctrl set "$p" ip pd; sleep 0.05
b=$(pinctrl get "$p" | grep -oE '\| (hi|lo)' | cut -c3-)
case "$a/$b" in
hi/lo) echo "GPIO$p: frei" ;;
hi/hi) echo "GPIO$p: von außen HOCH gehalten" ;;
lo/lo) echo "GPIO$p: von außen nach MASSE gezogen" ;;
*) echo "GPIO$p: unklar ($a/$b)" ;;
esac
doneGemessen habe ich alle frei verfügbaren GPIOs. Ergebnis: GPIO16 und GPIO26 von außen hochgehalten, GPIO20 und GPIO21 nach Masse gezogen, zwölf weitere Pins frei. Was das bedeutet, verrieten erst zwei alte Backups von 2023 – eine moonraker.conf und ein OctoPrint-Backup:
| Pin | Messung | Laut Backup | Bedeutung |
|---|---|---|---|
| GPIO26 | hi/hi | pin: !gpio26 | Antriebe, aktiv low – PS_ON |
| GPIO20 | lo/lo | pin: gpio20 | Spindel-Relais, aktiv high |
| GPIO16 | hi/hi | physicalbutton, Normally Closed | Not-Aus-Öffner gegen 3,3 V |
| GPIO21 | lo/lo | – | unbekannt |
Vor dem Blick in die Backups war GPIO16 der aussichtsreichste Kandidat fürs Netzteil – Claude schlug vor, ihn als Ausgang auf Masse zu legen. An GPIO16 hing aber ein geschlossener Not-Aus-Schalter gegen 3,3 V. Das wäre ein Kurzschluss direkt über den Ausgangstreiber geworden. Verhindert hat es nur, dass vor dem Treiben nachgefragt wurde.
Den Beweis, dass die graue Ader selbst Spannung liefert, brachte ein Umstecktest: Ader von GPIO26 auf den freien GPIO19 – und GPIO19 sprang von hi/lo (frei) auf hi/hi. Mit abgezogener Ader blieb GPIO26 trotzdem auf hi/hi. Erst diese zweite Messung belegt den Defekt. „Hält High“ heißt nur „hält High“, solange noch ein Kabel steckt.
Die Lösung: PC817 dazwischen
Ein Optokoppler trennt beide Seiten galvanisch. Der Pi treibt nur eine LED, auf der Netzteilseite zieht ein Fototransistor PS_ON gegen die Netzteilmasse. Die 5 V des Netzteils erreichen den Pi nie mehr.
| PC817-Pin | Funktion | Anschluss |
|---|---|---|
| 1 | Anode (LED) | GPIO19 über 330 Ω |
| 2 | Kathode (LED) | Masse des Pi |
| 4 | Kollektor | PS_ON (bei mir: graue Ader) |
| 3 | Emitter | Masse des Netzteils (schwarz, COM) |
Mit 330 Ω fließen durch die LED etwa (3,3 V − 1,2 V) / 330 Ω ≈ 6 mA. Das liegt unter dem, was ein GPIO liefern darf, und reicht, damit der Transistor den kleinen Strom des Pull-ups im Netzteil sicher gegen Masse zieht.
Zwei Nebeneffekte machen die Schaltung angenehmer als das Original:
- Logik aktiv high. GPIO auf High → LED an → Transistor leitet → PS_ON auf Masse → Netzteil an. Das liest sich so, wie man es erwartet.
- Aus ist der Ruhezustand. Ohne LED-Strom ist der Transistor offen, das Netzteil bleibt aus – auch während der Pi bootet und die GPIOs frei hängen.
Damit der Pin nach dem Booten definiert auf Low steht, kommt eine Zeile in die config.txt:
# /boot/firmware/config.txt
# Antriebe beim Booten aus (PC817 an GPIO19, aktiv high)
gpio=19=op,dl- Kein Kommentar am Zeilenende. gpio=19=op,dl # Antriebe wird komplett ignoriert. Kommentare nur auf eigene Zeilen.
- Zeilen werden nach 98 Zeichen abgeschnitten – ohne Warnung. Bei langen dtoverlay=-Zeilen ist mir das beim Touch-Display passiert; die Details stehen im Artikel zum Neuaufbau.
Schaltskript mit Vorabprüfung
Die erste Version des Schaltskripts trieb den Pin 50 ms lang und prüfte danach, ob der Pegel angekommen war. Gegen eine Leitung, die von außen auf 5 V liegt, ist das genau falsch herum – vermutlich hat das GPIO26 den Rest gegeben.
Die neue Version prüft vorher: Der Pin wird zuerst Eingang mit Pull-Widerstand in Zielrichtung. Folgt er nicht, hält die äußere Beschaltung dagegen, und das Skript treibt gar nicht erst.
#!/bin/bash
# Antriebs-Netzteil über PC817 an GPIO19 schalten (aktiv high)
set -eu
PIN=19
setze_gesichert() { # $1 = hi | lo
local ziel=$1 pull
[[ $ziel == hi ]] && pull=pu || pull=pd
pinctrl set "$PIN" ip "$pull"
sleep 0.05
if ! pinctrl get "$PIN" | grep -q "| $ziel"; then
echo "GPIO$PIN folgt dem Pull nicht – Fremdbeschaltung, treibe nicht." >&2
[[ ${ERZWINGEN:-0} == 1 ]] || exit 1
fi
pinctrl set "$PIN" op "d${ziel:0:1}" # dh oder dl
}
case "${1:-status}" in
ein) setze_gesichert hi; echo "Antriebe: ein" ;;
aus) setze_gesichert lo; echo "Antriebe: aus" ;;
status) pinctrl get "$PIN" ;;
*) echo "Aufruf: $0 {ein|aus|status}" >&2; exit 2 ;;
esacIn CNCjs ist das Skript als Command hinterlegt, damit die Netzteile direkt aus der Oberfläche schaltbar sind. Kleiner Stolperstein dabei: ~/.cncrc von Hand zu bearbeiten reicht nicht, CNCjs hält die Konfiguration im Speicher. Neue Commands über die Oberfläche oder POST /api/commands anlegen.
Der Test am 22.09.: antriebe ein → „Antriebe: ein“, pinctrl zeigt 19: op | hi, das Netzteil läuft an. Danach lief die Referenzfahrt wieder durch.
Was ich mitnehme
- Fremde Steuerleitungen nie direkt an einen GPIO. Wer nicht sicher weiß, dass auf der Leitung höchstens 3,3 V liegen, setzt einen Optokoppler dazwischen. Das kostet ein Bauteil und einen Widerstand.
- Erst passiv messen, dann treiben. Die Pull-up/Pull-down-Methode hat jede Frage beantwortet, ohne irgendetwas zu riskieren.
- Alte Backups sind Gold wert. Ohne die Konfigurationsdateien von 2023 wäre die Belegung nur durch Raten zu rekonstruieren gewesen – mit einem Not-Aus-Kontakt mittendrin.
- Einzelne Dupont-Buchsen halten in einer vibrierenden Maschine nicht. Die PS_ON-Ader wird neu gecrimpt, alle Steuerleitungen bekommen ein gemeinsames Steckergehäuse mit Zugentlastung.
Hardware: Raspberry Pi 3B, Arduino Uno mit CNC-Shield (Grbl 1.1h), umgebautes ATX-Netzteil für die Schrittmotoren, PC817 mit 330 Ω. GPIO-Zugriff über pinctrl.
Dieser Artikel betrifft nur die Niederspannungsseite am ATX-Stecker. Der Umbau des Netzteils selbst ist nicht Thema – im Inneren liegt Netzspannung, das Gehäuse bleibt zu.
Offen ist noch die Stromversorgung des Pi: Sie ist knapp, und ich vermute dahinter die gelegentlichen USB-Abbrüche zum Arduino. Ein kräftigeres Netzteil für den Pi und eine eigene Versorgung für den Arduino sind geplant.
Anzeige · Affiliate-Link – kaufst du darüber, erhalte ich ggf. eine Provision. Für dich ändert sich am Preis nichts.