R A I N B O  I  N I G H  L I G H T
When all other light sources go off at night, this night light floods the room with cycling rainbow colors using a small but powerful RGB LED. The lamp can be controlled remotely using any standard IR remote control unit. Because using it is so much fun, it easily leviates night fears in those kids not really fond of being in the dark.
Rainbow color generator
In order to generate countless vivid colors, this device uses an HSB color representation model in which colors are not specified by the ammount of red, green and blue component but by unique hue, saturation and brightness values. In order to figure out this concept, imagine that saturated colors such as those from the rainbow set are drawn along the circumference of a circle, while the center of the circle is pure white. Over the circle surface between its circumference and the center forms a gradient area is formed in which colors loose their saturation i.e. they become more "washed out" as we move from the outer most rim towards the center. One can pick a particular color by selecting two coordinates - the angle at which the radius line is to be drawn and the relative distance along the radius line where the point in interest sits. In this model, the angle of the radius is designated color "hue" value, while relative distance of the color point versus the radius length is designated color "saturation" value. Hue varies from 0 to 360 degrees, while saturation varies from 0 to 1. Brightness can be imagined further as a third dimension coordinate extruding vertically the 2D hue+saturation circle into a 3D HSB cylinder, as represented in the illustration below.
Simplified HSB color model Since in this application we only want to produce highly saturated colors, it suffices to specify a color to be displayed using a single hue coordinate. This way, making a micro- controller to cycle through all the colors that are available becomes as simple as changing the numerical value of a single runtime variable, while red, green and blue component values are easily derived from it using a lookup table or a fairly short subroutine. These values are then used by software PWM modulators to regulate brightness of RGB LEDs.
As for the color saturation, there is a simple rule that one should obbey in order to generate only the most saturated colors - all three component (red, green and blue) LEDs should never be turned on at the same moment. If this were not so, the amount of light given off by all three LEDs would be perceived as white light, while only the rest would be perceived as color hue. Such color would seem more or less bleached. This leads to a simple method of generating saturated colors: divide the hue circle into three regions; in each of the regions keep one of the component LEDs off while regulating brightness of the other two so that they mix their monochromatic lights gradually from one end of the region to the other.
Method for generating saturated colors
One of the advantages of using a precalculated lookup table for deriving RGB values over a simple subroutine call is that nonlinear gradients can be formed easily by a PC application with no penalties in terms of microcontroller program runtime speed. But while experimenting with the idea, we concluded that it is actually not very critical what shape the relative component brightness curves have - simple linear mixing as depicted in the diagram above works quite well in terms of generating very smooth color gradients and can be implemented in the form of a very short subroutine. The benefit is that such subroutine eats far less program memory than the lookup table of corresponding length. This is how actual rainbow RGB gradient calculator looks like in the microcontroller program currently in use:
// Global variables:
unsigned int  currentHSB;
unsigned char currentRed;
unsigned char currentGreen;
unsigned char currentBlue;

// ...

void calculateNewRGB(void)
{
    // Calculate new HSB value.
    currentHSB++;
    if(currentHSB >= 768)
    {
        currentHSB = 0;
    }
    
    // Calculate new red, green and blue component values.
    if(currentHSB <= 255)
    {
        currentRed   = currentHSB;
        currentGreen = 0;
        currentBlue  = 255 - currentHSB;
    }
    else if(currentHSB <= 511)
    {
        currentRed   = 255 - currentHSB;
        currentGreen = currentHSB;
        currentBlue  = 0;
    }
    else
    {
        currentRed   = 0;
        currentGreen = 255 - currentHSB;
        currentBlue  = currentHSB;
    }
}
Note that AVR GCC compiler automatically truncates 16-bit unsigned int ("currentHSB") to 8-bit unsigned char variables if put into righthand side of mathematical expressions such as those given above meaning that in this simple case there is no need to explicitly cast variable types.
designed by LP 2011