For my birthday in November, my girlfriend bought me one of those small IR helicopters, akin to
http://www.chinatopwin.com/3-channel-ir-helicopter-with-gyro-1589.html
A little information about these types of helicopters. They generally run on one of three different channels (a/b/c), and contain a flybar for stability. The control is done via an Infrared Transmitter controller, which contains thrust, elevator (forward/back), yaw and yaw adjustment.
A hardware and software how-to after the jump.
I’ve completed some RC hobby Arduino hacks before, though all have involved dismantling the RC controller to use transistors as switches to complete the circuit. While this has worked well in the past, I did not want to break the heli’s original functionality.
Now, the Arduino has the ability to pulse an IR LED the same way as a normal LED. The major issue here is working out the pulses on the IR codes. For a little more information, LadyAda has a great tutorial here: http://www.ladyada.net/learn/sensors/ir.html . I have utilized this in the past before for a standard TV remote control, however the helicopter protocol proved to be streaming data which ended up in me getting lost every two minutes.
So, I put it aside for a few months. I was researching another project and came accross this: http://www.open.com.au/mikem/arduino/IRrc/ – the IRrc library for Arduino, by MikeM. As a coincidence, I’ve learnt he lives just down the road from me too!
The IRrc library handles the translation from a simple Arduino command to the IR pulses required to stream data to the helicopter.
To get the IR signal out, it is best to use a transistor as an amplifier, and three IR leds. The circuit can be viewed on MikeM’s website here : http://www.open.com.au/mikem/arduino/IRrc/LED-Output.pdf
Reading the examples and documentation, the library is quiet easy to configure. My heli runs on Channel C. To set this, the library needs to be modified slightly.
File (on OSX): /Applications/Arduino.app/Contents/Resources/Java/libraries/IRrc/IRheli.cpp
Line to update:
_channel = IR_HELI_CHANNEL_C;
While the library has been written, it’s example programs are either a) Arduino sketches that read from a USB host shield (which I don’t have) or b) a Perl script that reads input from a USB gamepad (of which I also don’t have, or understand Perl in the slightest).
Not fazed, uploaded the HeliDemo.pde file that is included with the library, opened up a serial terminal and sent the data ’255 255 255 255 :’ – aka all motors full strength. It flew up to the roof, promptly fell and hit my girlfriend in the face. So, on to debugging and writing a better script.
The following uses Python, pySerial and TKInter. pySerial needs to be installed on OSX, however TKInter comes by default.
To control, arrow keys left and right are yaw, up and down is thrust, w and d is elevator, enter stops all yaw elevator and hovers at the current height, l makes the heli automatically start with 70 thrust and no direction, and p stops all motors. With this key configuration, it is also possible to use a Wii remote + DarwiinRemote to control the heli.
The full Python file (with correct indentation) can be downloaded here: http://dddanmar.net/python-heli.py
import serial; from Tkinter import * master = Tk() thrust = 0 direction = 127 elevator = 127 trim = 100 def printstat(*ignore): global thrust; global direction; global elevator; global trim; print 'Thrust = ' + str(thrust) + ' | Direction = ' + str(direction) + ' | Elevator = ' + str(elevator) + ' | Trim = ' + str(trim); def thrust10(*ignore): global thrust; global direction; global elevator; global trim; if thrust==255: thrust = 255 else: thrust = thrust + 5 #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def thrustdown10(*ignore): global thrust; global direction; global elevator; global trim; if thrust==0: thrust = 0 else: thrust = thrust - 5; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def hover(*ignore): global thrust; global direction; global elevator; global trim; direction = 127; elevator = 127; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def down(*ignore): #ser.write('0 127 127 127 :'); print 'DIWN'; def plusdirection(*ignore): global thrust; global direction; global elevator; global trim; if direction==255: direction = 255 else: direction = direction + 5; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def minusdirection(*ignore): global thrust; global direction; global elevator; global trim; if direction==0: direction = 0 else: direction = direction - 5; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def elevatorplus(*ignore): global thrust; global direction; global elevator; global trim; if elevator==255: elevator = 255 else: elevator = elevator + 5; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def elevatorminus(*ignore): global thrust; global direction; global elevator; global trim; if elevator==0: elevator = 0 else: elevator = elevator - 5; #ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') printstat(); def stopall(*ignore): global thrust; global direction; global elevator; global trim; thrust = 0; direction = 127; elevator = 127; #ser.write('0 127 127 127 :'); print 'All Stopped'; def liftoff(*ignore): global thrust; thrust = 70; #ser.write(str(thrust) + ' 127 127 127 :'); #ser = serial.Serial('/dev/tty.usbserial-A400fYYU', 9600); master.bind('', thrust10); master.bind('p', down); master.bind('', thrustdown10); master.bind('', minusdirection); master.bind('', plusdirection); master.bind('w', elevatorplus); master.bind('d', elevatorminus); master.bind('', hover); master.bind('p', stopall); master.bind('l', liftoff); mainloop()
More bonus points, this uses tkinter to monitor the mouse location in a box. Be careful, not much error correction here so it may fly off into a wall.
import Tkinter as tk import serial # y = height # x = turn trim = 127 thrust = 0 direction = 127 elevator = 127 def writetoserial(*ignore): global thrust; global direction; global elevator; global trim; if thrust == -1: thrust = 0; ser.write(str(thrust) + ' ' + str(direction) + ' ' + str(elevator) + ' ' + str(trim) + ' :') def printvar(*ignore): global thrust; global direction; global elevator; global trim; print 'Thrust = ' + str(thrust) + ' | Direction = ' + str(direction) + ' | Elevator = ' + str(elevator) + ' | Trim = ' + str(trim); def showxy(event): global thrust; global direction; global elevator; global trim; xm, ym = event.x, event.y # show cordinates in title direction = xm / 3 ; thrust = ym / 3; writetoserial() printvar() ser = serial.Serial('/dev/tty.usbmodemfa141', 9600); root = tk.Tk() frame = tk.Frame(root, bg= 'yellow', width=765, height=765) frame.bind("", showxy) frame.pack() root.mainloop()