Raspberry Pi and Gamepad Programming Part 3: Adding a Gun Turret

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.

Dream-Cheeky-Ltd-908-desktop-gadgets-computer-accessories-PACK

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.

# Protocol command bytes
DOWN = 0x01
UP = 0x02
LEFT = 0x04
RIGHT = 0x08
FIRE = 0x10
STOP = 0x20
PARK = 0x30
LED = 0x31

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

    DEVICE = usb.core.find(idVendor=0x2123, idProduct=0x1010)

    if DEVICE is None:
        DEVICE = usb.core.find(idVendor=0x0a81, idProduct=0x0701)
        if DEVICE is None:
            raise ValueError('Missile device not found')
        else:
            DEVICE_TYPE = "Original"
    else:
        DEVICE_TYPE = "Thunder"

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:

# Send command to the office cannon
def __cmd(cmd):
    if "Thunder" == dev_type:
        dev.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, cmd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
    elif "Original" == dev_type:
        dev.ctrl_transfer(0x21, 0x09, 0x0200, 0, [cmd])


# Send command to control the LED on the office cannon
def __led(cmd):
    if "Thunder" == dev_type:
        dev.ctrl_transfer(0x21, 0x09, 0, 0, [0x03, cmd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
    elif "Original" == dev_type:
        print("There is no LED on this device")

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:

def run_command(command, value):
    if command == RIGHT or command == LEFT or command == UP or command == DOWN:
        __move(command, value)
    elif command == PARK:
        # Move to bottom-left
        __move(DOWN, 2000)
        __move(LEFT, 8000)
    elif command == LED:
        if value == 0:
            __led(0x00)
        else:
            __led(0x01)
    elif command == FIRE:
        # Stabilize prior to the shot, then allow for reload time after.
        time.sleep(0.5)
        __cmd(FIRE)
        time.sleep(4.5)
    else:
        print "Error: Unknown command: '%s'" % command

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:

# Send command to move the office cannon
def __move(cmd, duration_ms):
    __cmd(cmd)
    time.sleep(duration_ms / 1000.0)
    __cmd(STOP)

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:

    for event in gamepad.read_loop():
        if event.type == ecodes.EV_ABS:
            absevent = categorize(event)
            print ecodes.bytype[absevent.event.type][absevent.event.code], absevent.event.value
            if ecodes.bytype[absevent.event.type][absevent.event.code] == 'ABS_HAT0X':
                if absevent.event.value == -1:
                    next_move = thunder.LEFT
                    event_time = absevent.event.timestamp()
                elif absevent.event.value == 1:
                    next_move = thunder.RIGHT
                    event_time = absevent.event.timestamp()
            if ecodes.bytype[absevent.event.type][absevent.event.code] == 'ABS_HAT0Y':
                if absevent.event.value == -1:
                    next_move = thunder.UP
                    event_time = absevent.event.timestamp()
                elif absevent.event.value == 1:
                    next_move = thunder.DOWN
                    event_time = absevent.event.timestamp()
        elif event.type == ecodes.EV_SYN:
            synevent = categorize(event)
            print synevent
            syntime = synevent.event.timestamp()
            move_duration = (syntime - event_time) * 1000
            print move_duration
            thunder.run_command(next_move, move_duration)

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!)

#!/usr/bin/python

import thunder_control as thunder
from evdev import InputDevice, categorize, ecodes, KeyEvent
import os
from gopigo import *

model_b_plus = True
next_move = "down"
event_time = 0
speed = 100
gamepad = InputDevice('/dev/input/event0')

thunder.setup()

# Enable USB to supply up to 1.2A on model B+
if model_b_plus:
    os.system("gpio -g write 38 0")
    os.system("gpio -g mode 38 out")
    os.system("gpio -g write 38 1")

try:
    thunder.run_command(thunder.PARK, 1000)
    print "Parked"
    for event in gamepad.read_loop():
        if event.type == ecodes.EV_ABS:
            absevent = categorize(event)
            print ecodes.bytype[absevent.event.type][absevent.event.code], absevent.event.value
            if ecodes.bytype[absevent.event.type][absevent.event.code] == 'ABS_HAT0X':
                if absevent.event.value == -1:
                    next_move = thunder.LEFT
                    event_time = absevent.event.timestamp()
                elif absevent.event.value == 1:
                    next_move = thunder.RIGHT
                    event_time = absevent.event.timestamp()
            if ecodes.bytype[absevent.event.type][absevent.event.code] == 'ABS_HAT0Y':
                if absevent.event.value == -1:
                    next_move = thunder.UP
                    event_time = absevent.event.timestamp()
                elif absevent.event.value == 1:
                    next_move = thunder.DOWN
                    event_time = absevent.event.timestamp()
        elif event.type == ecodes.EV_SYN:
            synevent = categorize(event)
            print synevent
            syntime = synevent.event.timestamp()
            move_duration = (syntime - event_time) * 1000
            print move_duration
            thunder.run_command(next_move, move_duration)
        elif event.type == ecodes.EV_KEY:
            keyevent = categorize(event)
            if keyevent.keystate == KeyEvent.key_down:
                if keyevent.keycode[0] == 'BTN_A':
                    print "Back"
                    bwd()
                elif keyevent.keycode == 'BTN_Y':
                    print "Forward"
                    fwd()
                elif keyevent.keycode == 'BTN_B':
                    print "Right"
                    right()
                elif keyevent.keycode == 'BTN_X':
                    print "Left"
                    left()
                elif keyevent.keycode == 'BTN_THUMBR':
                    print "Stop"
                    stop()
                elif keyevent.keycode == 'BTN_THUMBL':
                    thunder.run_command(thunder.FIRE, 250)
                elif keyevent.keycode == 'BTN_TR':
                    print "Faster"
                    speed += 20
                    if speed > 255:
                        speed = 255
                    set_speed(speed)
                elif keyevent.keycode == 'BTN_TL':
                    print "Slower"
                    speed -= 20
                    if speed < 50:
                        speed = 50
                    set_speed(speed)

except KeyboardInterrupt:
    # Disable high current mode on USB before exiting
    if model_b_plus:
        os.system("gpio -g write 38 0")

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.

Raspberry Pi and Gamepad Programming Part 2: Controlling the GoPiGo

Welcome to part 2 of my series on working with programming the GoPiGo  and a Gamepad controller with Python.

In part 1 I talked about what a gamepad “looks” like to a Raspberry Pi and how the excellent evdev package makes it easy to read and process information from it. I finished the post with a script that reads buttons on the gamepad and prints the direction it would send the GoPiGo in.

The final script from part 1 and all of the scripts from this part (and all future posts) can be found on github.

Let’s pick up where we left off and add code for the GoPiGo.

#!/usr/bin/python

from evdev import InputDevice, categorize, ecodes, KeyEvent
from gopigo import *

gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY:
        keyevent = categorize(event)
    if keyevent.keystate == KeyEvent.key_down:
        if keyevent.keycode[0] == 'BTN_A':
            print "Back"
            bwd()
        elif keyevent.keycode == 'BTN_Y':
            print "Forward"
            fwd()
        elif keyevent.keycode == 'BTN_B':
            print "Right"
            right()
        elif keyevent.keycode == 'BTN_X':
            print "Left"
            left()
        elif keyevent.keycode == 'BTN_THUMBR' or keyevent.keycode == 'BTN_THUMBR':
            print "Stop"
            stop()

This script looks familiar. The only changes are an import for the gopigo  module, and then calls to it for each button press. I also added a “stop” command at the end. It’s activated by the gamepad’s joystick buttons.

Stop Buttons

Try running the script.

pi@raspberrypi ~/gopigo-tutorial/part2 $ ./gamepad.py
Traceback (most recent call last):
 File "./gamepad.py", line 17, in 
 from gopigo import *
 File "build/bdist.linux-armv6l/egg/gopigo.py", line 30, in 
IOError: [Errno 13] Permission denied

Oops. We need root access to run anything that accesses the Gopigo device. Run the script with sudo.

pi@raspberrypi ~/gopigo-tutorial/part2 $ sudo ./gamepad.py
Back
Stop

Everything works now. Why is that? Let’s look at line 30 of gopigo.py. as it is called out in the error message.

I am running the Raspbian image from Dexter Industries, so there’s a copy of the GoPiGo scripts in the gopigo folder in the pi user’s home folder. You can also find the scripts here on github.

if rev == 2 or rev == 3:
    bus = smbus.SMBus(1) #

Smbus is a Python package that allows us to communicate with devices connected to Pi’s General Purpose In Out (GPIO) connection. When you assembled your GoPiGo you connected this to the circuit board on the robot. Here is a (slightly blurry) picture of a Pi with the GPIO highlighted with a red rectangle.

gpio

This GPIO has a file interface similar to the gamepad:

pi@raspberrypi ~ $ cd /dev
pi@raspberrypi /dev $ ls -l i2c-1
crw-rw---T 1 root i2c 89, 1 Jun 7 14:33 i2c-1

And here is the gamepad we saw earlier in part1.

pi@raspberrypi /dev $ ls -l input/event0
crw-rw---T+ 1 root input 13, 64 Jun 7 14:33 input/event0

Why does one require sudo and not the other? Let’s look closely at the listings side-by-side:

crw-rw—T 1 root i2c 89, 1 Jun 7 14:33 i2c-1

crw-rw—T+ 1 root input 13, 64 Jun 7 14:33 nput/event0

The first column shows the UNIX file bits, which contain information about the file type and permission. The first letter in both listings , ‘c’, indicates a “character special” file. In other words, a device we read and/or write bytes from.

The next 9 letters indicate the permissions for the file’s owner, the file’s group, and then for everyone else (“world”). Each user has 3 settings: r – read. w – write, and x – eXecute. Character special files are not executable they are not programs.

These files have the same permissions: we see ‘rw’ for both owner and group.

So what’s the difference? The answer is a couple of columns later, in red. After we see the file’s size (1) we see the owner “root,” then the group. For the i2c-1 it’s “i2c” and for the gamepad it is “input.”

What groups does pi belong to? Run the id command.

pi@raspberrypi /dev $ id
uid=1000(pi) gid=1000(pi) groups=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),105(netdev),999(input),1002(spi),1003(gpio)

There it is! We don’t see “i2c” in that long list of groups! We could remove the need for root access by adding pi to the i2c group.

But I have been off on this tangent for long enough. Let’s make the GoPiGo do something cool before I wrap this post up.

What we’re missing is a way to control the GoPiGo’s speed with the gamepad. But this requires a bit more code since speed is not “on” or “off” like a direction: a real speed control has “faster” or “slower.” We need 2 buttons and a way to track what our current speed is.

I want to use these 2 buttons:

Speed Buttons

So let’s write a quick script to identify the keycode for each button.

from select import select
from evdev import InputDevice, categorize, ecodes, KeyEvent
gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY:
        keyevent = categorize(event)
        if keyevent.keystate == KeyEvent.key_down:
            print keyevent.keycode

Let’s run that and press the buttons we want to use.

pi@raspberrypi ~/gopigo-tutorial/part2 $ ./show_buttons.py
BTN_TR
BTN_TL

And we have the code for the two buttons on the front of the controller now. Let’s put it all together.

#!/usr/bin/python

from evdev import InputDevice, categorize, ecodes, KeyEvent
from gopigo import *

gamepad = InputDevice('/dev/input/event0')

speed=50
set_speed(speed)
for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY:
        keyevent = categorize(event)
    if keyevent.keystate == KeyEvent.key_down:
        if keyevent.keycode[0] == 'BTN_A':
            print "Back"
            bwd()
        elif keyevent.keycode == 'BTN_Y':
            print "Forward"
            fwd()
        elif keyevent.keycode == 'BTN_B':
            print "Right"
            right()
        elif keyevent.keycode == 'BTN_X':
            print "Left"
            left()
        elif keyevent.keycode == 'BTN_THUMBR' or keyevent.keycode == 'BTN_THUMBR':
            print "Stop"
            stop()  elif keyevent.keycode == 'BTN_TR':
        elif keyevent.keycode == 'BTN_TR':
            print "Faster"
            speed += 50
            if speed > 550:
               speed = 550
            set_speed(speed)
        elif keyevent.keycode == 'BTN_TL':
            print "Slower"
            speed -= 50
            if speed < 50:
                speed = 50
            set_speed(speed)

The GoPiGo module has functions for increasing and decreasing speed, but I wanted a little bit more control over how high or low we set the speed.

At the top speed  is set to a starting value of 50.

When each speed button is pushed it is incremented or decremented and then checked against a minimum or maximum setting. We use these checks to avoid setting speed to zero or a negative value to to avoid setting it too high. (I have no idea if there really is a too high, it ‘s just an example.)

Go ahead and play with the settings to suit your taste.

That’s it for this week, but we’re not done. Next week we’re going to get a little, er, flashy and then we’ll take a look at how to read joysticks instead of just buttons.  We need something to aim those joysticks with.,,

gun

Raspberry Pi and Gamepad Programming Part 1: Reading the Device

gopigo with gamepad

A friend of mine recently asked if I could help out with a Raspberry Pi project he is working on. Pi was one of those things I had read about and always wanted to take a look at — and here was my excuse!

Moreover, Python, the language most Raspberry Pi tools are written in, is something else I had been meaning to take a look at.

Of course playing around with Raspberry Pi very quickly led to this:

gopigo with gamepad
A GoPiGo and a Logitech Gamepad

On the left is a GoPiGo (affiliate link), while on the right is a Logitech F710 wireless gamepad. These 2 devices do not work together “out of the box.” The GoPiGo has tutorials and sample scripts for working with a keyboard or mouse, but not for a gamepad. Here’s a chance to learn some basics and have a little fun!

Much of what makes working with the Raspberry Pi easy is that it runs Linux. (It runs other operating systems, but we’ll stick with Linux here.) As a matter of fact, this first tutorial will work on Linux running on pretty much any hardware.

Prerequisites

For this tutorial you need a Raspberry Pi (or a computer running Linux, but you will need a GoPiGo for the next article in this series) and a gamepad. I am using a Logitech Gamepad F710 (affiliate link,) which lists for $40 right now. I picked mine up at a local Target for $25. Other USB gamepads should work similarly to the F710, including wired models. (But wireless will work a lot better once you get your robot moving.)

You need to be comfortable with a Linux command line, as well as having as basic knowledge of Python. Nothing fancy: just the ability to enter a program in your favorite text editor and run it.

Reading from Device Files

In order to use the gamepad we need to get information from it into our program. Linux, like most UNIX-like operating systems, makes this easy via device files. When the gamepad (or it’s receiver in the case of a wireless device) is connected a file appears that can be read from.

Without the joystick or USB receiver connected, open a shell on your system and list the contents of /dev/input (click on the image for a larger version if you cannot read it):

no_device

On my system I see only a default file named mice, as I do not have an actual mouse or keyboard connected.

Now connect the gamepad or its USB receiver and wait a few seconds (especially if you are using a Pi B+ or Original) for your system to notice the device:

with_device

On my system we see 2 new files and 2 new folders. The event0 and js0 devices correspond the my gamepad. The by-id and by-path folders provide an alternative method to access the devices, as well as additional information about the device.

Since these devices are files, we can treat them as such. Let’s see what’s “in” them. Opening them in an editor will not work well, but we can use good old cat to have some fun.

Cat the event0 (or eventN if you had one before you connected the new gamepad) and then press a button on the gamepad:

cat_device

The image above is for a single button press. That’s a lot of characters! And most of them seem garbled.  But we do see that the gamepad device file is just like any other file that can be read.

Reading the Gamepad

We’re going to use the evdev package to interpret the input from the gamepad. I’ll explain what this package does as we go. The package has not been installed on any of the Pi distributions I have worked with, so let’s start by installing it with pip:

% sudo pip install evdev

Pip will do it’s thing and after a bit (it can take a while on a slower Pi) the package is installed.

Now let’s open the gamepad and print some information about it. For these exercises I am using idle but a command line session with Python or running these lines from a script will work too. (Again, click on the image for a larger version.)

print_device

The first line imports a few things from the evdev package: InputDevice, categorize, and ecodes.

The second line creates an InputDevice by passing the path to the gamepad device file.

And the we finally print this new device object to standard output and see some interesting information about it.

While the mechanics of reading from the gamepad device are as simple as any other file, this quick 3 line program demonstrates how evdev makes it even simpler. The package has a termendous of code built that for managing devices like a joystick or gamepad.

Let’s use evdev to read some buttons. Start with the same import statement and create an InputDevice like we did above, but now replace printing the device with the 2 lines below. Then press a button on your gamepad.

Basic Read Loop

Instead of the garbled characters we saw earlier, we see something that is at least readable.

Before we discuss the events, let’s talk about this line:

for event in gamepad.read_loop()

This illustrates another feature of the evdev package. The package provides a simple for loop that reads the device and creates events for us. Without this feature our code would look something like this:

from evdev import InputDevice
from select import select
gamepad = InputDevice('/dev/input/event0')
 while True:
     r,w,x = select([gamepad], [], [])
     for event in gamepad.read():
         print(event)

Evdev eliminates the outer While loop, as well as the call to select. In terms of lines of code, the difference is negligible, however the read_loop() version is a little easier to read and we’ll stick with it.

(In an application that needs to monitor more than one input device, such as a mouse and a keyboard, read_loop() won’t work, as it only lets us read one device. Select is excellent for reading and writing from multiple devices.)

Let’s identify what’s actually being pressed on the gamepad:

Categorize Events

Now for every event we examine the type value and see if it is a button. finally we are using the ecodes we imported.

If it is a button, we use categorize to return a more specific type of event to us and print it. If it is not, we simply print a message.

Here we see where evdev saves us a lot of work. In addition to reading the events a gamepad sends to our app, it also categorizes them for us too.

key event at 1433298890.180640, 304 (['BTN_A', 'BTN_GAMEPAD']), down

Note two things about this event info: we see the button I pressed (‘A’) and we see “down.” After a mysterious “not a button” event we then see the same button going up.

We know a lot about how to read the gamepad at this point. Let’s write a script that we could use to control the robot using the A-B-X-Y buttons on the gamepad. Enter the following into a text editor and save it.

#!/usr/bin/python

from evdev import InputDevice, categorize, ecodes, KeyEvent
gamepad = InputDevice('/dev/input/event0')

for event in gamepad.read_loop():
    if event.type == ecodes.EV_KEY:
        keyevent = categorize(event)
        if keyevent.keystate == KeyEvent.key_down:
            if keyevent.keycode[0] == 'BTN_A':
                print "Back"
            elif keyevent.keycode == 'BTN_Y':
                print "Forward"
            elif keyevent.keycode == 'BTN_B':
                print "Right"
            elif keyevent.keycode == 'BTN_X':
                print "Left"

I saved this as gamepad.py.

chmod +x gamepad.py
./gamepad.py

Run the script and press a few buttons.

As we receive each event we first check to see if it is a button. If it is, we check to see that the button is down (it’s been pressed) and then finally we check to see if it is one of the four buttons we currently care about.

That’s it for this week. Next week we’ll add code to make the GoPiGo respond to the buttons, and we’ll also a some more controls.

See you then! And feel free to leave a comment below.

Facebook, Trust, and the Clueless Engineer

I do this research every day.
I do this research every day.

So I’m guessing you’ve heard about this. tl;dr : Scientists at Facebook manipulated the posts they showed certain users and then evaluated the posts the people being manipulated made. Then they published a study on the results.

A lot of people are reacting very strongly to this news.

Some people are pissed at the idea of being manipulated. While I empathize and agree, I think a lot of their anger is based on a contract with Facebook that Facebook abandoned a long time ago. Many people seem to think that Facebook is designed for them to share things with their friends, while their friends share things with them.

It doesn’t work that way anymore. It hasn’t for a long time.

Facebook has been manipulating how things are displayed in the “timeline” for literally years now. They are manipulating it to sell more ads. Deal with it. (Seriously, please deal with it and stop complaining. I wish FB could figure out how to hide those complaints from me.)

So, while the research strikes me as incredible unethical, the idea that they manipulated what people see on their timelines surprises me the way that night following day surprises me.

Meanwhile, another group of people seem to think what Facebook did is equivalent to the A/B testing that many websites do all the time and are surprised at the people that are pissed.

At first I thought Andreessen was mocking the idea that what FB did was publishable research, but later, in a debate with another person:

Normally I consider Andreessen to be insightful and a bit of an iconoclast (as well as pathologically compelled to swipe at Piketty at least 20 times a day – I wonder what a psychological study on that would reveal? But I digress…), but he really jumped the shark on this one. I don’t know if he’s being deliberately disingenuous or is really as clueless as he comes across here.

Of course his point is completely correct. Just like the engineer talking to the balloonist, his description of what happened is completely accurate. Advertisers (at least the good ones) have been adjusting, tweaking, and optimizing, their offers for as long as there has been advertising.

And the mechanics of Facebook’s “research” was largely the same as “normal” A/B testing: show different pages/posts to different people and measure the varied results. So to an engineer that only knows how to think like an engineer, what FB did might seem like the same thing.

But, as I outlined above, people don’t realize they are being advertised at when they go there. They think Facebook is for sharing pictures of their kids, their cats, and their meals, and if they don’t “get it” they’re just stupid users. But alas,  that annoying little thing called “ethics” means that we should, dramatic sigh, worry about why people are looking at the things we put in front of them.

It’s easier, of course, to not worry about why people are on our websites. It’s almost as easy to pay a lawyer or fifty to take the phrase “caveat emptor” and stretch a few thousand unintelligible words from it that no one will ever read, and then hide behind them when you get caught doing something unethical or, bizarrely, decide to publish the result of something unethical.

But having a lawyer, or even the world’s most popular website, is not a substitute for ethics. Skepchick covers the ethics behind what’s wrong with FB’s research better than I ever could over here. (Hint: there’s this thing called “informed consent” that doesn’t apply to sign up pages for websites or ads for Cialis, but does apply to research.)

It seems obvious to “say” it out loud, but the ‘Net is almost completely intertwined into our lives now. If you’re using it it’s important to realize that free stuff is never free. Whenever you visit a website or tap on a phone app you’re accepting a contract and you have no reason to believe that that contract is equitable, fair, or ethical.

Meanwhile if you are an engineer or entrepreneur, being successful online requires an understanding and appreciation of how other think and how they might react to something that you think is completely normal. Unless of course, your definition of success doesn’t include how you effect other people. If that’s the case, just carry on. You’re already doing a great job.

 

The Porsche Professionals Guild

malletA man walks into a repair shop. He has a 1968 Porsche 912 that will no longer reverse.

“Excuse me,” he says to the man at the counter, “I need help with my Porsche.”

“Of course! We here at Gentle Motors would love to help you. We offer friendly force free repairs for all Porsche automobiles.”

The man seems a little confused at this response, but continues. “My car will no longer reverse. I’ve put up with it for a while, but it’s a real problem n–”

“We can certainly fix that sir! My car did the same thing! I’m confident I can fix that without scratching your paint.” The mechanic beams back at the car owner.

“Without scratching my paint? That’s great, it hadn’t really occurred to me that you might. I really like this car a lot. I had always dreamed of owning a classic Porsche. What qualifications do you have to work on them?” The car owner seems a little more confused.

“Well sir, I am a proud member of the Porsche Professionals Guild. The Guild holds us to the highest ethical standards! I am sworn to fix Porsches without damaging paint, carpet, or upholstery, and we never, ever, use steel mallets or sawzalls.”

“That’s great to hear. But have you worked on a 912 before? Have you taken any training with Porsche? I’d like to be sure that my car is fixed correctly and that I won’t have any more troubles. Transmission problems can be danger–”

The mechanic pulls a large mallet from his desk. “Did you know the guy down the street uses one of these?!? It’s unbelievable. I don’t even think all of his clients know! Back in the day we used hit transmissions with these, just to see if it might knock the synchros back into place. But not anymore.”

The man looks at the mallet, flummoxed.

Suddenly the mechanic looks afraid. He jams the mallet back into the drawer and slams it shut. “Please don’t tell anyone you saw that. If the guys find out I even have one they’ll be ethics charges.”

“This has been fascinating, but I really just need some sort of reassurance that you are qualified to fix my car.”

“Of course I am! I even own a 912 myself.” The mechanic points to a beautiful red 912 parked in the back, its front wheels on small ramps and a stone wedged against the back tire.

“What are the ramps for?”

“Sometimes it needs help getting started in reverse. I don’t trust the guy down the street to fix it, so I am managing the situation until I finish an online class on transmissions. It’s called BAT: Bushing Adjusted Transmissions.”

The man leaves. His car was fixed a few days later across town. His carpets looked a little cleaner when he got it back, and the car was also waxed beautifully. He never had transmission issues again.

 

How To Be Indispensable

I just finished Seth Godin’s Book Linchpin: Are You Indispensable? It’s a book that I will need to read at least two more times to fully digest. It’s also a book I think everyone should read.

If you haven’t heard of Seth Godin, look here. I’m not even going to try to cover his accomplishments in a blog post that I already know is going to be longer than I want. His blog is worth subscribing to, regardless of what you do or who you are. (You can get it delivered right to your inbox.)

The title of this book implies that it’s about being an indispensable something, and since the book is often found in the Business and Self Help categories, you might assume that the book is about being an indispensable employee, entrepreneur, or business owner. You’d be right, but you would also only be scraping the surface. Godin’s definition of a Linchpin is someone who creates art. He then defines art as a gift that is intended to create change.

If this sounds like a broad definition, that’s because it’s intended to be one. A lot of Linchpin: Are You Indispensable? is about what techie types like me refer to as “soft-skills” and how, when viewed with Godin’s definition of art, those skills finally seem as important as they really are. Art might be creating what we “traditionally” think of as art, such as painting, writing, or film. But it can also be creating something something more “technical” like software. Or it can be as simple as  making life better for someone.

The graph below, which I adapted from Godin’s hand draw version in the book, is a great example of what the book has to offer.

Four Different Quadrants
The Linchpin Scale

On the horizontal access we have a measure of passion. How much effort are you willing to put into your art? What sort of obstacles are you willing to overcome?

On the vertical access we have a measure of attachment. Attachment can mean attached to a set of rules, a specific outcome, or a specific solution. I was tempted to change this axis to neasure “flexibility,” but decided to stick with Seth Godin’s definitions. It’s his book.

On the lower left hand side we have the Whiner. The Whiner is attached to his world view and to how he thinks it should be, but he’s stuck since he has no passion – he lacks a willingness to create any change. The battle cry of the Whiner is “It’s not fair!”

On the lower right hand side we have the Fundamentalist. Regardless of what community you are in the Fundamentalists are easy to find and probably well known. Combine a rigid attachment with a lot of passion and noise is always part of the result. Often the only result. Unfortunately (or maybe fortunately?) not a lot of change is likely, since that rigid attachment almost always precludes even talking to someone with a different view, let only working together to make something happen.

The upper left hand corner is interesting. What happens when you combine a lack of passion with a lack of attachment? You get a Bureaucrat. When in doubt, fall back on rules and “It’s not my decision/fault/problem.” You know where to find them.

The upper right hand side is the home of the Linchpin: a passion to affect change combined with an ability to discern what a successful outcome can look like. Where the fundamentalist is focused on a rigid definition of success, the Linchpin is looking at what some might call the “big picture.” A desire to make things happen, combined with an ability to adapt is a recipe for results.

Where are you on this graph? Is your position always the same for every situation you are in? I know I tend to shift around, but now that I have it for a frame of reference, I think it will improve my ability to get things done.

This is a lot to try to cover in 500 words, and I am already well over that. Linchpin: Are You Indispensable? is worth your time and your money. If audio is your thing, there’s an edition read by Seth himself.

Trust, But Verify…Aw Hell, Don’t Trust Either.

Impressive, isn't it?
Impressive, isn’t it?

It took about 5 minutes to make this. It would have taken less if I hadn’t messed about with the fonts.

One reason to distribute quotes this way is that images are easier to share than text, so it more likely that it will go “viral.” Another seems to be to make it more memorable or even more credible.

We love quotes, especially when they validate what we already believe. We really love it when the quote comes from someone we already trust or are already seen as an authority in whatever arena the quote covers.

But when you see a quote online try to remember these two things:

“Quotation is a serviceable substitute for wit.” — Oscar Wilde

“The problem with internet quotes is that you cant always depend on their accuracy” — Abraham Lincoln, TED 2013