Jump to content
IGNORED

A Checkered Flag button tapper


else

Recommended Posts

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;
 }
}

post-2741-126418189252_thumb.jpg

Link to comment
Share on other sites

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?

Link to comment
Share on other sites

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
Link to comment
Share on other sites

Sounds interesting! :) If I order a kit:

 

http://www.adafruit.com/index.php?main_page=product_info&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
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

post-2741-126427899827_thumb.jpg

Edited by else
Link to comment
Share on other sites

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;
 }
}

post-2741-126479216883_thumb.jpg

post-2741-126479217556_thumb.jpg

  • Like 1
Link to comment
Share on other sites

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....

Link to comment
Share on other sites

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
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...