Simple Infrared PWM on Arduino

We are often asked on discussion boards, about conflicts between IRremote or IRLib and other Arduino Libraries. In this post, we present a sketch for ‘Simple Infrared PWM on Arduino’. This is the first part in a 3 part series of posts. Part 1 shows how to generate the Simple Infrared PWM on Arduino (AKA carrier frequency), using any available IO pin and without conflicting with other libraries. Part 2 will show how to send a RAW infrared signal using this approach and Part 3 will show how to send a common NEC signal from the binary or HEX value.

Example 56kHz generated Infrared signal @ 50% duty cycle
Example 56 kHz generated Infrared signal @ 50% duty cycle

Simple Infrared PWM on Arduino

Next we consider the example sketch, by section:

Definitions

#define Duty_Cycle 56 //in percent (10->50), usually 33 or 50
//TIP for true 50% use a value of 56, because of rounding errors
//TIP for true 40% use a value of 48, because of rounding errors
//TIP for true 33% use a value of 40, because of rounding errors

#define Carrier_Frequency 56000 //usually one of 38000, 40000, 36000, 56000, 33000, 30000

#define PERIOD (1000000+Carrier_Frequency/2)/Carrier_Frequency
#define HIGHTIME PERIOD*Duty_Cycle/100
#define LOWTIME PERIOD - HIGHTIME
#define txPinIR 8 //IR carrier output

First we configure the duty cycle for the Infrared carrier. Typically, values of 33% or 50% are used for IR, with 33% being popular in battery-powered systems. You will note our suggestions to use a value of 56 for 50% duty cycle and 40 for 33%, which is an artefact of rounding errors in the ‘#define’ statements. In simple terms set Duty_Cycle to 56 for a 50% duty cycle. Next we define the carrier frequency. Almost all IR systems us a carrier frequency of either 38 kHz, 40 kHz, 36 kHz, 56 kHz, 33 kHz or 30 kHz. In addition, Bang + Olufsen systems use a carrier frequency of 455 kHz. However, this is very rare in use and is outside the scope of this sketch. The PERIOD is a simple calculation based on the defined carrier frequency. Similarly, HIGHTIME and LOWTIME are calculated automatically based on the period and the duty cycle. AS we mentioned before, you can select any Arduino pin for the IR output signal, including all digital and analogue pins.

Setup

void setup() {
/* 
 Serial.begin(9600); //debug info
 while(!Serial);
 delay(500);
 Serial.println(PERIOD);
 Serial.println(HIGHTIME);
 Serial.println(LOWTIME);
*/

 pinMode(txPinIR,OUTPUT);
}

As you can see from the setup function above, you only need to set the txPinIR to OUTPUT. The other code is purely for debugging to see the calculated values and can be uncommented as you wish.

Loop

void loop() {
for (int i=0;i<10;i++){
 mark(500);
 space(1000);
}
delay(5000);
}

For part 1, we demonstrate how to generate a basic Infrared signal using a for loop, mark, space and delay functions. This generates a signal every 5 seconds approximately. The signal consists of 10 pairs of mark and spaces. The marks are 500 microseconds long and the spaces are 1000 microseconds long (or 1 ms). In the following parts in this series we will see how to generate real world examples of Infrared signals. You can see an oscilloscope image of this signal above.

Mark

void mark(unsigned long mLen) {
 if (mLen==0) return;
 unsigned long now = micros();
 while ((micros() - now) < mLen) {
 digitalWrite(txPinIR, HIGH);
 delayMicroseconds(HIGHTIME-6);
 digitalWrite(txPinIR, LOW);
 delayMicroseconds(LOWTIME-7);
 }
}

(At this point we suggest that you check out an excellent site that explains the structure of IR signals, if you are not familiar with the concept of marks and spaces.) The code in the mark function shown above simply toggles the txPinIR high and low very rapidly to create the carrier at the configured frequency and duty cycle. You will also notice the adjustments made (-6 -7) which are required because of the overhead in this code. In particular, digitalWrite is very (relatively) slow. An interesting feature of this mark function is that it can handle very long duration marks, unlike IRremote.

Space

void space(unsigned long sLen) {
 if (sLen==0) return;
 while (sLen>16383) {
 delayMicroseconds(16383);
 sLen -= 16383;
 }
 delayMicroseconds(sLen);
}

During a space the pin is held LOW, which is the default. Perhaps the simplest of all functions to implement! So in theory this function could be replaced with a delayMicroseconds function call. However, because of a designed limitation of delayMicroseconds on Arduino, it is not possible to delay for greater than 16,383 microseconds. This is particularly difficult for some IR signals for media devices and quite a few Air conditioner systems. We have implemented this function to check for zero and return immediately and handle situations where the duration is longer than the limit of delayMicroseconds, 16,383 uSecs. We have shown one approach to overcoming this limitation in the mark function above and an alternative approach in this space function.

Example Images

Below we present a selection of oscilloscope images, showing the various carrier frequencies and duty-cycles.

40kHZ

40kHz Infrared carrier signal @ 50% duty cycle
40kHz Infrared carrier signal @ 50% duty cycle

38kHz

38kHz Infrared carrier signal @ 33% duty cycle
38kHz Infrared carrier signal @ 33% duty cycle

36kHz

36kHz Infrared carrier signal @ 50% duty cycle
36kHz Infrared carrier signal @ 50% duty cycle

Conclusion

Although not 100% accurate in terms of carrier frequency and duty cycle, we have shown an extremely effective method of generating Infrared signals on Arduino, without the use of timers. It also allows use of any IO pin and avoids conflicts with all Arduino libraries. It can also handle very long IR signal, unlike IRremote. Nice!

Part 2 will show how to send a RAW Infrared signal and Part 3 will demonstrate how to send a standard NEC signal from its binary or HEX value.

What about other MCU platforms? This same method will work on any platform, with only a few minor adjustments.

Part 2 in this series can be found here.
Part 3 in this series can be found here.

Please feel free to use the code in this blog post without restriction. If you gain any benefit please link back to this article credit the Author where possible.

Get your own copy of AnalysIR here.

 

2 thoughts on “Simple Infrared PWM on Arduino

Leave a Reply