Controlling USB missile launchers from Python

Office gadgets and toys are quite interesting and useful to those spending long hours in work. From time to time everyone wants to have a little bit of fun. In this article I'll take Dream Cheeky USB missile launcher and I'll control it from within a Python applications.

The Dream Cheeky missile launcher is quite popular and there is a lot of code available to control it. In Europe you can get it from for example hightechtoyz.co.uk (it's not very widespread). In America use the company shop.

Among Python apps there is pyrocket. It has a nice GUI and a lot of features but there may be problem with getting it to work (problems with matching openCV or wxPython versions). Those using Jenkins for continuous integration can use retaliation to shoot a developers breaking builds. You can also check my repository with an Armageddon class and PyQt4 GUI application. This have been tested under Linux but should also work on other operating systems.

There are four launchers that use combusted air to launch missiles made out of foam. Before every shot the launcher compress air which is quite noisy and may blow the surprise factor. The range may vary. If you push the rockets to deep they may end up stuck on the launcher. If they will be loose the air may escape without ejecting the rocket. Find an optimum configuration for your launcher first. Usually it can fire up to few meters.

To control a USB based devices you need something like pyusb. This library can find, configure and exchange data with an USB devices. As the protocol for Dream Cheeky missile launchers is now known it's easy to write code controlling the device.

usb 3-1: New USB device found, idVendor=2123, idProduct=1010 usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 3-1: Product: USB Missile Launcher usb 3-1: Manufacturer: Syntekv

import platform import time import usb.core import usb.util class Armageddon ( object ): """ Based on https://github.com/codedance/Retaliation """ DOWN = 0x01 UP = 0x02 LEFT = 0x04 RIGHT = 0x08 FIRE = 0x10 STOP = 0x20 DEVICE_ORIGINAL = 'Original' DEVICE_THUNDER = 'Thunder' def __init__ ( self ): self . _get_device () self . _detach_hid () self . DEVICE . set_configuration () def _get_device ( self ): self . DEVICE = usb . core . find ( idVendor = 0x2123 , idProduct = 0x1010 ) if self . DEVICE is None : self . DEVICE = usb . core . find ( idVendor = 0x0a81 , idProduct = 0x0701 ) if self . DEVICE is None : raise ValueError ( 'Missile device not found' ) else : self . DEVICE_TYPE = self . DEVICE_ORIGINAL else : self . DEVICE_TYPE = self . DEVICE_THUNDER def _detach_hid ( self ): if "Linux" == platform . system (): try : self . DEVICE . detach_kernel_driver ( 0 ) except Exception , e : pass def send_cmd ( self , cmd ): if self . DEVICE_THUNDER == self . DEVICE_TYPE : self . DEVICE . ctrl_transfer ( 0x21 , 0x09 , 0 , 0 , [ 0x02 , cmd , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ]) elif self . DEVICE_ORIGINAL == self . DEVICE_TYPE : self . DEVICE . ctrl_transfer ( 0x21 , 0x09 , 0x0200 , 0 , [ cmd ]) def send_move ( self , cmd , duration_ms ): self . send_cmd ( cmd ) time . sleep ( duration_ms / 1000.0 ) self . send_cmd ( self . STOP )

In init we look for the device and configure it. After that we can use send_cmd method to send commands to the toy. The launcher can move up/down left/right and shoot. Hexed numerical values triggering each of those operations are at the top of the class. For ease there is also send_move method that moves the launcher in given direction for given amount of milliseconds. Sending move signal will start the movement but you also have to send the "STOP" signal to stop it at given point.

By default you will have to run scripts via sudo (or as root) as plain user doesn't have access to such hardware. Here is an example script:

instance = Armageddon () instance . send_move ( instance . LEFT , 100 ) instance . send_cmd ( instance . FIRE )

So now what sinister plans you have for your missile launcher? :)

RkBlog