Jump to content

Photo

A Checkered Flag button tapper


18 replies to this topic

#1 else OFFLINE  

else

    Dragonstomper

  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Fri Jan 22, 2010 11:57 AM

I recently picked up an Arduino starter kit from Adafruit Industries. For those unfamiliar with the Arduino, it is a popular open-source processor and development kit for hobbyists. Starter kits are available from lots of places for about $50 or so.

I wrote a quick little program for it to automatically tap the left or right buttons for you -- depending on how far left or right you turn a potentiometer. This helps makes the game more tolerable, in my opinion. It's not a cure all though -- Checkered Flag also suffers from a pretty low framerate.

Anyhow if anybody has an Arduino or decides to pick one up, here's the program in it's entirety -- along a picture of my modified controller (with Arduino attached). The program could probably use a bit more tweaking -- however I've probably taken it as far as I'm going to....

/*
 Jaguar button-tapper program for the Checkered Flag game.
 
 The circuit:
 * Potentiometer on sensorPin
 * -- center     pin              to sensorPin
 * -- one   side pin (either one) to ground
 * -- other side pin              to +5V
 * Left  output on LtPin
 * -- active low (i.e. low = button pressed)
 * -- see note below
 * Right output on RtPin
 * -- active low (i.e low = button pressed)
 * -- see note below
 
 Note: Depending on how you wire these in to your Jaguar
 Controller, you may have to qualify them with select line #4
 (yellow wire).  To do this, feed each of these outputs in to
 two separate 2-input OR gates, along with the select line.  The
 output of these OR gates is then the qualified version of these
 signals.  (OR gates are used because all signals are active low)
 
 */

int sensorPin = 0;    // select the input pin for the Potentiometer
int LtPin = 12;       // select the pin for the Left  output
int RtPin = 11;       // select the pin for the Right output

int sensorValue = 0;       // variable to store the value coming from the sensor
int sensorValueMapped = 0; // variable to store the value coming from the sensor
int loopCount = 0;         // variable to store the loop count
int buttonDirection = 0;   // variable to record latest direction - left = 0, center = 1, right = 2

void setup() {
  // declare our output pins
  pinMode(LtPin, OUTPUT); 
  pinMode(RtPin, OUTPUT);
}

void loop() {
  
  // ==============================================================
  // Set Both Buttons HI
  //
  // Simply set both high for a fixed amount of time.  This
  // seems to work best with the game, but needs more testing
  // to say for sure.
  // ==============================================================
  
  digitalWrite(LtPin, HIGH); // Lt not pressed, hold high
  digitalWrite(RtPin, HIGH); // Rt not pressed, hold high

  // Stop the program for () milliseconds:
  delay(250);
  
  // ==============================================================
  // Set One Button LO
  //
  // Here we want to sample the Potentiometer as often as possible.
  // In other words, we don't just want to sample it once, and then
  // stop for that amount of time, because the user could be
  // moving the Potentiometer while we are stopped.  So a better
  // way is to loop -- read pot, delay for 1ms, read pot, delay for
  // 1ms, etc.
  // ==============================================================
  
  loopCount = 0;
  sensorValueMapped = 1;
  buttonDirection = 1;
  
  while(loopCount < sensorValueMapped) {
    
    // read the value from the sensor:
    sensorValue = analogRead(sensorPin);
  
    if(sensorValue >= 512) { // press the Rt button
      digitalWrite(LtPin, HIGH);
      digitalWrite(RtPin, LOW);
      if(buttonDirection != 0) { // reset count if direction change
        loopCount = 0;
        buttonDirection = 0;
      }
      sensorValueMapped = map(sensorValue, 512, 1023,   0, 511); // remap by moving lower
    } else if (sensorValue <= 511) { // press the Lt button
      digitalWrite(LtPin, LOW);
      digitalWrite(RtPin, HIGH);
      if(buttonDirection != 2) { // reset count if direction change
        loopCount = 0;
        buttonDirection = 2;
      }
      sensorValueMapped = map(sensorValue,   0,  511, 511,   0); // remap by reversing
    } else { // at the middle, don't press either button
      digitalWrite(LtPin, HIGH);
      digitalWrite(RtPin, HIGH);
      if(buttonDirection != 1) { // reset count if direction change
        loopCount = 0;
        buttonDirection = 1;
      }
      sensorValueMapped = 0;
    }
    
    // ============================================================
      
    // At the extremes, keep the button pressed forever.  (don't
    // really know how much this helps -- needs more testing)
    if((sensorValueMapped > 506) || (sensorValueMapped < 5)) {
      loopCount = 0;
    };
  
    // ============================================================
      
    // Stop the program for for () milliseconds:
    delay(1);
    
    loopCount = loopCount + 1;
  }
}

Attached Thumbnails

  • IMG_1021.jpg


#2 Paolo OFFLINE  

Paolo

    Dragonstomper

  • 900 posts
  • Location:Italy

Posted Fri Jan 22, 2010 3:16 PM

YYYEEEEEEEAAAAAHHHH!!!
It is oh-so-many years I am working on a stuff like this, but only trying to do it in the electro-mechanical domain, with little or no luck.
I am replying without even trying to read the program. but tell me:
I remember that I noticed I needed two parameters to control:
* frequency of "click"
* duration of button-down.

I managed to set a potentiometer that controlled the duration of button-down, in order to calibrate, and then connected the frequency potentiometer to a steering wheel.

Did you take in account these two parameters as well?

#3 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Fri Jan 22, 2010 3:36 PM

Yep, what you're describing (frequency and duration) is commonly referred to as pulse-width modulation. In electronics, a Voltage Controlled Oscillator (VCO for short) is what is called for. This program does exactly that.

By the way, I did try doing this with discrete circuits (VCOs, OR gates, AND gates, Comparators, etc) a year or two ago. I had it all working, but in the end it just didn't offer the fine-grained control that this does.

But like I say, this program doesn't work miracles for Checkered Flag -- frame rate being a big issue...

Also, Arduino is made in Italy. Maybe you can get it cheaper since you live there :)

Edited by else, Fri Jan 22, 2010 4:05 PM.


#4 nonner242 OFFLINE  

nonner242

    River Patroller

  • 3,622 posts
  • ALL your base ALL belong to us!!!!!!!!!!!!!!
  • Location:OHIO

Posted Fri Jan 22, 2010 4:55 PM

Oh!Posted Image
This looks very interesting.
Posted Image hmmm...



#5 ovalbugmann OFFLINE  

ovalbugmann

    River Patroller

  • 2,131 posts
  • Location:Phoenix, Arizona

Posted Fri Jan 22, 2010 6:36 PM

Sounds interesting! :) If I order a kit:

http://www.adafruit....&products_id=68
(is this the one?)

I wonder if I could put one together like yours, I don't know, as I would have to have step by step instructions for each and every wire and component.

Where do you put in the program for this CF control method?

Edited by ovalbugmann, Fri Jan 22, 2010 6:37 PM.


#6 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Fri Jan 22, 2010 9:02 PM

Yes, that is the kit I ordered.

I'll work on drawing up a schematic. Probably won't be for ready for a couple of days (weekends I'm mostly busy with the kids). Note that to properly wire it in will require cutting a couple of traces inside the controller, something I didn't do for this experiment (I wasn't 100% sure if I wanted to sacrifice a controller for this or not).

#7 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Sat Jan 23, 2010 2:41 PM

Here you go. Forget what I said yesterday about having to cut traces in the Controller -- I came up with a better way (using a couple of diodes). No traces need to be cut, and all buttons on the Controller continue to operate.

Other than the Arudino, you can get everything else at your local Radio Shack for a couple of dollars. The two diodes are 1N4148's, and the OR gate is a 74HC32. Note that the Arduino gets its power from the Controller, so it should be pretty easy to mount everything in a nice looking little case if you wanted (please post pictures if you do!)....

Enjoy!

P.S. It would be pretty easy to extend this to also make a separate button tapper for the Accelerator. Just add another potentiometer and diode (and use one of the two unused OR gates in the 74HC32), and with a little cutting/pasting in the program I posted, you'd be in business.

Attached Thumbnails

  • Schematic.JPG

Edited by else, Sat Jan 23, 2010 2:50 PM.


#8 Zerosquare OFFLINE  

Zerosquare

    Stargunner

  • 1,828 posts
  • Location:France

Posted Sat Jan 23, 2010 5:31 PM

Nice !

But not really worth the effort for such a bad game :P
It would be interesting to try it on games with better framerate, e.g. Super Burnout.

#9 BuddyBuddies OFFLINE  

BuddyBuddies

    River Patroller

  • 2,211 posts
  • Location:A Secret Place

Posted Sun Jan 24, 2010 9:28 PM

Awesome else! :cool:

#10 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Mon Jan 25, 2010 8:18 AM

Thanks for the feedback guys -- it's been an idea that's been kicking around in my head for many years. Maybe I will try to mount everything in a nice case, just for kicks. I'll post pictures if and when I do.....

#11 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Fri Jan 29, 2010 1:19 PM

Okay, a few updates....

1) I decided to go ahead and mount everything in a case (see pictures). Its just a standard case from Radio Shack -- but it actually fits everything pretty nicely. If you look closely at the pictures, you can see that I offset the case to the left side when I mounted it. This way, I can still grip the controller comfortably with right hand, while steering with my left hand. Though I do wish the potentiometer would snap back to the center when I release it. Anybody know if they make such a thing?

2) I've made a few more tweaks to my program (see below). Nothing major, just a few defines and stuff like that.

Anyhow, it definitely makes the game more enjoyable -- there's a few tracks that are almost fun to play now. It's pretty cool to have "simulated analog" control of the car. Again, don't expect miracles if decide to build this yourself -- but if you set your expectations low enough, you might be pleasantly surprised.... :)

Enjoy!

/*
 Jaguar button-tapper program for the Checkered Flag game.
 
 The circuit:
 * Potentiometer on sensorPin
 * -- center     pin              to sensorPin
 * -- one   side pin (either one) to ground
 * -- other side pin              to +5V
 * Left  output on LtPin
 * -- active low (i.e. low = button pressed)
 * -- see note below
 * Right output on RtPin
 * -- active low (i.e low = button pressed)
 * -- see note below
 
 Note: Depending on how you wire these in to your Jaguar
 Controller, you may have to qualify them with select line #4
 (yellow wire).  To do this, feed each of these outputs in to
 two separate 2-input OR gates, along with the select line.  The
 output of these OR gates is then the qualified version of these
 signals.  (OR gates are used because all signals are active low)
 
 */

int sensorPin = 0;    // select the input pin for the Potentiometer
int LtPin = 12;       // select the pin for the Left  output
int RtPin = 11;       // select the pin for the Right output

int sensorValue = 0;       // variable to store the value coming from the sensor
int sensorValueMapped = 0; // variable to store the value coming from the sensor
int loopCount = 0;         // variable to store the loop count
int buttonDirection = 0;   // variable to record latest direction - left = 0, center = 1, right = 2
int buttonDirectionPrev = 0;   // variable to record latest direction - left = 0, center = 1, right = 2

#define REMAP_LO 90
//#define REMAP_HI 250 
#define REMAP_HI 350

#define CENTER_WIDTH 4
//#define EDGE_WIDTH   8
#define EDGE_WIDTH   0

#define STRAIGHT_SCALE

#define CENTER_TEMP_LO (REMAP_LO + CENTER_WIDTH)
#define EDGE_TEMP_HI   (REMAP_HI - EDGE_WIDTH  )

void setup() {
  // declare our output pins
  pinMode(LtPin, OUTPUT); 
  pinMode(RtPin, OUTPUT);
}

void loop() {
  
  // ==============================================================
  // While Loop #1: Set Both Buttons HI
  //
  // Here we want to sample the Potentiometer as often as possible.
  // In other words, we don't just want to sample it once, and then
  // stop for that amount of time, because the user could be
  // moving the Potentiometer while we are stopped.  So a better
  // way is to loop -- read pot, delay for 1ms, read pot, delay for
  // 1ms, etc..
  // ==============================================================
  
  digitalWrite(LtPin, HIGH); // Lt not pressed, hold high
  digitalWrite(RtPin, HIGH); // Rt not pressed, hold high

  // Stop the program for () milliseconds:
  loopCount = 0;
  while(loopCount < REMAP_LO) {
    
    // read the value from the sensor:
    sensorValue = analogRead(sensorPin);
    
    if(sensorValue >= 512) { // press the Rt button
      buttonDirection = 0;
    } else { // press the Lt button
      buttonDirection = 2;
    }
    
    // ***** we want to break out of this loop immediately under a few conditions *****
    
    if((buttonDirection != buttonDirectionPrev)) {
      loopCount = 100; // will cause us to break out of the loop!
    }
    
    // Stop the program for for () milliseconds:
    delay(1);
    
    loopCount = loopCount + 1;
  }
  
  // ==============================================================
  // While Loop #2: Set One Button LO
  //
  // Here we want to sample the Potentiometer as often as possible.
  // In other words, we don't just want to sample it once, and then
  // stop for that amount of time, because the user could be
  // moving the Potentiometer while we are stopped.  So a better
  // way is to loop -- read pot, delay for 1ms, read pot, delay for
  // 1ms, etc.
  // ==============================================================
  
  loopCount = 0;
  sensorValueMapped = 1;
  buttonDirection = 1;
  
  while(loopCount < sensorValueMapped) {
    
    // read the value from the sensor:
    sensorValue = analogRead(sensorPin);
    
    // ***** remap the input value *****
    
    buttonDirectionPrev = buttonDirection;
    
    if(sensorValue >= 512) { // press the Rt button
#ifdef STRAIGHT_SCALE
      sensorValueMapped = map(sensorValue, 512, 1023, REMAP_LO, REMAP_HI); // remap by moving lower
#else
      sensorValue = sensorValue - 512;
      sensorValue = sensorValue / 4;            // divide, so we don't exceed the size of an int when we square it
      sensorValue = sensorValue * sensorValue;  // square it
      sensorValueMapped = map(sensorValue,   0,  16384, REMAP_LO, REMAP_HI); // remap by moving lower
#endif
      buttonDirection = 0;
    } else { // press the Lt button
#ifdef STRAIGHT_SCALE
      sensorValueMapped = map(sensorValue,   0,  511, REMAP_HI, REMAP_LO); // remap by moving lower and reversing
#else
      sensorValue = sensorValue - 0;
      sensorValue = sensorValue / 4;            // divide, so we don't exceed the size of an int when we square it
      sensorValue = sensorValue * sensorValue;  // square it
      sensorValueMapped = map(sensorValue,   0,  16384, REMAP_HI, REMAP_LO); // remap by moving lower and reversing
#endif
      buttonDirection = 2;
    }
    
    // ***** press the right button *****
    
    if(sensorValueMapped >= CENTER_TEMP_LO) {
      if(buttonDirection == 0) { // press the Rt button
        digitalWrite(LtPin, HIGH);
        digitalWrite(RtPin, LOW);
      } else { // press the Lt button
        digitalWrite(LtPin, LOW);
        digitalWrite(RtPin, HIGH);
      }
    } else { // at the middle, don't press eithe button
      digitalWrite(LtPin, HIGH);
      digitalWrite(RtPin, HIGH);
    }
    
    // ***** we want to stay in this loop forever under a few conditions *****
    
    if((buttonDirection != buttonDirectionPrev) || (sensorValueMapped > EDGE_TEMP_HI) || (sensorValueMapped < CENTER_TEMP_LO)) {
      loopCount = 0; // reset the count
      sensorValueMapped = 511; // set this to some high value (don't really mattter, just so it's not 0 or 1 (which would cause us to break out of the while loop))
    }

    // Stop the program for for () milliseconds:
    delay(1);
    
    loopCount = loopCount + 1;
  }
}

Attached Thumbnails

  • IMG_1046.jpg
  • IMG_1045.jpg


#12 Zerosquare OFFLINE  

Zerosquare

    Stargunner

  • 1,828 posts
  • Location:France

Posted Fri Jan 29, 2010 3:33 PM

Great job :thumbsup:

#13 BuddyBuddies OFFLINE  

BuddyBuddies

    River Patroller

  • 2,211 posts
  • Location:A Secret Place

Posted Sat Jan 30, 2010 6:42 PM

will you be building more controllers like yours else? :cool:

#14 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Sat Jan 30, 2010 7:12 PM

I guess I wasn't really planning on building or selling any more, for a couple of reasons: (1) While it definitely improves the game, it's not a cure-all. And (2) they wouldn't be cheap -- probably cost at upwards $75 a piece to make (an Arduino kit is about $60, plus a Jaguar controller is around $7, a case is around $3, plus some other miscellaneous parts).

Because of these factors, I think there is the potential for people to be disappointed, upset, mad, you name it -- if I were to sell them. Unfortunately that's reality. And that's just something I don't need in my life.

If anyone else out there wants to build and sell them, go right ahead....

#15 poobah OFFLINE  

poobah

    Stargunner

  • 1,554 posts
  • Location:Beavercreek, Ohio

Posted Sun Jan 31, 2010 12:17 AM

Isn't an ardunio overkill for this?
You can do PWM with a $2 PIC or AVR.

#16 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Sun Jan 31, 2010 8:06 AM

Well, I'm not really doing straight PWM. If you look closely at my program, I always release the button for a very quick, constant amount of time. But I always hold it down for a variable amount of time based on the potentiometer position. This corresponds to how Checkered Flag works -- it turns the wheel left or right slowly, but snaps the wheel back very quickly when you release it (change to the cockpit view next time you play the game to see what I mean). By the way, the fact that you can't do just straight PWM is why I abandoned my first project, using all discrete components....

Anyhow, to answer your question -- yes there's always lots of other ways of doing things. I'm sure you could use a cheaper processor. I bought the Arudino to play around with, so this seemed like a fun weekend project. If you're so inclined to experiment with other processors, etc, by all means -- go for it!

Edited by else, Sun Jan 31, 2010 8:25 AM.


#17 poobah OFFLINE  

poobah

    Stargunner

  • 1,554 posts
  • Location:Beavercreek, Ohio

Posted Sun Jan 31, 2010 8:27 AM

Well, I'm not really doing straight PWM. If you look closely at my program, I always release the button for a very quick, constant amount of time. But I always hold it down for a variable amount of time based on the potentiometer position. This corresponds to how Checkered Flag works -- it turns the wheel left or right slowly, but snaps the wheel back very quickly when you release it (change to the cockpit view next time you play the game to see what I mean).

Anyhow, to answer your question -- yes there's always lots of other ways of doing things. I'm sure you could use a cheaper processor. I bought the Arudino to play around with, so this seemed like a fun weekend project. If you're so inclined to experiment with other processors, etc, by all means -- go for it!


Not knocking you... It's a nice solution to the CF control issue.

If someone else wants to try it, I suggest a different route, unless they already have the ardunio, since it's kinda opening a walnut with a sledge hammer.

The Ardunio actually is just an AVR on a standardized platform with a nice little language wrapped around it.

#18 Paolo OFFLINE  

Paolo

    Dragonstomper

  • 900 posts
  • Location:Italy

Posted Sun Jan 31, 2010 10:41 AM

While experimentig as described earlier, I just bought a cheap-o steering wheel for PC having a couple of springs for a self-centering motion. This way I just connected the potentiometer to the shaft of the steering wheel.

#19 else OFFLINE  

else

    Dragonstomper

  • Topic Starter
  • 964 posts
  • Location:New Hampshire (ex-Minnesotan)

Posted Mon Feb 1, 2010 2:47 PM

That's a good idea Paolo. I'll have to keep my eye out for a cheap steering wheel at my local Goodwill....




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users