If you are building a Home-Theater-PC (HTPC) one of the things you need to install is a remote control and its driver. Even without Media Center this is a simple thing to do.
A hardware circuit that plugs into an RS232 port is actually pretty simple and easy to build. Here is a circuit that works just fine.
I don't think that the Vishay TSOP1738 is available any longer, but any of Vishay's 38kHz IR Sensor Modules for remote Control Systems will work. I have used the TSOP31238 with no problem. You can get a few of these for free by requesting a sample from Vishay. http://www.vishay.com/product?docid=...uery=tsop31238 . LCD TVs output a lot of IR when they warm up, so you might want to instead use one of their modules designed for noisy environments. (i.e. TSOP31438)
RS232 ports have become less common on modern computers being replace with USB ports. If you don't have a serial port available and don't want to add one, you can add a RS232 to USB converter to your system.
There is a fantastic open source project for Linux called LIRC that handles the software interface to the device (http://lirc.org). Version 0.6.5 was ported to windows in 2003 (http://winlirc.sourceforge.net) but there is a serious flaw with this software. WinLIRC relies on the serial port interrupt for timing the pulses to decode the signal. The problem with this is that the serial port interrupt is a lower priority interrupt than the video interrupt so that when your system is performing some serious video task, such as video playback, the serial port interrupt will be delayed to an extent that makes the IR remote unusable. The solution to this problem is to perform the IR pulse detection and decoding via a polling technique instead. WinLIRC can still be used to generate the remote configuration file (obviously not while viewing motion video).
Below is a replacement IR Remote daemon that works reliably even when video playback is going on.
Most remote control units modulate the IR signal at 38kHz and follow the same basic protocol. When a key is pressed on the remote control, the unit produces a train of pulses who's widths identify what key was pressed. After demodulation, the signal consists of several square pulses of different widths separated by spaces of about 500 microseconds (us). The pulse train starts with a wide header pulse of about 5 milliseconds (ms). Next comes a series of 32 pulses. Each pulse represent a one or zero depending on its width. A one is represented by a pulse width of about 1600us and a zero is about 500us wide. These 32 digits can be combined to for a unique 32-bit unsigned integer that identifies the key pressed. Actually the high word identifies the remote control used and the low word identifies the key pressed. Finally a very wide tail pulse; about 40ms; is sent.
Here is an overview of the software does:
We can use the serial port interrupt to detect the first pulse edge since an inaccuracy in the width of the first pulse (header) does not affect the results. The com-port PinChanged event signals us that an IR signal has been detected. This could be the beginning of a valid pulse train.
When the first pulse edge has been detected, start polling the com-port in a background thread with higher than normal priority to watch for future edges.
When a pulse comes in, measure its width with the high performance counter.
Start a one shot pulse-train timer to signal us when the pulsetrain should be finished.
When the one-shot timer expires, we should have collected 32 pulses. Convert the 32 valid pulses to ones and zeros (depending on the pulse width) and convert that into a 32-bit integer which tells us which key on the remote controller has been pressed.
Start a repeat timer to prevent repeat keys from being reported for the next 500ms.
Look up the name of the key and remote that correspond to the 32-bit integer above.
Send this data over TCP/IP to the clients.
Last edited by moeur; Jun 13th, 2009 at 05:28 PM.
Reason: The attached file now is complete and will run
To detect the first pulse edge, monitor the com port's Pin Changed event.
Code:
Private Sub CommPort_PinChanged(ByVal sender As Object, ByVal e As SerialPinChangedEventArgs) Handles ComPort.PinChanged
'we only use the com port interrupt to detect the first pulse edge.
'This is allowed because the accuracy of the width of the first (header)
'pulse is not important.
If ThreadData.isBusy Then Return 'ignore interrupts while we are in the process of measuring a pulse train
If e.EventType = SerialPinChange.CDChanged Then 'only interested in the DCD line
If ComPort.CDHolding = False Then ThreadData.AR.Set() 'begin polling
End If
End Sub
The detection of the first pulse then signals us to start the polling process. We use the High performance Timer to measure pulse widths and start a one-shot timer to signal us to stop polling.
Code:
Private Sub doPolling(ByVal oData As Object)
'we cannot rely on interrupts to detect pulse edges since the com port interrupt
'has too low a priority and will give inaccurate results if many interrupts of higher
'priorities are occuring. This situation arrises during video playback.
Static LastState As Boolean
'data is sent along the DCD line (pin 1); this line remains high while idle
Dim ThreadData As clsThreadData = DirectCast(oData, clsThreadData)
With ThreadData
Do
'wait here for next pulse train
.AR.WaitOne()
.isBusy = True
.isFirstPulse = True
Do
LastState = .cPort.CDHolding
ThreadData.TimerValue = PerformanceTimer.CurrentCounts()
getData(ThreadData)
Do
'wait here until next change on DCD line
'or until pulse train is finished
If Not .isBusy Then Exit Do
Loop While (.cPort.CDHolding = LastState)
Loop While .isBusy
Loop
End With
End Sub
Here is the ThreadData class
Code:
Private Class clsThreadData
Public Sub New(ByVal Port As SerialPort)
HiData.Initialize()
LoData.Initialize()
cPort = Port
End Sub
Public cPort As SerialPort
Public isBusy As Boolean = False
Public AR As New Threading.AutoResetEvent(False)
Public isFirstPulse As Boolean = True
Public HiData As PerformanceTimer.Data
Public LoData As PerformanceTimer.Data
Public TimerValue As Long
End Class
When the one-shot timer expires, we should have collected 32 pulse widths.
Code:
Private Sub OneshotTimer_Tick(ByVal stateinfo As Object)
'this is the end of a pulse train
'no more data should be coming in now
Dim ThreadData As clsThreadData = DirectCast(stateinfo, clsThreadData)
ThreadData.isBusy = False
mDataCount = Index 'number of pulses found, should be 32 + header + tail = 34
'unless it is a repeat key then count should be 2
Index = 0
If (mDataCount = 34) Or (mDataCount = 2) Then
For i As Integer = 0 To mDataCount - 1
getDigit(Pulses(i))
Next
Else
isValid = False
End If
'here is the data we just gathered, convert pulses to an integer value
Dim data As DWORD = getKeyCode(Pulses, mDataCount)
'now that we have a value, see if it corresponds to a key in our list
If isValid Then
If Codes.ContainsKey(data.Value) Then
RepeatFlag = False
'start the repeat timer to ignore repet keypresses for the next 500ms (delay1)
'subsequent repeats can happen again within 100ms (delay2)
RepeatTimer.Change(delay1, delay2)
RaiseEventNewKey(Remotes(data.HiWord), Codes(data.Value), data.ToString)
LastKeyCode = data
Else 'unknown key code
LastKeyCode.clear()
RepeatFlag = False
RaiseEventUnknownKey(data.ToString())
End If
isValid = False
ElseIf (data.Repeats > 0) And (mDataCount = 2) Then
If LastKeyCode.Value = 0 Then Return
If data.Repeats > 1 Then
LastKeyCode.clear()
Return
End If
RepeatCount += data.Repeats
If RepeatFlag Then
RepeatFlag = False
RaiseEventRepeatKey(Remotes(LastKeyCode.HiWord), Codes(LastKeyCode.Value), RepeatCount, data.ToString)
End If
Else
'reject noise
RepeatCount = 0
LastKeyCode.clear()
End If
End Sub
As with LIRC, the daemon or server program transmits decoded IR remote data on a TCP/IP port to any number of client programs that need the data. Attached is a demo program that shows how to develop server and client programs. I've put together some classes that make the process of development easier. Here is a list of these classes.
clsLIRCserver - this handles all the com port polling and signal decoding
clsTCPserver - this class handles the server side of the TCP details
clsIniFile - A class to simplify the reading of Windows ini files. The server uses this class to read te key code mapping information.
clsLIRCclient - this class handles the TCP/IP connection between the client and the server program and raises events when remote control data arrives.
PerformanceTimer - a class that handles the timing of pulse width measurements.
Hi, I buid the Infra-red circuit some days ago and i find your code , that this awesome!!. But this seems has a problem, not even detect a code keys. sometimes arrive another code with the same button. Where is the problem?. WinLirc work fine.
If Winlirc works fine then I suggest you use it. I couldn't get WinLIRC to work while any video was playing.
I suppose the problem you are seeing could be coming from one of several sources:
1. you have a slow cpu that cannot keep up. Try running a compiled version of the code.
2. try changing the adjustable parameters in the code.
If Winlirc works fine then I suggest you use it. I couldn't get WinLIRC to work while any video was playing.
I suppose the problem you are seeing could be coming from one of several sources:
1. you have a slow cpu that cannot keep up. Try running a compiled version of the code.
2. try changing the adjustable parameters in the code.
Hi, WinLIRC work find but... And I have no problem playing video (I use crytal player to watch movies). I have a core 2 duo at 2.1, with 1gb of ram. wich part of code sould be modifiy?
Hi I built this circuit not long ago and was able to use WinLIRC and another program I know it definately works.
What I am trying to do now is to make my own program that reads in the commands from the remote into my program and then depending on what the command is I get it execute a program with the shell command. I know DCD is not used to read in data but can it be used?
And if so any sample code or ideas would be very useful!