Our eyes perceive light through receptors that are sensitive to red, green, and blue colors in the visual spectrum. People have used this fact to provide color images via film, television, computers, and other devices over the past one hundred years or so.
In a computer or phone's display, images are displayed in many colors by changing the intensity of tiny red, green, and blue LEDs that are next to one another on the screen. Millions of different colors can be shown by changing the intensity of the light from the red, green, or blue LEDs.
This project will help you explore the red, green, and blue (RGB) color space using an Arduino, an RGB LED, and a little math.
You can think of the intensities of the three colors, red, green, and blue, as coordinates in a cube, where each color is along one axis, and all three axes are perpendicular to one another. The closer you are to the zero point, or origin, of the axis, the less of that color is shown. When the values for all three colors are at the zero point, or origin, then the color is black, and the RGB LED is completely off. When the values for all three colors are as high as they can go (in our case, 255 for each of the three colors), the RGB LED is completely on, and the eye perceives this combination of colors as white.
Step 1: RGB Color Space
Thanks to Kenneth Moreland for permission to use his nice image.
We'd like to explore the corners of the 3D color space cube using an RGB LED connected to an Arduino, but also want to do this in an interesting way. We could do it by nesting three loops (one each for red, for green, and for blue), and running through every possible color combination, but that would be really boring.
Have you ever seen a 2D Lissajous pattern on an oscilloscope or a laser light show? Depending on the settings, a Lissajous pattern can look like a diagonal line, a circle, a figure 8, or a slowly rotating pointy butterfly-like pattern. Lissajous patterns are created by tracking the sinusoidal signals of two (or more) oscillators plotted on x-y (or, for our case, x-y-z or R-G-B) axes.
Step 2: The Good Ship Lissajous
The most interesting Lissajous patterns appear when the frequencies of the sinusoidal signals differ by a small amount. In the oscilloscope photo here, the frequencies differ by a ratio of 5 to 2 (both of which are prime numbers). This pattern covers its square pretty well, and gets into the corners nicely. Higher prime numbers would do an even better job of covering the square and poking even further into the corners.
Step 3: Wait - How Can We Drive an LED With a Sinusoidal Wave?
You caught me! We want to explore the 3D color space that ranges from off (0) to full on (255) for each of the three colors, but sinusoidal waves vary from -1 to +1. We're going to do a little math and programming here to get what we want.
- Multiply each value by 127 to get values that range from -127 to +127
- Add 127 and round each value to get values that range from 0 to 255 (close enough to 255 for us)
Values that range from 0 to 255 can be represented by single-byte numbers (the "char" data type in the C-like Arduino programming language), so we'll save memory by using the single-byte representation.
But how about angles? If you're using degrees, angles in a sinusoid range from 0 to 360. If you're using radians, angles range from 0 to 2 times π ("pi"). We're going to do something that again conserves memory in our Arduino, and think of a circle divided into 256 parts, and have "binary angles" that range from 0 to 255, so the "angles" for each of the colors can be represented by single-byte numbers, or chars, here too.
The Arduino is pretty amazing just the way it is, and though it can calculate sinusoidal values, we need something faster. We'll pre-calculate the values, and put them into a 256-entry long array of single-byte, or char values in our program (see the SineTable[...] declaration in the Arduino program).
Step 4: Let's Build a 3D LIssajous Pattern
To cycle through the table at a different frequency for each of the three colors, we'll keep one index per color, and add relatively prime offsets to each index as we step through the colors. We'll choose 2, 5, and 11 as the relatively prime offsets for the Red, Green, and Blue index values. The Arduino's own internal math capabilities will help us by automatically wrapping around as we add the offset value to each index.
Step 5: Putting This All Together on the Arduino
Most Arduinos have a number of PWM (or pulse width modulation) channels. We'll need three here. An Arduino UNO is great for this. Even a little 8-bit Atmel microcontroller (ATTiny85) works fabulously.
Each of the PWM channels will drive one color of the RGB LED using the Arduino's "AnalogWrite" function, where the intensity of the color at each point around the sinusoidal cycle is represented by a pulse width, or duty cycle, from 0 (all off) to 255 (all on). Our eyes perceive these varying pulse widths, repeated fast enough, as different intensities, or brightnesses, of the LED. Combining all three PWM channels driving each of the three colors in an RGB LED, we get the ability to display 256*256*256, or over sixteen million colors!
You'll need to set up the Arduino IDE (Interactive Development Environment), and connect it to your Arduino board using its USB cable. Run jumpers from the PWM outputs 3, 5, and 6 (processor pins 5, 11, and 12) to three 1 KΩ (one thousand ohm) resistors on your proto board or proto shield, and from the resistors to the LED R, G, and B pins.
- If the RGB LED is a common cathode (negative terminal), then run a wire from the cathode back to the GND pin on the Arduino.
- If the RGB LED is a common anode (positive terminal), then run a wire from the anode back to the +5V pin on the Arduino.
The Arduino sketch will work either way. I happened to use a SparkFun Electronics / COM-11120 RGB common cathode LED (pictured above, from the SparkFun web site). The longest pin is the common cathode.
Download the RGB-Instructable.ino sketch, open it with the Arduino IDE, and test compile it. Be sure to specify the correct target Arduino board or chip, then load the program into the Arduino. It should start up immediately.
You'll see the RGB LED cycle through as many colors as you can name, and millions you can't!
Step 6: What's Next?
We've just begun exploring RGB Color Space with our Arduino. Some other things I've done with this concept include:
- Directly writing to on-chip registers, instead of using AnalogWrite, to really speed things up
- Modifying the circuit so that an IR proximity sensor speeds up or slows down the cycle depending on how close you get
- Programming an Atmel ATTiny85 8-pin microcontroller with the Arduino bootloader and this sketch
- Building the ATtiny85, RGB LED, resistors, and watch batteries into a piece of jewelry. Moonstones do a wonderful job of diffusing the light from the RGB LED!