Background Infrared Receiver for 32-bit Codes


Senior Member

This software, written for the 20x2 PICAXE, is for an Infrared code receiver that runs in the background. It is designed to work with the 32-bit NEC IR format, which is used by most domestic IR remote controls. During testing, the code has performed reliably with a number of different IR remote controls including: Topfield, LG, Grundig and Akai.

Using the edge-triggered hardware interrupts of the 20x2 allows the IR code reception to run in the background, causing minimal interference with the main tasks of the user’s PICAXE code. Once enabled, the receiver software captures and stores the IR code in background processing, setting flags to indicate the status of the IR signal reception. A timer supervises the IR code reception to ensure that incomplete data packets are rejected.

For a description of the NEC IR protocol, refer to my following post.

Hardware and Firmware

The IR Receiver module should be wired in accordance with the circuit described in Manual 2, IrIn command except it is limited to pins that can handle hardware interrupts. In the case of the 20x2, this is B.0 (hInt1) and B.1 (hInt2). The code has been tested on hardware interrupt 2 (B.1). Firmware version C.1 or higher is required.

Features and Limitations

The 20x2 must run at 64MHz to process the incoming IR data. The length of the IR zero bits (1 to 1.2mS) restricts what code can be included in the interrupt routine. An IR status byte can be accessed at any time to determine the status of reception. During code reception, any instructions in the main code that last longer than normal should be avoided (eg serial, shift-in and –out etc). User code can test the tInfraBusy bit to determine whether to run long commands or to wait.

Timer interrupts are available for use but your code must allow for the timer being reset on reception of any IR signal.

The servo command uses timer 1 so is incompatible. Any features that use internal interrupts (refer Manual 2 Appendix 4) may interfere with IR reception.

The ‘repeat’ feature of the NEC IR format is not supported.


The sample code posted here is a working demonstration of background 32-bit IR reception. It simply displays the received IR code in the PE terminal window in binary and hexadecimal. The code should work if simply pasted into the PE and downloaded into a 20x2 connected to an IR receiver module as described.

The IR receiver uses hardware interrupts to determine the elapsed time of each bit. Initially triggered on the falling edge of the header, the reception process commences. At this point, several variables and the timer are initialised, with the major tick set to about 140uS while the timer rollover interrupt is set to 115mS.

The next hardware interrupt occurs on the rising edge of the header, preparing for data reception. The data bits are read on the falling edge of each bit’s IR signal. As each hardware interrupt occurs, a copy of the major tick timer value is taken and the elapsed time, since the last interrupt, is stored in a byte of scratchpad memory. The incoming bit times are stored in the last 34 bytes of the scratchpad memory, the initial 2 bytes for the header part 1 and 2 signature times and the remaining 32 for the 32 bits of data. When the scratchpad pointer rolls over back to zero, 32 bits have been received, the timer is disabled and other IR status bits configured. If 32 bits are not received before the timer rollover interrupt occurs, the whole process is reset and re-enabled for reception again.

The lower part of the scratchpad can be used for other tasks but must be managed to prevent overwriting.

To maintain the short processing times required, 3 word (or 6 byte) variables are reserved exclusively of IR reception. I have selected the highest 3 words for ease of management. A single byte variable within the range b0 to b3 is required to manage the IR status within the interrupt routine. This is poked to RAM when not required and can be accessed from the user code.

I am not a fan of the GoTo command; however it is required in the interrupt routine to reduce handling time of each zero bit. As a principle, interrupt routines should be kept as brief and simple as possible. Believe it or not, for the task, this routine is as simple as possible.

Adapting the code for your own requirements

When used in a normal situation, the receiver would check the device ID and accept or reject the signal. An additional check can be made on the values stored in the first 2 buffer bytes: the header signature values can be compared with expected values, although this is not normally required. For remote codes that support the command integrity check, you can perform an ‘Or’ function on the two command bytes (received Bytes 3 and 4). The result should always be $FF.

The attached screen shots show the activity on the oSynch and oTimer pins, useful in understanding and debugging the code.

Thanks to hippy for your assistance in configuring the hardware interrupts.



Senior Member
32-bit NEC-Format Infrared Signals 101

The diagram below shows a typical IR signal. For simplicity, only 8 bits of data are drawn. The 32-bit version just has more data bits. Note that there are no pauses or breaks in the pulse train e.g. between bytes.

'        |<---- Header ---->|<------- Data (8-bit example) ------>| |--- Stop Bit
'________|           _______|  _   ___   _   _   ___   ___   _   _   ___________
' ^      |__________|   ^   |_| |_|   |_| |_| |_|   |_|   |_| |_| |_|^         
' Idle   | Hdr Pt 1 |Hdr Pt2|  0    1    0   0    1     1    0   0   Idle (no carrier)
The waveform is drawn as if seen on the output of the IR receiver module: the output is open collector with an external pull up resistor. When a 38kHz carrier is detected, the output is pulled low by the IR receiver module and returns to the high condition when the carrier is removed.


The Header is a single burst of carrier (typically 4 to 8mS), followed by an "off"; period of between 2 and 8mS, called "Header part 2" in the diagram. The timings will vary between manufacturers and appliances but are consistent for each model.

Data Format

IR data consists of a series of 32 + 1 equal length carrier pulses. The 0 and 1 bits are determined by the "off" period between the carrier pulses. A "zero" is typically a 500 to 600uS pulse of carrier followed by an equal period of no carrier. A "one" is typically a 500 to 600uS pulse of carrier followed by a 1500 to1800us (1.5 to 1.8mS) period of no carrier. The final carrier pulse or stop bit is a "lead-out" to indicate the end of the data pulse train and delineates whether the final bit is a zero or one.

Repeat Indicator

If a button on the IR remote control sender is held down, a "repeat" signal is transmitted at regular intervals until the button is released. This can be seen on the oscilloscope images of the actual waveforms. The software described does not support the "repeat" data bursts.

Understanding the code: Device ID and Command Signals

The 32 bits of data equate to 4 bytes. The 4 bytes break down to a 2-byte device ID followed by a 2-byte command. This potentially allows for 65,536 device models, each with 65,536 commands. Having so many device models allows users to have multiple IR receivers Eg TV, PVR, DVD, VCR, etc, each able to accept their own commands and reject anything else. Additional discrimination of device model id can be done by comparing the header low and high periods with expected times.

It would be a rare device that needed 65,536 different commands! Many manufacturers allow for up to 256 commands using the first command byte, Byte3. In these cases, the second command byte (Byte4) is an inversion of Byte3, allowing the receiver to do a simple validity check: Ie. The equation Byte3 Or Byte4 should equal $FF.

Edit: Fix up quote characters, corrupted when new forum software was implemented.


Last edited:


Senior Member
Newer 28x2 and 40x2 as well

With the introduction of the "universal" 28x2 and 40x2, it should be possible for this code to work well on these chips. By "universal" I mean the 3- to 5-volt chips, which can run at 64MHz. However, I haven't tested the code on these chips.

The 28x2 or 40x2 will need a 16MHz resonator with the internal x4 PLL/multiplier and command SetFreq em64, resulting in 64MHz operation.



thanks for posting that data. Good to know it is available should the need arise thus obviating the need to reinvent the wheel or code in this case.


New Member
Forgive my posting to such an old thread but you might find the NEC datasheet which describes the IR protocol helpful.

X10 uses it with a slightly different timing for it's RF protocol.


New Member
NEC Codes for Air Conditioner on 18M2+


Thank you for the content of this thread, especially the link to the NEC format document.

I have a "Dimplex" portable air conditioner and my first project is to create a PicAxe thermostat for it. The existing A/C turns off the compressor when the temperature is below a specific temperature, but leaves the loud fan on. What's more I can't use a standard timer because the unit doesn't turn on when power is turned off then on. So I'm using Pickaxe to measure the temperature and turn the A/C on and off via remote where needed (including a Real Time Clock so that it isn't turning on at 2 am).

This sounded like a nice and simple first project until I found out that Dimplex (?? maybe a no-name brand - but available in Australia) didn't use Sony codes.

So I did multiple pulsin commands at m32 frequency in order to work out all the timings, which was no easy task in itself given that there are 32 bits as well as a header and stop bit, then lag to the next header - fitting that in to available words isn't easy and a single serial out means you lose a bunch of bits. I had to display it in blocks of 13 at a time (with a couple of pokes and peeks in order to output via BinToAscii because again, that was using up all variables).

Anyway, after finding out those timings I still had to find the carrier details which I found here. I knew NEC was the right code as the all the timings matched up almost exactly (i.e. header high and low times and all the bit 0 and bit 1 times).

Had to hack at it all day but I finally got the air conditioner to turn on and off using only a 32 mHz 18m2+. The key to getting this to work was not using any gosub, or ANY code that adds to any processing time as just that little too much of a pause causes time for the IR to be out and the IR won't work. So I had to code without any goto/gosubs between each call. I also had to use the pickaxe wizard to enter a 33% up time for a 38 kHz carrier on 32 mHz frequency. I believe that it only just works on 32 mHz, the "up" time of a bit is a pause of 5, with the down time of a zero bit a pause of 4 and the pause time of a 1 bit of 13. These are very short pauses.

But as I said, this works well. I would suggest that any NEC format SHOULD work on a 32 mHz chip as long as it is contained in the mainline code (i.e. don't add anything between each pwmout and pause pairing, not so much as an "If"). I'll play around with the code a little further and then post it here when I'm done.

What a great "simple" first project.


Hello pleitch,

Thanks for letting us all know what you have achieved. Sounds like a great project.

Why not post your code and a schematic for your project for the future reference by others.

Note that those chips are called PICAXE not pickaxe


New Member
Will do. I'm putting the finishing touches on another project first, a battery tester.

I'll finish that off this week and post it in the projects section (and enter the competition).

After that I'll come back to this this project and finish it off. It's still winter, but it's starting to warm up so I'm going to have to get this done fairly soon.


Senior Member
But as I said, this works well. I would suggest that any NEC format SHOULD work on a 32 mHz chip as long as it is contained in the mainline code (i.e. don't add anything between each pwmout and pause pairing, not so much as an "If"). I'll play around with the code a little further and then post it here when I'm done.
I'm glad you had success with your decoding of the 32-bit signals. There are many ways to do this. My first solution, using a CD4093 schmitt trigger chip to "condition" and split the code into header, clock and data lines, was published in the Silicon Chip magazine a few years ago. This model worked well with any PICAXE that was capable of 8MHz - I still use the circuit in my home theatre controller. However, it needed an extra chip and the PICAXE's receive code had to run in the foreground.

The code-only solution published above is intended to run in the background, allowing the power of the PICAXE to continue at all times, with code reception occurring with interrupts. It allows the PICAXE to do many tasks without being concerned that it would miss an IR signal.


New Member
I don't really understand your code, as it's a bit beyond my ability. I have your program running on a picaxe 20X2 and it returns a code &#8220; $E0E0 40BF &#8220; for the power button on my remote. How do I get that into my program to turn a relay on.


Senior Member
I don't visit these pages very often: over three years have passed since I created this thread. I have only just noticed your query.

I have written some code and done a syntax check. However, I don't have a spare 20X2 to test the code in at the moment, so you'll have to do that. The following code appears in three 'chunks' and these need to be placed as indicated, in the original code.

Add the following code in the definitions (Symbols) area:
Symbol wDevID     = w3
Symbol bDevID.Lo  = b6
Symbol bDevID.Hi  = b7
Symbol bIRCommand = b8
Symbol bIRInverse = b9
Symbol bIRCheck   = b10
Symbol oRelay     = B.4                 'Choose your own pin that will operate the relay
Symbol cDeviceID  = $E0E0               'Must match this ID with the chosen IR Remote Control
Symbol cIROnOff   = $40                 '$40 is the On/Off button of the remote
The next piece of code should be added near the end of the initialisation routine, before the "Do" command of the main loop
      Output oRelay                     'Initialise the relay pin as output
      Low oRelay                        'Ensure relay is not operated (default state)
The main code should replace the code between the "If tInfrComplete = True Then" and its matching "EndIf" statement.
         If tInfrComplete = True Then
            ' ++++ Action any valid IR Code +++++++++++++++++
            SetFreq m32                                'SerTxd is 38,400 bits/sec - Only required if using SerTxd
            bInfReadPtr = rInfraDevID1                 'Initialise the source pointer to the IR buffer, byte 1
            Peek bInfReadPtr, bDevID.Lo, bDevID.Hi, bIRCommand, bIRInverse 'Fetch the 4 IR bytes
            If wDevID = cDeviceID Then                 'Is the IR from the correct IR Remote?
               'Yes, the correct Device ID has been received
               bIRCheck = bIRCommand Or bIRCheck
               If bIRCheck = $FF Then                  'All bits should be set for a valid command: $FF = 255 = %11111111
                  'A valid command has been received
                  Select Case bIRCommand               'Analyse the command byte
                  Case cIROnOff                        'On-Off Button was pressed
                     Toggle oRelay                     'Invert the state of the relay driver pin
                  'Case                                'Another button	
            SetFreq m64                                'SerTxd is 76,800 bits/sec: too fast for Serial Port
            ' ++++ End Action any valid IR Code +++++++++++++
            'Reinitialise software Flags & Hardware interrupts for Infra Reception
            GoSub EnabInfra
Last edited:


New member
Any way this could be adapted to an 08m2? I know the 08m2 doesn't have hardware interrupt pins, so it can't be configured to interrupt on a rising or falling edge. But the 08m2 does support interrupts.


New member
Any way this could be adapted to an 08m2? I know the 08m2 doesn't have hardware interrupt pins, so it can't be configured to interrupt on a rising or falling edge. But the 08m2 does support interrupts.
Nevermind, the 08M2 can't support the m64 setfreq so it's not fast enough to handle the IR