Infrared transmitter driver for Netduino

Introduction.


There are several contexts where the infrared (IR) remote commanders are really useful: our home is plenty of appliances which are IR based. TVs, Air-conditioning, HiFi-set, games, and lot more. This technology is still alive, because is very simple to implement, and requires very little resources to be realized.
Many users are asking about how to send infrared (IR) messages using a Netduino. That’s because the pleasure of programming such a little board as Netduino is, inspires easily to create solutions where the IR communication may be involved.
At first glance seems hard making a real-time operation for a managed-code platform, and several users have solved this problem connecting an Arduino-like board to the Netduino. That is a very good solution, even pretty elegant, when well-designed. However, it sounded me like a challenge: is it possible for the Netduino sending IR messages without any external “help”?
Well, I’ve got it!

How is an IR message?

Well, there is a very good, yet detailed page, where the principle of sending data through infrared ray is explained. I guess it’s completely useless to rewrite what’s described there.
http://www.sbprojects.com/knowledge/ir/index.php
You can see that there are several protocols, almost every big manufacturer created its own protocol. Here is not important make any comparison between protocols: the aim is to implement them, because we do have some appliance using a certain protocol. So, we need that implementation.
Again, the real challenge is adding the minimum hardware to the Netduino, and make it able to reproduce almost any IR protocol.

How it works?

Once again, I chosen the SPI as primary device for generating the waveform. That because is very simple: just a shift register, and nothing else. And very fast, indeed.
If you take a look at some previous post of mine, it’s clear to see on the scope what’s the behavior of the signals outgoing from the Netduino SPI. If you perform the transfer of a single, long byte array, the SPI is able to shift the data out without almost no delay between bytes. It’s like a streaming.
This privilege gave me the idea on to shape the required waveform: create the bit-pattern on a long buffer, then let the SPI shift it all at once.
The only missing piece is obviously a good driver for the IR-led, since it requires enough current to “light” a room. Okay, we can’t see the IR light, but for the IR receiver “eye” the room is pretty dark, and even a little led can be an huge difference. Try yourself to light up a small led, when the room is completely dark.

Here follows a chart with the detailed principle of working.

Note that the reference is to the Philips RC-5 protocol, but it might be anyone else.

The physical driver circuit.

The circuit is really trivial, and it could be the same circuit pattern used many times for a normal led. By the way, an IR led actually *is* a led, but we need a lot more current flowing through.
The circuit could have been even easier, if only we avoid the ability to share the SPI with some other device. Personally, I would not like losing the ability to drive a SPI-RTC, for instance, because the IR driver. We should drive both.
Thus, there is a simple logic: an AND-gate, where one of the input is the “IR-driver enable”. When the enable is low (zero), no activity on the SPI can involve the IR stage.

The circuit is using two NAND gates (74LS00), because I haven’t at home the right chip. I would recommend the 74HC08 instead, which is a 4x AND gates, HCMOS.

Some consideration about the led current.

If you take a look at the resistor for the led is very low when compared to the typical values you are used to choose. I’ve used 22 Ohms, which is about 10 times lower than a normal led, but I should have used even a 2.2 Ohms (typical for most remote commands).

A straightful computation yields a current of about 150mA for a 22 Ohms resistor, and over 1A for a 2.2 Ohms. Such a high current is surely enough to burn the led, and it’s too high even for the Netduino supply.
You might wonder why a so high current doesn’t actually damage any part.
The answer is very simple: these values are “peaks” for a bunch of microseconds, “spikes” so short, that the average heating power is much less than the power led on the Netduino board.

If you are scared to burn your IR led, I’d suggest a small extra to the circuit. No manufacturer will ever add such a circuit, because is too expensive. However, the hobbist perspective is quite different, and I prefer to give you an opportunity to keep your parts safer.

The trick is simple: the big capacitor (1000 uF) acts as charge accumulator. The stored energy is surely enough to drive the led for some millisecond, but if you try to drive the transistor continuously (e.g. a breakpoint or a bug), the capacitor will lose all its charge, and the actual current will be limited by the additional resistor.

The Infrared library.

The library is targeted for any protocol within the range of the domestica appliances. I don’t know the IRDA protocol, nor I think should be feasible (nor convenient).
At the moment I’m writing this post, the library embeds only the Philips RC-5 (Extended mode included), for transmission. However, it’s very easy to implement many other protocols, since they are very similars.
Please, check the library progression, because I’ll add the missing modules.

Testing the circuit.

My primary goal is sending commands from Netduino to my Maerklin railroad model.

In my case the biggest effort is finding out the protocol, because Maerklin does not give any specs about its devices. However, by using my scope and a small IR-receiver module, I was able to understand that Maerkiln uses the Philips RC-5 Extended protocol.

Using the library is really simple. In the following snippet there’s an example where I wanted to mimic the original remote commander of my Maerklin railroad model.
I have used just three buttons: speed-up, speed-down and change direction. For each button, its event handler embeds a different command to be sent via infrared, according to the Maerklin code set below.

namespace Toolbox.NETMF.Hardware
{
    public class Program
    {
        /**
         * Maerklin remote commander codes:
         * light:   1.T.11000.1010000
         * btn 1:   1.T.11000.1010001
         * btn 2:   1.T.11000.1010010
         * btn 3:   1.T.11000.1010011
         * btn 4:   1.T.11000.1010100
         * btn -:   1.T.11000.0010001
         * btn +:   1.T.11000.0010000
         * dir:     1.T.11000.0001101
         **/
        private const int Address = 0x18;

        private const int SelectLight = 0x50;
        private const int SelectTrain1 = 0x51;
        private const int SelectTrain2 = 0x52;
        private const int SelectTrain3 = 0x53;
        private const int SelectTrain4 = 0x54;

        private const int SpeedDown = 0x11;
        private const int SpeedUp = 0x10;

        private const int Direction = 0x0D;


        public static void Main()
        {
            //create the infrared transmitter driver
            var irtx = new InfraredTransmitter(Pins.GPIO_PIN_D8);

            //create the codec to be used
            var codec = new InfraredCodecRC5(irtx);
            codec.ExtendedMode = true;

            //define the button for decrement speed
            var btn_dec = new InterruptPort(
                Pins.GPIO_PIN_D0,
                true,
                Port.ResistorMode.PullUp,
                Port.InterruptMode.InterruptEdgeLow
                );

            btn_dec.OnInterrupt += (a_, b_, dt_) =>
            {
                codec.Send(Address, SpeedDown);
            };

            //define the button for increment speed
            var btn_inc = new InterruptPort(
                Pins.GPIO_PIN_D1,
                true,
                Port.ResistorMode.PullUp,
                Port.InterruptMode.InterruptEdgeLow
                );

            btn_inc.OnInterrupt += (a_, b_, dt_) =>
            {
                codec.Send(Address, SpeedUp);
            };

            //define the button for the direction
            var btn_dir = new InterruptPort(
                Pins.GPIO_PIN_D2,
                true,
                Port.ResistorMode.PullUp,
                Port.InterruptMode.InterruptEdgeLow
                );

            btn_dir.OnInterrupt += (a_, b_, dt_) =>
            {
                codec.Send(Address, Direction);
            };

            Thread.Sleep(Timeout.Infinite);
        }

    }
}

Here is a small demo of the project:

Finally, it seems that this IR solution is able to solve the problem of another geek. The deal of Laurent Ellerbach was to send IR commands from the Netduino to his amazing Lego village.
Well, I’m really proud to have helped him.

Conclusion.

If your goal is having a simple yet versatile IR transmitter to add to your Netduino, then maybe this circuit is for you. However, the main reason of this project is demonstrating that even a simple real-time operation can be solved by taking advantage of certain features of your Netduino.
If you find this library useful, and you implement it somewhere, feel free to add a link to pictures, videos, or whatever else.

The library is now part of the Cet Open Toolbox, and can be downloaded together with schematics and Fritzing diagrams.

27 thoughts on “Infrared transmitter driver for Netduino

  1. nimble99

    Hi Mario,

    FIrstly, thank you for such a well written blog entry. I have followed your instructions and built the identical circuit for controlling my television (I already built the circuit to ‘learn’ the remote commands). However – my television refuses to behave, and accept the commands!

    I am wondering if it has anything to do with the signal spikes in the “low” part of the waveform. I have looked up some specs for SPI, and it seems there shouldnt be a ‘clock’ in the MOSI signal – corroborated by this image: http://intrepidcs.com/embeddedspy/images/spi_wave.png
    i.e. a sequential series of ‘lows’ should result in a waveform of ‘___’ .

    However, my MOSI output seems to have an overlaid clock – meaning that when I output a set of ‘lows’ I actually get ‘_|_|_|’.
    But, I do get the correct 50% duty cycle when I output a series of sequential highs.
    I am wondering if this is causing my television to ignore it, because its not expecting a carrier frequency during the ‘low’ sections of the waveform.
    Could you lend a hand?

    Thanks again – Adam, New Zealand

    • Mario Vernari

      Hello Adam, and thanks for the credit.
      I don’t think those spikes could be a problem for the TV. At worst, they could fail some message, but not all the time.
      When you deal with an IR-appliance, you should know:

      • the protocol, that is how the light is modulated upon a certain message;
      • the command codes used by *that* particular brand/model/appliance;
      • the carrier frequency of the modulated IR pulses: most of the time it falls within from 36 to 40kHz.

      A trivial test you can do is using any camera (e.g. your phone’s) to see whether the IR beam is actually generated. The camera trick is very smart because our eyes can’t see the infrared, but any dumb camera does!
      At this point, I’d ask you about your TV model: could you specify it, so that we try to check the protocol used?
      Cheers

      • nimble99

        Hi Mario,

        I have done the camera test, and yes pulses are being generated.
        I think I have found a difference though… there is a trailing ‘HIGH’ (of 21 pulses – thanks Fluke scope!! Helps to know how to use the tools!!)

        Now its working, I can turn the tv on and off… unfortunately it only works about 1 in 5 times….

      • Mario Vernari

        Adam, I’m happy that you made a step ahead, but I still have no clear what happens.
        In my library there are several protocols, which one did you use?
        The trailing high should be correct or not, upon the TV specs?

      • nimble99

        Hi Mario,
        I only saw 1 protocol file in your zip file, which implements RC5 – so I basically rewrote the entire file to handle Samsung (great infrastructure by the way – really easy to extend!). The biggest difference was that ‘0’s and ‘1’s dont have the same total number of pulses like your RC-5. I don’t know what the Samsung protocol is called: it doesnt match up with anything here (http://www.sbprojects.com/knowledge/ir/): but I figured it out to be (for anyone else who is interested):
        |START FLAG|32-BITS|END FLAG|

        start flag: 4.5ms HIGH, 4.5ms LOW
        32-bit data payload consisting of
        – logical ‘1’: 0.55ms HIGH, 1.65ms LOW
        – logical ‘0’: 0.55ms HIGH, 0.55ms LOW
        end flag: 0.55ms HIGH

        it was the end-flag that I was missing, once I had that in place, it worked. However, I do get an approximate 1 in 3 failure rate. I also think my IR transmitter is a bit dodgy, it doesnt seem to pulse as bringhtly as it should (I just grabbed it from my used parts bin) which I think is affecting things. I’ll go spend splash out on a new one today…

        I have three theories:
        1. I only send ONE command to turn the TV on. I have noticed that the samsung remote will very frequently send multiple commands if the button is pressed for a ‘typical’ duration. However, I can get the samsung remote to send EXACTLY ONE data-packet, by pressing the button very quickly. If I do this, the TV sometimes does not turn on…. So I am thinking its just cheap hardware/software in the television, and sometimes it just needs more than one data-packet to activate the TV….

        2. The PWM spikes are making a slightly noisy signal that the tv SOMETIMES doesnt like…

        3. The fact I am using a Netduino may mean that the SPI output is affected by the GC – causing a timing error/too-long pulse? I would have thought this would be unlikely, but I dont know how the SPI is implemented in hardware, and whether or not it can be affected by CPU load.
        What I HAVE noticed, is that my IR receiver works perfectly, and reports the pulse durations from my Samsung remote correctly. However, my IR received will NOT correctly report the pulses that are generated by the SPI output ON THE SAME NETDUINO. The interrupt event stops firing after 16 interrupts, and the timings are all wrong. So it seems that the Netduino is not capable of simultaneously receiving the infrared pulses that the SPI circuit is generating. I dont need this functionality, I just found it interesting.

        In the case of theory [2] – I was wondering if I could filter out the ‘spikes’ by using an AND gate and a cap. If I connect:

        MOSI -> AND input 1
        MOSI -> CAPACITOR -> AND input 2

        Then the AND output should be smoothed, right? My thinking is that the CAP should be BIG enough so that when AND1 is high, the CAP is charging, but so slowly that it never reaches a logical HIGH voltage. It just means that when we DO want a HIGH signal, the CAP needs to be SMALL enough so it doesnt reduce our pulse duty too much. Then in the code, we can compensate for the capacitor by increasing the pulse duty ever so slightly (theoretically the width of the ‘spike’ – so maybe PULSE_DUTY = 0.55)….

        What do you think? Even if this doesnt improve the performance, removing the spike would CERTAINLY make it easier to visualise the signal on my oscilloscope!

      • Mario Vernari

        Hello Adam.
        In the library should have to be present the Sony SIRC as well. If you don’t find it, it means that I made a mess with the files. I have a couple more of protocols, yet not embedded in the library, though.

        Upon the info contained here and here, the modem class related to the Samsung devices should be as follows. This link is also useful, I guess.

            public class InfraredCodecSamsung
                : InfraredCodecBase
            {
                private const float CarrierFrequency = 36.0f;   //kHz
                private const float PulseDuty = 0.5f;
        
        
                /// <summary>
                /// Create a new instance of codec
                /// </summary>
                /// A valid reference for the transmitter to be used
                public InfraredCodecSamsung(InfraredTransmitter transmitter)
                    : base(transmitter, CarrierFrequency, PulseDuty)
                {
                }
        
        
                /// <summary>
                /// Send a Samsung message
                /// </summary>
                /// Specifies the address in the message
                /// Specifies the command to be sent
                public void Send(
                    int address,
                    int command)
                {
                    //place the "START" pattern
                    this.MarkStart();
        
                    //... 31 or 32 bits
        
                    //stop-bit
                    this.Modulate(true);
        
                    //send
                    this.Transmitter
                        .Send(this);
                }
        
        
                /// <summary>
                /// Mark the start pattern of the SIRC message
                /// </summary>
                private void MarkStart()
                {
                    //"START": 160 pulses + 160 blanks = 320 as total
                    this.TotalPulseCount = 320;
                    this.InitialPulseCount = 160;
                    this.FinalPulseCount = 0;
        
                    //append the defined pattern to the stream
                    this.Transmitter
                        .Append(this);
                }
        
        
                /// <summary>
                /// Provide the modulation for the logic bit
                /// </summary>
                /// The logic value to be modulated
                private void Modulate(bool value)
                {
                    //each pulse is 27.78us (=1/36kHz)
                    if (value)
                    {
                        //logic "ONE": 20 pulses + 20 blanks = 40 as total
                        this.TotalPulseCount = 40;
                        this.InitialPulseCount = 20;
                        this.FinalPulseCount = 0;
                    }
                    else
                    {
                        //logic "ZERO": 20 pulses + 60 blanks = 80 as total
                        this.TotalPulseCount = 80;
                        this.InitialPulseCount = 20;
                        this.FinalPulseCount = 0;
                    }
        
                    //append the defined pattern to the stream
                    this.Transmitter
                        .Append(this);
                }
            }
        

        However, I don’t have any Samsung appliance to test. Perhaps I may find it one by some friend.

        After that, the Netduino SPI is managed at very low level using DMA. The C# byte-array is passed to the underlying manager, and the flow is very fast, yet smooth. This approach is much less involved in the GC breaks, but is not immune. When the stream is pretty long, and your C# app has a lot of objects to wipe out, the SPI can also be broken by the GC process. Since you can send many messages to your TV, the expectation is that at least one will produce the desired effect. From what I’ve seen, it is not so frequent to be broken.

        You can’t place a capacitor in series, because the waveform will be shaped dramatically different. The cap should be placed as I shown in my circuit: that’s for cutting spikes.
        Anyway, it sounds me odd that you report so many spikes. Do you have a long wiring? Bear in mind that the circuit should be pretty close to the Netduino, let’s say no more than 10-20 cm. Also the IR-led should not be wired too long, again no more than 10-20 cm.

        Try all that, and tell me back what’s the result.
        Cheers

      • nimble99

        Hi Mario,

        Thanks your Samsung driver looks VERY similar to my one – except that my carrier frequency is 38Khz, not 36 (my scope reported 38Khz).

        I will try your driver tonight and let you know if it works.

        My circuit is currently breadboarded, so no component is more than 5cm from the next.
        I followed both your recommendations and used the ‘quad AND gate’, and I also have the ‘overload’ circuit (the 1uF cap + resistor) to protect the LED.

        Now that I have everything _basically_ working, I am actually going to ditch the ‘send a byte’ method, and drive the pulses from a ‘PWM table’ (like you have here http://arduinostuff.blogspot.it/2011/06/samsung-remote-ir-codes.html). That way I dont need to know the encoding mechanism for a device because it is all described in the PWM table. My ultimate goal is to use a ‘record and reply’ approach.

        I also made an alteration to your ‘Send’ method in the Transmitter, to allow a ‘repeat count’.
        The transmitter will then repeat the message N times, with a 40ms delay in between.

        Because it takes the Netduino so long to generate the pulse data, it is only capable of sending about 1.5 messages/sec. So, with the extra argument, you can say “send 0xFF0356, 5 times” to turn the volume up 5 notches.
        It seems that the Samsung TV has two modes of receiving Volume messages, if two are received within 40ms of each other, it considers it a ‘long press’ and increases the volume quickly. If you wait longer than that, then the TV just ignores the second pulse. It does indeed seem like two quite distinct behaviors.

        Thanks so much for the help!

    • Mario Vernari

      Please, consider that the timing taken out the IR detector are slightly altered because the demodulator embedded in it. The real timing should be referred to the carrier frequency.
      About the 36/38kHz. The article reports 36kHz, and that’s used for the driver, but I could check as soon I have any IR commander made by Samsung. It’s also interesting that by using 36kHz, the carrier period is about 27.78us, which seems to be a perfect quantum for the composition of the actual bits: 20+20 pulses for the one, 60+20 for the zero, etc. As the results, the 36kHz carrier fits more the expected timings.
      Finally, as soon I find a Samsung transmitter, I’ll try to watch the actual beam waveform, by using a simple IR diode, instead of a complete demodulator. That yields a more realistic result, than the IR-module is able to give you.
      Cheers

  2. nimble99

    Hi Mario – I have used your driver that you posted and it does not work. However, I have replace my IR LED and mine is working MUCH better now. I think I need a lens of some kind though because the LED needs to be aimed DIRECTLY at the TV to work, which is not very desirable.

    I have uploaded my waveform that I scoped directly from the Samsung circuit board – I copied the pulse counts directly into my code… so I know it is perfect (-;

    This image is zoomed in to a HIGH/LOW that represents a ‘1’ (21 HIGHS followed by 63 LOWS)

    Here is the source code for my driver – test it by sending the code ‘-50198777’ to toggle power:

    ///
    /// Class acting as driver for the Samsung TV IR protocol
    ///
    public class InfraredCodecSamsung
    : InfraredCodecBase
    {
    private const float CarrierFrequency = 38f; //kHz
    private const float PulseDuty = 0.5f;

    ///
    /// Create a new instance of codec
    ///
    /// A valid reference for the transmitter to be used
    public InfraredCodecSamsung(InfraredTransmitter transmitter)
    : base(transmitter, CarrierFrequency, PulseDuty)
    {

    }

    ///
    /// Send a Samsung message
    ///
    /// The payload
    /// The number of times the payload should be transmitted. This is often required for operations like ‘volume up’/’volume down’
    public void Send(int data, int numberOfTimes)
    {
    SendStartFlag();

    for (int i = 0; i >= 1;
    }

    SendTerminateFlag();

    //send
    this.Transmitter
    .Send(this, numberOfTimes);
    }

    private void SendStartFlag()
    {
    this.InitialPulseCount = 171; // HIGHS
    this.FinalPulseCount = 0; // HIGHS
    this.TotalPulseCount = 171 * 2;
    this.Transmitter
    .Append(this);
    }

    private void SendTerminateFlag()
    {
    // end pulse
    this.InitialPulseCount = 21; // HIGHS
    this.FinalPulseCount = 0; // HIGHS
    this.TotalPulseCount = 21;
    this.Transmitter
    .Append(this);
    }

    ///
    /// Provide the modulation for the logic bit
    ///
    /// The logic value to be modulated
    private void Modulate(bool value)
    {
    if (value)
    {
    this.InitialPulseCount = 21; // 21 HIGHS
    this.FinalPulseCount = 0; // 63 LOWS
    this.TotalPulseCount = 21 + 63;
    }
    else
    {
    this.InitialPulseCount = 21; // 21 HIGHS
    this.FinalPulseCount = 0; // 21 LOWS
    this.TotalPulseCount = 21 + 21;
    }

    //append the defined pattern to the stream
    this.Transmitter
    .Append(this);
    }
    }

    • Mario Vernari

      The scope picture is *very* useful, because it clearly shows that the carrier is 38kHz. You were right, and the referred article is wrong, or there are more than one Samsung protocol.
      I’ll try to dig a bit around the Samsung IR protocol, however. Your test brings to me why a friend wasn’t able to make this circuit working: we’re supposing the Sony protocol, but probably the appliance used a version slightly different.
      Cheers

    • JuzKuz

      nimble99,

      do you have a sample project for your Samsung success that you’d be willing to share? That would be a HUGE help to others like myself. Thanks!

  3. nimble99

    Yes – the waveform helped significantly in decoding this. I have some more work to do in this space – I would like to trade email addresses so we can talk about this more ‘offline’ if that is ok with you? How could we do that without posting my email in this thread?
    Thanks

    – Adam

  4. Dirk

    Hi Mario,

    nice projects, learned much from that and did some test on sending by a RF 433 transmitter (successfully switch on/off rf 433 controlled electrical outlets). I used the same approach by generating waveforms and let the SPI shift these to the rf transmitter, because toggling normal outputs is much to slow. I reviewed some of your source, here my suggestion:

    In the source (InfraredTransmitter.cs(13)) you stated :

    //prevent a frequency too low: seems that Netduino hangs
    //when the frequency is below a certain value

    if (spi_freq 48 MHz / 255 = 188,235 kHz

    Looking forward for new projects from you.

    • Mario Vernari

      Thanks Dirk, good suggestion. I will apply the lower limit for the old Netduino, because I didn’t perform any test of this kind on the new Netduino Plus. It embeds a different MCU, so maybe the limit is different.

  5. bulletprooffool

    Hi – I am new at this – trying to get a basic On and Off signal to an Optoma projector.I am generally OK on the C# side and should be able to handle the driver change (I think I need t use NEC) myself – I am just super rusty on the electronics side.

    I am guessing I can wire it up like the basic LED sample, to test ( http://embedded-lab.com/blog/wp-content/uploads/2012/11/Day1_FlashingLED_Circuit_Diagram.png ) – obviously change the Resistor based on the LED I use, and swap the potentiometer with a transistor?

    • Mario Vernari

      Sorry for the delay.
      Not sure to understand what you’re going to do. I mean you want to command the projector via IR using the Netduino. I believe there’s no problem on implementing the NEC protocol on the board.
      However, before digging into the hardware, could you explain better what’s your goal, and the supposed function of the potentiometer?

      • bulletprooffool

        Sorry Mario – no pot – I meant for a transistor to be in the diagram, the potentiometer makes no sense.

        All I want is power off and power on functions for my projector (ir) and up and down for the screen (rf) – I’ll more than likely handle the rf by tapping straight into the remote with a pair of options isolaters.

        How reliable is the IR from the Netduino now? I’ve heard quite a few comments about the managed code making the timings unreliable – if this is true, I may offload the IR to an arduino or picaxe board (or similar)

        Thanks again

      • Mario Vernari

        The Netduino runs manager code, thus you really can’t rely on accurate timings. However, if you read my article, I managed an accurate pulses stream via SPI, which is driver natively. In this way you can implement your own IR protocol without problems.
        I believe my circuit is/was used several times with no problems.
        Having said that, your circuit *would be* fine for an Arduino, for instance, but if you want to use the Netduino you should stick to my circuit.

Leave a reply to Mario Vernari Cancel reply