Arduino Infrarood zender en ontvanger maken
Tegenwoordig kun je op veel manieren draadloos communiceren, maar wil je een project écht simpel houden dan kun je data versturen en ontvangen via infrarood. Het enige wat je nodig hebt is een infrarood ontvanger, een afstandsbediening en een Arduino. Ook kun je met een infrarood LED een afstandsbediening maken. Leer bij VDR Electronics hoe je makkelijk via infrarood kunt communiceren.
1. Benodigdheden Infrarood Tutorial
Om mee te bouwen met dit infrarood project heb je onderstaande hardware en software nodig. Helemaal onderaan de pagina zijn de producten te vinden die direct bij ons besteld kunnen worden.
Hardware
- 1x Arduino Uno, of een ander type Arduino
- 1x VS1838B Infrarood ontvanger - 38kHz
- 1x Infrarood afstandsbediening
- 1x Infrarood LED 940nm
- 1x Weerstand 100 Ohm
- 1x Breadboard
Software
- Arduino IDE
- IR Remote Library - IRremote.h
2. Hoe werkt infrarood communicatie
Zoals de naam al zegt werkt infrarood communicatie in het onzichtbare infraroodspectrum, en dan wel rondom het gebied met een golflengte van 780nm tot 1400nm. Welke golflengte je precies nodig hebt is afhankelijk van de ontvanger, maar de meeste apparaten maken gebruik van infrarood LEDs met een golflengte 940nm.
Om data via het infrarood licht te versturen heb je een zender (LEDs) en een ontvanger (VS1838B) nodig. Maar hoe stuur je de data door?
Modulatie
Het sturen van een logische '1' of '0' is niet zo makkelijk als een LED aan en uit zetten. Net zoals bij het draadloos zenden met radiogolven wordt er gebruik gemaakt van een draaggolf rondom een bepaalde frequentie. Door gebruik te maken van een draaggolf wordt de overdracht van data een stuk betrouwbaarder aangezien er overal willekeurige infrarood signalen (warmte) worden uitgestraald. Met behulp van deze draaggolf zullen de enen en nullen gemoduleerd via het licht verstuurd kunnen worden.
Wij maken gebruik van de VS1838B infrarood ontvanger, en die is geschikt om signalen te ontvangen met een draaggolf frequentie van 38kHz.
Hoe dat moduleren in zijn werk gaat zie je hieronder. Stel je wilt een hoog signaal versturen, dan zal dat hoog signaal worden verzonden als pulsjes van 38kHz. Dat pulserend signaal kun je direct koppelen aan een infrarood LED om data te versturen.

Demodulatie
Een signaal dat gemoduleerd is (het pulserend signaal) moet uiteindelijk ook weer gedemoduleerd worden, en in dit geval is dat de taak van de VS1838B infrarood ontvanger. In deze ontvanger zitten allereerst een aantal versterkers om het infrarood signaal waar te nemen, waarna het vervolgens door gaat naar een 38kHz bandfilter. Deze filter zorgt ervoor dat alleen signalen met een frequentie van ca. 38kHz door worden gelaten. Met andere woorden: alle achtergrondruis wordt weggefilterd! Tot slot wordt het gemoduleerde signaal van 38kHz omgezet naar enen en nullen die met een microcontroller uitgelezen kunnen worden.
Data protocol
Het signaal dat uit de infrarood ontvanger komt is opgebouwd uit logische blokken die meestal door een protocol zijn vastgelegd, met de nadruk op meestal. Voor infrarood communicatie zijn er niet echt richtlijnen, dus een fabrikant kan zelf zijn eigen protocol ontwikkelen en in zijn producten gebruiken.
Gelukkig zijn er wel een aantal veelgebruikte protocollen, waaronder het NEC protocol dat veel wordt gebruikt bij Japanse producten. Dit protocol is erg eenvoudig en bevat een dubbele check om de ontvangen infrarood data te verifiëren. Ook de afstandsbediening die wij gebruiken in deze tutorial stuurt data uit via het NEC protocol. Hoe dit protocol precies werkt hoef je niet te weten, maar toch lichten we het even (heel kort) toe.
NEC Protocol
Data die via het NEC protocol wordt overgestuurd maakt altijd gebruikt van een draaggolf frequentie van 38kHz. Ook definieert het protocol zogenaamde Marks en Spaces, een soort bouwstenen met een lengte van 562.50µs lang, om een logische '1' en '0' uit op te bouwen. Een Mark is een hoog signaal (bv. 5V), en een Space is een laag signaal.
Door gebruik te maken van Pulse Distance Encoding kunnen enen en nullen goed van elkaar onderscheiden worden.
- Logische '0' - Eerst één Mark (562.50µs), gevolgd door één Space (562.50µs).
- Logische '1' - Eerst één Mark (562.50µs), gevolgd door drie Spaces (1687.50µs).

Een volledig NEC infrarood signaal is als volgt opgebouwd:
- 38kHz signaal 9ms in lengte
- Geen signaal 4.5ms in lengte
- Het 8-bits adres
- Het geïnverteerde 8-bits adres ter verificatie
- Het 8-bits commando
- Het geïnverteerde 8-bits commando ter verificatie
- Een Mark van 562.50µs

De totale duur voor het versturen van effectief een 8-bits adres en een 8-bits commando is 67,5ms. Zeker niet de snelste manier van data versturen met een kleine 118 bits per seconde.
3. Infrarood zenden en ontvangen met Arduino
Nu je weet hoe de infrarood communicatie in elkaar steekt kun je beginnen met het opbouwen van de schakeling op een breadboard. Gebruik hiervoor onderstaand aansluitingsschema.
Zet een weerstand van 100 Ohm in serie met de infrarood LED om te stroom te begrenzen tot ca. 35mA. De maximale stroom dat de Arduino op een digitale poort kan leveren is 40mA, dus als je met meerdere LEDs wil zenden, dan moet je ze schakelen via een NPN transistor.
- Datapin VS1838B aansluiten op pin 2 van de Arduino
- Infrarood LED aansluiten op pin 3 van de Arduino

De opgebouwd schakeling werkt niet vanzelf, want er moet code geschreven worden voor het versturen en ontvangen van data via infrarood.
4. Arduino software voor een infrarood ontvanger
Deze tutorial is opgesplitst in twee delen, namelijk: ontvangen en zenden van infrarood signalen. Wij beginnen eerst met het ontvangen van infrarood signalen zodat het protocol van de zender (afstandsbediening) achterhaalt kan worden. Nadat we weten welke data de afstandsbediening uitstuurt kan er een code worden geschreven om datzelfde signaal met een Arduino te versturen. Daarnaast maken wij gebruik van onze infrarood afstandsbediening ter toelichting van de code.
Voordat je begint moet je de IRremote library downloaden en installeren in Arduino. In deze library zitten functies om verschillende infrarood signalen op te vangen en te versturen. Ook zitten er een aantal handige voorbeelden in die je direct kunt gebruiken.
Download Arduino Library <IRremote.h>
4.1 Afstandbediening uitlezen
Met het eerste programma dat we gaan schrijven zal het infrarood signaal worden ontvangen en weergegeven in de Serial Monitor. Zo kan er achterhaald worden om welk protocol het gaat, en welke data er verstuurd wordt.
Om te beginnen maak je een nieuwe sketch en voegt de IRremote.h library toe. Daarna geef je aan op welke pin de infrarood zender (LED) en ontvanger zit. In ons geval is dat respectievelijk pin 2 en pin 3.
Vervolgens maak je een nieuwe instantie aan van het IRrecv object, en geeft daarbij ook door op welke pin het infrarood signaal binnen komt op de Arduino. Ook wordt er een instantie aangemaakt van het type decode_results, waarin de ontvangen data wordt opgeslagen.
De code ziet er dan als volgt uit:
#include <IRremote.h>
#define IR_RECEIVE_PIN 2
#define IR_SEND_PIN 3
IRrecv irrecv(IR_RECEIVE_PIN);
decode_results results;
Dan kunnen we door naar de setup() functie om de seriële communicatie en de infrarood ontvanger te initialiseren. Ook gaan we gebruik maken van de ingebouwde led op pin 13 die gaat knipperen zodra er een infrarood signaal binnen komt.
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
// Start Serial port and wait until it's initialized
Serial.begin(115200);
while (!Serial)
;
irrecv.enableIRIn(); // Start the infrared receiver
Serial.println("Ready to receive IR signals!");
}
Voordat we door gaan naar de loop() programmeer je nog een functie die het type protocol en de ontvangen data netjes weergeeft op het scherm. Dit wordt uitsluitend gebruikt om te achterhalen wat er uit de afstandsbediening komt. Zet deze functie direct onder de setup(). Deze code is overigens ook terug te vinden bij het voorbeeld programma File --> Examples --> IRremote --> IRRecvDump.
void dump(decode_results *results) {
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
int count = results->rawlen;
if (results->decode_type == UNKNOWN) {
Serial.print("Unknown encoding: ");
} else if (results->decode_type == NEC) {
Serial.print("Decoded NEC: ");
} else if (results->decode_type == SONY) {
Serial.print("Decoded SONY: ");
} else if (results->decode_type == RC5) {
Serial.print("Decoded RC5: ");
} else if (results->decode_type == RC6) {
Serial.print("Decoded RC6: ");
} else if (results->decode_type == PANASONIC) {
Serial.print("Decoded PANASONIC - Address: ");
Serial.print(results->address, HEX);
Serial.print(" Value: ");
} else if (results->decode_type == LG) {
Serial.print("Decoded LG: ");
} else if (results->decode_type == JVC) {
Serial.print("Decoded JVC: ");
} else if (results->decode_type == AIWA_RC_T501) {
Serial.print("Decoded AIWA RC T501: ");
} else if (results->decode_type == WHYNTER) {
Serial.print("Decoded Whynter: ");
} else if (results->decode_type == BOSEWAVE) {
Serial.print("Decoded Bose Wave Radio / CD: ");
}
Serial.print(results->value, HEX);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
Serial.print("Raw (");
Serial.print(count, DEC);
Serial.print("): ");
for (int i = 1; i < count; i++) {
if (i & 1) {
Serial.print(results->rawbuf[i] * MICROS_PER_TICK, DEC);
} else {
Serial.write('-');
Serial.print((unsigned long) results->rawbuf[i] * MICROS_PER_TICK, DEC);
}
Serial.print(" ");
}
Serial.println();
}
In de loop() functie wachten we simpelweg op de infrarood data, en zodra deze ontvangen is zal de data in de Serial Monitor van de Arduino IDE worden weergegeven. Je kunt dit programma nu downloaden naar de Arduino, de Serial Monitor openen en de data analyseren.
void loop() {
if (irrecv.decode(&results)) {
dump(&results);
irrecv.resume();
}
}
4.2 Infrarood data analyseren
Zodra je het programma op de Arduino hebt draaien kun je op de afstandsbediening een willekeurige knop indrukken om de data en het protocol weer te geven in de Serial Monitor. Dit is belangrijk, aangezien we zo meteen in de IRremote library gaan sleutelen om het ontvangen van data stabieler te maken. Let erop dat je Serial Monitor een baudrate van 115200 gebruikt.
Een stukje data ziet er bijvoorbeeld als volgt uit:
Decoded NEC: FF38C7 (32 bits)
Raw (68): 8900 -4450 550 -600 550 -550 550 ...etc
Uit de eerste regel kun je opmaken met welk protocol je te maken hebt. In dit geval het NEC protocol. Direct daarna zie je de hexadecimale representatie van het signaal, oftewel de data.
In de tweede regel zie je de timing van de ontvangen data, maar die hoef je niet te analyseren.
Als je op de knoppen van je afstandsbediening drukt zul je al snel merken dat er ook andere protocollen naar voren komen, of misschien zelfs wel een onbekende (Unknown). Dit is helaas een probleem van de Library maar eenvoudig op te lossen:
- Richt de afstandsbediening recht op de ontvanger en druk steeds dezelfde knop in.
- Bekijk in de Serial Monitor welk protocol het meeste voorkomt, dat is hoogstwaarschijnlijk de juiste en die moet je onthouden.
- Lees dan nu snel verder hoe je dat probleem gaat verhelpen.
4.3 IRremote.h Library aanpassen
Je hebt gemerkt dat het ontvangen van infrarood met deze Library vreemd kan verlopen. De ontvangen data en het protocol veranderd steeds terwijl je dezelfde knop in drukt. Dit heeft ermee te maken dat de timing voor het uitlezen van de data erg nauwkeurig komt, en deze Library bevat veel logica om te bepalen om welk protocol het gaat. Die logica kost tijd, en het kan zijn dat daardoor infrarood signalen worden gemist omdat de processor te druk bezig is met andere dingen.
Door alleen het protocol in te schakelen dat je afstandsbediening gebruikt voorkom je dat de processor bits mist, waardoor het signaal een stuk stabieler ingelezen kan worden.
Om de Library aan te passen ga je als volgt te werk:
- Navigeer op je computer naar de folder waar de Arduino Library zich bevindt. Dat is meestal: c:\Users\username\Documents\Arduino\libraries\IRremote\src
- Open dan het bestand IRremote.h in kladblok, of een andere text editor zoals bijvoorbeeld Notepad++.
- Helemaal in het begin van dat bestand kun je protocollen in- en uitschakelen.
- Zet alle protocollen uit door een 0 in te voeren bij de #define. Dat begint vanaf regel #43, en eindigt op regel #100
- Schakel alleen het gevonden protocol in door een 1 in te vullen.
- De rest van de code kun je laten zoals het is, en sla het bestand op.
In ons geval maken wij gebruik van het NEC protocol, en dan ziet een deel van IRremote.h er als volgt uit:
...
#define DECODE_RC6 0
#define SEND_RC6 0
#define DECODE_NEC 1
#define SEND_NEC 1
#define DECODE_SONY 0
#define SEND_SONY 0
...
Upload dezelfde code nog maar een keer naar de Arduino, en je zult zien dat het uitlezen van je infrarood data een stuk beter is!
4.4 Infrarood codes van de afstandsbediening vastleggen
Nu je op een betrouwbare manier de codes van je afstandsbediening kunt uitlezen, kun je deze vastleggen in de Arduino zodat je er later iets mee kunt gaan doen. Dat doe je door de Serial Monitor te openen, op een knop te drukken en daarna de data op te slaan in een #define. Dat kan er bijvoorbeeld als volgt uit zien voor knop A. Vergeet niet 0x voor de data te zetten zodat Arduino weet dat het om een hexadecimaal getal gaat.
#define _knopA 0xFF28A4 // Example data for Button A
Al die definities, voor de knoppen die je wilt gebruiken, zet je net vóór de setup() functie.
Als je gebruikt maakt van onze afstandsbediening, dan kun je onderstaande definities gebruiken. Je ziet hier ook repeat staan, en dat is data die de afstandsbediening doorstuurt als je een knop ingedrukt houdt.
#define _1 0xFFA25D
#define _2 0xFF629D
#define _3 0xFFE21D
#define _4 0xFF22DD
#define _5 0xFF02FD
#define _6 0xFFC23D
#define _7 0xFFE01F
#define _8 0xFFA857
#define _9 0xFF906F
#define _0 0xFF9867
#define _star 0xFF6897
#define _hash 0xFFB04F
#define _up 0xFF18E7
#define _down 0xFF4AB5
#define _left 0xFF10EF
#define _right 0xFF5AA5
#define _ok 0xFF38C7
#define _repeat 0xFFFFFFFF
4.5 Ontvangen infrarood data verwerken
Nadat je alle knoppen hebt gedefinieerd in de software kan er eindelijk functionaliteit aan een bepaalde knop toegewezen worden. Dat gaan we realiseren door in de loop() een actie te ondernemen via een switch statement.
Om het voorbeeld makkelijk te houden zal er bij het indrukken van een bepaalde knop iets naar de Serial Monitor geschreven worden, in dit geval de waarde van de knop. Je loop() kan er als volgt uit zien:
void loop() {
if (irrecv.decode(&results)) {
switch(results.value){
case _1: Serial.print("1"); break;
case _2: Serial.print("2"); break;
case _3: Serial.print("3"); break;
case _4: Serial.print("4"); break;
case _5: Serial.print("5"); break;
case _6: Serial.print("6"); break;
case _7: Serial.print("7"); break;
case _8: Serial.print("8"); break;
case _9: Serial.print("9"); break;
case _0: Serial.print("0"); break;
case _star: Serial.print("*"); break;
case _hash: Serial.print("#"); break;
case _up: Serial.print("UP"); break;
case _down: Serial.print("DOWN"); break;
case _left: Serial.print("LEFT"); break;
case _right: Serial.print("RIGHT"); break;
case _ok: Serial.print("OK"); break;
case _repeat: Serial.print("_"); break;
}
Serial.println(" ");
irrecv.resume(); // Receive the next value
}
}
Als je dit programma opent kunt je via de Serial Monitor zien dat de waarde van de afstandsbediening juist wordt ontvangen en verwerkt.
4.6 Infrarood repeat commando verwerken
Ook het repeat commando zie je naar voren komen, en als je die wilt gebruiken zul je een variabele moeten aanmaken waarop je het eerst ontvangen commando in opslaat.
Stel je drukt op knop 1, dan wil je dat commando opslaan in een variabele. Als je knop 1 in blijft drukken zal namelijk vanzelf het repeat commando uitgestuurd worden door de afstandsbediening. In de software weet je dat het commando van knop 1 nog een keer uitgevoerd moet worden. Maar... stel je mist het allereerste commando, wat dan?
Repeat commando fouten opvangen
Als in jouw geheugen variabele een vorige commando staat, bijvoorbeeld knop 2, en houdt daarna knop 1 in, dan wil je dat het programma het commando voor knop 1 blijft uitvoeren. Maar misschien is het commando voor knop 1 niet aangekomen, en het gevolg is dat commando voor knop 2 nu uitgevoerd wordt!
Een manier om dat op te vangen is door softwarematig een tijdsvenster te programmeren waarin het repeat commando zich mag bevinden, bijvoorbeeld binnen 500ms nadat het eerste commando is ontvangen. Als het repeat commando te laat aankomt, en potentieel het eerste hoofdcommando heeft gemist, dan kun je er voor kiezen om het repeat commando in zijn geheel te negeren.
Helemaal bovenin het programma moet je dan drie variabelen toevoegen, namelijk:
- previousResults - Hierin sla je het hoofdcommando op. Dat is het eerste commando dat de afstandsbediening uitstuurt als je een knop indrukt
- previousMillis - een 32-bits variabele waarmee we de tijd van de Arduino bijhouden, in millisecondes
- maxRepeatDelay - een tijdsvenster waarbinnen het repeat signaal ontvangen moet worden nadat het hoofdcommando is ontvangen, in millisecondes. Deze waarde kun je aanpassen wat voor jou het beste werkt.
decode_results previousResults;
unsigned long previousMillis = 0;
const unsigned int maxRepeatDelay = 500;
Je kunt de loop() daarna als volgt veranderen, waarbij deze flow wordt aangehouden:
- Wacht totdat er een infrarood signaal is ontvangen.
- Controleer of dit niet het repeat commando is. Als dat zo is, dan hebben we te maken met een hoofdcommando, en die slaan we op in de variabele previousResults. Ook resetten we de interne timer die straks gaat bijhouden of het repeat signaal binnen het tijdsvenster is verstuurd.
- Controleer daarna of het repeat commando wel is ontvangen. Als dat het geval is kijken we of deze binnen het tijdsvenster van het hoofdcommando valt, en als dat zo is zal het repeat commando worden uitgevoerd. Ook wordt dan de interne timer gereset om het volgende repeat signaal ook op te vangen.
void loop() {
// Wait to receive a valid infrared command
if (irrecv.decode(&results)) {
// Check if this command isnt the repeat command, and store the result in a variable
if (results.value != _repeat) {
previousResults = results; // Catch the first command
previousMillis = millis(); // Reset previousMillis variable
}
// Check if the repeat commando was sent
if (results.value == _repeat) {
// Check if the time between the repeat command and first command
// is less than the set delay, to ensure the proper command is executed
if (millis() - previousMillis < maxRepeatDelay) {
Serial.print("_"); // Do something with the repeat command
previousMillis = millis(); // Reset previousMillis variable
}
}
// Do something with the received command
switch (results.value) {
case _1: Serial.print("1"); break;
case _2: Serial.print("2"); break;
case _3: Serial.print("3"); break;
case _4: Serial.print("4"); break;
}
Serial.println(" ");
irrecv.resume(); // Receive the next value
}
}
Met deze eenvoudige code kun je op een betrouwbare manier infrarood data ontvangen met een Arduino. In het volgende deel gaan we infrarood data versturen met de Arduino, en dan wel de commando's die je al uit de afstandsbediening hebt gehaald.
Heb je een TV afstandsbediening uitgelezen? Dan kun je hierna met de Arduino jouw TV aansturen via infrarood. Mocht je gebruik hebben gemaakt van onze afstandsbediening, dan zou je de infrarood ontvanger software op een tweede Arduino kunnen zetten zodat je kunt controleren of het uitgezonden infrarood signaal goed aan komt.
5. Infrarood data versturen met een Arduino
Voor het versturen maken gaan we wederom gebruik maken van de IRremote Library. Zorg ervoor dat je een infrarood LED met 100 Ohm serieweerstand hebt aangesloten op pin 3 van de Arduino, zie ook hoofdstuk 3 van deze handleiding.
In dit voorbeeld maken we gebruik van onze infrarood afstandsbediening, en gaan we het commando voor de OK knop versturen via het NEC protocol. Het versturen van het commando doe je met de functie sendNEC(data, numberOfBits). Het eerste argument dat je meegeeft is de data, en de tweede geeft aan hoeveel bits er verstuurd moeten worden. Voor andere data protocollen gelden andere functies, en daarvoor kun je het beste de Library bekijken.
irsend.sendNEC(_ok, 32);
Hieronder is de complete code te vinden om een commando via infrarood te versturen. Om de kans groter te maken dat het infrarood signaal goed aankomt, sturen we het commando drie keer kort achterelkaar uit. Dat wordt ook wel een burst genoemd.
#include <IRremote.h>
#define IR_SEND_PIN 3 // Connect an IR led to this pin
IRsend irsend;
unsigned long command
void setup() {
// The setup isn't used here
}
void loop() {
command = 0xFF38C7; // The command we want to send
// Send a burst of 3 commands
for (int i = 0; i < 3; i++) {
irsend.sendNEC(_ok, 32);
delay(50);
}
delay(850); // Wait 850 milliseconds and send the data again
}