Effizienter Arduino PWM-Reader & RC-Empfänger-Decoder mit Fail-Safe
Komponenten und Zubehör
| Arduino UNO | × | 1 |
| SG90 Mikro-Servomotor | × | 2 |
Über dieses Projekt
Generischer, effizienter Code zum Auslesen eines RC-Empfängers (oder beliebiger PWM-Signale) an jedem Arduino-Eingangspin – mit Fail-Safe bei Signalverlust.
Video zeigt Arduino Uno als Servo-Mixer mit dem Code PWMread_RCfailsafe.ino (unten). Funktionen kümmern sich um Interrupt-Register, leicht zwischen Projekten/Pins portierbar.
Im Video-Beispiel:
- Fail-Safe bei Empfänger-Signalverlust: Höhenruder voll hoch, Querruder neutral
- Richtung des blauen Servos durch Gasstellung
- Auslenkung (Rates) des blauen Servos per Slider am Sender
- Mischen ein/aus per Gears-Schalter
Inhalt
- Wie Servos per PWM gesteuert werden
- Beispielanwendungen: Arduino in RC-Modellen/Robotern
- Code-Übersicht: PWM-Decodierung mit Fail-Safe
- Anwendung von
PWMread_RCfailsafe.ino - Empfänger-Framerate & Frequenz anzeigen
- Servo-Mixing-Beispiel
- Begründung des Ansatzes
- Limitierungen
Wie werden Servos/Regler per PWM gesteuert?
Verständnis der PWM-Signale (1000–2000 µs Impulsbreite, ~50 Hz) wird vorausgesetzt. Gutes Erklärvideo. Erforderlich: Arduino-IDE, Variablentypen, If/For-Schleifen, Arrays, Servo-Library.
Beispielanwendungen
- Servo-Mixing, Lichtschaltung, Pumpen/Ventile, benutzerdefinierte Sequenzen
- Controller: Flugstabilisierung, Autopilot, Kurshalten, Höhen-/Tiefenhalten, Auto-Level, Hindernisvermeidung, Return-to-Home
- Reaktion auf Signalverlust oder niedrigen Akku
- Derselbe Sender für mehrere Modelle ohne Neukonfiguration
Code-Übersicht: PWM von RC-Empfänger decodieren mit Fail-Safe
Messung per Pin-Change-Interrupts. Funktionen automatisieren Interrupt-Setup und Datenextraktion an jedem digitalen/analogen Pin (außer A6, A7) auf Uno, Nano, Pro Mini. Einfach für Einsteiger.
Ziel: Generisches RC-Empfänger-Modul mit Fail-Safe, schnell zwischen Projekten verschiebbar. Beispielcode im „How-to“-Abschnitt als Mittel zum Zweck.
Hinweis: Funktioniert nicht mit SoftwareSerial oder anderen Libraries, die Pin-Change-Interrupts nutzen.
Funktionsweise:
- Eingangspins in Array
pwmPIN[](beliebige Länge) - Setup-Funktion aktiviert Pin-Change-Interrupts durch Register-Setzung für jeden Pin
- Spannungswechsel an gewähltem Pin triggert einen von drei ISR (PCINT0_vect→Port B, PCINT1_vect→Port C, PCINT2_vect→Port D)
- In ISR: FOR-Schleife + IF ermittelt geänderten Pin & Kanal, Zeit via
micros()merken, zurück zuloop() - Zeitintervalle → Pulsbreite & Wiederholungsperiode
- Flags in ISR kennzeichnen neue Pulse
- Flags werden von Funktionen genutzt, um ISR-Daten zu verarbeiten
YouTube-Video von Joop Brokking (erste 8 Min) erklärt Pin-Change-Interrupts für RC-Empfänger ähnlich. Alles wird von PWMread_RCfailsafe.ino erledigt (Download unten). Port-Manipulation: tronixstuff.com.
Zusätzlich: RC_decode() wandelt Pulsbreite (1000–2000 µs) in ±100 % Steuersignal um. Fail-Safe prüft Signaltoleranzen (10–330 Hz, 500–2500 µs). Bei Signalverlust → voreingestellter Fail-Safe-Wert. Kalibrierungswerte & Fail-Safe-Positionen pro Kanal in PWMread_RCfailsafe.ino einstellbar.
Anwendung von PWMread_RCfailsafe.ino
Schritt 1: Beispiel-Hardware-Setup mit Arduino Uno
- Empfänger an 5V/GND
- Signalleitungen an Pins 2–7 (2-Kanal: nur 2 & 3)
- Servo-Mixer: Servo-Signalleitungen an Pin 9 & 10

Schritt 2: PWMread_RCfailsafe.ino in Sketch-Ordner kopieren
Beispielsketch RC_Read_Example unten zum Download. Als Hauptsketch nutzen. Datei in Sketch-Ordner kopieren → zweiter Tab in IDE.

Schritt 3: Eingangspins festlegen
Tab PWMread_RCfailsafe öffnen, zu „USER DEFINED VARIABLES“ scrollen, Pins in pwmPIN[] eintragen. Beliebige Anzahl/Reihenfolge. A6/A7 nicht nutzbar. Mega nicht unterstützt (leicht erweiterbar). Erstes Array-Element = Kanal 1 usw. Empfänger-Kanäle idealerweise passend zuordnen.

Schritt 4: Verfügbare Funktionen prüfen

Schritt 5: Roh-Pulsbreiten zum Serial Monitor ausgeben
RC_Read_Example hochladen, Sender an, rohe Pulsbreiten via print_RCpwm() ausgeben (wenn RC_avail() neue Daten auf allen Kanälen meldet).

Schritt 6: Sender kalibrieren
Ausgegebenen Pulsbreiten nutzen, um Arrays RC_min[], RC_mid[], RC_max[] manuell anzupassen (Vollausschlag für beste Auflösung).

Schritt 7: Kalibrierte Kanäle ausgeben
print_RCpwm() auskommentieren. RC_decode(Kanal) wandelt in ±1 um. Mit decimal2percentage() + Serial.println() ausgeben.

Schritt 8: Fail-Safe setzen
RC_failsafe[] pro Kanal auf ±1 einstellen. Sender aus/ein → Fail-Safe testen. RC-Eingang nun im Sketch nutzbar.
Hinweis: Evtl. Fail-Safe im Empfänger deaktivieren, sonst reagiert Arduino nicht auf Signalverlust.

Empfänger-Framerate & Frequenz anzeigen
Pulswiederholungsperiode & Frequenz per Serial ausgeben. PWM_read(Kanal) prüft neue Daten, dann PWM_period(), PWM_freq(). Beispiel in RC_FrameRate.ino. Ersten Kanal nutzen (erster Puls pro Frame). PWM_read() vor RC_decode() aufrufen (gleiche Flags).

Periode nützlich: Zeit bis nächster Datensatz. Wenn RC_avail() nach z. B. 21 ms keine neuen Daten → RC_decode() aufrufen (Fail-Safe triggern / PID-Controller stabil weitertakten). In RC_Read_Example.ino:
now = millis();
if(RC_avail() || now - rc_update > 21) {
rc_update = now;
// RC_decode() nutzen
// PID regeln
// Servo-Mixing
// Servos positionieren
}
Servo-Mixing-Beispiel
RC_ServoMixer_Example.ino zeigt Mischen von 2 Empfänger-Kanälen (Kanal 2 & 3: Höhe & Quer) auf 2 Servos (Servo-Library an Pin 9 & 10). Richtung, Rate, Sub-Trim einstellbar.

Mischung: mix1 = Ch2 - Ch3, mix2 = Ch2 + Ch3, Ausgabe auf ±1 begrenzen. Vor Servo-Ansteuerung: ±100 % → µs-Pulsbreite. Funktion calc_uS() unten im Sketch.

Standard: 1500 µs Neutral, ±500 µs → 1000 µs (−100 %) bis 2000 µs (+100 %). Mit Rate, Richtung, Sub-Trim:
Puls [µs] = 1500 + (Servo_Position_% × Rate × Richtung + SubTrim) × 500
Richtung, Rate, Sub-Trim können statisch oder dynamisch (per weiterem Empfängerkanal) geändert werden.
Begründung des Ansatzes
pulseIn(PIN, HIGH) blockiert loop() bis Pulsende – verschwendete CPU-Zeit, bei mehreren Eingängen Datenverlust. Pin-Change-Interrupts + direkte Port-Manipulation = minimale Verzögerung in loop(), aber aufwendiger. Lösung: Generischer Code (.ino-Datei) kopieren, Pins angeben, Funktionen nutzen – Vorteile beider Welten.
Limitierungen
micros()-Funktion
4 µs Schritte → 4 µs Genauigkeit bei 1000–2000 µs Puls. Praktisch ausreichend. 0,5 µs via Timer-Interrupts möglich (Instructables-Link).
Effizienz von PWMread_RCfailsafe.ino
Bei 6/9-Kanal-Empfänger: 1,4–2,0 % CPU-Zeit in ISR – akzeptabel. ISR-Laufzeit pro Kanalanzahl:
- 1 Kanal < 8 µs
- 2 Kanäle < 12 µs
- 3 Kanäle < 16 µs
- 4 Kanäle < 20 µs
- 5 Kanäle < 20 µs
- 6 Kanäle < 24 µs
Mehr Kanäle → längere ISR (FOR-Schleife prüft alle Kanäle bei jedem Interrupt). Bei niedriger Frequenz (50 Hz) vernachlässigbar.
+ ~4 µs ISR-Overhead (Ein-/Austritt). Pro Puls 2× ISR (Anstieg + Abfall).
Bei 6 Eingängen: 2 × (4 + 24) = 56 µs pro Puls (min. messbare Pulsbreite). Alle 6 Kanäle: 336 µs. Bei 20 ms Periode → 1,68 % ISR-Last (deutlich besser als pulseIn(), das bis 20 ms blockiert). Bei 2 Kanälen nur 0,16 %.
Maximale praktische Frequenz
Für andere Zwecke: max. ~2,5 kHz (100 Schritte Auflösung via micros()). Bei 1 Eingang & 2,5 kHz: 3 % ISR-Last → min. Duty 0,03 → min. Puls 12 µs. Höhere Frequenzen: ISR anpassen.
Herstellungsprozess
- Eingabe und Ausgabe in C meistern: scanf, printf & Co.
- LCD-Animationen & Spiele mit Arduino UNO erstellen
- Intelligentes Voltmeter mit Arduino & Smartphone bauen – Einfaches DIY-Projekt
- Arduino Temperatur- & Feuchtelogger mit DHT11 & Ethernet Shield
- Temperatur & Luftfeuchte mit DHT11 auf Blynk auslesen – Arduino-Tutorial Schritt für Schritt
- Python 3 zu Arduino UNO: Einfache Steuerung & LED-Demo
- Echtzeit-Zelluläre Automaten auf Arduino mit 128x64 OLED-Display
- Kompaktes FM-Radio mit Arduino Nano und RDA5807M selbst bauen
- Leistungsstarker FM-Radio-Empfänger mit Arduino & TEA5767
- Isolierter Analogeingang für Arduino: Spannung/Strom galvanisch trennen