Controlling USB missile launchers from Python

Check out the new site at https://rkblog.dev.

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.

Dream Cheeky USB Missile Launcher
Dream Cheeky USB Missile Launcher

How does it shoots?

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.

How to control it?

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.

At start, when we connect it under Linux we can notice the manufacturer and its ID:
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
Then we can play with pyusb to control it. Here is my "Armageddon" class:
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)
I've also used this class in a PyQt4 application which uses arrows to move the launcher and "Enter" to shoot:
PyQt4 GUI application controlling missile launcher

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

RkBlog

Check out the new site at https://rkblog.dev.
Comment article
Comment article RkBlog main page Search RSS Contact