else Posted January 22, 2010 Share Posted January 22, 2010 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; } } Quote Link to comment Share on other sites More sharing options...
Paolo Posted January 22, 2010 Share Posted January 22, 2010 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? Quote Link to comment Share on other sites More sharing options...
else Posted January 22, 2010 Author Share Posted January 22, 2010 (edited) 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 January 22, 2010 by else Quote Link to comment Share on other sites More sharing options...
nonner242 Posted January 22, 2010 Share Posted January 22, 2010 Oh! This looks very interesting. hmmm... Quote Link to comment Share on other sites More sharing options...
ovalbugmann Posted January 23, 2010 Share Posted January 23, 2010 (edited) 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 January 23, 2010 by ovalbugmann Quote Link to comment Share on other sites More sharing options...
else Posted January 23, 2010 Author Share Posted January 23, 2010 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). Quote Link to comment Share on other sites More sharing options...
else Posted January 23, 2010 Author Share Posted January 23, 2010 (edited) 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. Edited January 23, 2010 by else Quote Link to comment Share on other sites More sharing options...
Zerosquare Posted January 23, 2010 Share Posted January 23, 2010 Nice ! But not really worth the effort for such a bad game It would be interesting to try it on games with better framerate, e.g. Super Burnout. Quote Link to comment Share on other sites More sharing options...
BuddyBuddies Posted January 25, 2010 Share Posted January 25, 2010 Awesome else! Quote Link to comment Share on other sites More sharing options...
else Posted January 25, 2010 Author Share Posted January 25, 2010 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..... Quote Link to comment Share on other sites More sharing options...
else Posted January 29, 2010 Author Share Posted January 29, 2010 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; } } 1 Quote Link to comment Share on other sites More sharing options...
Zerosquare Posted January 29, 2010 Share Posted January 29, 2010 Great job Quote Link to comment Share on other sites More sharing options...
BuddyBuddies Posted January 31, 2010 Share Posted January 31, 2010 will you be building more controllers like yours else? Quote Link to comment Share on other sites More sharing options...
else Posted January 31, 2010 Author Share Posted January 31, 2010 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.... Quote Link to comment Share on other sites More sharing options...
+poobah Posted January 31, 2010 Share Posted January 31, 2010 Isn't an ardunio overkill for this? You can do PWM with a $2 PIC or AVR. Quote Link to comment Share on other sites More sharing options...
else Posted January 31, 2010 Author Share Posted January 31, 2010 (edited) 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 January 31, 2010 by else Quote Link to comment Share on other sites More sharing options...
+poobah Posted January 31, 2010 Share Posted January 31, 2010 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. Quote Link to comment Share on other sites More sharing options...
Paolo Posted January 31, 2010 Share Posted January 31, 2010 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. Quote Link to comment Share on other sites More sharing options...
else Posted February 1, 2010 Author Share Posted February 1, 2010 That's a good idea Paolo. I'll have to keep my eye out for a cheap steering wheel at my local Goodwill.... Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.