Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

NEC Protocol IR Decoder


  • Please log in to reply
7 replies to this topic

#1 darko

darko

    New Member

  • Members
  • Pip
  • 1 posts

Posted 18 February 2011 - 08:35 AM

I purchased an IR kit from DFRobot for a project that I am working on. The remote control sends pulses encoded in the NEC protocol. The following is a class which I wrote which handles decoding the ir command.
The idea of how to implement it was based on how the forum member 'phil' did it in his RC6 Decoder class.
When a code is received, the OnIrCommandReceived event is called and is passed the decoded 32 bit number. Eg. on the remote the 0 button returns 217268479, the 1 returns 284115199.
Note: apple remote controls also use the NEC protocol, so its possible to decode apple remote commands with this class.

public class NecRemoteControlDecoder
{
    private Cpu.Pin _irReceiverPin;
    private long[] pulses;
    private int currentPulseIndex;
    private Timer irPulseTimeOutTimer;
    public delegate void IrCommandReceivedEventHandler(UInt32 irData);
    public event IrCommandReceivedEventHandler OnIrCommandReceived;
    private InterruptPort irReceiverPort;

    public NecRemoteControlDecoder(Cpu.Pin irReceiverPin)
    {
        _irReceiverPin = irReceiverPin;
        pulses = new long[200];
        currentPulseIndex = 0;
        irReceiverPort = new InterruptPort(irReceiverPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
        irReceiverPort.OnInterrupt += new NativeEventHandler(irReceiverPort_OnInterrupt);
        irPulseTimeOutTimer = new Timer(new TimerCallback(IrPulseTimeOut), null, Timeout.Infinite, Timeout.Infinite);
    }

    private void irReceiverPort_OnInterrupt(uint data1, uint data2, DateTime time)
    {
        if (currentPulseIndex >= 200) currentPulseIndex = 0;
        pulses[currentPulseIndex] = time.Ticks;
        currentPulseIndex++;
        irPulseTimeOutTimer.Change(10, Timeout.Infinite);
    }

    private void ConvertPulsesToMicroseconds(long numberOfPulses)
    {
        var firstMicrosecondsValue = pulses[0] / (TimeSpan.TicksPerMillisecond / 1000);
        var lastMicosecondsValue = firstMicrosecondsValue;
        for (int i = 1; i < numberOfPulses; i++)
        {
            var currentPulseMicrosecondsValue = pulses[i] / (TimeSpan.TicksPerMillisecond / 1000);
            var currentPulseLength = currentPulseMicrosecondsValue - lastMicosecondsValue;
            lastMicosecondsValue = currentPulseMicrosecondsValue;
            pulses[i - 1] = currentPulseLength;
        }
    }

    private void IrPulseTimeOut(object state)
    {
        var numberOfPulses = currentPulseIndex;
        currentPulseIndex = 0;
        irPulseTimeOutTimer.Change(Timeout.Infinite, Timeout.Infinite);

        ConvertPulsesToMicroseconds(numberOfPulses);
        if (numberOfPulses != 68) return;
        if (!IsPulseMatch(pulses[0], 9000) || !IsPulseMatch(pulses[1], 4500)) return;

        UInt32 data = 0;
        for(int i = 65; i >= 3; i--){
            if((i % 2) == 1){
                data = data << 1;
                if (IsPulseMatch(pulses[i], 565))
                    data = data | 1;
            }
            else{
                if(!IsPulseMatch(pulses[i], 560))
                    return;
                }
        }

        if (OnIrCommandReceived != null)
            OnIrCommandReceived(data);
    }

    private bool IsPulseMatch(long actualValue, long expectedValue)
    {
        var marginOfError = 200;
        if ((expectedValue - marginOfError) < actualValue && (expectedValue + marginOfError) > actualValue)
            return true;
        return false;
    }
}

Sample usage:
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

namespace Netduino.NecRemoteControlDecoderApp
{
    public class Program
    {
        public static void Main()
        {
            var necRemoteControlDecoder = new NecRemoteControlDecoder(Pins.GPIO_PIN_D0);
            necRemoteControlDecoder.OnIrCommandReceived += necRemoteControlDecoder_OnIrCommandReceived;

            Thread.Sleep(Timeout.Infinite);
        }

        static void necRemoteControlDecoder_OnIrCommandReceived(UInt32 irData)
        {
            Debug.Print("Ir Command Received: " + irData);
        }
    }
}


#2 Mercules

Mercules

    New Member

  • Members
  • Pip
  • 4 posts

Posted 28 January 2012 - 05:17 AM

This is great! Thank you for this very helpful class. Now I just need to figure out how to send signals...

#3 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 12 February 2012 - 06:02 PM

Perfekt, I picked up one of these detectors a couple of weeks ago and I will use your code for my usb audio project.

Thanks!

#4 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 13 February 2012 - 09:22 AM

Btw, did you connect the detector data line directly to your Netduino pin or did you go through a voltage divider in order to get 3,3V?

EDIT: The detector seem to work equally well when fed only 3.3V. Your code completed my USB audio project. Thanks!

#5 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 18 February 2012 - 10:01 PM

A suggestion or request if I may: I would like to receive an event when a button is released - this in order to implement repeating buttons for volume etc.. Maybe there are other possibilities such as general button repetitions from the remote. Either way, this would be a greatly appreciated extension of the class. So, what sayeth thee - can you do it or need I attempt to undertake such a conduct myself?

#6 Bondolon

Bondolon

    New Member

  • Members
  • Pip
  • 1 posts

Posted 28 February 2012 - 05:22 AM

Awesome post darko. I had a remote that implemented this protocol and one of the 38kHz IR receivers from Radio Shack (276-640), and used your code to help decode it. That said, I noticed that I was getting a number of incorrect values (the most common being that the number was shifted two bits too many to the left, though I was getting some just random outliers), and revamped the code a little to clean up the integrity of the received signals. I only know that it works with the Apple Remote (the only one I have on hand that implements this protocol), though I saw the same 9000->4500 control sequence on a completely different remote--a JP1.3 from SuddenLink. Either way, the modifications below have a bit more overhead in terms of memory, but they seemed to constrain the values very closely to what I was expecting (e.g. my first test with them replaced the OnIrCommandReceived call with a debug block for breaking when >50 valid values had been retrieved, and it stopped after exactly 51 button clicks).

namespace NecDecoder
{
    class NecProtocolDecoder
    {
        public event IRCommandEventHandler OnIRCommandReceived;

        private Timer _timeout;
        private InterruptPort _input;

        private long[] _pulses;
        private int _currentIndex;

        public NecProtocolDecoder(Cpu.Pin irReceiverPin)
        {
            _timeout = new Timer(new TimerCallback(PulseTimedOut), null, Timeout.Infinite, Timeout.Infinite);

            _input = new InterruptPort(irReceiverPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
            _input.OnInterrupt += new NativeEventHandler(OnInterrupt);

            _pulses = new long[200];
            _currentIndex = 0;
       }

        private void OnInterrupt(uint data1, uint data2, DateTime time)
        {
            if (_currentIndex >= 200)
            {
                _currentIndex = 0;
            }

            _pulses[_currentIndex++] = time.Ticks;

            _timeout.Change(10, Timeout.Infinite);
        }

        private void PulseTimedOut(object state)
        {
            const long toMicrosecondsDivisor = TimeSpan.TicksPerMillisecond / 1000;

            var firstValue = _pulses[0] / toMicrosecondsDivisor;
            var lastValue = firstValue;

            for (int i = 1; i < _currentIndex; i++)
            {
                var currentValue = _pulses[i] / toMicrosecondsDivisor;
                _pulses[i - 1] = currentValue - lastValue;
                lastValue = currentValue;
            }

            uint result = 0;

            int controlIndex = 0;
            int collectedLength = 0;
            bool isCollecting = false;

            for (int i = 0; i < _currentIndex; i++)
            {
                if (!isCollecting && (IsInRange(_pulses[i], 9000, 200) && IsInRange(_pulses[i + 1], 4500, 200)))
                {
                    controlIndex = i;
                    i = i + 2;
                    isCollecting = true;
                }
                else if (isCollecting && collectedLength < 32)
                {
                    if ((i - controlIndex) % 2 == 1)
                    {
                        result <<= 1;
                        collectedLength++;

                        if (IsInRange(_pulses[i], 1690, 200))
                        {
                            result |= 1;
                        }
                    }
                }
            }

            _currentIndex = 0;

            if (OnIRCommandReceived != null)
            {
                OnIRCommandReceived(result);
            }
        }

        private static bool IsInRange(long pulse, long expected, long tolerance)
        {
            return (expected + tolerance > pulse && expected - tolerance < pulse);
        }
    }

    public delegate void IRCommandEventHandler(uint command);
}


#7 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 13 March 2012 - 07:48 PM

Turns out, this 1.95$ IR-receiver does the same job as the DFRobot thing:

http://www.sparkfun.com/products/10266

Actually, a similar one is installed on the DFRobot. Should have done my homework Posted Image

#8 irda

irda

    New Member

  • Members
  • Pip
  • 4 posts

Posted 05 January 2014 - 08:34 PM

Good job, it's very usefull. Can you explain how the code working?






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.