S2 Radio-controlled navigation
Phil Pilgrim (PhiPi)
Posts: 23,514
I don't know why no one has thought of this before, it's so obvious. Maybe they have, and I missed it. My apologies if so. Anyway, it's drop-dead easy to add an RC receiver to an S2 and control it with a transmitter. I added a cheapo ($25 for the full set: transmitter and receiver) 2.4 GHz HobbyKing radio to my S2, hidden inside. It connects to the hacker port via two channels -- one for throttle and one for steering -- which also powers the receiver. Here's a photo of the installation:

I adhered the receiver in place with double-stick foam tape. Connections to the hacker port were made with servo extension cables. Here's the code I used to control the S2 with the RC transmistter:
A full archive is attached.
Of course, you can add more channels if you like. The hacker port will support six total. A simple circuit that ORs all the channels together could conceivably multiplex all channels into one input. I checked my HobbyKing receiver, and there's just enough of a gap between pulses that a PASM program could demultiplex the channels again.
-Phil
I adhered the receiver in place with double-stick foam tape. Connections to the hacker port were made with servo extension cables. Here's the code I used to control the S2 with the RC transmistter:
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
STEERING = 0
THROTTLE = 1
THREV = 1200
THOFF = 1500
THFWD = 1800
STLFT = 1200
STCTR = 1500
STRGT = 1800
OBJ
pst : "Parallax Serial Terminal"
s2 : "S2"
PUB start | lftrgt, fwdrev
pst.start(9600)
s2.start
s2.start_motors
repeat
lftrgt := ((pulse_in(STEERING) #> STLFT <# STRGT) - STCTR) * 256 / constant(STRGT - STLFT)
lftrgt &= ||lftrgt > 20
fwdrev := ((pulse_in(THROTTLE) #> THREV <# THFWD) - THOFF) * 256 / constant(THFWD - THREV)
fwdrev &= ||fwdrev > 20
s2.move_now(fwdrev - lftrgt, fwdrev + lftrgt, 0, (||fwdrev #> ||lftrgt >> 1) >> 3, 1)
PUB pulse_in(pin) | t0, t1
waitpne(1 << pin, 1 << pin, 0)
waitpeq(1 << pin, 1 << pin, 0)
t0 := cnt
waitpne(1 << pin, 1 << pin, 0)
t1 := cnt
return (t1 - t0) / (clkfreq / 1_000_000)
A full archive is attached.
Of course, you can add more channels if you like. The hacker port will support six total. A simple circuit that ORs all the channels together could conceivably multiplex all channels into one input. I checked my HobbyKing receiver, and there's just enough of a gap between pulses that a PASM program could demultiplex the channels again.
-Phil



Comments
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 STEERING = 0 THROTTLE = 1 THREV = 1200 THOFF = 1500 THFWD = 1800 STLFT = 1200 STCTR = 1500 STRGT = 1800 OBJ s2 : "S2" PUB start | lftrgt, fwdrev, plftrgt, pfwdrev s2.start s2.start_motors repeat lftrgt := ((pulse_in(STEERING) #> STLFT <# STRGT) - STCTR) * 256 / constant(STRGT - STLFT) lftrgt &= ||lftrgt > 20 fwdrev := ((pulse_in(THROTTLE) #> THREV <# THFWD) - THOFF) * 256 / constant(THFWD - THREV) fwdrev &= ||fwdrev > 20 if (lftrgt <> plftrgt or fwdrev <> pfwdrev) s2.move_now(fwdrev - lftrgt, fwdrev + lftrgt, 0, (||fwdrev #> ||lftrgt >> 1) >> 3, 1) plftrgt := lftrgt pfwdrev := fwdrev PUB pulse_in(pin) | t0, t1 waitpne(1 << pin, 1 << pin, 0) waitpeq(1 << pin, 1 << pin, 0) t0 := cnt waitpne(1 << pin, 1 << pin, 0) t1 := cnt return (t1 - t0) / (clkfreq / 1_000_000)-Phil
Funny, I am working on a few mods for my daughters S2 and was thinking about adding RC control. I hadn't bothered looking on the forums because I figured there would be several threads on it, so I was actually shocked when I read your post about it being the first.
I am hoping to be able to post the mods I am creating soon as I can't find any evidence of anyone doing what I am doing.
Thanks. I'm not sure mine is the first, though; and it would be strange if it were, since it's so obvious. It's just that I don't remember seeing any others.
-Phil
http://www.youtube.com/watch?v=wIbW-zhpLjw
I think XBees are great for data, but they're a bit overkill for remote control apps, given the much cheaper prices of available 2.4 GHz RC setups. At one point, I was considering an external S2 plug-in board for RC receivers. But sticking the thing inside is much tidier and obviates the need for a separate PCB to interface to the S2's RS232 port. The only downside is that, without some OR gating, you need one pin for each RC channel.
-Phil
The search linked to in my sig will allow even one character search terms.
Now I'll have to order an RC transmitter/receiver set and try it.
Rich, great videos on your Youtube channel. Love your Falcon hand glider. LONG ago, I bought a used Eipperformance Rogallo from Kitty Hawk Kites in Nag's Head NC with my paper route money. Best $140 I ever spent! I never went as high as you, maximum a hundred feet or so with that lame 4:1 glide ratio...!
I Tweaked Kye's pwm2_hbdengine to use with the S2 motor setup and modified your pulse_in routine to return 0 instead of 1500 when the aile/elev stick is centered. The pwm motor driver takes input from -400 to 400 for full reverse to full forward.
Con 'S2 RC receive/operate _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 Var Obj pwm : "s2motor_driver" Pub Main | aile, elev, left, right pwm.start(4_000) 'start S2 motor driver @ 4000 hert repeat aile := pulse_in(0) elev := pulse_in(1) left := elev - aile right := elev + aile pwm.leftrightduty(left, right) PUB pulse_in(pin) | t0, t1 waitpne(1 << pin, 1 << pin, 0) waitpeq(1 << pin, 1 << pin, 0) t0 := cnt waitpne(1 << pin, 1 << pin, 0) t1 := cnt return (t1 - t0) / (clkfreq / 1_000_000) - 1500Even if you do not have a rf receiver installed you can use this program to show the encoders output on the LED's by directly turning the wheels.
Con 'S2 RC receive/operate w/encoders triggering leds _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 Var Obj pwm : "s2motor_driver" Pub Main | aile, elev, left, right pwm.start(4_000) 'start S2 motor driver @ 4000 hertz cognew(@entry, 0) 'encoders/leds cog repeat aile := pulse_in(0) elev := pulse_in(1) left := elev - aile right := elev + aile pwm.leftrightduty (left, right) Pub pulse_in(pin) | t0, t1 waitpne(1 << pin, 1 << pin, 0) waitpeq(1 << pin, 1 << pin, 0) t0 := cnt waitpne(1 << pin, 1 << pin, 0) t1 := cnt return (t1 - t0) / (clkfreq / 1_000_000) - 1500 Dat org 0 entry andn outa, dirmask mov dira, dirmask mov time, cnt add time, period :loop test lftenc, ina wc if_c mov lights, #%1001_0000 if_nc mov lights, #%1010_0000 call #leds waitcnt time, period test rgtenc, ina wc if_c mov lights, #%1000_0100 if_nc mov lights, #%1000_1000 call #leds waitcnt time, period jmp #:loop leds mov cntr, #8 :loop shr lights, #1 wc muxc outa, data or outa, clk andn outa, clk djnz cntr, #:loop leds_ret ret lftenc long 1 << 13 rgtenc long 1 << 14 period long 8_000 data long 1 << 7 clk long 1 << 8 dirmask long $180 cntr res 1 lights res 1 time res 1