Subtitling this one “Adding a Gun Turret” seems almost like click bait, doesn’t it? But yes, that’s exactly what we’re doing. One of Dexter Industries’ sample projects is attaching the Dream Cheeky Thunder Cannon to the GoPiGo. I’m going to show you how to control it with the same gamepad that also controls the robot’s movement.

In part two of this series we connected the gamepad events to the GoPiGo movement commands. We used the evdev module to identify the button that was pressed and then moved (or stopped) the GoPiGo accordingly.

But the gamepad has more controls than the seven we have used so far. What about the joysticks? We’ll use the directional pad (which sends joystick events) to control the cannon.


Interfacing with the Cannon

The Thunder Cannon does not ship with any kind of API. However the “Office Cannon” project does have Python code in it for controlling the thunder cannon.  I modified the script in the project and made a small module for controlling the cannon. I have to give credit where credit is due: the Dexter Industries crew’s work made this possible. I just moved the code to a module and shifted a few things around.

We can’t use evdev to control the cannon : evdev is designed for reading from devices, not writing to them. Even if we did try to use it, the messages in evdev are not what the Cannon expects. The pyusb module allows us to see devices on USB and write messages to them.

The Raspbian Linux run on most Raspberry Pis is, of course, based on Debian Linux. Debian is very conservative when it comes to packages, and if you use apt-get to install pyusb you will get an older version that does include all of the features we need. I used Dexter’s modified image which already has pyusb installed. If you wish to use your own, follow the instructions for installing from Github here. (All things considered, taking the time to setup an SD Card with Dexter’s image is worth the effort.)

The office cannon script from the GoPiGo samples has some commands that can be sent to the cannon. I reused them, plus added a few constants of my own. I’ll explain my additions in a bit.

But before we can send a command we need to “find” the device:

Find() finds USB devices by probing for the vendor and product ids it is passed and returns an object (or None). In setup() we search for either a Thunder Cannon or an older model. We set DEVICE_TYPE as a flag to tell us which one we ended up with.

Once we have a device, we can send commands to it:

These 2 “hidden” methods send messages, or “transfers” to use USB lingo, to the cannon.  We see where the DEVICE_TYPE flag is used here. The older model cannon didn’t have an LED on it and also expects a different command syntax.

I hid these methods from other modules by prefixing them with two underscores.The reason for this is to simplify the interface for the user, as we’ll see below.

The public interface to the module is here:

This function accepts one of the constants defined above, along with an integer value. The function using the constant to figure out which internal (“hidden”) function to call. And how the integer value is used.

  • At the top we see that the directional constants are simply passed on to __move().
  • PARK is expanded into a __move DOWN and then LEFT.
  • LED turns the LED on or off.
  • FIRE pauses for half a second and the fires the cannon.

__move adds duration to the cannon movements:

So when a direction is sent, the integer argument is how long to move in that direction.

With an LED we expect 0 or 1 to turn it on or off.

With PARK and FIRE the integer is ignored. Not the most elegant API, but it’s good enough for this quick exercise.

Connecting the Gamepad to the Cannon

Joystick events in evdev are quite different from buttons. Buttons send us a simple up or down event. Joystick controllers send events that contain coordinates corresponding to a position on a screen.  If we wanted to use the actual joystick controllers we would need to simplify these into UP-DOWN-RIGHT-LEFT commands. However the directional pad does that for us. So here are our controls, including controlling to GoPiGo.

Turrent controls

We need to process the directional pad events in our evdev read loop:

There’s a lot packed in here.

When we press the directional pad we receive an ABS event. These events, similar to button events, have a timestamp. They also have a code (similar to the button codes we’re seen before) and a value. With an actual joystick this value corresponds to a position on an axis. With the pad we simply get 1 or -1.

So when we receive the event we check for horizontal (ABS_HAT0X) or vertical (ABS_HAT0Y) and then we save the direction and the time.

We save the time because when the directional pad is released we then receive a SYN event. This event is similar to the button UP events we saw in part 2.  If we subtract our saved time from this we have how may milliseconds the button was held.  We use this as our duration for the command.

With this code we have very simple control over the cannon. How long we hold a directional button roughly governs how far the turret moves, and we can fire with the left hand joystick button. The script sends a “Park” command on startup.

Here’s the whole thing. (Of course you can get it on Github too!)

At the top of the script you see some extra code (also from the original Dexter Industries script) where, if we are running on a Pi B+, we enable high current mode for the Cannon. In my experience the Cannon uses a lot of power.

The main loops is also wrapped in a try/except block that makes sure we disable high current if the script is interrupted.

That’s it for now. I apologize for the delay in posting this. There’s more coming soon, but before I return to the GoPiGo they’ll be a brief interlude with Raspberry Pi, MAME, and analog controls.