Download PDF Parts List View on YouTube Download Code

After five years, it’s time we took another look at using infrared remote controls with microcontrollers. We’ll work with some updated libraries and see what we can do to decode those “stubborn” air conditioner remote controls.  We’ll also take a closer look at the hardware used to exchange IR signals. And, of course, we’ll build many fun IR remote projects!


It’s a safe bet that you own at least one infrared remote control. In fact, it’s likely that you own several, perhaps so many that you would have trouble accounting for them all. Once exclusively used for televisions, remote controls are now included with all varieties of electronic gear.

IR Remote controls offer numerous possibilities for experimenters and microcontroller enthusiasts. We can use microcontrollers to emulate remote controls, placing a wide variety of devices under the control of our programs. We can also get our micros to respond to IR remote controls, providing a convenient way to control our projects.

Microcontrollers also open up several advanced capabilities, such as converting remote control codes from one manufacturer to another or playing back sequences of remote control codes.

Today, we will see how these little gems work and how we can use them in our projects.

IR Remote Controls

Televisions, air conditioners, sound systems, electronic blinds, humidifiers – all of these appliances come equipped with infrared remote controls.  Usually, they play nice together, although occasionally, we get into a conflict, and turning up the sound on the TV also changes the humidifier settings.

Let’s learn more about these devices to see if we can understand how they work, how we can use them in our projects, and why we have so many of these critters on our coffee table!

History of Remote Controls

It’s hard to imagine life without remote controls, but believe it or not, there was once a time when you actually had to get out of your chair to change channels or adjust the sound on the TV!

The remote control has a rich history that spans several decades. The first television remote control, named “Lazy Bones,” was developed by Zenith Radio Corporation in the late 1940s. However, it was connected to the TV with a wire, and at 30 dollars (387 dollars in 2023), it was an expensive accessory.


In 1955, Zenith’s Eugene Polley developed the first wireless remote control called the “Flashmatic.” It worked by directing a visible light beam toward photocells on the television set. As you might suspect, this design was prone to interference from other light sources, so it wasn’t very successful.

Just one year later, Robert Adler, also from Zenith, designed the “Zenith Space Command.” This was a true precursor to the modern remote. It was ultrasonic, meaning it emitted high-frequency sounds to control TV functions. When a button was pressed, it would strike a bar and produce a sound, which the television would then interpret as a command. This design eliminated the problems associated with ambient light interference.

Ultrasonic remotes were the dominant form of television remote controls for nearly two decades.  They were sometimes difficult to use; they emitted “chirping” sounds and were known to bother dogs and cats. But, on the plus side, they didn’t require any batteries!

By the late 1970s, the shift from ultrasonic to infrared technology had begun. IR remote controls became the standard, primarily because they were cheaper to produce, had a broader range of functionality, and avoided the audible sounds of ultrasonic remotes.

As consumer electronics proliferated, so did the applications of IR remotes. They started being used for VCRs, DVD players, stereos, and eventually, air conditioners, cameras, and more.  These days, they are everywhere.

Infrared Remote Control Basics

In its most basic form, an IR remote control sends a pulsed beam of infrared light, which contains the key code for the button you pressed. The receiver extracts the code and performs the desired operation.

Infrared Light

The light these devices use is in the “infrared” range, meaning the range of light below red in the visible spectrum.

Light can be measured by its wavelength, and most infrared remotes operate in wavelengths ranging from 850 to 960 nanometers.  By far, the most common wavelength is 940nm.

Carrier Frequency

The pulses of infrared light are also modulated with a carrier wave, just like a radio signal. This technique allows the receiver to distinguish the remote signal from the infrared background noise, which can sometimes be quite high.

Carrier frequencies can vary between 30 and 60 kilohertz, but the most common carrier frequency is 38 kHz.

Modulation Techniques

All remote control data is sent in a serial format. The data is encoded using a number of different modulation techniques. These different techniques determine how the zeros and ones are formatted for transmission over the infrared light beam.

There are three basic modulation techniques used, along with several variations of them.

  • Pulse Density Modulation (PDM) – Also called Space Encoding or Pulse Distance Modulation.
  • Pulse Width Modulation (PWM) – Also called Pulse Encoding.
  • Bi-Phase Modulation – Also called Manchester Encoding.

The following illustrations show how the data is formatted using these three modulation techniques:



IR Remote Protocols

Consider those remotes that are taking over your coffee table. Each of them has numerous functions, yet they seldom interfere with one another. This may be due to different IR wavelengths or carrier frequencies, but 940nm and 38kHz are pretty well the standard. It’s likely every remote you own uses these standards.

So, how do they keep from interfering? One way is by using different protocols.

IR remote protocols define all the parameters for exchanging IR data. Some of these parameters include:

  • The modulation technique used.
  • The timing and format of the start bit.
  • The number of address and data bits.
  • The format of any error checking used.
  • The format of the EOM (End Of Message) or stop bit(s).
  • Whether the data is LSB (Least Significant Bit) or MSB (Most Significant Bit) first.

There are many different protocols in use, common ones as well as propriety ones.  The chart below shows only a small sampling of protocols in common use; there are actually hundreds of them.

NEC IR Remote Protocol

The most popular infrared protocol is the NEC protocol. Hundreds of manufacturers use it, and it is easy to work with as it is extensively documented.  It’s likely used on most of the remotes you own, except for the “big brand name” ones like Sony, LG, Panasonic, Apple, and Samsung, who use their propriety protocols.

This chart illustrates the structure of the NEC IR protocol, as well as a few example commands. Note that the packet begins with a 9ms leading pulse burst followed by a 4.5ms space.

This is followed by 32 bits of address and command data. Note that both the address and command are 8 bits, and for each, a logical inversion is also sent. The logical inversion serves as a form of error checking.

For our purposes, the only data we need to work with are the Address and Command values. This is true for other protocols, as well as the NEC one.

In this illustration, we can see that using a different address allows two remotes that use the NEC protocol to coexist in the same room without interfering with one another. Although both devices use code 0x0A for power, they use different addresses.

IR Remote Control Components

Working with infrared light remote controls requires special components. These are inexpensive and easy to obtain.

IR Receiver Modules

The IR Receiver module packs a lot of functionality into a small package, as shown below:

These modules are available in different IR wavelengths, with 940nm being the most popular.  The wavelength is usually keyed into the part number, as shown here for the popular Vishay TSOP34xx series.

TL1838 Sensors

The TL1838 and many other sensors with “1838” in the part number are probably the most common IR sensors available to hobbyists. These devices have been around for decades, and while they certainly work, they lack a lot of the functionality that more modern sensors have.

In addition, the TL1838 is meant for 5-volt operation, so it is difficult to use with modern 3.3-volt microcontrollers.

Now, if you pick up one of those packages on Amazon or eBay with a handful of IR emitter diodes and unmarked IR sensors, you likely just picked up a bunch of TL1838 sensors.  And while you can certainly use them in your experiments, I would not recommend them for new designs.

IR Emitters

An IR Emitter is essentially an LED that emits infrared light. As with infrared receivers, these are available in different wavelengths.  

The illustration below shows the part numbers for the OSRAM SFH 45xx series of infrared emitters, with the popular 940nm model highlighted.

Driving IR Emitters

Infrared emitters can consume much more current than a standard visible LED, so driving them directly from a microcontroller’s GPIO pin isn’t practical.  Many of these devices can consume 100ma or more, with some devices allowing up to 1 ampere when pulsed.

In this illustration, a bipolar transistor is used to switch the IR emitter LED. As bipolar transistors are current-driven, a limiting resistor is used on the connection from the GPIO pin to the base of the transistor.

There is also a current limiting resistor for the IR LED. This is usually a low-value resistor; sometimes, a resistor is not even necessary.

You can also use a transistor to switch multiple IR emitters for increased range and performance.

Arduino Hookup

Here’s how we will hook up our Arduino Uno, IR emitter diode, and IR sensor. We will also add three pushbutton switches and a visible LED as well.


Here are a few things to note about this hookup:

  • The transistor used to drive the IR emitter is a 2N2222 NPN. You could substitute another NPN transistor if you don’t have a 2N2222; make sure you check out the pinout of any substitutions, as they may not match the 2N2222.
  • The 2.2uf capacitor is a power supply filter capacitor for the IR receiver module. It should be placed near the sensor’s power connection on your breadboard. The value is not critical, and any electrolytic capacitor from 1uf to 10uf can be substituted.
  • The 33-ohm dropping resistor for the IR emitter may need adjustment for different IR emitter diodes. In some cases, it may not even be necessary.

Of course, another thing you’ll need, aside from the Arduino hookup and your computer, is a few remote controls to experiment with. You’ll need to round a few up; the more, the merrier!

Updated Arduino-IRremote Library

The Arduino-IRremote Library is the most popular library for using IR remote controls with microcontrollers. It is constantly being updated, and it was one of those previous updates that made most of the code in the older article obsolete, as the library is now called using another method.

We will install the library in our Arduino IDE and then perform a number of experiments using it. 

The easiest way of adding the Arduino-IRremote Library to the Arduino IDE is to install it directly from the library manager.  

Once the library is installed, you can access several useful and interesting example sketches.

Arduino-IRremote Library Example Code

There are no less than 25 example sketches included with the Arduino-IRreamote Library. Each one is well documented, and the Arduino we just set up is hooked up using the default pins for most of the examples.

The exception is Pin D5, which is referred to as the APPLICATION_PIN in the examples. It is unused in many examples, but in some examples, it is an output, whereas in others, it is a switch input.  Currently, I have it hooked up to an LED. I used pin D5 as it is also PWM capable, a feature that we will need later on. It can also be used with examples like the ControlRelay example instead of the output relay module.

PinDefinitionsAndMore File

Almost all of the 25 IR remote examples use a file named PinDefinitionsAndMore.h.  As its rather long name would imply, this file contains pin definitions for a variety of microcontrollers, as well as the definitions of some common constants.

The file starts with a list of common microcontrollers and the default pins used. By wiring your microcontroller as per the list, you’ll make coding simpler. You can, of course, choose to ignore the default wiring and change the pin definitions in the code, but it’s a lot simpler just to follow the list.

Many of the constants we will encounter in the example sketches that follow are contained in the PinDefinitionsAndMore file.

SimpleReceiver Example Code

The SimpleReceiver is the first example sketch that we will run, and I’m pretty sure you can guess what it does!  You’ll find it, and all of the other example sketches, in the File/Examples/Examples from Custom Libraries/IRremote folder.

The sketch starts with definitions for many different remote protocols, with most of them remarked out (i.e., inactive). Only the NEC protocol is enabled by default.

We include both the library and the PinDefinitionsAndMore file, and then move into the Setup, as the pin definitions file has already defined all the constants we need.

In Setup, we start the serial monitor. After that, this sketch (and most of the others) does a print of the current IR Remote version.

We then enable the IR Receiver with this line:

The IR_RECEIVE_PIN is defined as 2 by the pin definitions file and is correct for our Arduino hookup. 

The ENABLE_LED_FEEDBACK is a boolean that is set to true. When enabled, the onboard LED will flicker whenever data is received.

Now, on to the Loop:

We start by looking for a decoded output, which will occur whenever a valid IR packet is received.

Once we receive the packet, we print out its contents to the serial monitor. This will include the address, command value, and protocol type. Note that in its default configuration, it will only decode NEC packets; you need to enable additional ones as you require them.

An important thing to note is that the receiver stops receiving after getting a valid packet. You will need to start it again with the following command:

The example also includes a framework for performing an action when a specific command value is received. You can add code here later if you wish.

Running SimpleReceiver

Load the sketch to the Arduino and open the serial monitor. Now aim a remote control at the receiver module and press a button on it.

If it was an NEC protocol remote (the most common), then you will get information regarding the protocol, address, and protocol on the serial monitor.

If it was another protocol, however, you will just get some raw data and a message saying, “Received noise or an unknown (or not yet enabled) protocol”.

Assuming that you know what the protocol should be, you can un-remark its line at the beginning of the sketch. In the video accompanying this article, I show examples of enabling it to recognize Sony and Panasonic remotes.

SimpleSender Example Code

Now we can move on to the SimpleSender sketch, and I’ll bet that you can guess what it does! It’s essentially the sending counterpart of the previous sketch.

The sketch will use the NEC protocol and send out command, address, and repeat codes. It will step through these, so if you leave it in a room with some remote-controlled devices, you may eventually affect one of them!

This sketch contains some constants that must be defined before the library is called. One of them, DISABLE_CODE_FOR_RECEIVER, is enabled. It disables the receiver function, restarting after each send, saving about 450 bytes of memory.

In Setup, we define the built-in LED as an output, which may seem odd but is due to its possible use later on in the code. It can be set to flash whenever an IR signal is sent, providing a visual indication of operation.

We start the IR sender with the following line:

Note that LED feedback is disabled; you can enable it if you wish to see the effect on the built-in LED.

We also define a few variables after Setup, a bit of an odd location, but perfectly valid. These are the “seed” values for the incrementing IR codes and repeat values.

In the Loop, we print out the current values, then send them within this command:

The first parameter is the Address, which is always 00 in this example. Feel free to change it.

After sending, we alter the values, delay a second, and do it again.

Load the code up to your Arduino and observe the serial monitor. You can also run a second Arduino, as I did, with SimpleReceiver. That will allow you to monitor the results.

IRMP Library

The IRMP, or Infrared Multi Protocol Decoder + Encoder Library, is an alternative to the Arduino-IRremote library.

This library has a lower memory footprint than the other library, and it supports 50 different protocols. The Arduino-IRremote library only supports 17 protocols as of this writing.

You can install the IRMP Library using the Library Manager in the Arduino IDE.  Once it is installed, it will also install several examples; most of these are reworked versions of the Arduino-IRremote library examples. This is great as you can use them to see the differences between the two libraries.

The wiring we hooked up on our Arduino will also work with the IRMP library examples.

AllProtocols LCD Hookup

We are going to run an example that is actually in both libraries, the AllProtocols example. This example reads IR protocols, along with address and command values, and displays them on a two-line LCD.

The example uses a 1602 LCD module, along with an Arduino. You’ll also need a 10k pot to use as a contrast control for the LCD.

Here is the pinout of a standard 1602 display module. Note that your module may have slightly different pin designations for power and ground.

There are three ways you can interface a microcontroller to a 1602 LCD module:

  • Parallel data, 8-bits
  • Parallel data, 4-bits
  • Serial data, I2C adapter (requires additional hardware)

Although the I2C adapter uses the least wiring, we will be hooking up our display in parallel. We will be using 4-bit mode to reduce the number of connections.  This method is preferred in the AllProtocols example; a serial hookup would be missing some data (according to the notes in the sketch).

Here is how we will be hooking up our 1602 display. You could also use a 1604 display; the pinout is identical.

AllProtocols Code & Demo

The AllProtocols sketch is one of the example sketches that was installed when you added the IRMP library to your Arduino IDE.

As with the examples centered around the Arduino-IRremote library, this code (and several of the other IRMP examples) has a pin definition file as well.

A lot of the code is centered around the LCD module.  Around line 60, you will see instructions for changing to a serial (I2C) LCD or not using an LCD and just using the serial monitor.

In line 70, you’ll find a definition you can change to use a 4-line LCD module, the 2004 (20 x 4 display).

In Setup, look for the following lines:

These lines illustrate how to use the IRMP library; it differs from the method used for the Arduino-IRremote library.

A function, printIRResultOnLCD, handles all the LCD formatting.

Load the code onto the Arduino, get out a remote, and try it out. You will likely have to adjust the contrast potentiometer for the best LCD visibility.

This is a handy tool to have around when coding for remote controls.

Emulating Transmitters and Receivers

By now, you should have a pretty good handle on using both libraries with IR receivers and emitters. So we can get to work and design or own remote-controlled devices.

To illustrate this, I have decided to “clone” (or perhaps “emulate” is a better word) an IR-controlled LED lamp. It’s a simple device with a simple IR remote control that uses NEC command codes.  The remote has buttons for power, plus controls to increase or decrease the LED lamp brightness. It also has some timer buttons, but I will ignore them for this experiment.

We will use an Arduino to emulate the lamp first, controlling the brightness and status of the “user LED” we wired to pin D5.

After that, we will use the three push buttons to emulate the transmitter.

Between the two projects, you should have enough information and sample code to design your own custom remote-controller devices with an Arduino or other microcontroller.  No additional wiring is needed, as we have everything we need on the existing Arduino setup.

Receiver Code

We’ll start with the receiver. We will receive the IR commands from the existing remote control and use them to control our “user LED.”  It should operate in a similar fashion to our LED lamp.

We’ll use PWM (Pulse Width Modulation) to control the LED brightness. On the Arduino Uno, six of the I/O pins are PWM-capable, including pin D5, where our User LED is attached.  The brightness will be set in increments, and you can adjust the value of these increments to suit your personal taste.

We begin by defining the protocol(s) we wish to use. As my lamp is an NEC protocol device, this is what I have defined. If you are building an IR control from scratch, the NEC protocol is a good choice.  Just be sure to set the address to a unique value to avoid conflicts with other IR remotes in the vicinity.

If you need to define a different protocol, see the SimpleReceiver example for a list of them.

We then include the library and define the pins for both the IR receiver and the User LED. After that, we define a boolean for the lamp power state and an integer for the level. These are both set volatile, as they are used in a callback function.

The next three lines are the codes for our buttons. If you are emulating another remote control, you will want to change these to match your control.

Next, we come to the Receive Callback function, which is called every time valid IR data is received.

In the callback function, we grab the button code and check it against the three code values we have defined.

If it matches the power key, then we toggle the boolean that represents the lamp state.

If it matches an up or down key, we increment or decrease the value of the LED PWM by 8 (you can change that if you like).  We ensure that it does not go above 255 or below 8. I picked eight instead of zero, as I didn’t want the lamp to completely dim. This is the way the lamp we are emulating works.

In Setup, we start the serial monitor, define our LED as an output and turn it off, start the IR receiver, and attach the callback to the function we just described.

In the Loop, we see if there is new data. If it is, we examine the variables for both the LED status (on or off) and LED brightness.  We use these to control the LED.

Note that we only change brightness when the LED status is “on”; if it is off, then the controls are recognized but ignored.

Load the sketch and get out your remote. If you coded the values correctly, you should be able to control the status and brightness of the LED.

Also note that if you turn the LED off, it retains its brightness level.

Transmitter Code

Now let’s take a look at the code we will use for our transmitter, or “remote control”.  We’ll use our three push buttons to control the power and brightness of our LED lamp. You can also use it to control the previous experiment if you have a second Arduino.

There are a couple of interesting things about this sketch:

  • It uses the TinySender library. This small library is included with the Arduino-IRremote library.  It has a smaller footprint, but not as many features.
  • It uses pin change interrupts to detect pushbutton presses. 

We begin by including the Arduino and TinySender libraries. 

Next, we define the pins for the IR emitter LED and for the three push buttons. After that are the button code variables; as with the receiver sketch, you can change these if you wish.

The next three variables hold the IR data values that we will be sending. Pay note to the first one, the address. I am using hexadecimal 0x00, as this is what my remote lamp uses, but if you are emulating a different device, you will need to change this. 

There is also a boolean that is set when a button has been pressed.

After defining the values, we move into the pin-change interrupt service routine. This will be called whenever one of the buttons in our pin-change group is called. There are three groups, and all of our push buttons are in group (or port) B , so any activity on any group B button triggers this interrupt.

In the interrupt service routine, we scan to see which button was pressed. We then set the value of the command variable to match the button, and we set the buttonPresed flag to true.

In Setup, we activate the Serial Monitor and define the buttons as inputs with internal pull-up resistors. We also define the built-in LED as an output.

After that, we set up pin-change interrupt masks. This enables the three pins we are using for our buttons. If you want to add buttons, you must change the mask. It’s a simple binary pattern; look at it, and you will see the three I/O pins we are using.

In the Loop, we see if a button was pressed by checking the buttonPressed boolean. If it has, then we send the IR code using the current command value.

We add a few short delays to debounce and then look for a pressed button again.

Load the sketch and try it out! If you have coded the buttons with the correct values, you should be able to use them to control your IR device.

This can obviously be expanded upon to build bigger remotes.

Capturing IR Remote Codes

Up to this point, we have been decoding IR remote control signals and sending them with a known protocol. But what do you do if you don’t know the protocol, or if you don’t have a setting for it in your library?

Instead of decoding the IR signal, another option is to capture it and save the raw data. This data can then be used to resend the IR signal or to recognize it again.

There are a couple of examples included with the IRremote library that illustrate how to capture and work with raw IR data.

Both of the following examples perform the same function; however, they use different techniques to achieve it.

The functionality is as follows:

  • The sketch sends and receives IR codes.
  • By default, it is in receive mode.
  • When an IR transmission is detected, it captures it and saves it. How it does this is different in each sketch.
  • After saving the transmission, you can use a pushbutton to send it again. You can keep using the button to send it.
  • If you are not sending data, then you are in receive mode.
  • If a new IR packet is captured, then it is now the one sent when the button is pressed.

ReceiveAndSend Code

In this sketch, we attempt to decode the incoming packet first, using known libraries. If we succeed, then we just store the protocol type, address, and command code for the button.

If we can’t determine what protocol the incoming data is, we store the raw data for retransmission.

The sketch starts like many of the other examples, with a list of protocols where only the NEC protocol is not remarked out.  

We then define a buffer whose size is dependent upon the memory available. This is determined using constants in the pin definitions file.

After including the library, we define the pin for our send push button. This is where we need to change the code to match our wiring.

Change the line

to read

This will have us use PB1 as our push button. 

We then build a data structure to store the recorded code. It has elements for the decoded IR data, as well as raw IR data, which we use for devices that we can’t decode.  We also define a boolean to indicate that the send button was active.

In Setup, we set the button pin to an input with an internal pull-up resistor, and we set up the serial monitor.  We start both the IR receiver and IR sender and print the status to the serial monitor.

In the Loop, we check to see if the button was pressed. If it was, then we stop receiving and send the stored code.

There is also a function named storeCode, which handles most of the receiver tasks. The function checks to see if it can decode the incoming data.  If it can’t decide the protocol, then it stores the timing entries as raw data.

Load the code to the Arduino. If you happen to still have the AllProtocols LCD demo assembled, it can be handy to test this with.  Otherwise, you can test with a remote and its target device.

Aim the remote control at the demo and press a button. You should observe the captured data on the serial monitor.  If it is a known protocol, you will see the decoded data; if it is unknown, then you’ll see some raw data and statistics.

Now, the data has been captured. Press button PB1. This should send the captured data out via the IR emitter LED.

This technique could be the basis of a number of advanced experiments.

ReceiveAndSendDistanceWidth Code

This sketch performs the same function as the previous one, but this time, we don’t bother trying to decode the signal. We also use a different method of capturing raw data, one that can work with many more controls.

The sketch is similar to the previous one in some respects, with one important consideration. Line 56 has a constant DECODE_DISTANCE_WIDTH. This puts the IR receiver into distance width protocol mode, which allows it to capture the distance between raw data pulses. It is important to put this line before the call to the IRremote library.

Once again, you need to change the button pin constant SEND_BUTTON_PIN. Change it from APPLICATION_PIN to 10.

The key to understanding this sketch is on lines 81-84. The three variables here are what we store every time we receive a valid IR packet, and it is what we transmit when the button is pressed.  We store an array of the data, a data structure with timing info, and an integer with the number of bits.

In the Setup, we do the usual serial monitor setup, as well as setting the pin mode of the pushbutton to an input with pull-up.  We also start the IR receiver and sender.

In the Loop, we check to see if the button was pressed. If it was, we send the packet using the IrSender.sendPulseDistanceWidthFromArray function.

Otherwise, we are in receive and store mode. We look for a valid packet, and when we find one, we extract its timing and data information and calculate the number of bits. Then, we store those values to be used for the next send.

Load the sketch to the Arduino and test it out. If you observe the serial monitor, you’ll see that every IR signal is treated as raw data.

I have some projects coming up in a future article and video that use this technique. It’s a great way to handle those remotes you can’t decode otherwise.

ESP32 Remote Control

We’ll finish everything up with a small project, a remote control based on the ESP32.

This remote will actually mimic the functionality of the 3-button remote we constructed with the Arduino, except we won’t have any physical buttons. Instead, we will use the WiFi capabilities of the ESP32 to have a web-based interface.  This will allow you to expand upon the design to create a custom remote with as many buttons as you desire.

Our ESP32 will be set up as a WiFi access point, so this design doesn’t need to connect to a WiFi network.

One feature we will implement in this design that was missing from the previous one is the repeat function when a button is held down. As we can’t implement it exactly, we will be “faking” it!

ESP32 Remote Control Hookup

The hookup for the ESP32 remote control is very simple. All we really need is an IR emitter LED and a method of driving it.

We can just drive the IR LED with the 2N2222 we used earlier; that will work fine. You may wish to drop the 1k resistor on the base to 680 ohms, but otherwise, the hookup is the same. We still use 5-volts for the LED, even though the ESP32 is a 3.3-volt microcontroller.

Another method of driving the infrared emitter LED is to use a FET (Field Effect Transistor). This has efficiency advantages and also eliminates the need for a resistor on the GPIO pin. This connection is illustrated here:

Here is how we will hook up our ESP32. Any ESP32 module will work for this design.  The TN0702 FET specified in the diagram is capable of 20 volts at about half an ampere, so it can be used with multiple emitter LEDs if you wish to experiment.

ESP32 Remote Control Code

Here is the code we will use for the ESP32 remote control. You can use it as the basis for designing your own remote.

Before you can use this code, you will need to install a couple of libraries, ones that are not in the Arduino IDE Library Manager.  You can get both of them on GitHub

Both of these libraries are required to allow asynchronous communications between the web client and the server. This is necessary, as we don’t want to have to refresh our web page every time we press a button.

We’ll include all of the required libraries and then set up the credentials for the access point that the ESP32 will create. You can change these to any value you want; the password should be at least eight characters. You can also use a null value if you don’t want a password.

Next, we start the asynchronous web server on port 80.

The next bit of the code might look familiar, as it’s essentially the same variables we used in the sketch with the tree pushbuttons. These define our IR emitter pin, the codes we are using, and some variables to hold the transmitted data values.  There is also a flag to indicate that a button is being held down.

The next constant is actually the entire HTML code for the web page, including CSS and JavaScript. It is easy to read and modify.

The JavaScript is the key to making things work. Each button looks for events caused by buttons being either clicked or pressed (you need both for web pages and phones). Those events call a JavaScript function (toggleCheckbox) that sends data to the server using an asynchronous method.

In Setup, we start the serial monitor and then set the ESP32 WiFi up as an Access Point. Once the access point is established, we print our server’s IP address to the serial monitor. Our client will need this to connect.

We then define how the server should respond to specific asynchronous transactions.  As most of the transactions are for buttons, we will use them to determine which button was pressed and if it is being held down.  We set the buttonDown boolean and command value according to the button and its state.

After defining the responses, we start the server.

In the Loop, we test to see if a button was pressed. If it was, we send the values for the address and command using sendNEC (check the documentation if you use another protocol).

We also delay and see if the button is still held down. If it is, we need to send a repeat code. 

As the TinyIRSender library does not have a function to only send a repeat code, we actually send a command again, followed by a repeat code. So, it is not a “true” repeat, but it accomplishes the same thing!

Load the code and give it a try. You will have to connect to the WiFi access point first with a phone, tablet, or computer. Afterward, go to the address in the serial monitor using a web browser.

The page does not have a security certificate, so if your browser gives you grief, try another one. Firefox accepts pages without certificates.

Once you are there, the page should display.

Try it out; it should duplicate the function of your remote control

Of course, you can extend this technique to have more buttons and functions.

ESP32 Receiver Hookup

One final hookup I will leave you with is for the ESP32 and an IR receiver. 

Unlike the Arduino, which operates at 5 volts, the ESP32 will require a more modern IR Sensor that can work with lower voltages.  The TSOP38438 we used earlier will work great, as will most 3.3-volt IR sensor modules.

As with the previous hookups, we are placing a 2.2uf capacitor near the IR sensor to reduce any power supply noise.

With this hookup, you can run the example code included with both libraries.


Interfacing a microcontroller with infrared remote controls and IR-controlled devices opens up dozens of project possibilities.  You could, for example, have some lights that are controlled by your television remote, coming on when the TV is switched on.

I have three more IR remote projects that I started working on while I was researching this article; you’ll be seeing them soon. And I’m sure you have several ideas for adapting this technology to your own needs.

So get out there and use invisible light beans to control your world. All you need is a microcontroller and a few IR devices, and you’re set to take command.

Just remember to get off the couch once in a while!


Parts List

Here are some components you might need to complete the experiments in this article. 

Vishay TSOP34438 DigiKey

Vishay TSOP38238 DigiKey

OSRAM SFH 4544 DigiKey



Code Samples – All of the code used in the article in one easy-to-use ZIP file!

Article PDF – A PDF version of this article in a ZIP file.

Arduino IRremote Library – Arduino IRremote Library on GitHub

IRMP Library – Infrared Multi-Protocol Decoder + Encoder Library on GitHub

ESPAsyncWebServer Library – ESPAsyncWebServer Library on GitHub

AsyncTCP Library – AsyncTCP Library on GitHub

Vishay TSOP34438 – IR Receiver Module Specs

Vishay TSOP38238 – IR Receiver Module Specs

TL1838 – IR Receiver Module Specs

OSRAM SFH 4544 – 940nm IR Emitter LED


IR Remotes Revisited – 2023
IR Remotes Revisited - 2023
Article Name
IR Remotes Revisited - 2023
Everything you need to know about using infrared remote controls with microcontrollers. Learn how to receive, decode and send IR signals, including ones from those hard-to-decode remotes.
Publisher Name
DroneBot Workshop
Publisher Logo
Tagged on:     
Notify of

Inline Feedbacks
View all comments
8 months ago

Another GREAT article! sIn the Infrared Light secstion you say Light can be measured by its wavelength, and most infrared remotes operate in wavelengths ranging from 850 to 960 nanometers. By far, the most common wavelength is 940nm.

Also in the Carrier Frequency the most common carrier frequency is 38 kHz.

Can you expand on both?
Is there a relationship between the two?

Personal note: You provide so many great videos. THANK YOU!

5 days ago
Reply to  Bryan Smith

I saw no one responded. Hope this might help. As frequency increases, wavelength decreases. Ever toss a stone in the water and see the waves? The closer each wave is to each other the frequency increases. In this case think of light in different colors. Like the rainbow. Infrared is beyond visible range, just beyond red. The carrier frequency is basically giving the light a variation in amount of power. So what you have is an infrared light that if visible would increase and decrease as a wave in itself, but always infrared. That is the carrier wave. From there… Read more »

3 months ago

Hi Bill.

Fantastic Video. Just a note you mentioned a 2N3906 transistor it should be a 2N3904. The 2N3906 in a PNP. Please disregard if you are already aware.

Kindest Regards

5 days ago

After dropping out of the ESP32 scene for a break I’m back. I had tried to create a simple mute button for the TV. With the idea of using like a big red button. We’re always fumbling to find the mute button all the time. I wanted one big one you couldn’t miss. lol However, all the code online seemed to work on older TVs, but not our Samsung. Even bought an IR Remote development board. Another bust. At that point I had spent more time than it was worth. This may make me give it a quick try again.