Universelle programmierbare Infrarot Fernbedienung

Diese Schaltung ist der Grund warum ich mir das Prototype Shield mit Mini-Breadboard zusammengebaut habe.

Eigentlich wollte ich ja die lernbare Fernbedienung auf meinem Multi Function Shield aufbauen, doch musste ich feststellen, dass das Modul KY-005 zum Senden von IR-Signalen mittels der IRRemote-Library unabdingbar den Port 3 benutzen muss, weil dieser mit dem Timer 2 des ATmega 328 verbunden ist. Und Timer 1 wird schon von dem MFS selbst benutzt.

Port D3 wird beim MFS aber schon durch den Buzzer benutzt und jedes Senden von IR-Signalen würde damit ein infernalischen Gepiepe auslösen. Es sei denn, man lötet den Buzzer aus, sockelt ihn, damit man ihn schnell abziehen kann und lötet zudem ein Header für Port D3 an.

Das Mini-Breadboard ist ausreichend für die zwei Module, die hier Verwendung finden:

Hardware: IR-Empfänger Modul KY-022


Das Modul KY-022 ist eine kleine Platine, auf der als Hauptbauteil ein 1838T Infrarot-Empfänger (bereits beim Raspberry Pi behandelt) sitzt. Daneben gibt es noch eine kleine LED mit Vorwiderstand, die leuchtet, wenn IR-Signale empfangen werden. Das visuelle Feedback, das sich daraus ergibt, ist schon ganz nützlich.

Das Modul wird wie folgt angeschlossen: KY-022 Kabelfarbe Arduino - (GND) schwarz GND ? (+5V) rot +5V S (Signal) orange D4 Im Programm werden wir das Modul dazu nutzen, die Signale, die wir mit unserer normalen Fernseh-Fernbedienung senden aufzufangen, auszuwerten und zu speichern (sogenannter Lernmodus), damit sie spter wieder abgespielt werden können (Sendemodus).

Hardware: IR-Sende-LED Modul KY-005


Auf dem Modul KY-005 geht es ziemlich leer zu. Im Grunde ist es einfach nur eine Infrarot-Sende-LED ohne alles, selbst der Vorwiderstand fehlt. Einziger Vorteil ist vielleicht, dass sie sich so leichter in eine Breadboard stecken lässt.

Wir werden diese IR-LED dazu benutzen, die vorher aufzeichneten IR-Signale wieder abzugeben. Dazu müssen sie auf eine Trägerfrequenz von 38 kHz moduliert werden, was uns aber die verwendete Library abnimmt. Aber dazu später.

Vorwiderstand berechnen


Das Modul kam - wie all die anderen Sensoren - natürlich ohne Beschreibung oder Spezifikation aus Fernost. Nicht mal eine Bezeichnung hatte es. Darum sind mir keine genauen Werte bekannt.

Die Durchlassspannung konnte ich mit meinem Bauteile-Testgerät (übrigens ein LCR-T4), welches es günstig aus Fernost gibt und welches einem das Leben echt erleichtert) messen: 1.15 V.

Bei dem Betriebsstrom, den die LED braucht, konnte ich nur raten. Die meisten haben zwischen zwischen 100 und 300 mA, wobei die mit 300 mA schon Hochleistungsdioden sind und nicht gerade billig. Darum tippe ich bei diesem Billigexemplar mal eher auf 100 mA.


Mit diesen Werten lässt sich dann eine Rechnung für den Vorwiderstand anstellen. Das Script schmeißt uns einen Wert von 38.5 Ohm aus, was ich großzügig auf 47 Ω aufgerundet habe, da das ein Widerstand war, den ich da hatte.




Die Anschlüsse des KY-005-Moduls sehen also wie folgt aus: KY-005 Kabelfarbe Arduino - (GND) schwarz über 47 Ω-Widerstand an GND (N/A) nicht angeschlossen S (Signal) weiß D3 (fest vorgegeben) Wer auf die bei Signal-Eingang leuchtende LED verzichten kann, der kann natürlich auch auf die Module verzichten und die "nackten" Bauteile (siehe rechts) auf das Breadboard stecken. Der Vorteil dabei ist, dass man sich die 5 mm - Gehäuse an den langen Beinchen noch in die richtige Richtung zurechtbiegen kann.

Eventuell wird es dann ein bisschen wackelig, aber dem kann man wieder entgegenwirken, indem man die Beinchen durch eine Lochrasterplatine steckt und damit stützt.




Wer sich für die Module entscheidet, kann diese wie rechts gezeigt so einstecken, dass die Platinen in der mittleren Mulde Platz finden.

Die Pins werden dabei in die unterste Reihe der oberen Hälfte des Breadboards eingesteckt. So bleibt oberhalb noch genügend Platz für die Verkabelung.

Rechts vom Breadboard finden sich Header, aus denen sich leicht GND (schwarze Kabel) und +5V (rotes Kabel) entnehmen lässt.








Wie die anderen Kabel angeschlossen werden, ist bei den jeweiligen Modulen erklärt. Hier nocheinmal die Reihenfolge von links nach rechts:

Software


Für die Arbeit mit Infrarot für Fernbedienungen gibt es eine fertige Library von Ken Shirriff, die IRremote. Diese kann in der Bibliotheksverwaltung gesucht und installiert werden.

Sie sorgt für die Erkennung des Fernbedienungsprotokolls, denn hier kocht jeder Hersteller sein eigenes Süppchen und dekodiert eintreffende Signale gleich in (meistens) 32-bit-Werte, die in einer unsigned long-Variablen gespeichert werden können.

Für das Senden muss man die Funktionen des jeweiligen Protokolls benutzen, z. B. sendNEC() oder sendSony(). Mein Fernseher benutzt das NEC-Protokoll, das 32 bit lange Codes benutzt, darum heißt es im Programm irSend.sendNEC(unsignedLong, 32);. Bei anderen Fernbedienung kann ein anderes Protokoll Verwendung finden, dass einen anderen Funktionsaufruf benötigt.

Darum wird das Protokoll (decoding_type) und die Anzahl der Bits mit über die serielle Schnittstelle ausgegeben. Dies allerdings nur als Zahl. Welches Protokoll dahinter steckt, zeigt folgende Tabelle (gültig für IRremote 2.2.3): Nr. Protokoll Aufruf -1 UNKNOWN - 0 UNUSED - 1 RC5 sendRC5 (unsigned long data, int nbits) 2 RC6 sendRC6 (unsigned long data, int nbits) 3 NEC sendNEC (unsigned long data, int nbits) 4 SONY sendSony (unsigned long data, int nbits) 5 PANASONIC sendPanasonic (unsigned int address, unsigned long data) 6 JVC sendJVC (unsigned long data, int nbits, bool repeat) 7 SAMSUNG sendSAMSUNG (unsigned long data, int nbits) 8 WHYNTER sendWhynter (unsigned long data, int nbits) 9 AIWA_RC_T501 sendAiwaRCT501 (int code) 10 LG sendLG (unsigned long data, int nbits) 11 SANYO - 12 MITSUBISHI - 13 DISH sendDISH (unsigned long data, int nbits) 14 SHARP sendSharp (unsigned int address, unsigned int command) 15 DENON sendDenon (unsigned long data, int nbits) 16 PRONTO sendPronto (char* s, bool repeat, bool fallback) 17 LEGO_PF sendLegoPowerFunctions(uint16_t data, bool repeat) Sobald das Programm gestartet ist, werden alle eingehenden IR-Signale dekodiert und ausgegeben. Hier ein paar Beispiele:

IR SIGNAL EMPFANGEN Protocol: 3 Address: 0 Code: 804E58A7 Bits: 32 Raw-Len: 68 Raw-Ovfl: 0 Raw: 1F69 B2 59 B 22 A C B C A C B C A C B C A C A C B 22 A C B C A 22 B 22 A 23 A C A C B 22 B B B 22 B 22 A C B C A C A 23 A C A 23 A C A C B 22 A 23 A 22 B Das Signal ist gültig und gehört zum NEC-Protokoll (Protocol: 3, siehe Liste oben). Der Aufruf zum Code-Senden lautet also: sendNEC (unsigned long data, int nbits);. Data ist in diesem Fall der Code 804E58A7, nbits entspricht den ausgegebenen Bits und ist bei NEC immer 32. irSend.sendNEC(0x804E58A7, 32); ist also hier der richtige Funktionsaufruf, um diesen Tastendruck wieder zu senden. Bei anderen Protokollen sieht der Funktionsaufruf gegebenenfalls anders aus, siehe die Tabelle oben.

IR SIGNAL EMPFANGEN Protocol: 3 Address: 0 Code: FFFFFFFF Bits: 0 Raw-Len: 4 Raw-Ovfl: 0 Raw: 356 B1 2D A Code FFFFFFFF bedeutet Wiederholung des letzten Kommandos und man erhält ihn, wenn man eine Taste auf der Fernbedienung länger drückt. Dies ist nicht der eigentliche Code, sollte also ignoriert werden.

IR SIGNAL EMPFANGEN Protocol: FFFFFFFF Address: 0 Code: CBD319DE Bits: 32 Raw-Len: 68 Raw-Ovfl: 0 Raw: 11B2 B0 5B A 23 A D 8 E 9 D A D 9 E 8 E 9 D 9 E 9 23 A C A D 9 23 A 23 9 24 9 D 9 D A 23 9 D A 23 9 24 9 E 9 C A D 9 24 9 E 8 23 A D A D 9 23 A 22 A 23 9 Hier wurde das Signal nicht sauber empfangen und eine Dekodierung war nicht möglich (Protkoll = FFFFFFFF = -1 = UNKNOWN). Dieser Eintrag ist nicht verwertbar. Wiederholen und die Fernbedienung etwa 10 cm gerade vor den IR-Empfänger halten.

Da wir 4 Taster auf dem Breadboard Shield haben, können wir damit 4 unterschiedliche Signale senden. Diese sind in der Zeile unsigned long speicher[4] = {0x804E58A7, 0x804EF807, 0x804E1AE5, 0x804E6897}; hinterlegt und sollten mit den Werten für die eigene Fernbedienung belegt werden.

Mit zusätzlicher Hardware wie Drehgeber und LCD könnte man natürlich noch mehr auswählen und würde den Speicher vergrößern. Jeder Eintrag nimmt 4 Bytes in Anspruch. Es sind beim Hochladen des Sketch noch 1499 Bytes für lokale Variablen übrig. Das heißt, man könnte noch gut 300 Codes unterbringen. Das sind schon ein paar Fernbedienungen.

Ich habe die Tasten für eine Demo mit den Senderplätzen 1 bis 4 vorbelegt und dazu ein kleines Funktionsvideo gemacht:



Wie man sieht, funktioniert die Fernbedienung auch auf ein paar Meter gut. Nur die Bedienung ist etwas umständlich. Mit einem Gehäuse, einem kleinen, beleuchteten 8-Zeichen-LCD und mehr Tasten und besserer Auswahlmöglichkeit wäre es sicher benutzerfreundlicher, aber da gibt es wohl günstigeren und bessere, fertige Universalfernbedienungen, die man auch anlernen kann.

Sinnvoll wird es wohl erst werden, wenn man etwas benötigt, was auf dem Markt so nicht zu kaufen ist, z. B. weil man eine Hochleistungs-Infrarot-LED verbaut, die auch noch funktioniert, wenn die Normalmodelle versagen. Oder sich einen Infrarot-Repeater bauen will, um lange Strecken zu überwinden oder um eine Ecke zu steuern. Oder andere ausgefallene Anwendungen, für die es kein Gerät zu kaufen gibt.

Den folgenden Source-Code kann man ja dann nach seinen Anforderungen noch ausbauen. Allerdings muss ich sagen, dass mir aufgefallen ist, dass unter seltsamen, von mir nicht ganz nachvollziehbaren Bedingungen das Empfangen nicht mehr richtig geklappt hat. Eventuell hängt das mit dem Timer 2 zusammen, der von der Library verwendet wird. Auch eigentlich harmlose Befehlskombinationen wie Funktionsaufrufe, millis(), digitalRead() und delay() schaffen es in bestimmter Konstellation dem IR-Empfang den Garaus zu machen.

Auch ist die Library nicht sonderlich gut dokumentiert. Die beste Dokumentation ist hier der Source-Code der Library selbst. Trotzdem ist das Verhalten von enableIRIn(), decode() und resume() nicht immer logisch nachvollziehbar, da hilft dann nur ausprobieren.

Source-Code

//////////////////////////////////////////////////////// // (C) 2018 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <IRremote.h> // für Empf./Send. IR #define PinLED1 13 #define PinLED2 12 #define PinTaster1 11 #define PinTaster2 10 #define PinTaster3 9 #define PinTaster4 8 #define PinEmpf 4 // wo ist Data des IR-Empfängers angeschlossen? #define PinSend 3 // fest wegen Library #define DurPause 10 // Pause in ms zwischen den Messungen IRrecv irEmpf(PinEmpf); // IR-Empf. instanziieren IRsend irSend; // fest Timer 2 / Pin 3 (beim MFS: Buzzer) decode_results decoding; // Speicher für Decoding bereitstellen void setup() { pinMode(PinLED1, OUTPUT); pinMode(PinLED2, OUTPUT); pinMode(PinTaster1, INPUT_PULLUP); pinMode(PinTaster2, INPUT_PULLUP); pinMode(PinTaster3, INPUT_PULLUP); pinMode(PinTaster4, INPUT_PULLUP); irEmpf.enableIRIn(); // IR-Empfang starten irEmpf.blink13(1); // lässt LED1 bei Empfang blinken Serial.begin (115200); } void showIRCode (decode_results* decoding) { Serial.print("Protocol: "); Serial.println(decoding->decode_type, HEX); Serial.print("Address: "); Serial.println(decoding->address, HEX); Serial.print("Code: "); Serial.println(decoding->value, HEX); Serial.print("Bits: "); Serial.println(decoding->bits, DEC); Serial.print("Raw-Len: "); Serial.println(decoding->rawlen, DEC); Serial.print("Raw-Ovfl: "); Serial.println(decoding->overflow, DEC); Serial.print("Raw: "); for (int i = 0; i < decoding->rawlen; i++) { Serial.print (decoding->rawbuf[i], HEX); Serial.print (" "); } Serial.println("\n"); } void loop() { unsigned long speicher[4] = {0x804E58A7, 0x804EF807, 0x804E1AE5, 0x804E6897}; byte taster; Serial.println("PROGRAMM GESTARTET"); while (1) { if (irEmpf.decode(&decoding)) { Serial.println("IR SIGNAL EMPFANGEN"); showIRCode(&decoding); irEmpf.resume(); } delay(10); // Taster auswerten taster = 0; if (digitalRead(PinTaster1) == LOW) { taster = 1; } else if (digitalRead(PinTaster2) == LOW) { taster = 2; } else if (digitalRead(PinTaster3) == LOW) { taster = 3; } else if (digitalRead(PinTaster4) == LOW) { taster = 4; } if (taster > 0) { digitalWrite (PinLED2, HIGH); irSend.sendNEC(speicher[taster - 1], 32); delay(200); digitalWrite (PinLED2, LOW); irEmpf.enableIRIn(); // muss nach einem Send erneut ausgeführt werden } } }