Je eigen Arduino Infrarood Afstandsbediening
Vandaag gaan we een TV afstandsbediening bouwen met een Arduino. We willen een infrarood signaal onderscheppen / opnemen en vervolgens met een druk op de knop weer uitzenden. In deze post gaan we aan de slag met Arduino, een elektrisch circuit en met programmeren. Doe je mee?
Ok, we gaan dus werken met infrarood. Als je meer van de infrarood theorie wilt weten bekijk dan de “Alles over: Infrarood” post van vorige week.
Wat hebben we nodig voor dit project:
- 2x Arduino Board (ik gebruik 2x een Arduino Uno R3)
- 2x Breadboard
- 1x Infrarood LED
- 1x Infrarood Receiver
- 1x Pushbutton
- 100 Ω resistor
- Jumper wires
Wat we ook absoluut niet mogen missen is de Arduino IRremote Library van z3t0. Stel je toch eens voor dat we alles zelf moeten gaan verzinnen 🙂 De IRremote library is hier te downloaden.
Kies ervoor om de library als ZIP te downloaden. Pak vervolgens de ZIP uit en plaats de “Arduino-IRremote-master” folder in de “libraries” folder van je Arduino installatie:

Als je de library geplaatst hebt en de Arduino software opnieuw start moet je de beschikking hebben over de library en dus de beschikking hebben over diverse voorbeelden:


Ok, nu we er softwarematig klaar voor zijn en alle hardware binnen handbereik hebben gaan we eens kijken naar 2 sleutel onderdelen van het circuit.
IR LED
De IR LED die ik gebruik is de Fairchild QEC11X te verkrijgen bij Antratek, Parallax en vele andere websites.

Als ik kijk naar de specificaties dan vallen de volgende op:
- Foreward Current: 50 mA
- Forward Voltage: 1.5V
- Golflengte: 940 nm
- Bijpassende Photosensor: QSC112
- Maximale hoek: 24°
Een 24° hoek betekend dat je de laser redelijk gericht op de ontvanger moet richten voor een goed signaal maar voor afstandsbedieningen is deze hoek niet ontzettend vreemd. De sensor bevindt zich met een golflengte van 940 nm in het “Nabij Infrarood” spectrum welke zich tussen de 700 en 1400 nanometer bevindt. De foreward current hebben we nodig om te weten welke resistor ideaal is voor deze IR LED. Laten we dat meteen uitrekenen.
Weten jullie de formule nog uit “Resistors, en het uitrekenen van weerstand”?
R = V / I
Dus:
Resistance = Volt (Source Volts – Device Foreward Volt) / Current (MiliAmpere mA / 1000)
Dit wordt dan:
(5V (Arduino) – 1.5V) / (50 / 1000 = 0,05)
Met andere woorden, de weerstand die we nodig hebben is: 3.5 / 0,05 = 70 Ω
Aangezien ik geen 70 Ω resistors heb gebruiken we een 100 Ω resistor voor deze LED.
De lange “poot” is de Anode en de korte poot is de Cathode:
IR Receiver
De IR Receiver die ik in dit project gebruik is een Sharp GP1Ux511QS en ook te verkrijgen op Antratek, Parallax e.a.
Deze heeft de volgende specificaties:
- Supply Voltage: 5V
- Band Pass Filter Center Frequency: 40KHz
De gemiddelde frequentie tussen de bovenste en onderste “cutoff” frequenties is 40 KHz. Deze waarde hebben we niet nodig in dit voorbeeld. De receiver moet voorzien worden van 5V. Voor de receiver hebben we dus geen resistor nodig.
De IR Receiver heeft 3 poten, namelijk:
Aansluiten van het IR LED en IR Receiver
Met bovenstaande informatie kunnen we prima een circuit maken voor het aansluiten van de IR LED:
We verbinden de korte poot van het IR LED met de ground van de Arduino. De Anode (+) verbinden we met signaalpin nummer 11 met als tussenkomst een 100 Ω resistor om de stroom voldoende af te zwakken zodat het IR LED niet beschadigd. Dit ziet er dus als volgt uit:
*Note: Voor het maken van bovenstaande schema’s gebruik ik de prachtige gratis tool: Fritzing!

*Op bovenstaande foto gebruik ik een Grove Shield op mijn Arduino. Met dit Grove Shield kun je gemakkelijk allerlei sensoren aansluiten. Dit zorgt voor minder losse kabeltjes. In de schema’s en verdere uitleg gebruik ik dit shield niet.
De IR Receiver bestaat uit 3 poten welke als volgt aangesloten worden. De signal pin verbinden we met nummer 3 van de digitale pinnen. De middelste pin is de ground pin en de laatste poot wordt verbonden met de 5V output pin op de Arduino. Dit ziet er als volgt uit:
Arduino IR Code
Nu het technisch allemaal netjes aangesloten is wordt het zaak om de Arduino te programmeren. We beginnen met het programmeren van de Arduino met de IR Receiver. We willen namelijk eerst codes kunnen ontvangen welke we kunnen onderscheppen en bekijken zodat we weten welk format we later door kunnen sturen.
Voor het onderscheppen staat er in de IRremote library een mooi voorbeeldscript, namelijk: IRrecvDemo. Dit script ziet er als volgt uit:
#include <IRremote.h> int RECV_PIN = 11; IRrecv irrecv(RECV_PIN); decode_results results; void setup() { Serial.begin(9600); // In case the interrupt driver crashes on setup, give a clue // to the user what's going on. Serial.println("Enabling IRin"); irrecv.enableIRIn(); // Start the receiver Serial.println("Enabled IRin"); } void loop() { if (irrecv.decode(&results)) { Serial.println(results.value, HEX); irrecv.resume(); // Receive the next value } delay(100); } |
Let erop dat je de juiste signaalpin definieert voor de variabele “RECV_PIN”.
Verifieer nu de code en upload hem vervolgens naar de Arduino. Open nu onder “Hulpmiddelen” de “Seriële Monitor”. Je kunt nu namelijk kijken naar de data die op de signaalpin voorbij komt. Zoek een afstandsbediening en richt die op de ontvanger, druk op een knop en als het goed is zie je data voorbij komen welke lijkt op onderstaande data:

Zoals bovenstaande tekening al aangeeft zien we hierin een aantal belangrijke zaken:
- RC6 – Dit is het gebruikte protocol. In dit geval is de capturing van Philips TV en Philips gebruikt inderdaad RC6
- Signaalcode ’s – De codes voor het besturen van de TV functies. Kanaal 1 is 10001, kanaal 2 is 10002 en kanaal 3 is 10003. Het aan/uit signaal is 1000C
- Niet juist ontvangen signaalcodes – De niet-rood omcirkelde codes zijn codes die niet volledig ontvangen zijn doordat de toets op de afstandsbediening te kort ingedrukt werd.
Nu we dus weten welke codes we moeten zenden is het zaak om de IR zender te programmeren.
Ook hiervoor is een mooi voorbeeld te vinden, namelijk de IRsendDemo:
#include <IRremote.h> IRsend irsend; void setup() { } void loop() { for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); delay(40); } delay(5000); //5 second delay between each signal burst } |
Belangrijk is om de signaalpin van de IR LED aan te sluiten op digitale pin nummer 3. Verder is deze regel enorm belangrijk:
irsend.sendSony(0xa90, 12); |
We kunnen namelijk een aantal signalen uitzenden. In het voorbeeld staat een sendSony welke de code in Sony formaat verzend. Wij gebruiken echter Philips en moeten dus RC6 gebruiken. Andere veelgebruikte encoding formaten zijn NEC (sendNEC) RC5 (sendRC5) en RAW (sendRAW).
De code die erachter staat is de infrarood zender code en de 12 staat voor het aantal bits welke er verzonden worden. Deze kun je terugvinden in de RAW signalen en niet in de gedecodeerde informatie zoals hierboven.
Om dit te weten start je de seriële monitor met het IRrecvDumpV2 voorbeeld. De uitkomst van het aan/uit signaal ziet er dan een stuk uitgebreider uit:

Dus we weten nu alles om ons verzendscript te laten werken.
irsend.sendSony(0xa90, 12); |
Bovenstaande regel veranderen we in:
irsend.sendRC6(0x1000C, 20); |
Als we de code nu verifiëren en uploaden naar de Arduino dan zal de Arduino elke 5 seconde het aan/uit signaal verzenden.
Maar dat kan natuurlijk mooier!
Zender voorzien van een pushbutton
Het is leuk dat het infraroodsignaal elke 5 seconde verzonden wordt, maar leuker is het als het met een druk op de knop verzonden wordt. Om dit te doen passen we het stroomcircuit als volgt aan:

Zoals je ziet hebben we nu een pushbutton toegevoegd. De pushbutton is met zijn signaalpin verbonden met digitale pin nummer 6. Verder wordt hij ook voorzien van 5V en een ground aansluiting.
De code van de verzender moeten we natuurlijk ook uitbreiden. Dat doen we als volgt:
#include <IRremote.h> IRsend irsend; const int buttonPin = 6; // the number of the pushbutton pin //const int ledPin = 3; int buttonState = 0; // variable for reading the pushbutton status void setup() { // pinMode(ledPin, OUTPUT); // initialize the pushbutton pin as an input: pinMode(buttonPin, INPUT); } void loop() { buttonState = digitalRead(buttonPin); // check if the pushbutton is pressed. // if it is, the buttonState is HIGH: if (buttonState == HIGH) { // turn LED on: irsend.sendRC6(0x1000C, 20); delay(40); } } |
In deze code definiëren we pin 6 (buttonPin) als input. We bouwen ook een controle in om te kijken of de button wordt ingedrukt. Als dat het geval is wordt de Philips aan/uit code weer verzonden die we in het vorige voorbeeld ook gebruikt hebben.
1 Arduino die verzend en ontvangt
In voorgaande voorbeelden hebben we 2 Arduino’s gebruikt welke beide voorzien waren van een separaat circuit. Het is erg lastig om een zend- en ontvang functie te bouwen op 1 circuit op de Arduino. Het probleem zit hem in de stabiele controle van signalen. Als de Arduino een signaal zend kan hij even niet luisteren naar signalen. Daarnaast wil je waarschijnlijk je laatst ontvangen signaal opslaan om die weer uit te zenden. Er is een script welke je hiervoor kunt gebruiken, namelijk het IRrecord voorbeeld. Dit script luistert naar infraroodsignalen. Als een signaal ontvangen wordt dan wordt deze opgeslagen in een buffer. Wanneer je vervolgens op de pushbutton drukt wordt het laatst opgeslagen signaal uit de buffer weer uitgezonden.
Dit is een voorbeeld van de code:
#include <IRremote.h> int RECV_PIN = 11; int BUTTON_PIN = 6; int STATUS_PIN = 3; IRrecv irrecv(RECV_PIN); IRsend irsend; decode_results results; void setup() { Serial.begin(9600); irrecv.enableIRIn(); // Start the receiver pinMode(BUTTON_PIN, INPUT); pinMode(STATUS_PIN, OUTPUT); } // Storage for the recorded code int codeType = -1; // The type of code unsigned long codeValue; // The code value if not raw unsigned int rawCodes[RAWBUF]; // The durations if raw int codeLen; // The length of the code int toggle = 0; // The RC5/6 toggle state // Stores the code for later playback // Most of this code is just logging void storeCode(decode_results *results) { codeType = results->decode_type; //int count = results->rawlen; if (codeType == UNKNOWN) { Serial.println("Received unknown code, saving as raw"); codeLen = results->rawlen - 1; // To store raw codes: // Drop first value (gap) // Convert from ticks to microseconds // Tweak marks shorter, and spaces longer to cancel out IR receiver distortion for (int i = 1; i <= codeLen; i++) { if (i % 2) { // Mark rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK - MARK_EXCESS; Serial.print(" m"); } else { // Space rawCodes[i - 1] = results->rawbuf[i]*USECPERTICK + MARK_EXCESS; Serial.print(" s"); } Serial.print(rawCodes[i - 1], DEC); } Serial.println(""); } else { if (codeType == NEC) { Serial.print("Received NEC: "); if (results->value == REPEAT) { // Don't record a NEC repeat value as that's useless. Serial.println("repeat; ignoring."); return; } } else if (codeType == SONY) { Serial.print("Received SONY: "); } else if (codeType == PANASONIC) { Serial.print("Received PANASONIC: "); } else if (codeType == JVC) { Serial.print("Received JVC: "); } else if (codeType == RC5) { Serial.print("Received RC5: "); } else if (codeType == RC6) { Serial.print("Received RC6: "); } else { Serial.print("Unexpected codeType "); Serial.print(codeType, DEC); Serial.println(""); } Serial.println(results->value, HEX); codeValue = results->value; codeLen = results->bits; } } void sendCode(int repeat) { if (codeType == NEC) { if (repeat) { irsend.sendNEC(REPEAT, codeLen); Serial.println("Sent NEC repeat"); } else { irsend.sendNEC(codeValue, codeLen); Serial.print("Sent NEC "); Serial.println(codeValue, HEX); } } else if (codeType == SONY) { irsend.sendSony(codeValue, codeLen); Serial.print("Sent Sony "); Serial.println(codeValue, HEX); } else if (codeType == PANASONIC) { irsend.sendPanasonic(codeValue, codeLen); Serial.print("Sent Panasonic"); Serial.println(codeValue, HEX); } else if (codeType == JVC) { irsend.sendJVC(codeValue, codeLen, false); Serial.print("Sent JVC"); Serial.println(codeValue, HEX); } else if (codeType == RC5 || codeType == RC6) { if (!repeat) { // Flip the toggle bit for a new button press toggle = 1 - toggle; } // Put the toggle bit into the code to send codeValue = codeValue & ~(1 << (codeLen - 1)); codeValue = codeValue | (toggle << (codeLen - 1)); if (codeType == RC5) { Serial.print("Sent RC5 "); Serial.println(codeValue, HEX); irsend.sendRC5(codeValue, codeLen); } else { irsend.sendRC6(codeValue, codeLen); Serial.print("Sent RC6 "); Serial.println(codeValue, HEX); } } else if (codeType == UNKNOWN /* i.e. raw */) { // Assume 38 KHz irsend.sendRaw(rawCodes, codeLen, 38); Serial.println("Sent raw"); } } int lastButtonState; void loop() { // If button pressed, send the code. int buttonState = digitalRead(BUTTON_PIN); if (lastButtonState == HIGH && buttonState == LOW) { Serial.println("Released"); irrecv.enableIRIn(); // Re-enable receiver } if (buttonState) { Serial.println("Pressed, sending"); digitalWrite(STATUS_PIN, HIGH); sendCode(lastButtonState == buttonState); digitalWrite(STATUS_PIN, LOW); delay(50); // Wait a bit between retransmissions } else if (irrecv.decode(&results)) { digitalWrite(STATUS_PIN, HIGH); storeCode(&results); irrecv.resume(); // resume receiver digitalWrite(STATUS_PIN, LOW); } lastButtonState = buttonState; } |
Dit is een hele lange code. Het grootste gedeelte bestaat echter uit logging. Het systeem moet namelijk exact weten om welke code, welk signaal en hoeveel bits het gaat. Ons circuit ziet er nu als volgt uit:


En nu?
Ik hoop dat je met deze bagage vooruit kunt om je eigen IR project te maken. Als je iets cools hebt gemaakt laat het me dan zeker even weten. Ik zou dit uiteraard graag uitproberen. Zelf wil ik binnenkort aan de slag met het maken van een complete afstandsbediening (i.p.v. 1 button) in combinatie met een Raspberry Pi en een touchscreen o.i.d. Als ik hem gemaakt heb dan lees je dat uiteraard hier!