IR Fernbedienungen

Über den Beitrag

In meinem letzten Beitrag hatte ich mich mit Infrarot (IR) Näherungssensoren beschäftigt. In diesem Beitrag möchte ich bei dem Thema Infrarot bleiben, jedoch geht es diesmal um IR Fernbedienungen. Zunächst gehe ich – recht detailliert – auf die Übertragungstechnik per IR ein. Dann werde ich erklären, wie man IR Fernbedienungen selber baut. Zum Schluss möchte ich zeigen, wie man mithilfe einer geeigneten Bibliothek eine IR Fernbedienung zur Fernsteuerung seiner Projekte einsetzen kann. Ihr könnt natürlich auch direkt zu dem letzten Kapitel übergehen, wenn euch die Hintergründe weniger interessieren oder ihr keine Zeit habt.

Sammlung alter IR Fernbedienungen
Ihr habt noch alte IR-Fernbedienungen? Nicht wegschmeißen! (Quelle: siehe Danksagung)

IR Datenübertragung

Wie die meisten sicherlich wissen, handelt es sich bei Infrarotstrahlung um nicht sichtbares Licht. Das Infrarotspektrum schließt sich dem sichtbaren Spektrum im längerwelligen Bereich an. Infrarotstrahlung wird auch als Wärmestrahlung bezeichnet. Mehr über Infrarotstrahlung im Allgemeinen findet ihr zum Beispiel hier auf Wikipedia.

Da jeder warme Körper je nach Temperatur mehr oder weniger infrarotes Licht abstrahlt, ist die IR Strahlung allgegenwärtig. Damit kann sie gezielte Signalübertragungen beeinträchtigen. IR Fernbedienungen nutzen einen Trick, indem sie gepulste Signale senden. Dadurch unterscheiden sich die Signale deutlich von der Umgebungsstrahlung, welche entsprechend herausgefiltert werden kann. Meistens verwenden IR Fernbedienungen eine Pulsfrequenz von 38 Kilohertz.

Infrarotempfänger

Infrarote Strahlung lässt sich mittels IR-sensitiver Fotodioden detektieren. Ich habe diese Art von IR Sensoren in meinem letzten Beitrag beschrieben. Ihr habt sie vielleicht auch schon mal als „Flame-Sensoren“ auf entsprechenden Modulen gesehen.

IR-Fotodiode: sieht aus wie eine schwarze LED
IR-Fotodiode: sieht aus wie eine schwarze LED

Mit solchen Fotodioden fängt man jedoch die gesamte IR Strahlung ein, egal ob gepulst oder ungepulst. Wenn ihr nur die gepulste Strahlung aus dem Infrarotlichtsalat herausfischen wollt, müsst ihr solche „Dreibeiner“ verwenden:

Infrarotempfänger für gepulstes Licht
Infrarotempfänger für gepulstes Licht

Ihr findet diese Empfänger unter der Bezeichnung VS1838B oder TSOPxx38 mit xx = z.B. 18, 22, 24, 48. Enden die Bezeichnungen nicht auf 38, wie z.B. beim TSOP2256, dann liegt die Pulsfrequenz nicht bei 38 kHz, sondern in diesem Beispiel bei 56 kHz. Eine Übersicht über die Nomenklatur von IR Empfängern findet ihr hier.

Auch ein Empfänger für gepulste Signale enthält zunächst einmal eine normale IR Fotodiode (siehe Schema unten). Die AGC Einheit (automatic gain control) verstärkt das Signal. Der Bandpassfilter reinigt es und schließlich wandelt der Demodulator es in ein kontinuierliches Signal um (kontinuierlich in Bezug auf die 38 kHz). Am Ende sitzt ein Transistor, der im Falle eines ankommenden Signals nach Ground durchschaltet. Damit geht Pin 1 des Empfängers von HIGH auf LOW. Hinweis: nicht alle IR Empfänger haben dieselbe Pinbelegung – schaut ins Datenblatt. Auch im Fritzing Schaltbild weiter unten habe ich eine andere Belegung (weil ich nur dieses Bauteil gefunden habe).

Schema eines Infrarotempfängers für gepulste Signale
Schema eines Infrarotempfängers für gepulste Signale

IR Fernbedienungen: Analyse der Signale

Ich möchte nun zeigen, wie ihr ein IR Signal analysieren könnt. Weiter unten verwende ich dazu eine komfortablere Bibliothek. Ich finde es aber ganz spannend, das auch einmal „zu Fuß“ zu machen.

Zunächst aber die Schaltung dazu. Beachtet dabei erstmal nur den Empfängerteil (an Pin 2), zum Sendeteil (an Pin 5) kommen wir später.

Arduino Schaltung zum Senden und Empfangen von IR Signalen
Arduino Schaltung zum Senden und Empfangen von IR Signalen

GND des Empfängers kommt an GND, VCC an 5 Volt und DATA an einen I/O Pin (hier 2). Den Empfänger kann man auch direkt an die 5V Versorgung des Arduinos hängen, denn er verbraucht weniger als 1 mA Strom.

Signalstruktur von IR Fernbedienungen

Signale von IR Fernbedienungen sind sozusagen zweifach gepulst. Ein Signal besteht aus einer Folge von Pulsen, wobei jeder Puls eine Breite von mehreren hundert Mikrosekunden bis einigen Millisekunden aufweist. Diese Pulse nenne ich mal Hauptpulse, da mir nichts Besseres einfiel. Das ist also kein Fachbegriff. Zwischen den Hauptpulsen gibt es unterschiedlich lange Pausen. Die Hauptpulse bestehen wiederum aus vielen 38 kHz Pulsen.

Hier ein typisches Beispiel eines IR Signals:

IR-Sendesignal am Oszilloskop
IR-Sendesignal am Oszilloskop

Die Hauptpulse sind gut zu erkennen. Ihr seht auch, dass das Signal im Grunde aus zwei identischen Hauptpulsfolgen besteht, die durch eine längere Pause getrennt sind. Das ist nicht ungewöhnlich bei IR Fernbedienungen.

Bei höherer Auflösung kommen dann die 38 kHz Pulse innerhalb Hauptpulse zum Vorschein. Hier ein Beispiel eines aufgezogenen Hauptpulses:

Ein höher aufgelöster Hauptpuls - ihr erkennt die 38 kHz Pulse
Ein höher aufgelöster Hauptpuls – ihr erkennt die 38 kHz Pulse

Auf der Empfängerseite werden die 38 kHz Pulse demoduliert. Außerdem wird das Signal aufgrund des oben beschriebenen Aufbaus der Empfänger umgekehrt. Das heißt, LOW wird HIGH und umgekehrt. Hier seht ihr wieder das Signal vom vorletzten Bild, aber am Empfänger:

Das demodulierte Signal auf der Empfängerseite - die 38 kHz Pulse sind verschwunden
Das demodulierte Signal auf der Empfängerseite – die 38 kHz Pulse sind verschwunden

Der Auswertesketch für Signale am Empfänger

Bei der Auswertung geht es nun darum die Puls- und „Pausenbreiten“ zu vermessen. Das macht man am besten, indem man zunächst eine geeignete Zeiteinheit (timeSegment) definiert, die die Auflösung darstellt. Im nachfolgenden Sketch prüft der Arduino nach jedem Ablauf einer Zeiteinheit, ob der Datenpin am IR Empfänger HIGH oder LOW ist. Die Zeiteinheiten werden addiert bis ein HIGH/LOW bzw. LOW/HIGH Wechsel stattfindet. So hangelt man sich Stück für Stück durch das Signal.

Der Zähler für die Pulsbreite (bzw. Pausenbreite) ist der pulseWidthCounter. Die Puls-Pausen Paare werden als Array gespeichert (irSignal). Die Paare werden durch den pulseCounter gezählt. Das Ende eines Signals ist erreicht, wenn über längere Zeit kein Puls mehr kommt (maxCount ist erreicht).

Bei diesem Verfahren sind zwei Dinge zu beachten:

  1. Die Überprüfung des Pinstatus mittels digitalRead ist mit ca. drei Mikrosekunden (jedenfalls auf dem UNO) recht langsam. Die direkte Abfrage des Eingangsregisters über (PIND & (1<<PD2)) ist wesentlich schneller.
  2. Ist timeSegment zu klein, muss der Pinstatus sehr häufig abgefragt werden. Die zum Auslesen benötigte Zeit summiert sich dann, selbst mit der schnellen Methode, zu einem signifikanten Fehler. Ist timeSegment zu groß, werden Anfang und Ende der Pulse entsprechend unscharf. Zwanzig Mikrosekunden haben sich Wert bewährt.

Ist ein Signal vollständig übertragen, dann wird es auf dem seriellen Monitor einmal übersichtlich paarweise und einmal als Array ausgegeben. Letzteres könnt ihr direkt in den Sendesketch, den wir gleich besprechen, hineinkopieren. Der letzte gemessene Wert entspricht maxCount. Diesen brauchen wir nicht, da er nicht mehr zum Signal gehört. Entsprechend lasse ich ihn in der Array Ausgabe weg.

unsigned int pulseWidthCounter, pulseCounter;
unsigned int maxCount = 60000;
const int timeSegment = 20;
bool completed;
unsigned int irSignal[100][2];

void setup() {
  Serial.begin(9600);
  Serial.println("IR Decoder Sketch - ready");
}

void loop() {
  pulseCounter = 0;
  if(!(PIND & (1<<PD2))){
    completed = false;
    
    while(!completed){

      pulseWidthCounter = 0;
      while(!(PIND & (1<<PD2))){
        pulseWidthCounter++;
        delayMicroseconds(timeSegment);
      }
      irSignal[pulseCounter][0] = pulseWidthCounter;
      
      pulseWidthCounter = 0;
      while((PIND & (1<<PD2)) && (pulseWidthCounter < maxCount)){
        pulseWidthCounter++;
        delayMicroseconds(timeSegment);
      }
      irSignal[pulseCounter][1] = pulseWidthCounter;
      if(pulseWidthCounter >= maxCount){
        completed = true;
      }
      
      pulseCounter++;
      if(pulseWidthCounter >= maxCount){
        completed = true;
      }
    }

    Serial.println("IR Signal erhalten:");
    for(int i=0; i<pulseCounter; i++){
      Serial.print(irSignal[i][0]);
      Serial.print(" | ");
      Serial.println(irSignal[i][1]);
    }
    Serial.println();
    Serial.print("{");
    for(int i=0; i<(pulseCounter-1); i++){
      Serial.print(irSignal[i][0]);
      Serial.print(",");
      Serial.print(irSignal[i][1]);
      Serial.print(",");
      if(i==(pulseCounter-2)){
        Serial.print(irSignal[i+1][0]);
      }
    }  
    Serial.println("}");
  }
}

 

Die Ausgabe an einem konkreten Beispiel

Zum Testen habe ich die Fernbedienung meines Sat-Receivers, genaugenommen die „Programm +“ – Taste, ausgewählt. Diese habe ich übrigens auch für die Oszilloskop Messungen weiter oben benutzt. Um aus den Zahlen die Pulsbreiten in Mikrosekunden zu errechnen, müsst ihr sie natürlich noch mit zwanzig multiplizieren. Ich fand es aber übersichtlicher, mit den kleineren Zahlen zu arbeiten.

Ausgabe des IR Decoder Sketches
Ausgabe des IR Decoder Sketches

Eigene IR Fernbedienungen bauen

Und jetzt können wir das eben analysierte Signal nehmen und es mit einer IR LED senden. Wir bauen sozusagen eine eigene Fernbedienung bzw. simulieren die vorhandene Fernbedienung. Aber zunächst muss ich noch ein bisschen ausholen und erklären wie man ein 38 kHz Signal erzeugt.

Erzeugen eines 38 kHz Puls Signals

IR LEDs

Als Infrarotquelle benutzt Ihr am besten eine IR LED. Sie funktioniert im Prinzip wie eine LED für das sichtbare Licht. Auch IR LEDs gibt es in unterschiedlichen „Farben“, sprich in verschiedenen Wellenlängen. Gängig sind dabei 850 oder 940 nm. Nach meinen Recherchen scheint 940 nm besser zu den meisten Empfängern zu passen. Ansonsten unterscheiden sich die IR LEDs vor allem in ihrer maximalen Stromstärke. Die von mir verwendeten IR LEDs haben eine Stromstärke von 100 mA bei 1,2 bis 1,5 Volt. Bei der Festlegung von Vorwiderständen ist zu beachten, dass gepulst effektiv nur die halbe Stromstärke fließt. Zudem sind die IR Signale sehr kurz. Trotzdem sollte man erwägen, eine externe Stromquelle zu verwenden und diese wie in meiner Schaltung oben mit einem Transistor zuzuschalten. Als Vorwiderstand habe ich 15 Ohm verwendet.

IR LED
Eine IR LED

Pulssignale mit IR LEDs erzeugen

Um die IR LED nun mit 38 kHz pulsen zu lassen, kann man theoretisch die Funktion tone(pin, Frequenz) bzw. noTone() benutzen. Weitere Informationen zu dieser Funktion findet ihr hier. Für tone() muss man einen PWM-fähigen Pin wählen. Tatsächlich habe ich mit tone(5, 38000) schöne Pulse der richtigen Breite an Pin 5 erzeugen können:

38 kHz Puls für IR Fernbedienungen  mit der tone() Funktion
38 kHz Puls mit der tone() Funktion

Die tone() Funktion hatte ich schon in meinem letzten Beitrag über Selbstbau – Näherungssensoren erfolgreich verwenden können. Also wollte ich auch hier über tone – delay – noTone – delay mein Signal zusammenbasteln. Aus mir schleierhaften Gründen funktionierte das aber nicht. Hat jemand eine Idee dazu? Nehmen vielleicht die tone / noTone Befehle zu viel Zeit in Anspruch? Die Variante tone(pin, Frequenz, Länge), also ohne delay und noTone funktioniert übrigens auch nicht, da Länge in Millisekunden angegeben wird, was für diesen Zweck zu grob ist.

Wie auch immer, es gibt noch zwei Alternativen die funktionieren. Entweder ihr kreiert die Signale über die digitalWrite Funktion oder über die schnellere Variante des direkten Portzugriffs. Hier der Sketch dazu:

const int irLEDPin = 5;

void setup() {
  pinMode(irLEDPin, OUTPUT);
}

void loop() { 
  //PORTD |= (1<<PD5);                // much faster than digitalWrite
  digitalWrite(irLEDPin, HIGH);   // too slow: 3 microseconds (Arduino UNO / Atmega 328P)
  delayMicroseconds(13);            // does not work - switch to 10 or 9 µs
  //PORTD &= ~(1<<PD5);
  digitalWrite(irLEDPin, LOW);    // again 3 microseconds
  delayMicroseconds(13);            // does not work - switch to 10 or 9 µs
}

 

Entscheidet ihr Euch für digitalWrite, dann müsst ihr die delays von 13 µs auf 10 Mikrosekunden verkürzen, da die digitalWrite Funktion ca. 3 µs benötigt (jedenfalls auf dem Arduino UNO). Sonst passiert das:

Signalerzeugung für IR Fernbedienungen mit digitalWrite und 13µs delay - die Pulsbreite wird zu groß
Signalerzeugung mit digitalWrite und 13µs delay – die Pulsbreite wird zu groß

Wählt ihr die Variante mit dem Direktzugriff, könnt ihr bei 13 µs bleiben. Den Messergebnissen zu Folge müsstet Ihr sogar auf 14 µs gehen (siehe nächstes Bild). Interessanterweise scheint eine Mikrosekunde irgendwo verschluckt zu werden!? Kann mir das jemand erklären? Egal, ich habe es bei 13 Mikrosekunden belassen und es hat funktioniert. 

Signalerzeugung für IR Fewrnbesienungen über direkten Portzugriff und 13 µs delay - etwas zu kurz
Signalerzeugung über direkten Portzugriff und 13 µs delay – etwas zu kurz

Es gibt noch weitere Methoden schnelle PWM Signal zu erzeugen (über die internen Timer der AVR MCUs), das würde hier aber zu weit führen. 

Der Sendesketch

Der folgende Sketch simuliert die „Programm +“ Taste meiner Fernbedienung. Die Schaltung dazu entspricht immer noch dem Fritzing Schema von weiter oben. Alle zehn Sekunden wird das Programm hochgeschaltet. Ein automatischer Hochzapper sozusagen. Ziemlich schlicht, aber ich möchte damit auch nur das Prinzip verdeutlichen. Wenn ihr noch mehr Signale einarbeitet und diese dann Tastern zuordnet, könnt ihr auf diese Weise eine ganze Fernbedienung nachbauen.

Der Sketch sollte nicht schwer zu verstehen sein. Verwendet wird das zuvor dekodierte Signal. Die Länge das Arrays wird über seine Größe in Bytes ermittelt. Die pulseIR Funktion habe ich mir aus einem Adafruit Tutorial ausgeliehen und modifiziert.

Ich habe das ganze ausprobiert und es funktioniert.

const int timeSegment = 20;
const int irLEDPin = 5;
int irSignal[] = {48,42,47,43,92,87,92,43,47,42,47,87,92,43,47,43,47,42,47,43,46,4538,47,43,47,42,93,87,92,42,47,43,47,87,92,42,47,43,47,42,48,42,47};

int irSignalLength;
unsigned long phase;

void setup() {
  irSignalLength = sizeof(irSignal)/2; // One int = Two bytes
  pinMode(irLEDPin, OUTPUT);
}

void loop() {
  int i = 0;
  
  phase = 20*irSignal[i];
    pulseIR(phase);
    i++;
  do{
    phase = 20*irSignal[i];
    delayMicroseconds(phase);
    i++;
    phase = 20*irSignal[i];
    pulseIR(phase);
    i++;
  }while(i<irSignalLength);

  delay(10000);
}

void pulseIR(long microsecs) {
  cli();  // interrupts switched off
  
  while (microsecs > 0) {
  // 38 kHz is about 13 microseconds high and 13 microseconds low
  PORTD |= (1<<PD5);                // much faster than 
  //digitalWrite(irLEDPin, HIGH);   // too slow: 3 microseconds (Arduino UNO / Atmega 328P)
  delayMicroseconds(13);            // 10 microseconds, if you chose digitalWrite
  //digitalWrite(irLEDPin, LOW);    // again 3 microseconds
  PORTD &= ~(1<<PD5);
  delayMicroseconds(13);            // 10 microseconds, if you chose digitalWrite
  microsecs -= 26;
  }
  
  sei();  // interrupts switched on
}

 

IR Fernbedienungen mit der IRLib2 Bibliothek

Die meisten von euch werden IR Fernbedienungen einsetzen wollen, um Projekte fernzusteuern und nicht um vorhandene Geräte zu bedienen. Dafür ist die bisher erläuterte Vorgehensweise ein wenig komplex. Außerdem stellt sich noch die Frage, wie man ein Signal als mit einem Soll übereinstimmend definieren kann. Denn wenn ihr mehrere Male ein Signal aufzeichnet, dann seht ihr, dass die gemessene Länge der einzelnen Pulse etwas variiert.

Fleißige und schlaue Leute haben für die Erkennung und Kodierung der Signale von IR Fernbedienungen Bibliotheken entwickelt. Diese basieren auf dem Umstand, dass man die Signale von IR Fernbedienungen bestimmten Protokollen zuordnen kann, wie zum Beispiel NEC, Sony, Panasonic usw. Diese haben bestimmte Muster und erlauben es, die Signalfolgen in einem kompakten Wert anstelle eines unhandlichen Arrays darzustellen. 

Es gibt eine Reihe guter Bibliotheken dazu. Der Klassiker schlechthin ist IRRemote von Ken Shirriff. Ich persönlich empfehle aber die Bibliothek IRLib2, da sie mit einer Vielzahl an Boards kompatibel ist. Bei der Installation ist zu beachten, dass die Bibliothek eigentlich eine Bibliothekensammlung ist. Wenn ihr die Zip-Datei herunterladet, dürft ihr sie deshalb nicht direkt im library Ordner entpacken. Stattdessen entpackt ihr die Zip-Datei irgendwo und kopiert dann die Hauptordner in das Arduino library Verzeichnis. Über die bequeme Arduino Bibliotheksverwaltung findet man die Bibliothek nicht.

Ein gutes Tutorial zu der Bibliothek findet ihr hier auf den Adafruit Learning Seiten. Ich möchte hier nur einen Kurzeinstieg geben.

IR Fernbedienungen: Signale dekodieren mit IRLib2

Nach der Installation der Bibliothek nehmt das Beispiel dump.ino ohne weitere Anpassung. Die Schaltung zu dem Sketch ist immer noch dieselbe wie zu Beginn dieses Beitrages.

/* dump.ino Example sketch for IRLib2
   Illustrates how to receive an IR signal, decode it and print
   information about it to the serial monitor.
*/
//This includes everything. Not generally recommended.
//It's better to include only the parts of library you really need.
//But for this example it's quick and easy. See "comboDump" example
//for a more efficient way.
#include "IRLibAll.h"

IRrecvPCI myReceiver(2); //create receiver and pass pin number
IRdecode myDecoder;   //create decoder

void setup() {
  Serial.begin(9600);
  delay(2000); while (!Serial); //delay for Leonardo
  myReceiver.enableIRIn(); // Start the receiver
  Serial.println(F("Ready to receive IR signals"));
}

void loop() {
  //Continue looping until you get a complete signal received
  if (myReceiver.getResults()) {
    myDecoder.decode();           //Decode it
    myDecoder.dumpResults(true);  //Now print results. Use false for less detail
    myReceiver.enableIRIn();      //Restart receiver
  }
}

 

Als Fernbedienung habe ich ein sehr handliches und günstiges Modell ausgewählt. Solche Fernbedienungen gibt es in großer Auswahl bei Amazon oder eBay:

IR Fernbedienung

Dann habe ich die Signale der Tasten 1, 2 und 3 dekodiert. Hier die Ausgabe des dump.ino Sketches für die Taste 1:

IR Fernbedienungen dekodieren mit IRLib2
IR Fernbedienungen dekodieren mit IRLib2

Entscheidend ist der „Value“ (rot markiert), in diesem Fall FD08F7. Das ist schon etwas umgänglicher als die langen Zahlenreihen zuvor, nicht wahr?

Ein einfaches Anwendungsbeispiel

Mit den entsprechenden Codes kann man dann alles Steuerbare fernsteuern, sei es Lampen, Autos, Musik oder was auch immer. Hier ein ganz kleines Beispiel bei dem mit meiner Fernbedienung jeweils eine von drei LEDs angeschaltet wird, ein LED-Zapper sozusagen.

#include "IRLibAll.h"

IRrecvPCI myReceiver(2); 
IRdecode myDecoder;   
const int led1 = 10;
const int led2 = 11;
const int led3 = 12;

void setup() {
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
  myReceiver.enableIRIn(); // Start the receiver
}

void loop() {
  if (myReceiver.getResults()) {
    myDecoder.decode();           //Decode it
    switch(myDecoder.value){
      case 0xFD08F7:
        switchOnLed(led1);    
        break;
      case 0xFD8877:
         switchOnLed(led2);  
        break;
      case 0xFD48B7:
         switchOnLed(led3);     
        break;
    }           
  }
  myReceiver.enableIRIn();      //Restart receiver
}

void switchOnLed(int pin){
  digitalWrite(led1, LOW);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW);
  digitalWrite(pin, HIGH);
}

 

Und hier die Schaltung dazu:

Ferngesteuerter LED Zapper
Ferngesteuerter LED Zapper

Eine weitere Anregung

Vor einiger Zeit habe ich einen Beitrag mit dem Titel „Eigene Funkprotokolle“ geschrieben. Darin ging es um die Kommunikation mit 433 MHz Modulen mittels eigens entwickelter Protokolle. Die dort vorgestellten Methodiken lassen sich auf die Infrarottechnik übertragen. So könnt ihr dann z.B. auch Text senden, Messergebnisse oder ähnliches. Schaut mal rein, wenn ihr Interesse habt.

Danksagungen

Die Fernbedienung im Beitragsbild stammt von hojun Kang auf Pixabay. Der Arduino im Hintergrund des Beitragsbildes stammt von Seven_au, ebenfalls Pixabay. Allerdings habe ich das Bild noch weichgezeichnet. Die Fernbedienungssammlung habe ich Mirko Maibach auf Pixabay zu verdanken.

Chris Young (cyborg5) danke ich für seine Bibliothek und das Tutorial.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.