Inhaltsverzeichnis
RC Receiver
Author: Dipl.-Ing. Wilfried Klaas
Board: Arduino Duemilanove, Arduino Uno, Arduino Leonardo (mit Einschränkungen)
Für die verschiedenen Projekte hier, habe ich mal eine kleine Bibliothek geschrieben, die die Anbindung an einen Modellbauempfänger etwas einfacher macht. Die Bibliothek beinhaltet ein Objekt RCReceive, welches für die Anbindung zuständig ist.
Die Bibliothek ermittelt automatisch den Nullpunkt aus den ersten 10 Werten. Weiterhin wird der Wertebereich automatisch auf den Bereich von 0..255 beschränkt. Neu ist die Fehlererkennung. Werden mehr als 3 fehlerhafte Impulse vom Empfänger erkannt, kann man das in seinem Programm abfragen und entsprechend reagieren, z.B. Failsafe oder RTH.
21.08.2015 Neue Version 1.4.0 in Github
Die Bibliothek ist nun im Library Manager der Arduino IDE zu finden und wird auch auf GITHUB gehosted.
(RCReceiver auf Github)
Die aktuellen Releases findet man ab sofort hier RCReceiver Releases
Die Bibliothek gibt's hier: RCReceiver (letzte Änderung 09.04.2014)
2 Programmtemplates sind mit dabei.
09.04.2014 Neue Version 0.2.0
Jetzt funktioniert die Lib auch endlich mit einem Arduino Mega 256 sauber.
Viel Spass damit.
06.10.2013 Neue Version
Ich habe jetzt etliche Tage an einer neuen Version gebastelt. Ziel war es, das ganze zusammen mit einer einfachen Servo Bibliothek auf den ATTiny 25/45/85 zum laufen zu bekommen. Das ist mir auch geglückt. Deswegen gibt es jetzt hier eine neue Version.
31.01.2013 Es gibt eine neue Version.
- Verbesserte Fehlererkennung
- einfachere Initialisierung im Interruptmodus
- Werte nun auch als ms abholbar
- schnellere Interruptroutine durch eigene Timerinitialisierung
- Unterstützung von Arduino Mega und Arduino Leonardo
Benutzt wird dabei der Timer1, der auch von der Servo Bibliothek benutzt wird. Da ich die gleiche Initialisierung mache, ist das aber kein Problem.
Installation
Die Installation ist einfach, Für die Installation einfach das ganze Zip in das Sketchverzeichniss auspacken. Danach sollte dort ein libraries Verzeichniss extistieren und dortdrin sind 2 neue Libs. RCRecive und MCSTools.
Einbinden
Zum Einbinden einfach auf [Sketch]/[Library importieren…], und schon sind in ihrer Quelldatei einfach folgende Zeilen hinzugefügt:
#include <RCReceive.h>
Das gleiche bitte auch mit der MCStools Bibliothek.
#include <debug.h> #include <makros.h> #include <RCReceive.h>
Für den ATTIny 8 und für den Mega gibt es noch 2 extra #define's.
- ATTiny8
über
#define USE_TIMER_1
kann der Timer 1 anstatt der internen micros() benutzt werden. Dadurch wird die Messung genauer und evt. braucht man den Timer0 noch für was anderes.
- ATMega256
über
#define USE_TIMER_5
kann der Timer 5 anstatt des Timer 1 benutzt werden. Dadurch werden dei PWM Ports auf den Pins 11,12,evt. 13 nicht mehr beeinflußt und evt. braucht man den Timer1 noch für was anderes.
Bei Timer 5 gehen dann die PWM auf 44, 45 und 46 (die sind auf dem XIO Stecker) aber nicht mehr.
Die defines kann man entweder direkt in der RCReceive.h aktivieren oder aber man schreibt diese vor dem
#include "RCReceive.h"
Beispiel:
#define USE_TIMER_5 #include <RCReceive.h>
Hardwareanbindung
Schliessen Sie einfach die Masse und den Impulspin an den Arduino an. Masse an den entsprechenden Massepin vom Arduino und der Impulspin an den Pin, den Sie im Programm definiert haben.
Beim Testen des Programms ist es sinnvoll, das man den Empfänger vom Arduino mitversorgen kann. Dann, und auch nur dann, können Sie auch den mittleren Pin vom Servokabel an den +5V Pin des Arduino anschliessen. Bitte vermeiden Sie, den Empfänger in dem Fall anderweitig mit Strom zu versorgen. Denken Sie bitte auch daran, daß manche Fahrtregler eine eingebaute Versorgungsfunktion (BEC) haben. Eine Versorgung aus 2 verschiedenen Quellen kann sowohl für den Arduino wie auch für den Empfänger, ESC gefährlich sein. Im Zweifel benutzen Sie dann den Vin Pin bzw. den DC-Anschluss des Arduinos.
Im Pollingbetrieb kann jeder Pin verwendet werden. Beim Interruptbetrieb können nur die Pins 2 und 3 benutzt werden.
Benutzung
Möchten Sie nun einen Kanal auswerten, müssen Sie zunächst einen Pin zur Verfügung stellen. Die korrekte Initialisierung übernimmt die Bibliothek. Dazu müssen Sie pro Kanal eine Variable vom Typ RCRecive definieren und diese mit dem Pin verbinden.
... // Hardwareanbindung für Arduino Hardware, Empfängerkanäle const byte PIN_RC_STE = 3; RCReceive rcReceiver; ... void setup() { ... // und Pin mit dem Objekt verbinden rcReceiver.attach(PIN_RC_STE); ... }
Soweit so einfach. Es gibt 2 Varianten der Einbindung.
Polling
Beim Polling müssen Sie dem Objekt, denn darum handelt es sich nun, sagen, wann es den Wert vom Empfänger lesen soll. Dieses geschiet mit der Methode poll()
. Am besten macht man das z.B. am Anfang in der loop()
Methode.
void loop() { // neuen RC Wert für Steuerservo lesen. rcReceiver.poll(); ... }
Die ersten 10 Werte gehen immer in die Nullpunktbestimmung. d.h. wenn man den Arduino einschaltet und dieser vernünftige Werte vom Empfänger erhält, sind die 10 ersten Werte nur für die Nullpunktbestimmung relevant. Das geschiet automatisch im Hintergrund. Es gibt eine Funktion zur Abfrage, ob der Nullpunkt bereits gelesen wurde. hasNP()
Den Nullpunkte sollte man also abfragen und solange mit der eigentlichen Funktion warten, bis dieser korrekt bestimmt worden ist.
Machen kann man das z.B. so:
... void loop() { ... // Aktuellen RC-Wert lesen rcReceiver.poll(); // Nullpunktsbestimmung ? if (rcReceiver.hasNP() && !rcReceiver.hasError()) { doWork(); } else if (rcReceiver.hasError()) { // Fehlerbehandlung failsafe oder sowas... } ... }
Auch kann man an dieser Stelle nach Fehlern abfragen und entsprechend reagieren.
Um im Programm jetzt den aktuellen Wert herauszulesen, muss man folgende Methode aufrufen:
... byte value = rcReceiver.getValue(); ...
Damit erhält man den gemittelten Wert der letzten 10 Werte. Die Mittlung mache ich deswegen, damit etweige Fehler in der Übertragung nicht so ins Gewicht fallen. Natürlich hat das eine Verzögerung zur Folge. Und zwar von genau 200ms.
Nachteil Wenn Sie den Polling Mechanismus einsetzten wollen und zus. noch weitere Bibliotheken, die mit Interrupts arbeiten, kann es sein, das der Wert nicht konstant ist, sondern unruhig wird. Die zur Messung des Empfängerwertes verwendete Funktion pulseIn()
wird dann von den im System vorhandenen Interrupts gestört.
Natürlich kann man vor dem pulseIN()
mit cli()
die Interrupts abschalten und mit sei()
wieder einschalten, dann wird der gemessene Wert wieder schön ruhig, aber leider funktionieren dann evt. die anderen Bibliotheken nicht mehr richtig. Sehr Ärgerlich ist, daß gerade die für uns wichtige Servo Bibliothek einer der Störenfriede ist. Und die Servo Bibliothek reagiert ganz schlecht auf das Ausschalten des Interruptes.
Eine Verbesserung bringt da das 2. Messverfahren.
Interrupts
Für die Messung des Empfängersignales kann man auch Interrupts verwenden. Dazu müssen aber die Pins 2 und 3 verwendet werden. Andere Pins gehen dann nicht. (Nur Deumillanove und Uno) Nur diese Pins sind mit den Softwareinterrupts 0 und 1 verbinden. D.h. Pin 2 ist für den Interrupt 0 zuständig und Pin 3 für den Interrupt 1.
Um den Interrupt zu benutzen müssen wir lediglich zum Initialisieren eine andere Methode verwenden. Also ändert sich der setup()
-Code zu:
void setup() { rcReceiver.attachInt(PIN_RC); // put your setup code here, to run once: }
Wichtig ist die attachInt()
Funktion. Der Parameter bezeichnet den richtigen Pin und nicht die Interruptnummer. Die Umsetzung erfolgt intern.
Ein poll()
in der loop()
Funktion kann jetzt entfallen.
Hier mal eine Auflistung der verschiedenen möglichen Pins der verschiedenen Boards.
- Duemillanove, Uno (328): Pins 2 und 3
- Leonardo: Pins 3, 2, 0, 1 (Wichtig: 2 und 3 sind vertauscht, macht für uns aber nix, da die Bibliothek alles richtig verdrahtet.)
- Mega2560: Pins 2, 3, 21, 20, 19, 18
Methoden
Hier jetzt mal alle Methoden auf einen Streich.
void attach(byte pin);
Verbindet das Objekt mit einem Arduino Pin.
void attachInt(byte pin);
Verbindet das Objekt mit einem Arduino Pin und startet den Interruptmodus.
void detachInt(byte pin);
zugewiesenen Interruptroutine lösen.
byte getValue();
Holt den aktuellen gemittelten Wert im Bereich von 0-255.
int getMsValue();
Holt den aktuellen gemittelten Wert in ms.
byte poll();
Den aktuellen Wert vom Empfänger holen.
byte hasNP();
Nullpunkt wurde ermittelt.
byte getNP();
Aktuellen Nullpunkt holen.
byte hasError();
Es wurden mehr als 3 fehlerhafte Pakete vom Empfänger übermittelt.
Diskussion
ich tüftle schon lange an einer Fahrwerkssteuerung mit 2 Servus und dem Schalter über 2,4, kHz Sender und Empfänger. Ausgelesen werden soll die Stellung eines 2 Wege Schalters am sender. Leider hapert es hier an meinem begrenzten Wissen. Wäre net wenn jemand dafür einen code hat
Wir sind eine Schulklasse ohne Programmiererfahrung. Fuer unser Solarboot basteln wir gerade einen MPP Tracker.
Der erste Test war vorgestern.
Unser Boot bleibt stehen wenn es soll, es faehrt rueckwaerts und auch vorwaerts. Erstaunlich ;)
Allerdings gibt es auch noch Probleme.
Die Steuerung reagiert stark verzoegert. Allerdings geben wir noch Werte ueber die serielle Schnittstelle aus und haben da noch delays drin. Vermutlich ist das der Grund.
Falls nicht, irgendwo weiter oben steht, dass man die Anzahl der Messwerte fuer die Mittelwertbildung verringern kann.
In RCReceive.h steht stackSize=10 drin. Reicht es diesen Wert zu verkleinern?
Was uns allerdings mehr Sorgen bereitet, manchmal laeuft der Motor nicht an.
Man kann den Gasknueppel mehrfach vor und zurueck bewegen ohne dass sich was tut.
Ploetzlich geht es dann doch wieder.
Irgendwie ist das nicht optimal fuer ein Rennen ;)
Haette da zufaellig jemand eine Idee was das sein koennte? Oder besser, wie man das abstellen koennte.
Die debug Option gibt Werte auf der seriellen Schnittstelle aus? Koennte sowas bei eingrenzen der Ursache weiter helfen?
Eine Abfrage auf Fehler haben wir nicht im Programm
Vielen Dank schon mal
also ich bin auch noch Anfänger und beschäftige mich seit ein Paar Tagen damit ein Empfängersignal
auszulesen. Ich habe das Beispiel aus der Bibliothek genommen und um 2 Zeilen ergänzt um mir den Wert
des Signals mittels byte getValue(); zu holen und im seriellen Monitor anzuzeigen.
Leider funktioniert das nicht, hier mein code:
#include "makros.h"
#include "debug.h"
#include "RCReceive.h"
/*
RC_Template.ino - Template for RC Receiver enabled programs interrupt version - Version 0.2
Copyright (c) 2014 Wilfried Klaas. All right reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
const byte PIN_RC = 2;
// Der Empfänger
RCReceive rcReceiver;
void setup() {
// RC Receiver in Interruptvariante
rcReceiver.attachInt(PIN_RC);
Serial.begin (9600);
// put your setup code here, to run once:
}
void loop() {
// nur wenn der Nullpunkt bestimmt worden ist,
// und es keinen Fehler gegeben hat soll die eigentliche Arbeit gemacht werden
if (rcReceiver.hasNP() && !rcReceiver.hasError()) {
doWork();
}
else if (rcReceiver.hasError()) {
// Fehlerbehandlung failsafe oder sowas...
}
}
void doWork() {
// put your main code here, to run repeatedly
byte value = rcReceiver.getValue();
Serial.println(rcReceiver);
// hier würde ich dann was zwischen 0 und 255 erwarten ????
// und bitte nicht steinigen..... :-)
}
Danke für diese super Binliothek ich hab nur leider zwei Probleme, leider bin ich noch nicht so gut im programmieren der Arduinos und ich finde 1.nicht heraus wie ich im Polling Beispiel das für mehrere Kanäle am Mega umwandle, 2.wie ich dann die Werte vom Empfänger am seriellen Monitor anzeige (mit den serial.printin) nur halt an welcher Position und Vorallem wie ich das empfängersignal dann verwende um daraus ein Relais und ein PWM Signal zu steuern...
Bitte um Hilfe! 😀
Und danke schon im Voraus
Gruß Patrick
ein echt sehr gute Seite, ich bin absoluter Neuling was das Programieren angeht. Sehr viel habe ich mir da schon beigebracht, aber eine Sache will mir einfach nicht in den Kopf rein.
Mal als Beispiel:
Ich baue eine Tirpitz in 1:100,
darauf sitzen vorne 2 Geschütztürme (angetrieben mit Servo) und dadrin sind jeweils ein Servo für die Geschützrohle.
Die Spannungsversorgung würde ich jetzt für die Servos extern machen, da ja sonst der Arduino kaputt gehen würde.
Jetzt wollte ich ein Signal von dem Empfänger einlesen (das habe ich schon hin bekommen, hoffe ich zumindest)
Mein eigentliches vorhaben ist einen Propkanal nutzen um die beiden Geschütze sich langsam (ServoSlow) drehen zu lassen.
Links Mitte Rechts
0° 90° 180°
Dann kommt das bedenklichste für mich,
die Geschützrohre sollen sich dann auch heben, ab einem bestimmten Winkel des 2 Drehservo
der erste Turm soll sich ein ganz kleinen bischen langsamer drehen oder ein 1/4sekunde später.
wenn ich jetzt aber eine Taste am Modell drücke, sollen beide Geschütze in die 90° Position zurück kehren und da verharren, egal ob der Poti am Sender seinen wert verändert.
Das ganz möchte ich mit einem Arduino Uno verwirklichen ist dieses überhaupt möglich?
Ich finde Dein Know-How-Paket resp die RC-Bibliothek sehr gut !
Gerne Deine Hilfe zu folgedener Routine für die Nullpunktbestimmung "rcReceiver.hasNP()":
Um was für einen Nullpunkt handelt es sich hier? Was wird hier genau bestimmt?
Danke!
Romeo
GEHT DEINE BIBLIOTHEK AUCH IN VERBINDUNG MIT EINEM Arduino Nano 3.0?
DANKE UND GRUß
MATTHIAS
vorne weg, ich habe noch keinerlei Erfahrung mit dem Arduino (in meinem Fall Leonardo), jetzt wollte ich das Interupt Beispiel ausführen (habs an Pin 0, 1, 2 und 3 versucht)
Wo kann ich bitte das TX Signal auslesen?
Danke und Gruß
Matthias
erstmal -> Super geile Sache die du da gemacht hast ;)
So, aber ich verstehe nicht, warum ich die Wert für die Lenkung (von Empfänger auf PIN2 | Arduino MEGA) richtig bekomme, die von "Gaspedal" aber nicht (auf PIN3)... Dort liegt immer der Wert 65 an... hier ein Codeauszug:
const byte PIN_RC_SER = 2; //SERVOPIN
const byte PIN_RC_THR = 3; //THROTTLEPIN
Servo myservo;
RCReceive rcReceiver_ser;
RCReceive rcReceiver_thr;
int value2 = 0;
void setup() {
// RC Receiver in Interruptvariante
rcReceiver_ser.attachInt(PIN_RC_SER);
rcReceiver_thr.attachInt(PIN_RC_THR);
myservo.attach(5);
Serial.begin(9600);
// put your setup code here, to run once:
}
void loop() {
// nur wenn der Nullpunkt bestimmt worden ist,
// und es keinen Fehler gegeben hat soll die eigentliche Arbeit gemacht werden
if (rcReceiver_ser.hasNP() && !rcReceiver_ser.hasError()) {
doWork();
}
else if (rcReceiver_thr.hasError()) {
Serial.println("ERROR THROTTLE");
}
}
void doWork() {
// put your main code here, to run repeatedly
byte vser = rcReceiver_ser.getValue();
byte vthr = rcReceiver_thr.getValue();
for(int i = 0; i<10; i++)
value2 += vser;
value2 = value2 / 10;
myservo.write(value2);
Serial.println(vthr);
Serial.println(value2);
}
Gruß
Ein super Programm endlich ein Prozessor der RC Signale verarbeiten kann,
Hier ein groses Lob an den Programmierer,
Meine Frage zum Ardunio Uno wie kann Ich Pin 2 und Pin 3 gleichzeitig
Einbinden und auswerten ?,
Gruss Dietr,
wie nutze ich denn das "byte getValue();" ? Ich bekomme nämlich immer den Wert 65 zurück, egal was ich mache. Gibt es dafür ein einfaches Beispielprogramm mit Interrupts?
was prüfst Du, um die fehlerhaften Pakete des Empfängers zu identifizieren?