RGB and XYZ
I think it's going on 14 years or more when I first had the idea: a lighted juggling ball that changes color as it rotates. Seriously - 14 years. I'm fairly sure of this because I told my girlfriend at the time (who eventually became my wife) and she made a prototype. But I get ahead of myself.
The first thing I tried - even though I knew it wouldn't work - was to just let the accelerometer drive the LEDs. The RBG LED's I had worked at an operating voltage of near what the maximum output the accelerometer would produce: 3.3 volts.
It was always LED's that did the lighting, of course. It was also a ball. This is important to note as juggling equipment comes in many shapes and sizes. It is lighted from within, hopefully evenly. As this ball (or many) rotates it changes color. Hopefully it is bright enough to be seen from a far in a dark theater and it's obvious that the color is changing. It's not crucial that the juggler be able to control the changes, but that might be a nice option.
When I told Jeanette (afore mentioned wife) she made a prototype for a class she was taking in college. It was just after we had started dating - I think during her Junior year. Second half she says, and it was for a physics class.
What she created involved two mercury switches and three LEDs. The LEDs were two color - red and green, though they did a passable yellow as well. It worked fairly well as a test, but wasn't anything we could use in a juggling ball. Mercury switches would be too dangerous as they would most likely break.
As I didn't have any other ideas for a switching mechanism, I shelved the idea focusing instead on other matters such as getting married, buying a house, having kids - the usual stuff. But the idea stuck around and when I started getting into the Arduino last year it was the first thing I thought of building. After many other tests and trials of ideas I decided to take another crack at it.
By this point I'd had lots of experience with the accelerometer (see previous posts). It's functionality is rather simple. It expresses the position of each axis as strength of voltage. The highest voltage meant the position was at one side of the axis. No or low voltage meant the position was at the other side of the axis. The Arduino is capable of measuring the voltages with analog pins and can distinguish something like 693 different positions. Using the combination of all three axis you can determine which way is down - one axis, or a couple will always be pulled by gravity - except when in free-fall, which could be another position of sorts.
This was fairly simple to set up and get running. The Arduino was only there to provide power, in this case off of the 3.3 volt "always on" pin. I plugged the accelerometer into the protoboard, set the input and sleep with the 3.3 volt, ground to the ground and the sensitivity pins. The X, Y, and Z axis, in turn, provided the voltage directly to the LED. Oh, and the LED also has a ground.
All this might be easier to see with the accelerometer off to the side. I ran the lines from the three axis under the accelerometer. The two pins sticking up are just there to keep the board level.
I have no code to share for this set up, of course. Again - the Arduino is just providing power.
Running this way did work - sort of. The red, green and blue faded in and out nicely. When any given color tied to an axis was on high voltage it lit nicely. But when it was on low voltage it didn't light at all. What I want is something that shows a color regardless of how the thing is turned. Preferrably there's a way I can tie half of each axis to a color. The RGB LED does a nice job of showing mixes. Sure, the dominant colors are red green and blue, but mix 'em and you also get yellow (sort of), purple (very nice), and aqua (also pretty good). Mix all three and it's a fairly good white.
Next I tried working out some fairly complicated math to assign various positions of the axis to certain colors. These, in turn, were used to set PWM values for the LEDs. The PWM maxes out to something close to 3.3 v for green and blue and 2.0 v for read. But based on the positions of the axis, the strength of the lighting was altered.
This didn't work out very well. To do this I had picked a point between zero and full and treated that as half point. Doing this with each axis gives six "limbs" as it were. Given a position somewhere in or between these six I figured I could use this as a means of lighting or fading between colors.
I won't go into too many details as my failed attempts at this math are something of a private shame. Some of this still shows up - commented out - in the code I share below. I still think my goal of having all 6 colors show is possible - even with some nice fading and maybe having white as the "free fall" color. But I think I need to come up with something different.
What I did come up that works somewhat was to assign a color to each of the eight quadrants. If you bisect the three axis the same way you can think of a three dimensional cross with six points coming out of the middle. If you set the cross inside a cube, it actually looks like eight cubes stacked together - four cubes, two by two on two levels. Reading the accelerometer values I can "decide" that the position is in one of these eight sections and then light the appropriate color.
The problem is I really only have 7 colors: red, yellow, green, aqua, blue, purple, white. So I assinged "off" to the eighth. Here's another video:
This video has got some interesting interferance lines, doesn't it? That's probably the PWM singing in tune with the camera I was using. Might be fun to play with later - but I digress.
If I ever figure out some code that works with just six colors - and free-fall white - I might look into another Arduino board. I was thinking of an Arduino Pro Mini from Sparkfun. Its tiny, its output voltage is already 3.3 volts, and it's fairly cheap. All surface mount to boot, though I think I've gotta solder on my own connectors. I'd probably have to work out a way to connect the accelerometer directly to the thing and somehow tack on some power.
I'd most likely want to figure out a way to connect the LED's somewhat loosely to the board. Maybe connected via a loose wire. There'd probably be eight of these arranged also in quadrants. I'd probably put the bundle into a clear plastic ball and suspend it in that ball in some clear plastic beads that'd (hopefully) diffuse the light of the LED's a bit.
Its all ideas until I actually do it, which at this rate will be next year. Too many distractions...
Anyway, here's the code:
/*
* XYZ (accelerometer) and RGB (LED)
* Fading between colors controlled by accelerometer
* PWM: 3, 5, 6, 9, 10, 11
* USE: 9, 10, 11
*/
// axis analog pins
int xPin = 0;
int yPin = 1;
int zPin = 2;
// mid value of any axis
int max = 693;
int mid = 346;
// pins controlling red/green/blue
int rPin = 9;
int gPin = 10;
int bPin = 11;
// maximum PWM we want for each
int rMax = 102;
int gMax = 163;
int bMax = 163;
/*
* The run-once setup method
*/
void setup()
{
// set up to communicate with serial
Serial.begin(9600);
// set axis/analog pins to input
pinMode(xPin, INPUT);
pinMode(yPin, INPUT);
pinMode(zPin, INPUT);
// set LED pins to output - not
// really necessary for PWM, tho
pinMode(rPin, OUTPUT);
pinMode(gPin, OUTPUT);
pinMode(bPin, OUTPUT);
}
/*
* Main loop - shift lights with accelerometer values
*/
void loop()
{
// fetch value from various analog
int xVal = analogRead(xPin);
int yVal = analogRead(yPin);
int zVal = analogRead(zPin);
// determine which zone that is
int zone = findZone(xVal, yVal, zVal);
// tell us which zone
Serial.print("Zone: ");
Serial.println(zone, DEC);
/*
// calculate how "on" each LED should be
float rPer = redCalc(zone, xVal, yVal, zVal);
float gPer = grnCalc(zone, xVal, yVal, zVal);
float bPer = bluCalc(zone, xVal, yVal, zVal);
// light 'em
lightLed(rPer, gPer, bPer, 0);
*/
switch(zone)
{
case 1:
lightLed(1.0, 1.0, 1.0, 0);
break;
case 2:
lightLed(1.0, 0.0, 0.0, 0);
break;
case 3:
lightLed(1.0, 1.0, 0.0, 0);
break;
case 4:
lightLed(0.0, 1.0, 1.0, 0);
break;
case 5:
lightLed(1.0, 0.0, 1.0, 0);
break;
case 6:
lightLed(0.0, 0.0, 1.0, 0);
break;
case 7:
lightLed(0.0, 1.0, 0.0, 0);
break;
default:
lightLed(0.0, 0.0, 0.0, 0);
}
}
/*
* Based on the values of the 3 axis
* determines which of 8 zones we are in
*/
int findZone(int x, int y, int z)
{
int zone = 0;
if (x > mid && y > mid && z > mid) { zone = 1; }
else if (x <= mid && y > mid && z > mid) { zone = 2; }
else if (x <= mid && y <= mid && z > mid) { zone = 3; }
else if (x > mid && y <= mid && z > mid) { zone = 4; }
else if (x > mid && y > mid && z <= mid) { zone = 5; } else if (x <= mid && y > mid && z <= mid) { zone = 6; } else if (x <= mid && y <= mid && z <= mid) { zone = 7; } else if (x > mid && y <= mid && z <= mid) { zone = 8; } return zone; } /* * Calculate how "on" red LED is */ float redCalc(int zone, int x, int y, int z) { float per = 0.0; switch (zone) { case 1: per = 1.0; break; case 2: per = z - mid / mid; break; case 3: per = z - mid / mid; break; case 4: per = ((z + x) / 2) / mid; break; case 5: per = ((x + y) / 2) / mid; break; case 6: per = y / mid; break; case 7: per = 0.0; break; case 8: per = x - mid / mid; break; } return per; } /* * Calculate how "on" green LED is */ float grnCalc(int zone, int x, int y, int z) { float per = 0.0; switch (zone) { case 1: per = y - mid / mid; break; case 2: per = ((y + x) / 2) / mid; break; case 3: per = x - mid / mid; break; case 4: per = 0.0; break; case 5: per = ((z + y) / 2) / mid; break; case 6: per = 1.0; break; case 7: per = ((x + z) / 2) / mid; break; case 8: per = z / mid; break; } return per; } /* * Calculate how "on" blue LED is */ float bluCalc(int zone, int x, int y, int z) { float per = 0.0; switch (zone) { case 1: per = z - mid / mid; break; case 2: per = ((z + x) / 2) / mid; break; case 3: per = 1.0; break; case 4: per = ((y + z) / 2) / mid; break; case 5: per = 0.0; break; case 6: per = x / mid; break; case 7: per = ((y + x) / 2) / mid; break; case 8: per = y / mid; break; } return per; } /* * Light the LEDs based on the percentages */ void lightLed(float rPer, float gPer, float bPer, int wait) { // caclulate percentages lit int rVal = rMax * rPer; int gVal = gMax * gPer; int bVal = bMax * bPer; // light the LEDs to these percents analogWrite(rPin, rVal); analogWrite(gPin, gVal); analogWrite(bPin, bVal); // if given a delay if (wait > 0)
{
// delay for a given milliseconds
delay(wait);
}
}
Comments
Post a Comment