Showing posts with label Colors. Show all posts
Showing posts with label Colors. Show all posts

Monday, January 31, 2011

Arduino notebook: an RGB LED and the color wheel (Part 3)

In my last article, I demonstrated use of a potentiometer to control the hue of an RGB LED. In the HSV color space, the hue is one of three parameters controling the color. The other two are saturation and value. With a single control, there isn't a way to vary the other two parameters to give us a greater color range. In this article, I show one technique to control any of the three color variables.

Designing the input

But first, a short design discussion. In order to change what the potentiometer does, we need some method to change its mode of operation. I toyed with several ideas. My preferred method was three buttons: one button puts it in hue change mode, the second button puts it in saturation change mode and finally, the third button puts it in value change mode. The user would press a button and twist the potentiometer, setting that button's associated value. I like this because there is a one-to-one mapping between the buttons and the mode. It makes it easy for the user to understand and use. The problem: I only have two buttons.

My next idea was no button pressed changes hue, one button pressed changes saturation and the other button pressed changes the value. However, as I thought this through, I realized this would not give the user a nice flow between modes since every time one of the buttons was released, the hue mode would be entered, setting it to whatever value the potentiometer was in for the end of the last mode. This would cause the colors to jump around as modes changed and the un-pressed mode, hue, would have to be changed last. This clearly would not work very well.

After further thought, I hit upon the idea of using one button to cycle through the three modes with each button press and using the other button to enter that mode (or in other words, activate the potentiometer). Sort of like one button controls a menu and the other selects and deselects that menu option. To implement this, two additional LEDs are needed to indicate the mode and a third LED to indicate whether the potentiometer is enabled or not. This allows switching between modes without changing the output of the LED until the mode is selected. When the mode is exited, the LED would not change color as it would have in the previous scenario.

I implemented this design. However, after building it, I didn't like it since a binary readout is used to indicate the mode. I considered this sub-optimal from a usability standpoint. How many people can read binary numbers? In addition to the usability issue, it required three LEDs in addition to the switches, increasing the parts count and clutter on the breadboard. It worked but just seemed pretty clunky.

As I thought about it some more, I realized I could use the buttons directly in a binary pattern to select the mode and simply use a short delay before entering the mode. In other words, press and hold one button to change hue. Press and hold the other button to change saturation. Press and hold both buttons to change the value. I like this. It will reduce the parts count and eliminate a level of abstraction in the user interface; the buttons will directly control the mode. This is what I'll present below.

Hardware changes

Starting with the last circuit with in the previous article, I added two switches, two resistors and some jumpers. If you look at the schematic, pins 12 and 13 are connected to ground through a resistor when the switch is open. This means these two pins are normally in the LOW state when digitalRead is called with their pin numbers. When the button is closed, they are connected to the 5V side and become HIGH. The resistor puts a load on the circuit when the button is closed. If the resistor is removed, a direct short between ground and power exists when the button closes; not a good thing to do.

That's it. Pretty simple circuit changes.RGB LED color wheel with HSV controls
Click the image for a larger view or here for a schematic.

Software changes

The software changes are a bit more involved. First, the simple part, I added two declarations for the pins. The next change is a semi-advanced technique, but first a bit of analysis.

There are two buttons. For each button, the software needs to time how long the user has pressed it and, when they have held it down long enough, switch into active mode. To do this, each button requires a timer and a memory of the last button state. The code is the same for both, but the memory to track their status needs to be separate.

To facilitate this type of design, where there are two sections of memory with the same functions working on it, I used something called a class. Put simply, a class associates data with code. To create a class, start with the class keyword followed by the name of the class. This is the name you as a programmer decide to call it. Since this class contains the state information for a mode switch, I called the class ModeSwitchState.

Next, I defined the state information tracked for each switch. This includes the pin the switch is connected to, a timer to track when the last change was made to the switch's state and the switch's last value (i.e. was it pressed or not). These three things are what are called private variables, as indicated by the private keyword. This means nothing outside of functions defined in the class can access their values. The big, technical term for this is data encapsulation. All that means is it's harder to accidentally change the wrong thing because the only things that can get to the memory is this one class.

After I defined the private memory, I created two functions that any code can use. The first is what's called the constructor. This simply means it's the function that is called when a new variable of this type is created. In this case, the constructor takes a parameter to indicate the Arduino pin the rest of the functions will use. Since the pin variable is private, this is the only way to set its value. The idea is you create a variable of this class type for each button to be controlled. The constructor also sets initial values for the other variables and calls pinMode for the pin it reads.

Finally, I wrote a function to read the current value of the pin and, if it's different from the last value, reset the timer and the last value read variables. It then returns true if the button is pressed and the difference between the timer and the current time is greater than 500 ms. Otherwise it will return false.

That's it for the definition of this class. Next, I created two variables of this class type, called modeSwitch1 and modeSwitch2. The constructor is called with the values of the pin assignment previously defined.

And that's it for the advanced stuff. All the other changes are in the loop function.

First the loop gets the current mode state of each switch. Remember, since this is calling the function on the class, all the timer code is neatly tucked away out of sight from the main processing loop. All we have to do now is worry about getting the current value as defined by the readModeSwitch function. That's done by the first two lines.

Next I check to see if either of the switches are currently active. If so, then the potentiometer is read. Following this, either hue, saturation or value is changed to the pot's value based on which combination of switches are pressed. The case where both switches are pressed needs to be checked first, followed by the test for a single switch. If neither of these conditions are met, then the other switch must be pressed and its associated value changed.

Finally, after all this, the RGB color is computed and the LED is set to that color.

If neither switch is pressed, then nothing happens, the loop exits and immediately begins again.

Here's the final code.
int ledPins[] = { 11, 9, 10 };
int analogPin = 0;
int modeBit0pin = 12;
int modeBit1pin = 13;

int hueValue = 0;
double satValue = 1.0;
double lumValue = 1.0;

class ModeSwitchState
{
  private:
    int pin;
    long timer;
    boolean lastValue;

  public:
    ModeSwitchState(int switchPin)
      : pin(switchPin), timer(0), lastValue(false)
    {
      pinMode(pin, OUTPUT);
    }
    
    boolean readModeSwitch()
    {
      boolean curValue = digitalRead(pin) == HIGH;
      if (curValue != lastValue)
      {
        lastValue = curValue;
        timer = millis();
      }
      return curValue && ((millis() - timer) > 500);
    }
};

ModeSwitchState modeSwitch1(modeBit0pin), modeSwitch2(modeBit1pin);

void setup()
{
  pinMode(analogPin, INPUT);
  
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  boolean mode1enabled = modeSwitch1.readModeSwitch();
  boolean mode2enabled = modeSwitch2.readModeSwitch();
  
  if (mode1enabled || mode2enabled)
  {
    int analogValue = analogRead(analogPin);

    if (mode1enabled && mode2enabled)
        hueValue = map(analogValue, 0, 1024, 0, 360);
    else if (mode1enabled)
        satValue = analogValue / 1024.0;
    else
        lumValue = analogValue / 1024.0;
  
    byte* colors = hsvToRgb(hueValue, satValue, lumValue);
    setColor(ledPins, colors);
  }
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];
byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        
        return rgb;
}

Conclusion and downloads

That's about all I'm going to do with the RGB LED for now. The source code for this and the past two articles can be downloaded here:
In the next article I will talk about the joys and sorrows of building a simple LCD kit to use with the Arduino.

Friday, January 21, 2011

Arduino notebook: an RGB LED and the color wheel (Part 2)

In the last Arduino Notebook article, I showed how to take one of the standard tutorials and modify the sketch to do something a bit different than given. I've updated that article with breadboard and schematic diagrams.

In this article I'll add a potentiometer that came with my kit to allow additional control over the LED. With this new input, I'll show how to use it to control two different aspects of the LED. First, I will change the sketch so the potentiometer controls the speed the LED cycles through the color wheel. At one end it will be so fast it will look more like a rapid switch between a reddish color and a blueish color. At the other extreme end, it will take about 30 seconds to cycle all the way through.

Second, I will make the position of the potentiometer control the color. So, as you turn the control, it will change to the hue represented by that position of the control. In other words at the starting position it will be red. Turn it one third of the way and it'll be green. Another one third and it will be blue. Turn it all the way to the end and it'll go back to red.

So, let's get started.

First, I place a potentiometer on the breadboard so each of the three pins is in a separate row of the breadboard. Then I connect one side to ground and the other side to 5V. The middle pin I connect to the Arduino's analog 0 pin. That's it for the hardware. It should look something like this.
RGB LED color wheel with pot_bb
Click the image for a larger view or here for a schematic.

Ok, now for the software.

The first set of changes will let the potentiometer change the speed the LED cycles through the color wheel. Starting with the sketch I ended with in the previous article, in the setup function, I add a call to initialize pin 0 for input. Next I change the loop to get the current value of the analog pin. This value will be from 0 to 1024 depending on the position of the potentiometer. Since I want the total time to cycle through the colors to be 30 seconds and there are 360 steps, then each step should take 83 milliseconds. To convert the number read from pin 0 from one range to another the Arduino has a function called map. This is much easier to use than doing the math to handle the conversion. So, I call this function with the value read from the potentiometer and the beginning and ending values of the two ranges. The result is fed into the delay in the loop. Compile and upload to the Arduino and now the potentiometer controls the speed.
int ledPins[] = { 11, 9, 10 };
int inputPin = 0;

void setup()
{
  pinMode(inputPin, INPUT);
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  int inputValue = analogRead(inputPin);
  int delayValue = map(inputValue, 0, 1024, 0, 83);

  setHueValue(computeNextValue());
  
  delay(delayValue);
}

int colorValue = 0;

int computeNextValue()
{
  colorValue = (colorValue+1) % 360;
  return colorValue;
}

void setHueValue(int hueValue)
{
  setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        return rgb;
}

The second set of changes let the potentiometer set the color. This is a really simple change. Everything is done in the loop function. Remember, setColorWheel takes a value in the range 0 to 360 representing the angle of the hue on the color wheel. All that needs to be done is to change the second range in the map function to end at 360 instead of 83. The result is now fed into the setColorWheel function instead of computeNextValue. And finally, the time delay is removed and the computeNextValue can be deleted.

And that's it. Pretty simple changes and now the potentiometer can be used to set the color.
int ledPins[] = { 11, 9, 10 };
int inputPin = 0;

void setup()
{
  pinMode(inputPin, INPUT);
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  int inputValue = analogRead(inputPin);
  int hueValue = map(inputValue, 0, 1024, 0, 360);

  setHueValue(hueValue);
}

void setHueValue(int hueValue)
{
  setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        return rgb;
}


All in all, I think this demonstrates pretty well how a small amount of hardware and some trivial software changes can provide very different end user results. The combination of hardware and software that Arduino makes available to the experimenter I find really cool.

Next time, I have some other ideas to add another input method to this circuit and some minor tweaks in software to increase the range of colors that can be generated by the RGB LED.

Tuesday, January 18, 2011

Arduino notebook: an RGB LED and the color wheel (Part 1)

As I recently wrote about, I received a new Arduino experimental kit a couple weeks ago for Christmas. In a simple learning phase, last weekend I built one of the projects in the getting started book and then expanded on it to make it do a bit more.

The project was to control a RGB LED that came with the kit. A RGB LED basically has three LEDs in a single physical package. The LEDs have a common anode so the package has four leads coming out of it. One you connect to +5V and each of the others you connect to its own pin on the Arduino through a resistor. This configuration allows you to turn on each color individually by setting that color's pin off (to ground). If the pin is set on, the voltage is +5V and the LED will turn off since there is no current going through the LED. This is a bit backwards from normal intuition, but it makes sense if you understand what's happening with the voltage/current.

RGB LED color wheel_bbClick image to see larger. Here's a schematic view.

I started with a simple circuit as shown in CIRC-12 here. [Update: also shown above.] It simply consists of the LED connected to +5V on its anode and three resistors on the other legs. The other side of the resistors are connected to three pins on the controller. Pretty simple stuff. I then entered the following program from the manual and ran it.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const boolean ON = LOW;
const boolean OFF = HIGH;

const boolean RED[] = {ON, OFF, OFF};
const boolean GREEN[] = {OFF, ON, OFF};
const boolean BLUE[] = {OFF, OFF, ON};
const boolean YELLOW[] = {ON, ON, OFF};
const boolean CYAN[] = {OFF, ON, ON};
const boolean MAGENTA[] = {ON, OFF, ON};
const boolean WHITE[] = {ON, ON, ON};
const boolean BLACK[] = {OFF, OFF, OFF};
const boolean* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
//setColor(ledPins, YELLOW);
randomColor();
}

void randomColor()
{
int rand = random(0, sizeof(COLORS) / sizeof(boolean*));
setColor(ledPins, COLORS[rand]);
delay(1000);
}

void setColor(int* led, const boolean* color)
{
for(int i = 0; i < 3; i++)
digitalWrite(led[i], color[i]);
}
This simple program (sketch in Arduino terminology), will either set the color to one of the predefined values if the first line in loop() is uncommented or will randomly select one of the colors each second if the second line is uncommented. One thing to note, because the LED has a common anode, to turn a color off, we have to give that pin a voltage. This is why the ON constant to indicate the color should be lit is defined as a LOW voltage and the OFF is defined as a HIGH voltage. This definition makes it easier to read the color definitions.

So far, so good, but a bit on the boring side. I decided in order to juice things up a bit I wanted it to cycle through the colors of the color wheel rather than randomly pick things out of an array. In addition to this, I wanted to fade from one color to the next rather than have abrupt jumps. At this point, one of the beauties of microcontrollers comes to the forefront: all this can be done through simply software changes.

The example above turns each LED either full on or full off. To make a smooth transition, I needed to display the LEDs in partially lit states. This is done with what's called pulse-width modulation, or PWM. The beauty of the Arduino is that it has this facility built-in. Instead of calling digitalWrite as in the code above, you call analogWrite with a value between 0 and 255 where 0 is full off, 255 is full on and values in-between represent a proportional state between full off and full on. So, the first step was to change the boolean types to bytes. This was a simple change of type and the definitions for ON and OFF. Finally, I changed setColor to use analogWrite instead of digitalWrite. This gave a functionally equivalent version that was now setup to use intermediate values.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const byte ON = 0;
const byte OFF = 255;

const byte RED[] = {ON, OFF, OFF};
const byte GREEN[] = {OFF, ON, OFF};
const byte BLUE[] = {OFF, OFF, ON};
const byte YELLOW[] = {ON, ON, OFF};
const byte CYAN[] = {OFF, ON, ON};
const byte MAGENTA[] = {ON, OFF, ON};
const byte WHITE[] = {ON, ON, ON};
const byte BLACK[] = {OFF, OFF, OFF};
const byte* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
//setColor(ledPins, YELLOW);
randomColor();
}

void randomColor()
{
int rand = random(0, sizeof(COLORS) / sizeof(byte*));
setColor(ledPins, COLORS[rand]);
delay(1000);
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], color[i]);
}

The next step is to change the code to display colors in the order of the color wheel rather than randomly. To start, I used the existing COLORS array. I changed the loop method to call a new method setHueValue with a parameter that computed the new hue value and added a delay. I also removed the randomColor function since I wasn't using it anymore.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const byte ON = 0;
const byte OFF = 255;

const byte RED[] = {ON, OFF, OFF};
const byte GREEN[] = {OFF, ON, OFF};
const byte BLUE[] = {OFF, OFF, ON};
const byte YELLOW[] = {ON, ON, OFF};
const byte CYAN[] = {OFF, ON, ON};
const byte MAGENTA[] = {ON, OFF, ON};
const byte WHITE[] = {ON, ON, ON};
const byte BLACK[] = {OFF, OFF, OFF};
const byte* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
setHueValue(computeNextValue());
delay(1000);
}

int colorValue = -1;

int computeNextValue()
{
colorValue = (colorValue+1) % 6;
return colorValue;
}

void setHueValue(int hueValue)
{
setColor(ledPins, COLORS[hueValue]);
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], color[i]);
}
This was great for five values, but I wanted it to cycle smoothly from one color to the next. To do this with a table lookup method was going to require a huge table. A different approach to the problem was needed.

RGB is a common way to model color with anything that projects light, such as computer monitors and LEDs. However, there are other ways of thinking about color. For example, things that reflect light such as painting and printing, use CMYK or Cyan-Magenta-Yellow-Black. If you ever mixed paint in art class, this is the color wheel you learned. There are another models that can be handy when doing various types of computations, one of which is Hue-Saturation-Value, or HSV. For what I wanted to do, this works well.

The first step in this stage was to find an easy to use function for converting HSV values to RGB values. A simple search of Google for "hsv to rgb converter" gave me an open source PHP (or perhaps it was Python) function. A quick conversion to C used by the Arduino and I had the hsvToRgb function below. This function takes a hue value in the range 0 to 360. This is because the HSV model maps the hue to a circle and so it uses a degree measurement to specify the hue value. The Saturation and Value portions of the model are a percentage from 0 to 100%, represented by fractional values from 0 to 1. For the purposes of this experiment, I just set them both to 1.

Once I had the color converter, I next changed computeNextValue to return a max value of 359 instead of 5. This will give me the proper input for the hue value. Then I removed all the color definitions at the top of the sketch since the color values are now computed and the constants won't be needed. I reduced the time delay to 10 milliseconds. With 360 steps, this will give about a 3.5 second cycle time. Next I changed the COLORS array reference in setColor to call the new function. Since the HSV code returns values in 0 to 255 range and the pins need things inverted for voltage reasons, in setColor, I subtract the color in the byte array from 255 rather than passing it in directly. The only real difference this makes from a user perspective is the starting color is at red instead of 180 degrees out of phase at cyan.

With all this, I accomplished my goals for this experiment. Here is the final sketch.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;
void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
setHueValue(computeNextValue());
delay(10);
}

int colorValue = 0;

int computeNextValue()
{
colorValue = (colorValue+1) % 360;
return colorValue;
}

void setHueValue(int hueValue)
{
setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
// Make sure our arguments stay in-range
h = max(0, min(360, h));
s = max(0, min(1.0, s));
v = max(0, min(1.0, v));
if(s == 0)
{
// Achromatic (grey)
rgb[0] = rgb[1] = rgb[2] = round(v * 255);
return rgb;
}
double hs = h / 60.0; // sector 0 to 5
int i = floor(hs);
double f = hs - i; // factorial part of h
double p = v * (1 - s);
double q = v * (1 - s * f);
double t = v * (1 - s * (1 - f));
double r, g, b;
switch(i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
}
rgb[0] = round(r * 255.0);
rgb[1] = round(g * 255.0);
rgb[2] = round(b * 255.0);
return rgb;
}
In the next installment, I'll walk through adding some simple control mechanisms to this circuit along with various software changes to make it respond differently with the same hardware.

Tuesday, May 12, 2009

WPF Colors, static classes and reflection in .Net

Today I wanted to know the definitions of the colors in WPF's static Colors class. A two-minute web search didn't turn anything up so I decided to write a quick and dirty little program to emit them. It should have been simple enough; I should have known better.

Using reflection, I wanted to cycle through each property of the Colors class and output its name and RGB value. To this end, I typed this into Visual Studio:
foreach (var prop in typeof(Colors).GetProperties())
{
    var propColor = (Color)prop.GetValue();
}

Then I hit a snag: GetValue wanted an instance of the class. However, Colors is static and I could not create an instance of it. In looking through the interface, I found GetConstantValue method. That sounded promising. I tried it. It compiled but threw an exception at run-time indicating it wasn't going to work.

I read the PropertyInfo documentation and didn't see anything more promising than GetValue. I searched the web some more but the only thing I could find talked about calling static methods on normal classes with instances of them created. I went back to the documentation where the description for the first parameter still said "The object whose property value will be returned." Reading on, the second parameter was the index value and said, "This value should be null for non-indexed properties."

Hmm... What happens if the first parameter is null?

Having nothing to lose, I tried it with both parameters passed as null. It compiled. A breakpoint set after the assignment and then inspection of propColor indicated it worked as expected. Cool!

I put a WrapPanel in the XAML file and named it ColorItems. Then I fleshed out my for loop:
foreach (var prop in typeof(Colors).GetProperties())
{
    var propColor = (Color)prop.GetValue(null, null);
    var desc = string.Format("{0} ({1})", prop.Name, propColor);
    Console.WriteLine(desc);
    ColorItems.Children.Add(new Grid
        {
            Width = 20,
            Height = 20,
            Margin = new Thickness(2),
            ToolTip = desc,
            Background = new SolidColorBrush(propColor)
        });
}

This gave me a nice little window with squares of color. Each square showed a tool tip with the name and color. Moreover, I had the table shown below in the Output window of the IDE.

Download the zipped project with compiled application.

ColorValueSample
AliceBlue#F0F8FF 
AntiqueWhite#FAEBD7 
Aqua#00FFFF 
Aquamarine#7FFFD4 
Azure#F0FFFF 
Beige#F5F5DC 
Bisque#FFE4C4 
Black#000000 
BlanchedAlmond#FFEBCD 
Blue#0000FF 
BlueViolet#8A2BE2 
Brown#A52A2A 
BurlyWood#DEB887 
CadetBlue#5F9EA0 
Chartreuse#7FFF00 
Chocolate#D2691E 
Coral#FF7F50 
CornflowerBlue#6495ED 
Cornsilk#FFF8DC 
Crimson#DC143C 
Cyan#00FFFF 
DarkBlue#00008B 
DarkCyan#008B8B 
DarkGoldenrod#B8860B 
DarkGray#A9A9A9 
DarkGreen#006400 
DarkKhaki#BDB76B 
DarkMagenta#8B008B 
DarkOliveGreen#556B2F 
DarkOrange#FF8C00 
DarkOrchid#9932CC 
DarkRed#8B0000 
DarkSalmon#E9967A 
DarkSeaGreen#8FBC8F 
DarkSlateBlue#483D8B 
DarkSlateGray#2F4F4F 
DarkTurquoise#00CED1 
DarkViolet#9400D3 
DeepPink#FF1493 
DeepSkyBlue#00BFFF 
DimGray#696969 
DodgerBlue#1E90FF 
Firebrick#B22222 
FloralWhite#FFFAF0 
ForestGreen#228B22 
Fuchsia#FF00FF 
Gainsboro#DCDCDC 
GhostWhite#F8F8FF 
Gold#FFD700 
Goldenrod#DAA520 
Gray#808080 
Green#008000 
GreenYellow#ADFF2F 
Honeydew#F0FFF0 
HotPink#FF69B4 
IndianRed#CD5C5C 
Indigo#4B0082 
Ivory#FFFFF0 
Khaki#F0E68C 
Lavender#E6E6FA 
LavenderBlush#FFF0F5 
LawnGreen#7CFC00 
LemonChiffon#FFFACD 
LightBlue#ADD8E6 
LightCoral#F08080 
LightCyan#E0FFFF 
LightGoldenrodYellow#FAFAD2 
LightGray#D3D3D3 
LightGreen#90EE90 
LightPink#FFB6C1 
LightSalmon#FFA07A 
LightSeaGreen#20B2AA 
LightSkyBlue#87CEFA 
LightSlateGray#778899 
LightSteelBlue#B0C4DE 
LightYellow#FFFFE0 
Lime#00FF00 
LimeGreen#32CD32 
Linen#FAF0E6 
Magenta#FF00FF 
Maroon#800000 
MediumAquamarine#66CDAA 
MediumBlue#0000CD 
MediumOrchid#BA55D3 
MediumPurple#9370DB 
MediumSeaGreen#3CB371 
MediumSlateBlue#7B68EE 
MediumSpringGreen#00FA9A 
MediumTurquoise#48D1CC 
MediumVioletRed#C71585 
MidnightBlue#191970 
MintCream#F5FFFA 
MistyRose#FFE4E1 
Moccasin#FFE4B5 
NavajoWhite#FFDEAD 
Navy#000080 
OldLace#FDF5E6 
Olive#808000 
OliveDrab#6B8E23 
Orange#FFA500 
OrangeRed#FF4500 
Orchid#DA70D6 
PaleGoldenrod#EEE8AA 
PaleGreen#98FB98 
PaleTurquoise#AFEEEE 
PaleVioletRed#DB7093 
PapayaWhip#FFEFD5 
PeachPuff#FFDAB9 
Peru#CD853F 
Pink#FFC0CB 
Plum#DDA0DD 
PowderBlue#B0E0E6 
Purple#800080 
Red#FF0000 
RosyBrown#BC8F8F 
RoyalBlue#4169E1 
SaddleBrown#8B4513 
Salmon#FA8072 
SandyBrown#F4A460 
SeaGreen#2E8B57 
SeaShell#FFF5EE 
Sienna#A0522D 
Silver#C0C0C0 
SkyBlue#87CEEB 
SlateBlue#6A5ACD 
SlateGray#708090 
Snow#FFFAFA 
SpringGreen#00FF7F 
SteelBlue#4682B4 
Tan#D2B48C 
Teal#008080 
Thistle#D8BFD8 
Tomato#FF6347 
Transparent#FFFFFF 
Turquoise#40E0D0 
Violet#EE82EE 
Wheat#F5DEB3 
White#FFFFFF 
WhiteSmoke#F5F5F5 
Yellow#FFFF00 
YellowGreen#9ACD32