Autonomous Game Controllers

JCB_01012009__142528_4160_DRV001

Continuing on my strange pursuit of designing weird interfaces that disrupt conventional game interaction rituals, I put together a bit more of my “PSX” project. You probably don’t recall, but this is the project where I’ve created a little “dongle” that can fool a PS2 (or Playstation 3, as it turns out..) into thinking that the dongle is actually a Playstation controller. You can also hook up a real Playstation controller to it and it can “read” the controller buttons and joystick and pass that data through, or do whatever you want with it. It appears as an TWI/I2C device to anything that talks to TWI/I2C devices..like my favorite TWI/I2C talking thing — the Arduino.

You can see here that the dongle — the white box with those annoying, huge Playstation connectors on either end, one male, one female — has four wires going into it. Those are for power, ground and the TWI signals, SDA and SCL. The power and ground are necessary because the Arduino (at least this one here) and the dongle run at different voltages. The Arduino is a 5V device, while the dongle is a 3.3V device, so there’s some level shifting going on inside there.

JCB_03012009__195744_4176_DRV001

190808_200037

So, that’s all fine. It works, etc. But then the question that’s actually more intriguing than building the thing..what do you do with it?

Well, I have some ideas, including hooking up a bike to it and attaching a giant human form kick-boxing dummy to play old school fighting games. For some early fun and nonsense, I connected a Wii Nunchuck up to it to control Playstation games with Wii-y gestures and stuff. Which could be cool if I find the right game, or spend a little more time thinking things through rather than just writing and revising microcontroller firmware to make a TWI controllable device, and doing Catia to CAD-up and print plastic enclosures.

But, in the meantime, I decided to do an absolutely crucial bit of game science. Something that I am entirely sure is mulled over constantly, but never properly investigated. The question is best stated thusly: how long would it take the Little Prince to roll up an entire room based on a random path algorithm?

How long indeed. Well, short answer is a long time. I let it go for about 70 minutes, after which he had just 10 things to go in a particularly tricky location that required rolling across a narrow bridge. At this point, I took over..but check out the 8x speed video anyway!

Katamari Autonomy from Julian Bleecker on Vimeo

I wrote a quick little Arduino code for my PSX dongle to have the Little Prince roll forward and then, after a few moments, make a random directional change. (Or stop, take a load off and look around the world.)

This was all done by sending TWI commands to the appropriate registers in my little DIY Playstation 2 controller emulator. All the buttons and the joysticks can be emulated as to their state through a series of write-to registers. If there’s a controller stuck in the other side of the dongle, there are a complement of read-from registers so you can see if any of the buttons are pressed and how much the joysticks are displaced. (I set up an “escape” buttons sequence — pressing both L2 and R2 — to bring control back to the normal joystick so I could navigate menus or take control over, which I had to do after I realized, with four items left, the completion of cleaning the room would probably not happen before the universe ran out of steam.)

Here’s the Arduino code. Pretty straight forward Wire / TWI library stuff.


#include
#include "nunchuck_funcs.h"

#define is_bit_clear(a, n) (0 == (a & (1<<n)))
#define is_bit_set(a, n)  (1 == (a & (1<<n))

#define is_button_pressed(a, button) is_bit_clear(a, button)
#define is_button_released(a, button) is_bit_set(a, button)

#define BTN_SELECT 0
#define BTN_L3 1
#define BTN_R3 2
#define BTN_START 3
#define BTN_UP 4
#define BTN_RIGHT 5
#define BTN_DOWN 6
#define BTN_LEFT 7
#define BTN_L2 8
#define BTN_R2 9
#define BTN_L1 10
#define BTN_R1 11
#define BTN_TRIANGLE 12
#define BTN_CIRCLE 13
#define BTN_X 14
#define BTN_SQUARE 15

// register addresses in the PSX I2C device
#define W_BUTTONS_0 0x00 // (, ^, start, R3, L3, select
#define W_BUTTONS_1 0x01 // (square, x, o, triangle, R1, L1, R2, L2)
#define W_RIGHT_X 0x02
#define W_RIGHT_Y 0x03
#define W_LEFT_X 0x04
#define W_LEFT_Y 0x05

#define R_BUTTONS_0 0x12 // (, ^, start, R3, L3, select)
#define R_BUTTONS_1 0x13 // (square, x, o, triangle, R1, L1, R2, L2)
#define R_RIGHT_X 0x14 // a value from 0x00 - 0xFF
#define R_RIGHT_Y 0x15 // a value from 0x00 - 0xFF
#define R_LEFT_X 0x16
#define R_LEFT_Y 0x17

// I2C address of the PSX dongle
int psx_dongle_addr = 0x72;
int j;
void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(0));
  Wire.begin(); // join i2c bus (address optional for master)


// this is the control register. setting it to 1 means that
// we have to tell the PSX device what data to send to the
// Playstation2. Setting it to 0 means that it simply passes
// through the data from the controller to the PS2. We can
// read the state of the contorller at any time.

writeToAddress(psx_dongle_addr, 0x24, 1);
}

// we'll use count to figure out when to change direction
int count = 0;
int buttons;

// mode is used to indicate either "pass thru" where we can use the
// actually real human controller to control the PS2, or to generate
// data via the PSX dongle.
// pressing both L2 and R2 simultaneously toggles the mode
int mode = 1;
byte randomNumber;

void loop()
{
  byte val;
  count++;
  //Serial.println(count, DEC);
 // 0x70 shows up as either ID $20 or ID $E0 on Propeller

/*******************************************/
/*
  BTN_SELECT = $0001
  BTN_L3 = $0002
  BTN_R3 = $0004
  BTN_START = $0008
  BTN_UP = $0010
  BTN_RIGHT = $0020
  BTN_DOWN = $0040
  BTN_LEFT = $0080
  BTN_L2 = $0100
  BTN_R2 = $0200
  BTN_L1 = $0400
  BTN_R1 = $0800
  BTN_TRIANGLE = $1000
  BTN_CIRCLE = $2000
  BTN_X = $4000
  BTN_SQUARE = $8000
**/


// 0x00 write to BUTTONS_0, 0x12 read from BUTTONS_0
// 0x01 write to BUTTONS_1, 0x13 read from BUTTONS_1
// 0x02 write to RIGHT_X, 0x14 read from RIGHT_X
// 0x03 write to RIGHT_Y, 0x15 read from RIGHT_Y
// 0x04 write to LEFT_X, 0x16 read from LEFT_X
// 0x05 write to LEFT_Y, 0x17 read from LEFT_Y
//Serial.println(getButtons(), HEX);
//int buttons = getButtons();
//Serial.print(buttons, BIN);
//  passThruButtons();

if(count > 512) {
  count = 0;
}
//Serial.println(mode, HEX);

// get the buttons
buttons = getButtons();

// mode is used to indicate either "pass thru" where we can use the
// actually real human controller to control the PS2, or to generate
// data via the PSX dongle.
// pressing both L2 and R2 simultaneously toggles the mode
  if(mode == 1 &&
     is_button_pressed(buttons, BTN_L2) && is_button_pressed(buttons, BTN_R2)) {
    mode = 0;
    delay(1000);
  } else
  if(mode == 0 &&
     is_button_pressed(buttons, BTN_L2) && is_button_pressed(buttons, BTN_R2)) {
     mode = 1;
     delay(1000);
     }
 if(mode == 1) {
passThruAllAndShow();
 }
 if(mode == 0)
  {

  writeToAddress(psx_dongle_addr, W_LEFT_Y, 0x00);
  writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0x00);
  passThruButtons();

  if(count == 512) {
    count = 0;

    randomNumber = random(1,5);
        Serial.print("FLIP! ");
        Serial.println(randomNumber, HEX);
    switch(randomNumber) {
      case 1: case 2: case 6:
        writeToAddress(psx_dongle_addr, W_LEFT_Y, 0x00);
        writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0xAF);
        delay(500);
        break;
      case 3: case 4: case 5:
        writeToAddress(psx_dongle_addr, W_LEFT_Y, 0xAF);
        writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0x00);
        delay(500);
        break;
          default:
         delay(500);
         break;
    }
  }

  /*
writeToAddress(psx_dongle_addr, W_LEFT_X, (float)map(nunchuck_accelx(), (float)0x48, (float)0xB0,
                (float)0x00, (float)0xFF));
writeToAddress(pssx_dongle_addr, W_LEFT_Y, (float)map(nunchuck_accely(), (float)0x48, (float)0xB0,
                (float)0x00, (float)0xFF));

writeToAddress(psx_dongle_addr, W_RIGHT_Y, (float)map(nunchuck_joyy(), (float)0x1D, (float)0xDF,
                (float)0x00, (float)0xFF));
writeToAddress(psx_dongle_addr, W_RIGHT_X, (float)map(nunchuck_joyx(), (float)0x1D, (float)0xDF,
                (float)0x00, (float)0xFF));
*/
}

delay(10);
}


//
int getButtons()
{
int result = 0x00;

result = readFromAddress(psx_dongle_addr, 0x13, 1);
//  Serial.print(result, HEX); Serial.print(" ");
result <> 8)); // MSB
  Wire.send((int)(addr & 0xFF)); // LSB
  Wire.send(data);
  Wire.endTransmission();
}

byte readFromAddress(int twi_addr, int addr, int bytes_to_read)
{
  byte rdata;
 Wire.beginTransmission(twi_addr);
  Wire.send((int)(addr >> 8)); // MSB
  Wire.send((int)(addr & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(twi_addr,bytes_to_read);
  while (Wire.available()) rdata = Wire.receive();
  return rdata;
}

Continue reading Autonomous Game Controllers

Refining

I’m getting closer to have a second prototype of the PSX project. Strangely, I seem to be building the breadboard prototype (lower image) simultaneous with the PCB prototype (the thing on top). I think the breadboard prototype is mostly for working on the firmware for the Propeller, which is going better than I had hoped, especially seeing as I’ve never developed for it, and that I’m mostly in Spin assembly (not so ugly, but still..bleech..) The breadboard version is basically a core Propeller with some wiring for programming and for chatting to an EEPROM for permanent firmware storage. I started playing with this PCA9306 bidirectional level-shifter, which is sitting on the right half breadboard. This is basically the same circuit as what goes on the surface mount PCB, except for the addition of a LP2985 3.3v regulator.

This is a curious approach, getting slightly ahead of things, but it’s been a circuitous, round-about project from the original “hey..wouldn’t it be cool if..?” motivation that I thought would maybe be a weekend’s work of getting an Atmega324 to emulate a PSX controller. That’s the one in the image above, from last fall. I began the design last summer thinking I’d be done in short order. It should be..I’ve just got grease in my head or something. I was anticipating perhaps some trickiness because I took the trouble to pull out the boundary scan signals to the edge of the board so I could do some JTAG debugging. What went wrong? Well, mostly timing related things. This project motivated me to get my own logic analyzer so I could figure out what the heck was going wrong. There were mostly timing-related problems that I sort of pushed to the side to pursue another possible solution, which led to this current (v07) design.

How did this all start? I thought it’d be an interesting little bit of game controller provocation to have a controller that “got tired” just as one’s avatar might in a video game. To do this, I figured I’d have to Continue reading Refining

Scale

20080526_19-18-15

20080523_11-56-26

Not knowing a heck of a lot about solenoids in practice — I know what they do, and, as an example of the sometimes impracticality of higher-ed, am fairly fluent in the E&M principles at work here. But, when it comes to the practical matter of finding one with the necessary “umph” to articulate a simple controller’s buttons, it’s all guess work.

(Parenthetically, this mechanism is a subcomponent of a larger project called “Air Guitar Hero” which uses a remote glove controller to articulate the solenoids here. Yes. It makes no practical sense. It points to “something” as an experiment, if nothing more than to learn a few things about controlling solenoids and such all. But, mostly it is a design provocation. That’d be the easiest way of describing this whole thing, for those who have asked.)

The first solenoids I used were the smallish ones on top, bought at close-out prices from Electronics Goldmine for about $2 a piece. They couldn’t push the button completely, nor with the surety of purpose the design demanded. They would actuate, but not push the button closed. The best they could do was kind of rattling things around a bit.

Not really knowing precisely how to “engineer” a solution (probably something about determining the closing force of the switch and back-stepping to an appropriate solenoid), I just bought a few different sizes. The first one to arrive was enormous and, had I been a bit more careful, I would’ve realized that the centerline to centerline spacing of a row of four of these would’ve been wider than the center to center distance between the Guitar Hero buttons. Poppa Bear is a Guardian Electric TP12X19-I-24D, push style solenoid, runs at 24 volts. Way too big. So..that one is now a paperweight on my desk.

(Here’s a link to Guardian Electric that has specifications on their other tubular push/pull solenoids.)

The other two were closer, and I ended up using the “Momma Bear” solenoid — a Guardian Electric TP6X12-I-24D, also push style, with a load force of 18-0.06/2.5-0.75 Ounce-Inch. The data sheet is here.

I’m running all of these at 12 volts, which makes them less umph-y, but sufficient for what I’m doing. The solenoids have more push force at the low end of teir travel, so I designed the little supporting bridge there to hold the articulating shaft right on top of the controller button so that most of the force would be committed to pushing the button and not traveling through space.

Speaking of scale, on the left there is the breadboard prototype circuit to drive five solenoids. The right is the PCB with the same circuit (minus a bunch of Arduino icing, just a plain vanilla Atmega168 and crystal). Scaled down, the circuit is much easier to manage and cart around than the relatively fragile breadboard edition, especially cause I’m using janky, untrimmed jumpers to make connections and so forth.

For the curious, here’s the circuit’s schematic and the PCB layout pictures.