Wednesday, April 11, 2012

Super Mario Kart Cycle



I love Super Mario Kart and the SNES in general.


When I was young I used to think about how much fun it would be to play SMK with a real bike. Now, many years later, we finally have the ability to create such a thing without a lot of very specific knowledge and tools.

Some of the things I used:
Arduino microcontroller (UNO)
A few transistors
Some pushbutton switches
A few resistors
A 2$ photo interrupter
An SNES controller
A very old stationary bike
A breadboard


Nothing there is overly expensive and none of it should require more then a little time and googling to figure out how to use. That said I'm not particularly experienced with electronics so there may very well be better ways to do some of this. (Actually I've found a few things I'd like to correct myself quite recently)

This was my 2nd Arduino project, the first being an n-back test game which I'll make a post about in the future (https://github.com/statuswoe/nBackGame).

There are two separate parts to this project, the first is finding an easy way to output from Arduino into the SNES and the second is having the Arduino recognize when the bike is being pedaled and when the buttons are being pressed.

Arduino to SNES

The SNES controller isn't particularly complicated, there looks to be a multiplexer and a couple resistors but little else. I wasn't able to find a really good diagram though. There is a description of the pinouts here: http://www.gamesx.com/controldata/snesdat.htm but I wasn't really interested in playing with a clocked signal when I didn't have to since I had an old SNES controller that I wasn't using. All I needed to do was put an NPN transistor across the points that the buttons contacted and I can fake a button press by applying voltage to the base pin, this was pretty simple.

My soldering is getting a little better, couldn't get much worse.


What I ended up with is a series of input pins that I plugged into my Arduino board and when I set them high the associated SNES button is triggered.

I mounted the project board and the modified controller into a small toolbox which was the cheapest container I could find that fit them both. The SNES connector comes out of one end and the Arduino connector comes out of the other. One of my goals was to make the SNES controller adapter entirely separate so that I can use it for other games/peripherals.




Bike to Arduino

The Handlebar buttons are pretty self explanatory. I used momentary push button switches that are on unless pressed just because I had some on hand. Because I'm running them into the Arduino I was able to invert the state in software.

The fun part was finding a simple way to track pedaling. I found a few caveats:
- Because the wheel doesn't stop when you stop pedaling and there are no real breaks I didn't want to use the wheel to track speed. I need to know when the user stopped pedaling.
- Running wire onto the pedal would be a pain because the pedal rotates relative to the bike so I'd need to set up brushes or something that wouldn't twist up when you pedal.

Because of these issues I decided to mount a photo interrupter next to the sprocket and have something attached to the sprocket at regular intervals that can pass through the interrupter and trigger the state-change. I ended up using 6 small pieces of playing card to do this.


Once the photo interrupter and buttons were hooked up to the Arduino I wrote some quick code to read the inputs and set the appropriate outputs to the SNES Controller adapter. The code is available here(and could use some polish).

I have the code and SNES Controller adapter set up to accept up, down, left, right, A and B, but I don't have the up/down hooked up to the bike yet because I'm not satisfied with the toggle switch I was going to use for it.

So in the end we have The Bike pushing data into Arduino, which does a little logic and sets some appropriate outputs which saturate the transistors to press the appropriate button.





EDIT:
Maicol asked for some more info on using transistors to control digital buttons, so here's a better image of that part of it, click on it for full size. The common ground is the red blocks, the blue side is the per-button input from the arduino (high to simulate the push).  The NPN transistors bridge the path from the (green block) SNES controller wires to ground which the SNES reads as a button having been pressed.
The SNES is a bit odd in this sense, what you really want to do if you're using this for any other sort of input is put one side of the NPN transistor on the wire leading into one side of the button and the other side of the NPN transistor on the wire coming from the other side of the NPN transistor. You can then apply a charge to the middle pin of the transistor and it will act as though the button had been physically bridged. Hope this helps.


Friday, April 29, 2011

The value of a drop in a bucket

I frequently listen to the ESPN podcast "Baseball Today" with Eric Karabell and guests. It's not my favorite podcast, but the guy knows his baseball and his co-hosts are usually really good.
On a recent podcast Eric mentioned that the current decline in attendance at Dodgers games being partially blamed on fans wanting to protest the ownership was silly. He stated that a fan not going to a game to protest the ownership situation wouldn't even be noticed and that no real fan would want to do that.
This reminded me of something else that's been getting a lot of attention this (and every) election season, the value of one vote. On a larger scale the concept is often likened to a drop in a bucket, an idea worth looking at.

The price of a coffee:

If I asked you right now the exact maximum price you would pay for a coffee, to the penny, what would you say? Do you think you're guess is accurate? In my case I'd have to say no, it's just a ball-park of what I would expect, rather than an instantaneous reflection of how much I would actually pay. It's really hard to prove the exact price you would pay for something at any given time since any initial offer or haggling would likely change your perspective.
However, If I had 1000 clones of you, all of whom are exactly like you in every way and offered them all a different price for a cup of coffee then I could find out exactly what your maximum price would be. Consider the following example

Clone 1 is offered the coffee for $.01 and accepts
Clone 2 is offered the coffee for $.02 and accepts
...
Clone 143 is offered the coffee for $1.43 and accepts
Clone 144 is offered the coffee for $1.44 and declines
...

We can expect that since clone 144 declined to pay $1.44 for the coffee that all subsequent clones, being the same person effectively, would decline to pay more. At this point we know that the maximum price you (being the one who was cloned) would pay for a coffee at this time.

This kind of accuracy is impossible to get simply by offering a person the same coffee consecutive times since as I mentioned earlier previous offers can bias their perceived value of the coffee. It also doesn't last, ask me 5 minutes from now and I'd bet the number would have changed, particularly if I was still growing thirsty.

Now you might be thinking "Wow Brent you've wasted like 3 minutes of my day, thanks! Who cares how much I'd have payed for a coffee at a specific time, other than perhaps the man running the coffee shop at that time. Also, since this is a theoretical experiment I can't even find that price in the real world rendering this whole post useless. I hate you." Ouch. Well, maybe we can use this idea to help increase our perception on the value of one penny/vote/drop?

Consider that not every person would have the same "instantaneous maximum" for a given offer or question. The "Would you pay $1.73 for a cup of coffee." question could easily translate into "Do you consider Party X, who got 10,173 votes to be a valid party worthy of consideration in the next election?" or "173 people complained about a decision I made, is that number high enough to make me take their perspective into consideration?" All of these numbers could easily fall into the instantaneous maximums of people looking at them. For example:

Party A received 10,417 votes
Party B received 9,567 votes
Party X received 4,351 votes


Party A wins the seat, Party B supporters are disappointed about losing such a close race, but know they have a shot next election. Party X however didn't really come all that close, so they might be asking themselves a big question.

Did my vote count for anything?

One way to look at it is that in such a close race both of the top parties will be looking to gain votes in the following election, one way to do this is to make sufficient concessions toward an outside party's platform to woo some of their voters. For instance if Party X was really pro-environment then Party A or B might alter their platform a little to be more environmental in order to get a few of Party X's votes. This is at least a partial success since you are going to see some of your desires fulfilled.
Another view is that with any smaller party there is likely to be people who support the platform but don't want to "waste their vote" and vote strategically for whomever would be their choice of the front-runners. This seems to be a very popular way to vote, most people feel very strongly either in favor of or opposed to the incumbent and want to make sure "he/she does('nt) get back in". What can get lost in this is the idea that a third party that loses a lot of votes to strategic voting can end up looking much further out of the running than their actual support would be. Each vote for party X adds increased likelihood that when the results come out that party would look like they have a legitimate chance in a future election.

This is where the instantaneous maximum concept I mentioned above comes in. In the sense of voting, where everyone can see the resulting vote totals and decide for themselves if a party had enough to be considered a viable choice in future elections that's a lot of potential voters using their own maximums to make that call subconsciously.

As an example if I was a party X supporter, but voted A strategically and tuned in to see the vote totals I have a number, albeit a number I don't consciously know, where I would realize that party X might have a shot next election. Say that number was 4,352 then in the above example I wouldn't yet be convinced and would likely vote strategically next election, but I wouldn't know exactly how close I came to being convinced. Now consider there are a large number of people like me, but who's number is slightly (or significantly) different than mine. This means that instead of having one solid number (to win the election) that if you miss you feel like you've wasted your vote you actually have hundreds or even thousands of numbers where your single vote could influence the next election in a much more dramatic way.

This kind of thing can snowball too. If my vote this election catalyzes additional people to consider party X and maybe vote for them in the following election then their votes can do the same in subsequent elections.

In conclusion even though third party vote totals might not win them an election, the value of the actual vote count (or penny charged, or drop added) can have direct impact on results even if the number seems arbitrary.

I didn't really intend for this to become so much about voting, but it seemed a decent example, and I think it's important that people consider not only the short term outcomes of elections but use their votes as a path to the future that they want.

Thanks,
Brent

Thursday, March 3, 2011

Maven Release and Directory Locations

A co-worker of mine ran into a strange error while running a maven release:prepare goal on a new project we were setting up, and the issue is kind of odd.

When he ran the prepare goal he would quickly get (on the first module) this error:
[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
[INFO] ------------------------------------------------------------------------
[INFO] null
[INFO] ------------------------------------------------------------------------
[INFO] Trace
java.lang.NullPointerException
at org.apache.maven.shared.release.util.ReleaseUtil.getBaseWorkingDirect
oryParentCount(ReleaseUtil.java:233)
at org.apache.maven.shared.release.phase.RewritePomsForReleasePhase.tran
slateScm(RewritePomsForReleasePhase.java:109)
at org.apache.maven.shared.release.phase.RewritePomsForReleasePhase.tran
sformScm(RewritePomsForReleasePhase.java:62)

Google didn't really help much so I had a look at source for those classes and tried to figure out where the null was coming from. While I was looking around I remembered that he had checked out the project into his root directory, so the working directory was directly inside "C:\".

That is:
C:\project\pom.xml was the top level pom getting built.

Since the release plugin looks for some base and parent directories I wondered if this made a difference, if asking for the base directory and just getting "C:\" was an issue. I tried to run the same goal on my machine, with a more nested working directory and it finished successfully. We tried adding one level of directories between his root and his workspace (C:\blah\project\pom.xml) and that seemed to do the trick.

I've logged a bug report with the maven release plugin about this: http://jira.codehaus.org/browse/MRELEASE-663, but in the interm if anyone runs into it just make sure you aren't doing your dev too close to root.

Thanks,
Brent

Thursday, February 24, 2011

Hudson/Jenkins and the missing key

A while back we began moving from one old hudson server to a new box with a newer version of hudson on it that could better handle the ever increasing load it was being given. For a while things were working fine, I do mostly AS3/Flex builds using flex-mojos and I moved a couple project to the new server with no issues. At some point (still unsure why) our flex jobs started failing. The error given was as follows:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] key can't be empty

[INFO] ------------------------------------------------------------------------
[INFO] Trace
org.apache.maven.lifecycle.LifecycleExecutionException: key can't be empty
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:719)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.lifecycle.LifecycleExecutorInterceptor.execute(LifecycleExecutorInterceptor.java:65)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)

I found a couple references online to "missing key in Hudson" but none seemed to fit my situation.

Eventually I ended up creating a new test project as a multi-configuration project instead of my standard Maven 2 project, adding an empty shell script and the Maven 2 build step. This ended up solving the missing key issue, though I'm at a loss as to why.

Since I struggled with this for so long I figured I'd post it and hopefully save someone else some pain. It's quite possibly a flex-mojos bug since it didn't seem to affect the Java jobs on the server. If anyone can explain why this seems to happen I'd love to hear about it.

This occurred in a version of Hudson just before the start of the Jenkins project, so it would likely occur there as well and I thought it's worth mentioning.

Thanks,
Brent

Thursday, November 18, 2010

Pidgin MSN Cert update

This morning pidgin was complaining about my MSN accounts and the cert coming from omega.contacts.msn.com. I seems that the cert was changed.

The fix:

Using Firefox visit: https://omega.contacts.msn.com/

Right-click on the page (an error page) and select page-info

Select the Security tab and click "View Certificate"

In the Details tab click export and save it in the certs dir in your .purple folder
ex:(Win XP) C:\Documents and Settings\{username}\Application Data\.purple\certificates\x509\tls_peers

(if you can's find this on your machine a simple search for tls_peers should point you in the right dir)

In Pidgin disable then enable any MSN accounts and it should connect.

For people who don't want to grab the cert themselves a helpful blogger has a link to a copy of the cert (and similar instructions) here: http://blog.andreineculau.com/2010/11/pidgin-and-msn-certificate-error-for-omega-contacts-msn-com/ BUT BE WARNED that certs are the sort of things you should get from the site itself if you care about security.

Hope This Helps,
Brent

Thursday, June 17, 2010

AS3 Boolean Assignment

I came across a situation where I had a series of function calls that could alter an array of objects and I wanted to know at the end if any of them had actually made any changes. I decided to have each function return true if it had made a change and false otherwise. Then I created a boolean variable before running through the set of functions and assigned the result of each of the function calls to that variable using the following:

b = foo() || b;


I did it this way because boolean logic operators work left to right and had I done either of these:

b = b || foo();
b ||= foo();


then if b was true foo() would never have been called.

I found myself wishing that there was a way to explicitly set which direction the expressions would be checked in ( =|| instead of ||= for right to left) or even a means of indicating that you want ALL of the expressions to be validated (efficiency be damned!).

Fortunately there is a (less optimized?) way to do something similar using numerical operators. When a boolean is used in a numerical operator it gets treated as a 1 or 0. This allow you do do the following

var b:Boolean = true;
var c:Boolean; //defaults to false
var d:Boolean;

trace(b+b+c+b)// 3
trace(b+c+c+c)// 1

d = c+b+b
trace(d)// true;
d = c+c+c+b;
trace(d)// true;
d = c+c;
trace(d)// false;

d = false;
d += b // true;


This means I can do the following in my example situation:

var b:Boolean;
b += foo();
b += boo();
b += who();
b += do();


and if any of the functions return true b will become true.

Monday, May 17, 2010

First Climbing Experiance

Last Friday I was visiting a friend who invite me to go climbing with him. Having never been climbing before I jumped at the chance. I figured we would be going to an indoor wall of about 20-25 feet and have an easy day of mocking my inability to do a chin-up. This was not the case and I found myself standing at the top of a cliff becoming a little nervous.

Since I'd never rappelled before and seeing as we were already at the top of the cliff Steve thought this would be a good opportunity for me to give that a try so after explaining to me how the gear worked and showing me how to do it I leaned back over the cliff and began my decent. All in all it was pretty easy, I'm not scared of heights and you tend to be in total control of your own decent which is comforting when you're hanging 70 feet over a pile of rocks.

After we reached the bottom the fun part began. Steve explained how belaying worked and showed me the equipment that we would be using along with the basic terminology I would need to know. I can't stress enough how much I love knowing how things work, especially when these things are directly related to me and my continued state of being alive.

Steve climbed first so I could watch his route and try belaying. he scampered up the cliff face and then relied on me to regulate the speed of his decent. Next was my turn, after Steve explained some of the techniques I might need to use to make it to the top of this and the next route like smearing. The actual climbing, while strenuous, wasn't the series of chin-ups that I expected but seemed to require more focus, balance and flexibility (2/3 ain't bad). That being said I don't think my heart stopped pounding the entire time I was climbing.

After the climb I figured I would rappel down and we would move on to the next location, however I found myself with only one side of the rope and in a situation where I had absolutely 0 control over the speed of my decent. I should have realized this when I was controlling Steve during his decent but it never really occurred to me until I was leaning over the edge holding on to a rope that we essentially falling with me. Fortunately I trust Steve and he's scared of my wife so I was pretty confident that he'd take good care of me. That being said the constant sound of rope grinding over granite while you are being lowered 60 feet down will probably always raise alarms in my head.

The second route we took was about 10 feet higher and required quite a bit more thought, but was also more interesting in some of the approaches that were possible. I think Steve was a little disappointed that I didn't fall on either of the climbs (Isn't that the point) and told me I'd have greater respect/confidence in the gear if I had tested it 40 feet up and seen how solid it was. I'll take him at his word on that.

I think I'll try an rock wall indoors next and I'll certainly stick to top-rope climbing for a while before I start relying on those amazing cams as more than a guide.

All in all it was a lot of fun.

Photo set on Flickr.