Controlling the BX24 using a Sony TV Remote Control

copyright, Peter H. Anderson and Timothy Wallace, Baltimore, MD, Dec, '01


Introduction

This discussion deals with interfacing a Sony TV remote control unit with a BX24. The material was developed by Timothy Wallace as a part of his Senior Project (2 credits) in the Dept of Electrical and Computer Engineering at Morgan State University.

The TV remote control technology is mature and inexpensive and an infrared remote control might be used in many BX24 applications in place of a keypad or other means of inputting data. One need only look to the home entertainment industry to see that consumers insists on remote control and for the industry, they save on all of the fancy and expensive mechanics associated with knobs and buttons, usually providing but a bare minimum of locally controlled buttons.

Obvious applications are movement, e.g., turn a motor one way or another, turn it faster or slower or turn it 23 clicks in the defined direction. However, the remote might be used for virtually any data entry, e.g., perform a temperature measurement on sensor 1, or set a thermostat threshold to 28 degrees C.

A Sony TV remote was used in developing this discussion, simply because I happened to have a Sony TV remote control laying around. The following routine was also tested with a Sony CD remote (number keys only) and with two "One For All" universal remotes using TV programming code 000. However, this material was developed by reverse engineering the Sony remote. That is, we observed the operation of the remote on a high quality storage scope as opposed to consulting authoritative literature (if such exists). The danger here is assuming our observations extend to all Sony TV remotes. However, with a "Universal", you should be able to duplicate our results.

Configuration

All that is required is a Sony TV remote or a "Universal" and a small 38 kHz three terminal infrared receiver which is available from Jameco as their numbers #139889, or #131908. These are nominally $3.00 each.

Note that the following routine uses the input capture capability of the BX24 which is associated with terminal 12 of the BX24


                	IR Receiver				BX24

			+5 VDC


			     ------------------------------------ Term 12

			GRD

Discussion.

When idle, the output of the IR receiver is high. When the receiver detects a 38 KHz burst of infrared, the output goes low for the duration of the burst.

We looked at the output of the IR receiver using a good quality storage scope and reached a number of conclusions.

1. When a button on the remote is depressed, a code consisting of a start pulse, immediately followed by twelve bits is sent. As long as the button is depressed, this is repeated with a substantial idle time between each sequence.

2. A start pulse is a burst of nominally 2.5 ms. I assume one reason for this length is to assure the automatic gain control (AGC) associated with the IR receiver has sufficient time to adjust to the proper level.

3. A zero bit consists of no burst for nominally 0.5 ms followed by a burst having a duration of nominally 0.75 ms. That is, the output of the IR receiver is high for 0.5 ms and then low for 0.75 ms.

4. A one bit consists of no burst for nominally 0.5 ms followed by a burst having a duration of nominally 1.25 ms.

5. The bits are sent, least significant bit first.

In function FetchCode(), terminal 12 is made an input and a pulse train is captured to array Pulses, beginning with the first negative going transition. Note that each count is 135.6 nanoseconds and we found it a bit more convenient to convert this to array Times, where the units of time are milliseconds.

From 2. above, for a valid pulse train, element 1 of array Times should be nominally 2.5 ms. We used limits in the range of 2.1 to 2.8 ms. From 2 and 3 above, each even element of the array should be nominally 0.5 ms. We used 0.32 to 0.6 ms. If, these two conditions are met, boolean variable valid is TRUE and the do loop is exited. Otherwise, another pulse train is captured and this continues until a valid pulse train is captured.

Function FetchCode() as Byte

    Dim Pulses(1 to 17) as New UnsignedInteger
    Dim Times (1 to 17) as Single
    Dim N as Integer
    Dim Valid as Boolean
    Dim Code as Byte

    Call PutPin(12, 2)				' make pin 12 an input

    Do
        Call InputCapture (Pulses, 17, 0)

        For N = 1 to 17
           Times(N) = CSng(Pulses(N))* 135.6e-9 * 1000.0	' times in msecs
        Next

        Call PrintArray(Times, 17)		' used for debugging

        Valid = True

        For N = 1 TO 8
          If ( (Times(N*2) < 0.32) OR (Times(N*2) > 0.6) ) Then
              Valid = False
          End If
       Next

       If ((Times(1) < 2.1) OR (Times(1) > 2.8)) Then
           Valid = False
       End If

   Loop Until (Valid = True)

   Debug.Print "Success!!!!!!"
Elements 3, 5, 7, .. 17 are then tested and if less than 1.0 ms (or greater than 1.0 ms), a logic zero (or one) is placed in the appropriate bit of byte variable code, beginning with the least significant bit. Code is thus a value in the range of 0 - 127.

We used only the Start bit plus eight information bits and thus the arrays consist of one plus eight times two transitions or 17 transitions. Had we desired to capture the entire twelve bits, the arrays would have contained 25 elements.

   Code = 0

   For N = 1 to 8
      If (Times(2*N+1) < 1.0) Then
          Call PutBit(Code, CByte(N-1), 0)
      Else
          Call PutBit(Code, CByte(N-1), 1)
      End If

    Next

    FetchCode = Code

End Function

Program IRRemote.Bas.

In Sub Main(), the code is fetched. Only the lower five bits are used. If the code is in the range of 0 - 9, it is a number key 1 - 10 and either the red or green surface mount LED on the BX24 is flashed that number of times. Note that key 1 corresponds to code 0, key 2 to code 1, etc and thus the number of flashes is set to code plus 1.

The channel up (code 16) and channel down (code 17) are used to toggle the LED from red to green or green to red. Note that the LED is briefly flashed to provide some feedback to the user that the command was received. Similarly, volume up (code 18) and volume down (code 19) are used to adjust the speed at which the LED is flashed.


' Program IRRemote.Bas
'
' Illustrates an interface with a Sony TV remote control.
'
' A pulse train is fetched using the Capture function of the BX24 and converted to an array of singles which
' are the time between transitions in ms.  This is displayed to the terminal using function PrintArray.
'
' The content of the array is checked; i.e., element 1 is nominally 2.5 ms and even elements are nominally 0.5 ms.
' If valid, a code is calculated using elements 3, 5, 7, etc, beginning with the least significant bit.
'
' If the code is a key, one of the surface mount LEDs is flashed the number of times corresponding to the
' value of the key.  If the code is channel up or channel down, feedback is provided by a brief wink of the LED
' and the selected LED is changed from red to green or green to red.  If the code is volume up or down, the delay
' time is adjusted to flash the LED faster or slower.
'
' The program continually loops.
'
' Compile with SerialPort.Bas
'
' copyright, Peter H Anderson, Timothy Wallace, Baltimore, MD, Dec, '01

Sub Main()

    Dim Code as Byte, LEDNum as Byte, NumFlashes as Byte, DelayVal as Single

    LEDNum = 25
    DelayVal = 0.2

    Call PutPin(25, 1)	' Turn off both surface mount LEDs
    Call PutPin(26, 1)

    Call OpenSerialPort(1, 19200)
    Do
       Code = FetchCode() AND &H1f		' take only the five least sig bits
       Debug.Print "******"; CStr(Code)

       If (Code < 10) Then			' its a number key

          NumFlashes = Code + 1
          Call FlashLED(LEDNum, DelayVal, NumFlashes)

       ElseIf (Code = 16) Then			' up channel
          If (LEDNum = 25) Then
             LEDNum = 26
          Else
             LEDNum = 25
          End If
          Call FlashLED(LEDNum, 0.200, 1)	' give some feedback

       ElseIf (Code = 17) Then			' down channel
          If (LEDNum = 25) Then
             LEDNum = 26
          Else
             LEDNum = 25
          End If
          Call FlashLED(LEDNum, 0.200, 1)	' feedback to user

       ElseIf (Code = 18) Then	' up volume
          DelayVal = DelayVal - 0.05		' faster
          If (DelayVal < 0.05) Then
             DelayVal = 0.05
          End If
          Call FlashLED(LEDNum, 0.05, 3)	' feedback

       ElseIf (Code = 19) Then	' down volume
          DelayVal = DelayVal + 0.05		' slower
          If (DelayVal > 0.50) Then
             DelayVal = 0.50
          End If
          Call FlashLED(LEDNum, 0.05, 3) 	' feedback

       Else
          Debug.Print "Unknown Code "; CStr(Code)

       End If
    Loop

End Sub

Function FetchCode() as Byte

    Dim Pulses(1 to 16) as New UnsignedInteger
    Dim Times (1 to 16) as Single
    Dim N as Integer
    Dim Valid as Boolean
    Dim Code as Byte

    Call PutPin(12, 2)				' make pin 12 an input

    Do
        Call InputCapture (Pulses, 16, 0)

        For N = 1 to 16
           Times(N) = CSng(Pulses(N))* 135.6e-9 * 1000.0	' times in msecs
        Next

        Call PrintArray(Times, 16)		' for examination of pulse train

        Valid = True

        For N = 1 TO 8
          If ( (Times(N*2) < 0.32) OR (Times(N*2) > 0.6) ) Then
              Valid = False
          End If
       Next

       If ((Times(1) < 2.1) OR (Times(1) > 2.8)) Then
           Valid = False
       End If

   Loop Until (Valid = True)

   Debug.Print "Success!!!!!!"


   Code = 0

   For N = 1 to 7
      If (Times(2*N+1) < 1.0) Then
          Call PutBit(Code, CByte(N-1), 0)
      Else
          Call PutBit(Code, CByte(N-1), 1)
      End If

    Next

    FetchCode = Code

End Function

Sub FlashLED(ByVal LEDNum as Byte, ByVal DelayVal as Single, ByVal NumFlashes as Byte)

   Dim N as Byte

   Call PutPin(25, 1)	' first, turn off both LEDs
   Call PutPin(26, 1)

   For N = 1 to NumFlashes
      Call PutPin(LEDNum, 0)
      Call Sleep(DelayVal)
      Call PutPin(LEDNum, 1)
      Call Sleep(DelayVal)
   Next

End Sub


Sub PrintArray(ByRef X() as Single, ByVal NumEle as Integer)

   Dim N as Integer

   For N = 1 to NumEle
      Call PutS(X(N))
      Call NewLine()
   Next

   Debug.Print "******************"	' separator
End Sub

A Final Note.

In executing the Capture command, the processor will hang in this command if no transition on terminal 12 is encountered. Thus, depending on the application, one may wish to enable the watch dog timer prior to executing this command. However, in many applications, one may wish to simply hang in this command, and simply respond to the IR pulse train. In other applications, one might require the user to boot the BX24 with an input set to ground and fork to the code associated with fetching the information from the remote control.