Ir send and receive troubles
Hi everyone, I have been trying to make a laser tag gun using the propeller, however, I am having troubles with one big part of it. I am having troubles with sending and receiving of the ir signal. I have tried looking at some of the files on object exchange but I am having troubles understanding them. Can anyone help me with this? I'm wanting to be able to push a button (trigger) and then send a playerid. Thanks for any help.

Comments
-- http://www.parallax.com/Portals/0/Downloads/docs/cols/nv/prop/col/nvp4.pdf
The article discusses receiving and transmitting.
We all learn at different rates and you may need to read that article more than a few times for it to sink in -- I buy a lot of books so I can keep going back to them. Please do not think that the availability of an object is going magically make you understand something (even if you can successfully use it). For many it has the opposite effect: once they have working object they don't bother trying to understand it. I would suggest that you should try to understand SIRCS so that you can use it as a springboard for your laser-tag system. I'm working on a commercial product for that now and have no troubles, but then, I took the time to really understand SIRCS before thinking I could code a commercial laser-tag system (with my own IR protocol).
All of my articles come with fully-commented object code and demos, though I don't put them all in ObEx. You may have noticed that the article is hosted by Parallax; well, they host the code, too. You can find it on this page (column #4):
-- http://www.parallax.com/Resources/NutsVoltsColumns/TheSpinZone/tabid/781/Default.aspx
The reason there are no code comments in what you read is that this is the raw material sent to the magazine which uses a 2-column format; this means each line has to be very short, hence, no comments.
This is the link to a protocol reference that I use: http://www.sbprojects.com/knowledge/ir/sirc.php
Henry Ford once said, "If you think you can or you think you can't... you're right!"
If I may be so bold... perhaps you should change your mindset to, "Spin is an interesting language that I haven't taken the time to master yet." -- this change could be what sets your mind free to master it! There is a BASIC for the Propeller but dare I say you won't find it any easier than Spin. You're still dealing with a multi-core processor and for many programs that requires parallel thinking. Spin is designed specifically for the Propeller and, in my opinion, is by far the easiest language to master for it. You wan't confusion? -- have a look at Forth!
IR_Remote_NewCog.spin in the OBEX
Simplified / reduced to what I thought I could understand and work with.
It works on my QuickStarts with an IR decoder attached.
It calls the IR_Remote.spin object's getSonyCode method.
(Did I say that right?)
..which does the Sony Receive trick...
That's an example of receiving codes from a Sony remote control
which sounds like half the problem solved?
But I can't claim to understand the inner workings of that trick.
It's all timing.
Jon, your articles are amazing, and I mean that.
You've covered a lot of stuff in the depth that it takes to learn to work with it.
But we beginners still need to pull together code.
That's hard in the beginning.
I've always wanted to ask one of the real gurus to "read" some piece of code.
"How do you say that in English?"
What it's doing, and why.
Especially Pasm.
I didn't like Spin all that much at first, either.
I'd STILL much prefer more traditional structural phrasing.
But it's workable.
And you might note:
The language is ideally suited to the Propeller and it's unique architecture.
So, snakie, just read it....
Quckstart board with IR decoder on pin whatever it sez.
{{ IR remote control lights up the appropriate LED. Numbers 7 through 8 CH^ increments LED, CHv Decrements LED Vol< and VOL> work backwards so the LEDs go in the same direction as the buttons. Vol and CH both wrap around. PWR runs chase and forth once. OK short blink all LEDs once }} CON _CLKMODE = XTAL1 + PLL16X ' 80 Mhz clock _XINFREQ = 5_000_000 IRdetPin = 2 ' IR Receiver - Propeller Pin BlinkPin = 16 ' Proof of life OBJ ir : "IR_Remote" VAR byte IRcode , LED, IVcode, X LONG MS001, Twait PUB Init | freq, index, cog, lcode Twait := ClkFREQ / 20 '/3 for slo-mo MS001 := CLKFREQ / 1_000 ' define count for 1 millisec outa[ 16..23 ] := $00 ' all LED pins off ' dira[ 16..23 ] := $FF ' all LED pins outputs ' LED := 6 LEDON(LED) cog := ir.Start(IRdetPin, @IRcode) ' Propeller pin connected to the IR receiver, address of variable if cog > 0 repeat If IRcode <> ir#NoNewCode lcode := IRcode ir.Start(IRdetPin, @IRcode) ' set up for next code case lcode 'Numbers ir#zero : 'ignore ir#one : LED := Lcode ir#two : LED := Lcode ir#three : LED := Lcode ir#four : LED := Lcode ir#five : LED := Lcode ir#six : LED := Lcode ir#seven : LED := Lcode ir#eight : LED := Lcode ir#nine : 'ignore '----------------------------------------------------- 'Channel ir#chUp : 'pst.str(string("chUp ")) if LED < 7 LED++ else ' wrap around LED := 0 ir#chDn : 'pst.str(string("chDn ")) if LED > 0 LED -- else LED := 7 'Volumn ir#volUp : 'pst.str(string("volUp")) if LED > 0 LED-- else LED := 7 ir#volDn : 'pst.str(string("volDn")) if LED < 7 LED++ else ' wrap around LED := 0 ir#ok : 'pst.str(string("ok ")) repeat 1 outa [16..23] := 0 outa [16..23] := %11111111 waitMS(101) ir#power : 'pst.str(string("power")) X := LED Twait := ClkFREQ / 20 '/3 for slo-mo dira[16..23] := %11111111 outa[16..23] := 0 repeat 1 repeat Led FROM 16 to 23 outa[LED..LED-2] := %111 WaitCnt(Twait+cnt) outa[Led-2] := 0 WaitCnt(Twait+cnt) outa [LED-1] := 0 WaitCnt(Twait+cnt) repeat Led FROM 23 to 16 outa[LED..LED+2] := %111 WaitCnt(Twait+cnt) outa[Led+2] := 0 waitCnt (Twait+cnt) outa [LED+1] := 0 WaitCnt(Twait+cnt) LED := X LEDON(LED) PRI LEDON(n) outa [16..23] := 0 outa [16+n]:= 1 PRI LEDOFF(n) outa [16..32] := 0 PUB WaitMS(W) 'wait for W milliseconds W := W * MS001 WaitCNT (W+cnt){{ IR_Remote_NewCog.spin Tom Doyle 2 March 2007 Panasonic IR Receiver - Parallax #350-00014 Receive and display codes sent from a Sony TV remote control. See "Infrared Decoding and Detection appnote" and "IR Remote for the Boe-Bot Book v1.1" on Parallax website for additional info on TV remotes. The procedure uses counter A to measure the pulse width of the signals received by the Panasonic IR Receiver. The procedure waits for a start pulse and then decodes the next 12 bits. The entire 12 bit result is returned. The lower 7 bits contain the actual key code. The upper 5 bits contain the device information (TV, VCR etc.) and are masked off for the display. Most TV Remotes send the code over and over again as long as the key is pressed. This allows auto repeat for TV operations like increasing volume. The volume continues to increase as long as you hold the 'volume up' key down. Even if the key is pressed for a very short time there is often more than one code sent. The best way to eliminate the auto key repeat is to look for an idle gap in the IR receiver output. There is a period of idle time (20-30 ms) between packets. The getSonyCode procedure will wait for an idle period controlled by the gapMin constant. This value can be adjusted to eliminate auto repeat while maintaining a fast response to a new keypress. If auto repeat is desired the indicated section of code at the start of the getSonyCode procedure can be commented out. The procedure sets a tolerance for the width of the start bit and the logic level 1 bit to allow for variation in the pulse widths sent out by different remotes. It is assumed that a bit is 0 if it is not a 1. The procedure to read the keycode ( getSonyCode ) is run in a separate cog. This allows the main program loop to continue without waiting for a key to be pressed. The getSonyCode procedure writes the NoNewCode value (255) into the keycode variable in main memory to indicate that no new keycode is available. When a keycode is received it writes the keycode into the main memory variable and terminates. With only 8 cogs available it seems to be a good idea to free up cogs rather than let them run forever. The main program can fire off the procedure if and when it is interested in a new keycode. }} CON _CLKMODE = XTAL1 + PLL16X ' 80 Mhz clock _XINFREQ = 5_000_000 NoNewCode = 255 ' indicates no new keycode received gapMin = 2000 ' minimum idle gap - adjust to eliminate auto repeat startBitMin = 2000 ' minimum length of start bit in us (2400 us reference) startBitMax = 2800 ' maximum length of start bit in us (2400 us reference) oneBitMin = 1000 ' minimum length of 1 (1200 us reference) oneBitMax = 1400 ' maximum length of 1 (1200 us reference) ' Sony TV remote key codes ' these work for the remotes I tested however your mileage may vary one = 0 two = 1 three = 2 four = 3 five = 4 six = 5 seven = 6 eight = 7 nine = 8 zero = 9 chUp = 16 chDn = 17 volUp = 18 volDn = 19 ok = 20 power = 21 last = 59 VAR byte cog long Stack[20] PUB Start(Pin, addrMainCode) : result {{ Pin - propeller pin connected to IR receiver addrMainCode - address of keycode variable in main memory }} stop byte[addrMainCode] := NoNewCode cog := cognew(getSonycode(Pin, addrMainCode), @Stack) + 1 result := cog PUB Stop {{ stop cog if in use }} if cog cogstop(cog~ -1) PUB getSonyCode(pin, addrMainCode) | irCode, index, pulseWidth, lockID {{ Decode the Sony TV Remote key code from pulses received by the IR receiver }} ' wait for idle period (ir receiver output = 1 for gapMin) ' comment out "auto repeat" code if auto key repeat is desired ' start of "auto repeat" code section dira[pin]~ index := 0 repeat if ina[Pin] == 1 index++ else index := 0 while index < gapMin ' end of "auto repeat" code section frqa := 1 ctra := 0 dira[pin]~ ' wait for a start pulse ( width > startBitMin and < startBitMax ) repeat ctra := (%10101 << 26 ) | (PIN) ' accumulate while A = 0 waitpne(0 << pin, |< Pin, 0) phsa:=0 ' zero width waitpeq(0 << pin, |< Pin, 0) ' start counting waitpne(0 << pin, |< Pin, 0) ' stop counting pulseWidth := phsa / (clkfreq / 1_000_000) + 1 while ((pulseWidth < startBitMin) OR (pulseWidth > startBitMax)) ' read in next 12 bits index := 0 irCode := 0 repeat ctra := (%10101 << 26 ) | (PIN) ' accumulate while A = 0 waitpne(0 << pin, |< Pin, 0) phsa:=0 ' zero width waitpeq(0 << pin, |< Pin, 0) ' start counting waitpne(0 << pin, |< Pin, 0) ' stop counting pulseWidth := phsa / (clkfreq / 1_000_000) + 1 if (pulseWidth > oneBitMin) AND (pulseWidth < oneBitMax) irCode := irCode + (1 << index) index++ while index < 11 irCode := irCode & $7f ' mask off upper 5 bits byte[addrMainCode] := irCodewelcome to the propeller-forum. Please give us more information where you have trouble to understand.
There are a lot of possible reasons why your lasergun isn't working yet.
So I want to ask some questions:
Did you do some tests if your IR-Diode is sending anything at all?
Did you do some tests if your IR-receiver is receiving anything at all?
What kind of IR-Diode and IR-receiver are you using?
Working with microcontrollers isn't as easy as working with standard USB-devices (which work plug and play)
You wrote that you did understand an example in basic. Can you please post this basic-example?
can you please post your SPIN-code where you have trouble to understand it?
Keep the questions coming!
best regards
Stefan
@cavelamb that code helps me understand this more
@StefanL38 when using picbasic i can have something like
send_data: 'send 14 bits CCP1CON = 12 'send header pulse pauseus mtheader CCP1CON = 0 @ bcf PORTC, 2 main_byte: databyte = p_ID & %01111111 '(packet_ID=0; shot) GoSub send_byte send_byte: 'send 8 bits (databyte) For bit_counter = 1 TO 8 pauseus mtspace 'space between data pulses IF databyte.7 = 1 Then trans1 CCP1CON = 12 'send zero pauseus mtbit0 CCP1CON = 0 @ bcf PORTC, 2 @ rlcf _databyte,F GoTo bypassx trans: CCP1CON = 12 'send one pauseus mtbit1 CCP1CON = 0 @ bcf PORTC, 2 @ rlcf _databyte,F bypassx: Next bit_counter Returnis there an easy way to put this into spin or something spin can use?
I havent been able to get an ir-reciever to get anything at all yet but going to try playing with JonnyMac's code from the link.
Also i find sometimes my propeller does some odd stuff for example i had an led and a button and i can take the button out and put my hand close to the wires and it will turn on the led anyone have any idea why this is happening and can i stop it?
@cavelamb thanks for the code that helped me understand it more, its better then the file i found previously.
@StefanL38 i have not been able to recieve or send (atleast i think) i put an led in to tell me when it gets to each step and it doesnt go past before the send bit
I am using a 56 khz reciever TSOP1256.
and SFH 4547 high powered emitter
This code is what I would use on a PIC. Its in PICbasic is there any way to convert this easily to spin?
[/FONT][/COLOR]send_data: 'send 14 bits CCP1CON = 12 'send MT header pulse (2.4mS) pauseus mtheader CCP1CON = 0 @ bcf PORTC, 2 Main_byte: databyte = P_ID & %01111111 '(packet_ID=0; shot) GoSub send_byte send_byte: 'send 8 bits For bit_counter = 1 TO 8 'send 8 data bits pauseus mtspace 'space between data pulses IF databyte.7 = 1 Then trans CCP1CON = 12 'send zero pauseus mtbit0 CCP1CON = 0 @ bcf PORTC, 2 @ rlcf _databyte,F GoTo bypassx trans: CCP1CON = 12 'send one pauseus mtbit1 CCP1CON = 0 @ bcf PORTC, 2 @ rlcf _databyte,F bypassx: Next bit_counter ReturnAlso my propeller board does some random things sometimes. If i have a program to make a button turn on a led sometimes the led can turn on by itself if your hand is close to it. If you take the button out it still does it why is this and can i fix it?
Thank you everyone for your help
about the LED-turn on/off phenomen: welcome to the analog part of digital circuits!
If an IO-pin is configured as input the input-impedance (the input-resistance) is so high that even a short wire
is enough to trigger the input high through the electromagnetic noise that is in the air evereywhere.
To avoid this even the simplest switch needs a pull-down-resistor.
instead of IO-Pin
switch
+3.3V you should use
GND---PullDownResistor----10kOhm---IO-Pin---switch
+3.3V
als long as the switch is opened the IO-pin "sees" 0.0V (through the 10 kOhm-resistor) if the switch is closed
the IO-pin "sees" 3.3V as all voltage drops over the pull-down-resistor.
So an opened switch creates a low
a closed switch creates a high.
But there is another effect. switch-bouncing. mechanical switches open and close in the millisecond area a few times if you change their state.
So for real proper operation you need debouncing too.
About the code: If you name your variables less cryptic but much more self-explaining I could write you a spin-equivalent.
Or you could write the SPIN-equivalent with questions and I will answer them
As you are using a 56kHz decoder. your diode must "send" a 56kHz signal. This can't be done in SPIN only when using a counter of a cog.
@JonnyMac: do you have a code-snippet handy for sending a modulated IR-signal?
For the further progress of your project. chunk down the whole project in small pieces.
1 piece creating a 56kHz signal.
2 piece testing if the receiver is reacting on this signal
3 sending a hardcoded test-bit-stream and checking if the receiver decodes the bitstream
you might think Oh man that will take a lot of time! I can tell you from my experience: investing this time in the beginning saves you 3 to 10 times later for searching
small (but "efficient") bugs in a big grown code
best regards
Stefan
P.S: in the meantime I tried to understand your picbasic-code
but I havesome questions. So my questions are a good demonstration of what you should do in SPIN
My questions are inserted behind a "!" similar to a comment
send_data: 'send 14 bits CCP1CON = 12 'send MT header pulse (2.4mS) pauseus mtheader !what does this line of code do pausing execution for the value inside mtheader?? CCP1CON = 0 @ bcf PORTC, 2 !does this define port C pin 2 as output? Main_byte: databyte = P_ID & 111111 '(packet_ID=0; shot) GoSub send_byte send_byte: 'send 8 bits For bit_counter = 1 TO 8 'send 8 data bits pauseus mtspace 'space between data pulses IF databyte.7 = 1 Then trans CCP1CON = 12 'send zero pauseus mtbit0 CCP1CON = 0 @ bcf PORTC, 2 !please explain the meaning of "@ bcf" @ rlcf _databyte,F !please explain the meaning of "@rlcf _databyte,F" GoTo bypassx trans: CCP1CON = 12 'send one pauseus mtbit1 !where does "mtbit1" gets its value??? CCP1CON = 0 @ bcf PORTC, 2 @ rlcf _databyte,F bypassx: Next bit_counter ReturnFor anyone willing to spend a few minutes studying the Propeller, controlling a modulated IR LED becomes trivial.
1) It's easy to setup a counter for any popular IR modulation frequency
2) The output of the counter is OR'd with the normal pin driver in that cog
What this means is that you assign a counter (NCO mode) to a pin and make that pin an output. With this you have your modulation signal. The easiest way to control a IR LED with this scheme is to connect the output pin to the CATHODE side of the IR LED circuit (connect the anode to 3.3v through a suitable resistor). Here's where it gets fun: if you make that pin high you will stop the modulation.
Why? See #2 above. If either input to an OR gate is high then the output his high. As the output of the OR gate is to the cathode side of the LED circuit the LED will turn off. As soon as you take the pin low counter output will toggle the pin again. Easy-peazy. My SIRCS TX object does exactly this.
For those just wanting to experiment you can use this Spin method (from my standard template) to set a pin to modulate an LED. To control the LED take that same pin high (off) and low (modulated).
pub set_freq(ctrx, px, fx) '' Sets ctrx to frequency fx on pin px (NCO/SE mode) '' -- fx in hz '' -- use fx of 0 to stop counter that is running if (fx > 0) fx := ($8000_0000 / (clkfreq / fx)) << 1 ' convert freq for NCO mode case ctrx "a", "A": ctra := ((%00100) << 26) | px ' configure ctra for NCO on pin frqa := fx ' set frequency dira[px] := 1 ' make pin an output "b", "B": ctrb := ((%00100) << 26) | px frqb := fx dira[px] := 1 else case ctrx "a", "A": ctra := 0 ' disable counter outa[px] := 0 ' clear pin/driver dira[px] := 0 "b", "B": ctrb := 0 outa[px] := 0 dira[px] := 0mtbit0 CON 600 This is in a file for variables and constants
@JonnyMac what would it look like connected I'm not sure I understand that part.
Thank you guys for the help, I'm slowly getting used to spin and some parts begin to make a little more sense now.
The main reason I am using the propeller is for the different cogs allowing the ability to do more and be faster.
Con mtheader = 2400 mtspace = 600 mtbit0 = 600 mtbit1 = 1200 P_ID = x IR_LED = y Var byte bit_counter Pub main Dira[IR_LED]~~ Repeat If ina[trig] send_data Pub send_data 'send 14 bits CCP1CON = 12 'send MT header pulse (2.4mS) pauseus mtheader CCP1CON = 0 Outa[IR_LED]~ Pub Main_byte databyte = P_ID & 111111 '(packet_ID=0; shot) send_byte Pub send_byte 'send 8 bits For bit_counter = 1 TO 8 'send 8 data bits pauseus mtspace 'space between data pulses IF databyte.7 = 1 Then trans CCP1CON = 12 'send zero pauseus mtbit0 CCP1CON = 0 Outa[IR_LED]~ @ rlcf _databyte,F bypassx Pub trans CCP1CON = 12 'send one pauseus mtbit1 CCP1CON = 0 Outa[IR_LED]~ @ rlcf _databyte,F Return Pub bypassx Next bit_counter Returnthank you for posting your code.
From this I can see you are a basic-guy. Now basic is a programming-language that is quite-non-structural compared to other languages like C, Pascal, Delphi, Java and SPIN.
(visual basic from microsoft is called "basic" but it is much more structural than old basic versions like the one on the famous C64)
Structural means here you have a set of predefined commands that can be used do define new commands that can be used (and called) exactly the same way as the basic ones.
This allowes to code in a way that each part of the code that is a senseful unit will become one new powerful command.
a small example a command to wait for microses:
There is no predefined command for that in SPIN. But you can easily expand the set of predefined commands to have one
PUB PauseUs (p_waitTime) if p_waitTime < 385 p_waitTime := 385 WaitCnt(CLkFreq / 1_000_000 * p_waitTime + cnt) PUB PauseMs (p_waitTime) if p_waitTime < 1 p_waitTi= 1 WaitCnt(CLkFreq / 1_000 * p_waitTime + cnt)to use this command you write
I guess you think SPIN is 99% similar to picbasic. It is only 89% similar. So you have to learn these 11% by going through the PE-Kit lab
that shows you in small steps how the propeller-chip and SPIN works.
Download the text http://www.parallax.com/Portals/0/Downloads/docs/prod/prop/PELabsFunBook-v1.1.pdf and the sourcecode-examples
http://www.parallax.com/Portals/0/Downloads/docs/prod/prop/PELabsFunCode-v1.1.zip
and work through them. This will need time in the beginning but will speed you up later.
best regards
Stefan
I started learning C a long time ago maybe 6 years ago or so and since then not using it all through college I have forgotten it. The closest I have been since was a language for an industrial robot which was a mixture of c and basic for college.
Getting there I thank you guys for all the help.
what is the module control-register good for? Or at least can you say wich pic-type does picbasic support?
So I can lookup the manual what the module control register is good for. (the name is only half selfexplaining)
Almost 30 years ago I coded a little bit assembler for 6502. Since 2006 I have coded only delphi and with the propeller-chip.
best regards
Stefan
That said, high-performance processes really do want to be coded in Assembly which is why my IR code is. Still, you *can* do simple SIRCS in Spin as the attached program demonstrates. I probably wouldn't use this code in a commercial application, but for simple projects and fun and learning, it may serve to help you understand the implantation of an IR protocol in Spin.
Please understand that the rx_sircs method blocks the program until a code shows up. If your app needs to do other things while waiting on a code then you need to launch another cog that can wait on the code and stuff it into a global variable for you,
Again, SIRCS is an easy protocol to understand and implement. Once you do, you can modify it and create you own (as I'm presently doing for a shooting gallery game).
Edit: Renamed file to indicate SIRCS, removed bogus note about pulse timing resolution.
I've attached my PASM RX and TX objects -- they're very easy to use.
Obviously, you need to do some empirical testing. Try to transmit with the receiver enabled and see if there is bleed from the TX to RX sides. If there is, use .disable() and .enable() with the receiver side. If there's no bleed, you're good to go -- write the rest of your program. If there is, ignoring your own transmission is this easy:
EDIT: Technically, you don't need to use .disable() in this case. When you use the .enable() method after transmitting, the RX buffer is flushed; even if you RX your own code, this cleans it out.
Your 16 Mhz crystal is out of spec for the Propeller. The Data Sheet , (page 25), specifies the frequency to be between 4 and 8 Mhz. Outside of that range you may get strange results
As with most of my objects the SIRCS RX and TX code is clock frequency agnostic (though you should head the warning from Publison about the input frequency). While working on the DEFCON badge I tested it at 20MHz (5MHz, PLL = 4x) and it worked fine. SIRCS is a slow protocol, so one doesn't need blazing processor speed to run it. Using a counter to handle the IR LED modulation simplifies things a great deal, as well.