Introduction: DIY USB IR Receiver
In this tutorial, I'm going to show you how to make a small, cheap (less than $5) USB IR receiver.
The microcontroller used acts as a standard keyboard, so it should work on every computer.
With this receiver you can use a remote control to control things on your computer.
I made this instructables based on one of my previous instructables. The difference is that it's a lot smaller and it acts as a keyboard, so you don't need extra software on your PC.
Let's get started!
Step 1: Parts
Step 2: Soldering the IR Receiver
First you need to bend one leg (OUT) of the IR receiver so it goes into P2 and the other legs go into 5V and GND.
After that you need to add a little piece of heat shrink (or tape) to the horizontal part of the IR receiver.
Place the IR receiver back on the digispark and solder the three legs together.
The only thing you need to do now is clip of the useless part of each leg.
Now you're done with the hardware part.
Step 3: Getting the Keycodes of the Remote
In this part you need to see what code is "connected" to every button on the Remote control. We need to do this, because every remote uses other codes for every key.
To do this, I used the setup and program of my previous instructables. I just opened the serial monitor to see which code was send. If you don't have an arduino for getting the codes, you can also use the part you just made. This is a little more difficult, but it gives you the same result.
First you need to install the digiusb drivers. Goto this link and on the right click "download zip". After you've downloaded the zip extract all the folder "DigisparkExamplePrograms-master" to any directory you like.
The goto "DigisparkExamplePrograms-master/C++/DigiUSB Windows Driver/" and click "installdriver".
After you've done that, you need to follow these instructions to install the right boards in the arduino IDE.
Now copy this code to a new arduino project and upload it to your digispark.
/************************************* * This code is written by Laurens Wuyts * For questions: laurens.wuyts@gmail.com * * * Microcontroller: ATtiny85 * Dev board: Digispark * *************************************/ #include <DigiUSB.h> uint8_t situation = 0; uint8_t START = 0; uint8_t x = 0; uint8_t BIT = 0; uint8_t Id = 0; uint8_t Id_inv = 0; uint8_t Data = 0; uint8_t Data_back = 0; uint8_t Data_inv = 0; uint8_t Repeat = 0; uint8_t sended = 0; uint16_t Time_old = 0; uint16_t Time = 0; uint16_t TimeDelta = 0; void setup(void) { attachInterrupt(0, IR_Read, FALLING); pinMode(1,OUTPUT); digitalWrite(1,LOW); DigiUSB.begin(); } void loop(void) { if(sended == 1) { DigiUSB.println(Data_back, DEC); sended = 0; } else { DigiUSB.delay(10); } } void IR_Read(void) { digitalWrite(1,HIGH); Time = micros(); if (Time_old != 0) { TimeDelta = Time - Time_old; if ((TimeDelta > 12000)&&(TimeDelta < 14000)) { START = 1; x = 0; situation = 1; Id = 0; Id_inv = 0; Data = 0; Data_inv = 0; } else if ((TimeDelta > 10000)&&(TimeDelta < 12000)) { situation = 2; // repeat } else if ((TimeDelta > 1500)&&(TimeDelta < 2500)) { situation = 3; //"1" BIT = 1; } else if ((TimeDelta > 1000)&&(TimeDelta < 1500)) { situation = 3; //"0" BIT = 0; } else situation = 5; if (situation == 3) { if (x < 8) { Id |= BIT; if (x < 7) Id <<= 1; x++; } else if (x < 16) { Id_inv |= BIT; if (x < 15) Id_inv <<= 1; x++; } else if (x < 24) { Data |= BIT; if (x < 23) Data <<= 1; x++; } else if (x < 32) { Data_inv |= BIT; if (x < 31) { Data_inv <<= 1; } else { /* DO SOMETHING HERE */ sended = 1; Data_back = Data; Repeat = 0; } x++; } } else if (situation == 2) { if(Repeat == 1) { /* DO SOMETHING HERE */ /*sended = 1;*/ } else { Repeat = 1; } } } Time_old = Time; digitalWrite(1,LOW); }
After you've done that, goto "DigisparkExamplePrograms-master/Python/DigiUSB/Windows/" and open "monitor.exe". In this window the keycode is printed when you press a button. Just note the button and then the code you see. You'll need them later on.
Step 4: Digispark Sketch
Ok now the "real" code for the Digispark.
In this code you need to add the keycodes you got in the previous step. For example if the "mute" key has code 100 you change:
#define mute 240to
#define mute 100
!! Attention !!
When you define a new key, you can't use spaces in the name.
And for adding functions go into the loop function and add/change the key you like.
When you have any question at all, feel free to ask!
/************************************* * This code is written by Laurens Wuyts * For questions: laurens.wuyts@gmail.com * * * Microcontroller: ATtiny85 * Dev board: Digispark * *************************************/ /**** Define Remote control keys ****/ #define Power 64 #define key_1 32 #define key_2 160 #define key_3 96 #define key_4 16 #define key_5 144 #define key_6 80 #define key_7 48 #define key_8 176 #define key_9 112 #define key_0 136 #define vol_up 224 #define vol_down 208 #define ch_up 72 #define ch_down 8 #define mute 240 #define next 172 #define prev 164 #define up 6 #define down 134 #define left 166 #define right 70 #define playpause 156 #define key_stop 180 /************************************/ #include "TrinketHidCombo.h" uint8_t situation = 0; uint8_t START = 0; uint8_t x = 0; uint8_t BIT = 0; uint8_t Id = 0; uint8_t Id_inv = 0; uint8_t Data = 0; uint8_t Data_back = 0; uint8_t Data_inv = 0; uint8_t Repeat = 0; uint8_t sended = 0; uint16_t Time_old = 0; uint16_t Time = 0; uint16_t TimeDelta = 0; void setup(void) { /* Use INT0(P2) on the Digispark */ attachInterrupt(0, IR_Read, FALLING); pinMode(1,OUTPUT); digitalWrite(1,LOW); TrinketHidCombo.begin(); } void loop(void) { if(sended == 1) { /* Assign functions to the buttons */ if(Data_back == vol_up) { TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_UP); } else if(Data_back == vol_down) { TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_DOWN); } else if(Data_back == next) { TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_NEXT_TRACK); } else if(Data_back == prev) { TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_PREV_TRACK); } else if(Data_back == key_stop) { TrinketHidCombo.pressMultimediaKey(MMKEY_STOP); } else if(Data_back == playpause) { TrinketHidCombo.pressMultimediaKey(MMKEY_PLAYPAUSE); } else if(Data_back == mute) { TrinketHidCombo.pressMultimediaKey(MMKEY_MUTE); } else if(Data_back == Power) { TrinketHidCombo.pressSystemCtrlKey(SYSCTRLKEY_SLEEP); } else if(Data_back == key_0) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_0); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_1) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_1); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_2) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_2); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_3) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_3); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_4) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_4); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_5) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_5); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_6) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_6); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_7) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_7); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_8) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_8); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == key_9) { TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_SHIFT, KEYCODE_9); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == up) { TrinketHidCombo.pressKey(0, KEYCODE_ARROW_UP); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == down) { TrinketHidCombo.pressKey(0, KEYCODE_ARROW_DOWN); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == left) { TrinketHidCombo.pressKey(0, KEYCODE_ARROW_LEFT); TrinketHidCombo.pressKey(0, 0); } else if(Data_back == right) { TrinketHidCombo.pressKey(0, KEYCODE_ARROW_RIGHT); TrinketHidCombo.pressKey(0, 0); } sended = 0; } else { TrinketHidCombo.poll(); } } /* Read the IR code */ void IR_Read(void) { digitalWrite(1,HIGH); Time = micros(); if (Time_old != 0) { TimeDelta = Time - Time_old; if ((TimeDelta > 12000)&&(TimeDelta < 14000)) { START = 1; x = 0; situation = 1; Id = 0; Id_inv = 0; Data = 0; Data_inv = 0; } else if ((TimeDelta > 10000)&&(TimeDelta < 12000)) { situation = 2; // repeat } else if ((TimeDelta > 1500)&&(TimeDelta < 2500)) { situation = 3; //"1" BIT = 1; } else if ((TimeDelta > 1000)&&(TimeDelta < 1500)) { situation = 3; //"0" BIT = 0; } else situation = 5; if (situation == 3) { if (x < 8) { Id |= BIT; if (x < 7) Id <<= 1; x++; } else if (x < 16) { Id_inv |= BIT; if (x < 15) Id_inv <<= 1; x++; } else if (x < 24) { Data |= BIT; if (x < 23) Data <<= 1; x++; } else if (x < 32) { Data_inv |= BIT; if (x < 31) { Data_inv <<= 1; } else { /* DO SOMETHING HERE */ sended = 1; Data_back = Data; Repeat = 0; } x++; } } else if (situation == 2) { if(Repeat == 1) { /* DO SOMETHING HERE */ sended = 1; } else { Repeat = 1; } } } Time_old = Time; digitalWrite(1,LOW); }
Now upload the sketch to the digispark. Unplug and replug it into your computer, wait ~5sec and enjoy your remote control.
Step 5: Resume
In this tutorial, I showed how to make a low cost USB remote control.
I hope you liked it, please add suggestions what you want to see next, questions or improvements below.
Laurens
62 Comments
Question 1 year ago on Step 4
Hello
I have a question about getting the ir remote control keys from the remote.
I do not have windows, but have Xenial64 puppy linux.
Do I still need usb drivers.
Any way I can monitor codes from a terminal.
Thanks, help realy appreciated as I am stuck.
Answer 9 months ago
You do not need any driver, but you may need to use xbindkeys to capture key events and run the appropriate command
4 years ago
Thank you very much. Your instructions allowed me to build my own IR receiver. Anyway, after some days using it i saw a problem (maybe due to the codes my remote was sending). Some times, after pressing a key and then in a short time another different key, the second key was processed as a repetition of the first key. I tried changing the timing parameters but could not find a solution. As I could not find where the problem came from I ended looking at the NEC protocol and rewriting a part of the algorithm. Here is my code:
uint8_t STEP = 0;
uint8_t BIT = 0;
uint8_t Id = 0;
uint8_t Id_inv = 0;
uint8_t Data = 0;
uint8_t Data_inv = 0;
uint8_t Data_back = 0;
uint8_t sended = 0;
uint32_t Time_old = 0;
uint32_t Time = 0;
uint32_t TimeDelta = 0;
void loop(void) {
if (sended == 1) { // Key press
// Control all keys
sended = 0;
} else if (sended == 2) { // Repetition
// Control keys that you want to have repetitions
sended = 0;
} else {
TrinketHidCombo.poll();
}
}
void IR_Read(void) {
Time = micros();
TimeDelta = Time - Time_old;
if (TimeDelta > 100000 || (TimeDelta > 12500)&&(TimeDelta < 14500)) {
STEP = 0;
}
STEP++;
if (STEP == 1) {
if ((TimeDelta > 12500)&&(TimeDelta < 14500)) {
Id = 0;
Id_inv = 0;
Data = 0;
Data_inv = 0;
} else {
STEP = 0;
}
} else if((STEP>=2) && (STEP<34)) { //Accumulate the bit values between 0-31.
if ((TimeDelta > 1750)&&(TimeDelta < 2750)) BIT = 1;
else if ((TimeDelta > 625)&&(TimeDelta < 1625)) BIT = 0;
else STEP = 0;
if (STEP < 10) {
Id |= BIT;
if (STEP < 9) Id <<= 1;
} else if (STEP < 18) {
Id_inv |= BIT;
if (STEP < 17) Id_inv <<= 1;
} else if (STEP < 26) {
Data |= BIT;
if (STEP < 25) Data <<= 1;
} else {
Data_inv |= BIT;
if (STEP < 33) Data_inv <<= 1;
else {
if (Data^Data_inv == 0xFF && Id^Id_inv == 0xFF) {
Data_back = Data;
sended = 1;
} else {
STEP = 0;
}
}
}
}
else if ((TimeDelta > 40100)&&(TimeDelta < 42100)) {} // Ignore the First repetition block
else if ((TimeDelta > 10250)&&(TimeDelta < 12250)) {} // Ignore Begin of new repetition block
else if ((TimeDelta > 95750)&&(TimeDelta < 99750)) // End of a repetition block
{
// Repeat
sended = 2;
} else {
STEP = 0;
}
Time_old = Time;
}
Reply 3 years ago
Indeed, this works better than original in my Kodi with old TeVii remote.
To make repeat feature to work, I'v changed TimeDelta value:
...
else if ((TimeDelta > 95750) && (TimeDelta < 110750)) // End of a repetition block
...
Reply 4 years ago
I'm having this problem as well. I'm not really sure how to interpret your code.
Where you've put "//Control all keys", do I write every button keypress here, the ones I want to repeat as well as the ones that shouldn't repeat?
And then where you've written "// Control keys that you want to have repetitions". Do I put keys that should be able to repeat here? Like up or down for scrolling through lists?
If you still have your sketch maybe it would be possible for you to paste it here? So that we can see an example?
Thank you in advance!
3 years ago
A video tutorial for the same is available here.
https://www.youtube.com/watch?v=ERvW2MvN4hQ
Reply 3 years ago
Hi, could you post the link to your sketches again? The link the video-description is dead.
Thanks
3 years ago
Thx for everybody that tweaked the code, but please guys, it is easier for others, if you can upload your sketch directly, because a lot around, do not understand exactly how to add lines in the sketch, I mean where to add the modif exactly .... and so on....
Thx
Reply 3 years ago
it is explained in this video.
3 years ago
Thanks for the well explained tutorial! I rebuilt this in order to use
the remote on a Linux PC (xubuntu) and while everything works very well
in principle (after "switching off the repeat function" as described
here earlier), I have a major problem in everyday use. After plugging in
the device while the pc is running, it works as expected, showing up as
HID among the usb devices.
However, after rebooting the system the HID
device isn't "initialized" by the system/show up with lsusb and accordingly won't work. The only way to get it running is physically unplug and replug the digispark. I
tried resetting the usb port from linux terminal, didn't work. Anybody
having these issues, too? And/or ideas, how to resolve it? (I didn't
check whether this is also an issue with windows, yet...)
4 years ago
Does anyone else know how to get rid of the repeat of last pushed button when pushing new button?
Quite annoying when I paus playback and then turn off the TV, the receiver repeats my paus command and turns on the video/netflix again (among other problems).
Any hints would be greatly appreciated.
Thanks!
(can't get Juan Antonio's code to work properly)
Reply 3 years ago
Did you find a solution to this? It makes the device quite unusable when trying to control Kodi.
Reply 3 years ago
No, unfortunately I did not.
What I did was that I turned off repeat, as explained here in the comments somewhere, then I mapped channel up/channel down to page up/page down. It makes it quite easy to navigate without button repeat, I don't really miss it anymore.
Reply 3 years ago
No probs. My button mapping is the same but it hasn't resolved the problem for me
4 years ago
Hi,. Great little project, many tx for sharing..... but one question I have is how can I produce the 'Ctrl, Alt, Left/Right Arrow',.. to mimic the Ubuntu screen switch.. within/from the code..
I have tried the following,.. but this does not generate the expected result:-
} else if (Data_back == ScreenRt) {
TrinketHidCombo.pressKey(0, KEYCODE_LEFT_CONTROL, KEYCODE_LEFT_ALT, KEYCODE_ARROW_RIGHT);
TrinketHidCombo.pressKey(0, 0);
thankyou...
Reply 3 years ago
I know it's been 10 months but...
Have you tried with KEYCODE_MOD_LEFT_CONTROL and so on(add MOD)?
Otherwise open the Trinkethidcombo folder in your libraries and open Trinkethidcombo.h in a text editor, like Notepad. Here you can see all the possible usable combinations and some hints/explanations.
4 years ago
Thx for this project, not everybody are into Arduino world, but this project is explained well anough, to reproduce it
I have be using Remote app on my phone for very long and sometime it is annoying when you watching a movie because the screen off the phone must be always On, when you want to pause, or increase/decrease the volume for example, and I wont say about battery consuption!
So for all those who want to make this project, go for a simple remote, like the kind with the RGB strips, or any chinese low cost remote.
Personnaly I went with the small remote
4 years ago
So far I've used two remotes to program with 1 little generic pc remote (i think it was a hdtv remote) did have a problem with remote code 0 was on/off button and the pc went to sleep without pressing a button after a while, weird.
second harman brand remote press buttons too sensitive, how to stop that?
and I've tried to get response from a foxtel remote but no dice so far.
4 years ago
I just posted a case for this on thingaverse. https://www.thingiverse.com/thing:3529064
4 years ago
after getting the codes, I want to make a sender for other usage.
Any ideas? help