Building/transmitting NEC IR

lewisg

Senior Member
First off, it's been a long time since I have been to this most excellent forum! I ran across a problem yesterday that cried PICAXE and sure enough it responded! I sure hope Hippy is still around!

The issue was lightning struck a long ago client's house and the equipment we used in the whole house audio system is no longer made (Niles RVL-6) and they wanted something much simpler, specifically a volume control knob in each room with speakers. Since the speaker wires don't go through the room's control box that isn't easy. Additionally they are not interested in using their phones/pads to turn speaker volume up or down. The only wire available at the control locations is 22/2 with shield. BIG house, running more wire is cost prohibitive. (I got involved AFTER the wire was run. I'd always run cat5...)

So anyway some of the older Xantech amps were IR controlled and fairly easy to find. (a ZPR68 could do in a pinch) So all I needed to do is transmit a few (vol+ vol- vol0) IR commands in relationship to the position of a potentiometer. Naturally this forum is where I looked! I found quite a few examples of code including some excellent Hippy code that I don't understand BUT got me started! The other invaluable resources were NEC Infrared Transmission Protocol and AnalysIR.

Hardware wise this is a piece of cake. The 22/2 ws cable has grnd, +12VDC and a conductor for the IR signal to go back on to the amp. I'll likely have to make a buffer circuit but for testing I'm using a IR LED. AND IT WORKS! Tested with a ZPR68 and while I'll have to make some clean up when the amp arrives it's real close!

As normal I hate the code I have written! While it works and fits I know better could be done and hopefully someone here can help me (and others in the future) out.

NEC protocol is fairly simple and is built around 562.5us. A big start burst of 16x562.5us is followed by a space of 8x562.5us then data. While it would be nice to do that math I didn't. Data 0's are a burst of 562.5us followed by a space of 562.5us. Data 1's are a burst of 562.5us followed by a space of 3x562.5us.

The data consists of a 8bit device address and a 8bit command. Each are sent followed by their logical inverse making a 32 bit string followed by a final burst of 562.5us. I didn't get into repeat codes but they are the big start burst, space and the final burst.

Still with me? The most excellent IRtoy coupled with AnalysIR software are amazing! I used to do this stuff with a scope but that has changed! In any case it identified the protocol, modulation and decoded the commands! So what I needed to send was device code 38 and commands 38 (off), F0 (vol+) and C8 (vol-) (for actual use there will be different ones). After trying to understand Hippy's method I gave up and just hard coded it. I, along with those interested, am sure something better is possible! Hippy's code was for RC6 which is built differently but his method is VERY solid! Build a bunch of bits, iterate through and then transmit. I , and others, have found timing is too critical to go dinking back and forth once transmission starts.

The parts of Hippy's code I don't understand are;
Code:
SkyChanUp:
  Gosub ForSky
  bits = 48
  bitx = %1000001011000000 : Gosub SetToggleTime
  bitx = %0000000000000000 : Gosub SetToggleTime
  bitx = %0000011000000000 : Gosub SetToggleTime
  Goto SendIr

SetToggleTime:
  For bitCount = 0 To 15
    If bits <> 0 Then
      If bitx >= $8000 Then
        @ptrInc = set1
      Else
        @ptrInc = set0
      End If
      bitx = bitx << 1
      bits = bits - 1
    End If 
  Next
  @ptr = 0
  Return

SendIr:
  High C.0
  ptr = 0
  Do
    Toggle C.0
    PauseUs time
    time = @ptrInc
  Loop Until time = 0
  High C.0
  ptr = 0
  Return
I don't know if an explanation would be more useful or simply some example code that is applicable to NEC protocol. Specifically I can't figure out how to manipulate the space after the big start burst. That is where I gave up and went the LONG way...

Attached is my code and a screen shot of AnalysIR when I was halfway through the string. Hope this interests, helps or provides amusement for someone!

View attachment XantechControl1.basIRtoy1.jpg


P.S. AnalysIR has come a long was since that screenshot but I don't have the new version up and running. I'm sure the PICAXE Programming Editor has too but...
 

lewisg

Senior Member
Thanks for the reply's!

I looked at both of those posts before I started. Neither "turned my crank" like Hippy's code but I did pick up some inspiration. The more I think about it I'm starting to understand how loading the memory space works and see the advantage of doing it that way. Like I said what I have works and only fills about 1/4 of a 20X2 chip.

I continue to be amazed at the value of PICAXE. I hadn't done a PICAXE project in at least a year and in about 4-5 hours I had a working proof of concept that could be close to being the final. Nicer code would be nice but... I have a BIG toolbox and that usability (enabled by this forum) is impressive.

Besides PICAXE I cannot say enough good things about AnalysIR. I know a lot of you are "roll your own" folks but for under $50 Irtoy + AnalysIR can display and decode two captures. Put the remote capture in the bottom window and then capture your work as many times as needed in the top window. And that is just the start of it's capabilities. SO much easier than any thing I have used before!

I know I wrote too much but having been on the reading end of long ago threads more is usually better. Glad all of you are still here! I spent so much time in the archives I forgot to look at current posts before posting!
 

lewisg

Senior Member
Is there any way to do the SendIR: "if bitX = 0 then" in a loop?

Code:
Main:

  b0 = $38                'device address
  b1 = NOT b0             'logical inverse

  Do
    If pinC.1 = 1 Then
      b2 = $F0            'volume up command
      b3 = NOT b2
      Gosub SendIr            
      SetFreq M8
      Pause 300
    End If
    If pinC.2 = 1 Then
      b2 = $37            'volume down command
      b3 = NOT b2   
      Gosub SendIr
      SetFreq M8
      Pause 300
    End If
    If pinC.3 = 1 Then
      b2 = $38            'off command
      b3 = NOT b2
      Gosub SendIr
      SetFreq M8
      Pause 300
    End If
    pause 100 
  Loop


SendIR:
  SetFreq M64
  pwmout pwmdiv4, C.5, 104, 211   '38kHz at 64MHz
  Low C.0
  pauseUS HEAD            'pause for start high - 9ms
  High C.0                'turn off IR LED
  pauseUS QUIET           'pause before data - 4.5ms

  if bit7 = 0 then
    Low C.0               'ZERO
    PauseUS BitTime 
    High C.0  
    PauseUS ZeroOff       
  else
    Low C.0               'ONE
    PauseUS BitTime
    High C.0
    PauseUS OneOff
  end if
    
*** break in code so it doesn't get as boring ***
*** there are 32 if statements here stacked   ***
*** as documented in manual two  (yikes!)     ***              
    
  if bit24 = 0 then
    Low C.0               'ZERO
    PauseUS BitTime 
    High C.0  
    PauseUS ZeroOff       
  else
    Low C.0               'ONE
    PauseUS BitTime
    High C.0
    PauseUS OneOff
  end if
    
  Low C.0                 'ETX marker
  PauseUS BitTime 
  High C.0
  
  Return
 

rossko57

Senior Member
Is there any way to do the SendIR: "if bitX = 0 then" in a loop?
You could write some code that left-shifts (or right-shifts) the 32-bit word and sends each bit as they pop off the end.
It's going to be much slower and trash the timing though.
 

lewisg

Senior Member
It's going to be much slower and trash the timing though.
Dang... The timing is already flakier than my first attempt as seen by having to used different delay values.

I'm now trying to figure out how to store delay values in RAM. Is there a way to write and read words at 80 and above?
 

AllyCat

Senior Member
Hi,

Is there a way to write and read words at 80 and above?
Yes, there's POKE/PEEK pointer , WORD w1 etc.. but I doubt if it's any faster than POKE/PEEK pointer , b2 , b3 , for example.

However, you should be able to define an adequate time delay using only a byte value. But you do need to accept that PICaxe's interpreted Basic is far from ideal for defining precise or fast time delays.

Cheers, Alan.
 

Rick100

Senior Member
Hello lewisg,

I worked on some code a while back to produce IR control signals for different devices. It runs on a 14M2 using the hardware PWM and DSM for the output to the led. It can output up to 64 on/off pairings with a minimum pulse width of approximately .000266 seconds. The method I used was to build a sequence of bytes in ram and play them back with inline code using @bptrinc to read the bytes from ram into the variable used for the pauseus command . A jump table(on goto command) was used to start at a variable place in the inline code. This avoids the if then statement a loop would need. Since you know you need 34 pairings you could omit the on goto command and 30 of the inline code sections. My data for the timing was stored in eeprom and read into ram. That's simple and works fine for a couple of codes but could be replaced by a routine to build the ram table from 2 word variables(32 bits) stored in ram or eeprom. I used the code to control an LG tv (power button) and the timing and data structure look like what you described in your first post. If you have a 14M2 you could probably just edit the eeprom statements for your codes and try it. The pokesfrs at the start of the program set up the DSM. It's been about 8 months since I wrote it so I hope I haven't forgot anything.

Here's the code.
View attachment IR_universal_6.bas

Edit:
Ignore the #macro parts.

Good luck,
Rick
 

lewisg

Senior Member
Thanks for the code! Parts of it look familiar and other parts...

I keep trying to implement Hippy's method of building a string of toggle times. Once built the loop execution may be acceptable. I tried doing it with byte*10 but I couldn't get the pauseUS values close enough for reliability. The off commands worked ever time, volume up most of the time and volume down not so much. the I do really like this:
Code:
  b0 = $38                'device address
  b1 = NOT b0             'logical inverse
Not only does it do away with the PITA of hand coding but it allows for a lot of commands if needed. However after testing the output from Hippy's original code using AnalysIR I see some flakiness. That does not mean it doesn't work since consumer IR is fairly imprecise. When I used to program fairly large home automation systems IR problems could hold us up for days.

I'll probably keep poking at this and see if I can get it to work. But the amp came in and I have decided to try a rotary encoder so it's back to the original code until those two items are worked out. In any case I'll post what I come up with because that is the beauty of this forum!
 
Top