Deciphering Infrared Communication, using the propeller

I wrote this as a final project in my microprocessor class this past semester. It uses Spin (the programing language used on the Parallax Propeller) and Propeller assembly. Feel free to modify the code, I only ask that you give me credit, and if you like it and meet me some day you consider buying me a beer, I am broke after all. I intend to port it to the Arduino platform.

Here comes the code, if you have any questions I’d be more then happy to answer them.

Get the word document of the .spin code: Infrared binary string reconstruction

{{
********************************************************************************************************
*  Title:       Final project Test 2                                                                   *
*  Author:      Elias-John Fernandez-Aubert                                                            *
*  Version:     1.0.0                                                                                  *
********************************************************************************************************
*  HEADS UP AND WARNING!!                                                                              *
********************************************************************************************************
Most indoor lights give of some small amount of IR light, as dose the sun, computer monitors and several
other common items. WATCH OUT FOR THIS! It may cause false triggering, and or create noise in the
transmission. ALSO Glass and other reflective objects will reflect and or refract IR light once more
leading to possible false triggering or noise.
********************************************************************************************************
*  Explanation of Methodology of Construction:                                                         *
********************************************************************************************************
This code reads in an IR (infrared) communication signal, tested using a TV remote and garage door
opener, and then displays the binary string that makes up the the Infared comunication.

********************************************************************************************************
*  Overview of Functionality:                                                                          *
********************************************************************************************************
The program starts by initializing variables to make sure everything runs smoothly. 

Then a new COG is started running the assembly code, it is given the address in main memory of Command,
and from there reads in the next three variables. 

The program monitors pin0 which is connected to the IR receiver, waiting for it to go to low (logic 0) to
indicate the start of an IR pulse, which are generally negative edge trigger, also helps cut down on false
triggering due to noise I have found.

When it does go low (logic 0) the assembly starts reading in the time each time the state changes on pin0
this does mean that the last element of the array stores garbage and often the time differences from the
previous element is several seconds.

Once the array has been read in and stored in main memory the spin code takes control once more, having
waited for one second before executing, this is more then enough for the whole IR pulse to have passed,
as each section is at most several millisecond long. The time difference is then calculated by
subtracting the first element from the second and so on till it hits a time of zero, meaning no IT data
was collected at that point. Then the time differences are displays the in second format.

Next the array is searched for the smallest time difference which would be the time it takes for a single
binary bit to be transmitted. The code could be improved by collecting all the time differences that
represent a single bit, and averaging them together to help cut down on possible interference from
outside noise.

Each element of the Array is then divided by the time difference, since this is done with integers the
numbers are automatically truncated, this may or may not mess up the reconstruction of the IR pulse, I
haven't had any problems with it, but it is possible. Once this is done, it is displayed showing how many
time periods passed for each section of 1's or 0's

Lastly the IR pulse is reconstructed by a double nested loop, the outer one steps though the array while
the inner one prints out a 1 or 0 based on if it's index position is even or odd, and keeps doing so
until it has done so a number of times equal to the time periods now stored at that index position.
}}

CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

  _RX           = 31
  _TX           = 30
  ARRAY_SIZE    = 256

OBJ
  SPORT   : "FullDuplexSerial"
  FM      : "FloatMath"
  FS      : "FloatString"

VAR
  long    Index                                         ''used for stepping through the array
  long    Command                                       ''command is used to pass commands back and forth from the cog
  long    Size                                          ''the size of the array
  long    IR_bit[ARRAY_SIZE]                            ''the array that will hold the time stamps of the IR data sent
  long    Time_diff                                     ''used to store the differances in time between IR pulses
  long    Binary_array[ARRAY_SIZE]                      ''will hold a modified array for easier manipulation
  long    Min_time                                      ''holds data used to build the binary IR transmissions
  long    Limit                                         ''used to know when to end the IR transmissions

PUB Start

  dira[0] := 0                                          ''sets pin0 to input so that it can be read with out getting garbage

  SPORT.start(_RX,_TX,%0000,19200)                      ''start serial port to display messages
  Command := 1                                          ''sets up the variables just to be sure
  Index := 0
  Size := ARRAY_SIZE
  Min_time := posx                                      ''sets Min_time to the larges possible number

  longfill(@IR_bit, $feed_face, ARRAY_SIZE)             ''fill array with $feedface so if $feedface shows up it's obvious something went wrong

  SPORT.str(STRING("Starting assembly in a new cog:"))  ''print a string to the screen to indicate activity
  SPORT.tx(13)

  cognew(@IR_capture,@Command)                          ''starts a new cog running the assembly code  

  SPORT.str(STRING("Ready:"))                           ''indicates the cog is ready to receive IR transmissions
  SPORT.tx(13)
  waitpeq(0, 1, 0)                                      ''waits for Pin0 to go low indicating the start of a IR transmission
  SPORT.str(STRING("Data Capture Started:"))            ''print a string to the screen to indicate activity
  SPORT.tx(13)
  waitcnt(clkfreq+cnt)                                  ''waits a second to make sure the assembly has done it's magic  

  SPORT.str(STRING("Printing the differences in time "))
  SPORT.tx(13)
  SPORT.dec(1)
  SPORT.str(STRING(": "))
  SPORT.dec(0)
  SPORT.tx(13)
  Index := 1                                            ''start array loop at second long, the first will always be zero
  repeat while Index < Size
    Time_diff := ||(IR_bit[Index] - IR_bit[Index - 1])  ''get the time difference as a floating point number
    Binary_array[Index] := Time_diff
    Time_diff := FM.FMul(FM.FFloat(Time_diff), 0.0000000125)
    if (Time_diff == 0)                                 ''checks to see if Time_diff is zero, if it is then no more data was collected
      SPORT.str(STRING("End of IR transmission"))       ''if Time_diff is zero the stop displaying the data and move on
      SPORT.tx(13)
      quit
    SPORT.dec(Index + 1)
    SPORT.str(STRING(": "))                             ''display the time difference in floating point form
    SPORT.str(FS.FloatToString(Time_diff))
    SPORT.str(STRING(" seconds"))
    SPORT.tx(13)
    Index++
                                                        ''starts at the first position that holds relevant data
  Index := 1
  SPORT.str(STRING("Searching for the smallest time differance"))
  SPORT.tx(13)
  repeat while Index < Size                             ''searches the array for the smallest non-zero number
    if (Min_time > Binary_array[Index]) and (Binary_array[Index] <> 0)
      Min_time := Binary_array[Index]
    if (Binary_array[Index]  == 0)                      ''eventually will hit a 0 where no data was copied in
      SPORT.str(STRING("Smallest time differance found it is: "))
      SPORT.dec(Min_time)
      SPORT.tx(13)
      quit
    Index++

  Index := 1
  SPORT.str(STRING("Finding out how many pulses happend per time period"))
  SPORT.tx(13)
  SPORT.dec(1)
  SPORT.str(STRING(": "))
  SPORT.dec(0)
  SPORT.tx(13)
  repeat while Index < Size                             ''while stepping through the array divide each element by Min_time to get the period of it's logic state
    Binary_array[Index] := (Binary_array[Index] / Min_time)
    if (Binary_array[Index] == 0)
      quit
    SPORT.dec(Index + 1)
    SPORT.str(STRING(": "))
    SPORT.dec(Binary_array[Index])
    SPORT.tx(13)
    Index++
  SPORT.tx(13)  

  Limit := ((Index - 1))                                ''Index - 1 is the the second to last location in the array where the last meaningful long is
  SPORT.str(STRING("Index Value of the last long with meaningful information"))
  SPORT.tx(13)
  SPORT.dec(Limit)
  SPORT.tx(13)

  SPORT.str(STRING("Reconstructing the IR pulse"))
  SPORT.tx(13)
  SPORT.dec(0)
  Index := 1
  repeat while Index < Limit
    repeat while (Binary_array[Index] > 0)
      if ((Index // 2) == 0)                            ''check to see if the number is even or odd. since the first time period is always 0, then odds are 0's and evens are 1's
        SPORT.dec(0)
      if ((Index // 2) <> 0)
        SPORT.dec(1)
      Binary_array[Index]--
    Index++

  SPORT.tx(13)
  SPORT.str(STRING("End of program"))
  SPORT.tx(13)

DAT

              ORG       0
IR_Capture    mov       cmd,par                         ''copies memory address of the Command to cmd
              mov       ptr,par                         ''copies memory address of the Command to ptr
              add       ptr,#4                          ''moves the pointer from Command to Size
              mov       ptr2,par                        ''copies memory address of the Command to ptr2
              add       ptr2,#8                         ''moves the pointer from Command to IR_bit[] The storage array
              mov       count,#0                        ''double checks that count is 0
              rdlong    length,ptr                      ''reads the size of the array into size

Loop          waitpeq   state0,pin                      ''waits for P0 to go to 0
              mov       time,cnt                        ''copies the contents of the CNT register into time
              wrlong    time,ptr2                       ''writes the time to the array in main memory
              add       ptr2,#4                         ''moves the pointer to the next long in the array
              add       count,#1                        ''increments the counter by one
              cmp       count,length wz                 ''checks if count is equal to size, if it is write a 1 to the the zero flag
        if_z  jmp       #Exit                           ''if the zero flag is a 1 then jump to exit

              waitpeq   state1,pin                      ''waits for P0 to go to 1
              mov       time,cnt                        ''copies the contents of the CNT register into time
              wrlong    time,ptr2                       ''writes the time to the array in main memory
              add       ptr2,#4                         ''moves the pointer to the next long in the array
              add       count,#1                        ''increments the counter by one
              cmp       count,length wz                 ''checks if count is equal to size, if it is write a 1 to the the zero flag
        if_nz jmp       #Loop                           ''if the zero flag is NOT 1 then jumps back to waiting for P0 to go to 0

Exit
              wrlong    end,cmd                         ''writes zero to the Command, to let the spin code know the cog is done
              cogid     id                              ''get COG id
              cogstop   id                              ''use id to stop COG

pin           long      1                               ''the pin being used as input from the IR receiver
state1        long      1                               ''state of the pin, logic high 1
state0        long      0                               ''state of the pin, logic low 0
end           long      0                               ''command value to end the program     

cmd           res       1                               ''variable used to pass commands back and forth
count         res       1                               ''counter for to count the number of times the code loops, checked against length
id            res       1                               ''id of the COG, used to stop the COG
ptr           res       1                               ''address in main memory of the size
ptr2          res       1                               ''address in main memory of the array
length        res       1                               ''size of the array used for bound checking on the array
time          res       1                               ''time on the CNT register

              FIT                                       ''checks to see if the program will fit into COG memory

Leave a comment

Filed under electronics, projects

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s