Infrarot-Empfänger

Programmieren

Wie funktioniert eigentlich eine Fernbedienung? In diesem Tutorial lernst du wie du eine benutzt, um eine an den Arduino angeschlossenen LED zu steuern.

IR-Fernbedienungen

Eine einfache Möglichkeit, Daten mithilfe von Licht zu übertragen, ist die Verwendung von Morsezeichen. Vielleicht hast du es als Kind schon einmal mit einer Taschenlampe versucht. IR-Fernbedienungen funktionieren auf ganz ähnliche Weise. Es gibt jedoch ein Problem. Das Sonnenlicht enthält auch infrarot Licht, und du möchtest nicht, dass sich dein Fernseher aufgrund des Sonnenlichts einschaltet. Um dieses Problem zu lösen, wird eine sogenannte Trägerfrequenz verwendet. Üblicherweise werden die Impulse als ein 38-kHz-Signal übertragen. Der IR-Empfänger reagiert nur auf Infrarotsignale mit dieser Frequenz. Um Daten zu übertragen, wird das 38-kHz-Signal ein- und ausgeschaltet. Der Empfänger wandelt dies in ein digitales Signal um. Im Falle des von mir verwendeten Empfängers wird ein niedriger Spannungspegel ausgegeben, wenn das 38-kHz-Trägersignal erkannt wird.

Schauen wir uns die Ausgabe des IR-Empfängers an: Übermitteltes Signal beim Drücken einer Taste

Der Beginn der Übertragung wird durch einen langen Impuls signalisiert. Auf diesen Startimpuls folgen 32 Datenbits und ein abschließender Stoppimpuls. Bei einer Null gibt es nur eine kurze Pause im Infrarotsignal und bei einer Eins eine längere Pause. Die Datenbits variieren für jede Taste auf der Fernbedienung und auch für Fernbedienungen verschiedener Geräte. Das von den meisten Arduino IR-Fernbedienungen verwendete Protokoll ist das NEC-Protokoll. Es gibt auch andere Protokolle und sogar andere Trägerfrequenzen, aber das NEC-Protokoll ist ziemlich verbreitet und wird von allen meinen TV-Fernbedienungen verwendet.

Das NEC-Protokoll enthält auch ein Tastenwiederholungspaket, das keine Daten enthält, sondern stattdessen signalisiert, dass die Taste immer noch gedrückt wird. Das entsprechende Datenpaket ist unten dargestellt: Tastenwiederholungssignal

Ziel dieses Tutorials ist es, eine LED mit der Fernbedienung und unserem Arduino Uno ein- und auszuschalten. Zu diesem Zweck werden wir eine Taste der Fernbedienung benutzen, um die LED einzuschalten und eine andere, um sie wieder auszuschalten. In unserem Anwendungsfall können wir alle Tastenwiederholungsnachrichten einfach ignorieren.

Der Schaltkreis

Anschließen des IR-Empfängers und der LED an den Arduino Uno

Für unseren Schaltkreis benötigen wir die LED, die wir ansteuern wollen, und einen IR-Empfänger. Die LED wird wie üblich mit einem 220 Ω Widerstand in Reihe geschaltet. Zur Ansteuerung der LED habe ich Pin 9 verwendet. Der IR-Empfänger hat drei Anschlüsse. Einer davon ist die Datenleitung, die ich an Pin 8 angeschlossen habe. Die beiden anderen Pins dienen der Stromversorgung. Einer ist mit 5V und der andere mit 0V verbunden. Ich habe einen VS1838B-Empfänger verwendet. Andere Empfänger, die zur Trägerfrequenz und Wellenlänge der Fernbedienung passen, funktionieren ebenfalls. Die Pin-Belegung kann jedoch abweichen. Im Video am Ende dieses Tutorials wirst du sehen, dass die Pinbelegung meines Empfängers von der im obigen Schaltplan gezeigten abweicht. Mein Empfänger zeigt genau in die entgegengesetzte Richtung. Bitte achte darauf, die Verkabelung an deinen Empfänger anzupassen.

Erstellen des Codes

Unser Ziel ist es, die LED mit der Fernbedienung zu steuern. Zu Beginn sollten wir diese Aufgabe, jedoch in kleinere Schritte aufteilen. Zuerst brauchen wir eine Möglichkeit, die Länge der Impulse zu messen. Wir müssen die Impulslängen in einen Wert umwandeln, mit dem wir später feststellen können, welche Taste gedrückt wurde. Sobald wir in der Lage sind, zwischen den einzelnen Tasten der Fernbedienung zu unterscheiden, können wir zwei von ihnen benutzen, um die LED ein- und auszuschalten.

Messen der Pulslängen

In der Einführung zu IR-Fernbedienungen habe ich dir gezeigt, wie das übertragene Signal aussieht. Wir können bestimmen, ob ein Bit null oder eins ist, indem wir die Länge der Lücke zwischen den Impulsen messen. Während dieser Lücke ist Pin 8 für eine kurze Zeitspanne HIGH. Die Arduino-Plattform bietet die Funktion pulseIn zur Messung der Länge dieses HIGH-Impulses an Pin 8. Hier ist die Beschreibung dieser Funktion:

  • pulseIn(pin, value, timeout)
    • pin: Nummer des Pins zur Messung der Pulslänge
    • value: HIGH oder LOW, je nachdem, ob wir die Länge von Perioden mit niedrigem oder hohem Spannungspegel messen wollen
    • timeout: Optionaler Parameter, um die maximal erwartete Pulslänge in Mikrosekunden anzugeben (1 s, falls nicht angegeben)
    • Gibt die Impulslänge in Mikrosekunden zurück oder 0 im Falle eines Timeouts

Um die Funktion zu testen und einen Eindruck davon zu bekommen, mit welchen Impulslängen wir zu rechnen haben, können wir einfach die Impulslängen an die serielle Konsole senden. Wir müssen die Baudrate auf einen höheren Wert als üblich einstellen, weil sonst die Übertragung zum Computer zu lange dauert und wir den nächsten Impuls verpassen. Eine Baudrate von 115200 hat bei mir funktioniert. Denk daran, die gleiche Baudrate im seriellen Monitor zu wählen.

Der folgende Code protokolliert die Länge jedes HIGH-Impulses im seriellen Monitor:

void setup() {
  Serial.begin(115200);
  pinMode(8, INPUT);
}

void loop() {
  Serial.println(pulseIn(8, HIGH));
}

Dekodierung der Daten

Der schwierigere Teil besteht darin, die gemessenen Impulslängen zu dekodieren und daraus einen Wert zu erzeugen, mit dem wir die gedrückte Taste identifizieren können. Da wir 32 Datenbits haben, die von der Fernbedienung übertragen werden, ist es sinnvoll, daraus eine 32-Bit-Ganzzahl zu konstruieren. Aber wie?

Lass mich dir zunächst den Code zeigen und dann erklären, wie er funktioniert.

void setup() {
  Serial.begin(9600);
  pinMode(8, INPUT);
}

unsigned long int receiveIR() {
  // Wait for start pulse
  while(digitalRead(8) != LOW);
  if(pulseIn(8, HIGH, 20000) == 0) return 0;

  // Receive pulses
  unsigned long int result = 0;
  for(int i = 0; i < 32; i++) {
    int pulseLength = pulseIn(8, HIGH, 5000);
    if(pulseLength == 0) return 0;
    if(pulseLength > 1000) result |= (1UL<<i);
  }

  // Stop pulse
  while(digitalRead(8) != HIGH);

  return result;
} 

void loop() {
  Serial.println(receiveIR(), HEX);
}

Ich habe eine neue Funktion zum Empfang und zur Dekodierung des IR-Signals deklariert. In loop sende ich einfach ihren Rückgabewert als hexadezimale Zahl an die serielle Konsole. Den zweiten Parameter von Serial.println haben wir bisher noch nicht verwendet, aber es gibt nicht viel mehr zu sagen als die Tatsache, dass er das Anzeigeformat für Integer-Zahlen ändert. Mögliche Werte sind BIN, OCT, DEC und HEX um Zahlen in binärer, oktaler, dezimaler oder hexadezimaler Schreibweise anzuzeigen.

Die Magie geschieht innerhalb der Funktion receiveIR. Zuerst warten wir auf den Startimpuls und auf die Pause direkt danach. Diese Pause ist etwa 10 ms lang, sodass ein Timeout von 20 ms ausreichen sollte. Wenn wir diese Lücke nicht erhalten, brechen wir ab. Vielleicht war die Fernbedienung außerhalb der Reichweite oder verwendet ein unbekanntes Protokoll. Als Nächstes lesen wir die 32 Datenbits innerhalb der for-Schleife. Dazu messen wir die Länge der Pausen mit der Funktion pulseIn. Im vorherigen Schritt haben wir die Länge dieser Pausen bereits gemessen. Bei meiner Fernbedienung gibt es eine kurze Pause von 500 µs für ein Nullbit und eine längere Pause von 1500 µs für eine Eins. Deshalb habe ich für die Messung der Pulslänge einen Timeout von 5000 µs gewählt. Dieser Timeout sollte für Datenbits nie überschritten werden. Wenn er überschritten wird, kann dies an einem Stoppbit liegen, wie beispielsweise beim Datenpaket für eine Tastenwiederholung, oder an einer Signalunterbrechung. In beiden Fällen werden wir keine Daten lesen können. Die Wartezeit wird überschritten und pulseIn wird 0 zurückgeben. In diesem Fall bricht die Funktion receiveIR ab, indem sie einfach 0 zurückgibt, um anzuzeigen, dass keine Daten gelesen wurden.

Um das Eingangssignal zu dekodieren, müssen wir die empfangenen Bits zu unserem 32-Bit Integer result kombinieren. Um ein Bit in einer Ganzzahl zu setzen, können wir eine bitweise ODER-Verknüpfung verwenden. In C++ können wird dafür der |-Operator verwendet. Der Operator |= ist nur eine Kurzform für result = result | (1UL << 1). Im Ergebnis einer bitweisen ODER-Verknüpfung werden alle Bits, die in einer der beiden Ganzzahlen den Wert eins haben, auf eins gesetzt. Am Anfang sind alle Bits Null, weil wir result mit 0 initialisiert haben. Jedes Mal, wenn wir eine Pause von mehr als 1000 µs messen, haben wir eine Eins empfangen. In diesem Fall müssen wir das entsprechende Bit in result mit der bitweisen ODER-Verknüpfung auf Eins setzen. Die Bits werden mit dem niederwertigsten Bit zuerst übertragen. Das erste Datenbit, das wir erhalten, ist Bit 0 in unserer Ganzzahl. Danach folgt Bit 1, dann Bit 2 und so weiter. Im Falle einer Eins für Bit 3 müssten wir result |= 8 ausführen, um Bit 3 zu setzen, da 8 die Zahl ist, in der Bit 3 auf Eins gesetzt ist.

Der generische Weg, dies zu tun, ist die Verwendung eines Schiebeoperators.
Der <<-Operator schiebt alle Bits einer Ganzzahl nach links:

Ein Wert von 1 als Bits dargestellt ...

Bit 7 6 5 4 3 2 1 0
Zustand 0 0 0 0 0 0 0 1
Wert 1

... um 3 nach links verschoben (1<<3), ergibt einen Wert von 8

Bit 7 6 5 4 3 2 1 0
Zustand 0 0 0 0 1 0 0 0
Wert 8

In der Schleife verwenden wir die Variable i, um über alle Bits von 0 bis 31 zu iterieren und sie auf eins zu setzen, wenn wir eine Eins von der IR-Fernbedienung erhalten. Dies wird erreicht, indem wir 1 um i nach links schieben und so mithilfe der bitweisen ODER-Verknüpfung das i-te Bit setzen. Als Ergebnis erhalten wir eine Nummer, die zur eindeutigen Identifizierung der gedrückten Taste verwendet werden kann.

Ein letzter Punkt, die etwas verwirrend sein könnte, ist die Tatsache, dass 1UL anstelle von 1 verwendet wird. Dies stellt klar, dass diese 1 vom Typ unsigned long ist. Dies ist nötig, weil der Compiler sonst von einer normalen 16-Bit-Ganzzahl ausgehen würde. Als Ergebnis würden wir dann immer 0 erhalten, wenn wir um mehr als 15 Bits verschieben, da Bits, die am linken Ende einer Zahl herausgeschoben werden, einfach wegfallen.

Zum Schluss der Stoppimpuls. Wir warten einfach, bis die Datenleitung in den Zustand HIGH zurückkehrt. Der dekodierte Tastencode in result wird anschließend von der Funktion zurückgegeben.

An- und Ausschalten der LED

Um die LED mit der Fernbedienung zu steuern, müssen wir nun zwei Tasten auswählen. Mithilfe einer Bedingung können wir überprüfen, ob eine dieser Tasten gedrückt wurde und die LED mit digitalWrite in den entsprechenden Zustand versetzen. Um die Tastencodes zu finden, können wir das Programm benutzen, das wir gerade geschrieben haben, und die Codes im seriellen Monitor einsehen. Möglicherweise musst du die Tastencodes an deine Fernbedienung anpassen.

Hier ist der komplette Code, der zwei Tasten meiner Fernbedienung (CH- und CH+) zum Ein- und Ausschalten der LED benutzt:

void setup() {
  pinMode(8, INPUT);
  pinMode(9, OUTPUT);
}

unsigned long int receiveIR() {
  // Wait for start pulse
  while(digitalRead(8) != LOW);
  if(pulseIn(8, HIGH, 20000) == 0) return 0;

  // Receive pulses
  unsigned long int result = 0;
  for(int i = 0; i < 32; i++) {
    int pulseLength = pulseIn(8, HIGH, 5000);
    if(pulseLength == 0) return 0;
    if(pulseLength > 1000) result |= (1UL<<i);
  }

  // Stop pulse
  while(digitalRead(8) != HIGH);

  return result;
} 

void loop() {
  unsigned long key = receiveIR();
  if(key == 0xB847FF00) {
    digitalWrite(9, HIGH);
  }
  else if(key == 0xBA45FF00) {
    digitalWrite(9, LOW);
  }
}

Das Ergebnis

Wir sind jetzt in der Lage, die LED mit unserer Fernbedienung ein- und auszuschalten. Die LED reagiert nur auf die beiden angegebenen Tasten. Dies wird in dem Video unten gezeigt. Natürlich kannst du dieses Beispiel erweitern und noch komplexere Dinge steuern. Es liegt ganz bei dir! Warum kombinierst du dieses Beispiel nicht mit dem Code aus dem PWM-Tutorial und steuerst die LED-Helligkeit mit der Fernbedienung?

Übrigens, es gibt auch Arduino-Bibliotheken, die verschiedene Protokolle für IR-Fernbedienungen unterstützen. Wahrscheinlich ist es einfacher, sie zu benutzen, anstatt eigenen Code zu schreiben. Ich denke jedoch, es ist gut zu wissen, wie IR-Fernbedienungen funktionieren und einmal einen einfachen Decoder zu schreiben, wie wir es in diesem Tutorial getan haben.

Vorheriger Beitrag Nächster Beitrag