There were some flaws in the post HSV colour cycling LED on Arduino. This does much more what I wanted:
/*
HSV fade/bounce for Arduino - Stewart C. Russell - scruss.com - 2010/09/19
Wiring:
LED is RGB common cathode (SparkFun sku: COM-09264 or equivalent)
* Digital pin 9 → 165Ω resistor → LED Red pin
* Digital pin 10 → 100Ω resistor → LED Green pin
* Digital pin 11 → 100Ω resistor → LED Blue pin
* GND → LED common cathode.
*/
#define RED 9 // pin for red LED; green on RED+1 pin, blue on RED+2 pin
#define DELAY 2
long rgb[3];
long rgbval, k;
float hsv[3] = {
0.0, 0.5, 0.5
};
float hsv_min[3] = {
0.0, 0.0, 0.4 // keep V term greater than 0 for smoothness
};
float hsv_max[3] = {
6.0, 1.0, 1.0
};
float hsv_delta[3] = {
0.0005, 0.00013, 0.00011
};
/*
chosen LED SparkFun sku: COM-09264
has Max Luminosity (RGB): (2800, 6500, 1200)mcd
so we normalize them all to 1200 mcd -
R 1200/2800 = 0.428571428571429 = 109/256
G 1200/6500 = 0.184615384615385 = 47/256
B 1200/1200 = 1.0 = 256/256
*/
long bright[3] = {
109, 47, 256
};
void setup () {
randomSeed(analogRead(4));
for (k=0; k<3; k++) {
pinMode(RED + k, OUTPUT);
rgb[k]=0; // start with the LED off
analogWrite(RED + k, rgb[k] * bright[k]/256);
if (k>1 && random(100) > 50) {
// randomly twiddle direction of saturation and value increment on startup
hsv_delta[k] *= -1.0;
}
}
}
void loop() {
for (k=0; k<3; k++) { // for all three HSV values
hsv[k] += hsv_delta[k];
if (k<1) { // hue sweeps simply upwards
if (hsv[k] > hsv_max[k]) {
hsv[k]=hsv_min[k];
}
}
else { // saturation or value bounce around
if (hsv[k] > hsv_max[k] || hsv[k] < hsv_min[k]) {
hsv_delta[k] *= -1.0;
hsv[k] += hsv_delta[k];
}
}
hsv[k] = constrain(hsv[k], hsv_min[k], hsv_max[k]); // keep values in range
}
rgbval=HSV_to_RGB(hsv[0], hsv[1], hsv[2]);
rgb[0] = (rgbval & 0x00FF0000) >> 16; // there must be better ways
rgb[1] = (rgbval & 0x0000FF00) >> 8;
rgb[2] = rgbval & 0x000000FF;
for (k=0; k<3; k++) { // for all three RGB values
analogWrite(RED + k, rgb[k] * bright[k]/256);
}
delay(DELAY);
}
long HSV_to_RGB( float h, float s, float v ) {
/*
modified from Alvy Ray Smith's site:
http://www.alvyray.com/Papers/hsv2rgb.htm
H is given on [0, 6]. S and V are given on [0, 1].
RGB is returned as a 24-bit long #rrggbb
*/
int i;
float m, n, f;
// not very elegant way of dealing with out of range: return black
if ((s<0.0) || (s>1.0) || (v<0.0) || (v>1.0)) {
return 0L;
}
if ((h < 0.0) || (h > 6.0)) {
return long( v * 255 ) + long( v * 255 ) * 256 + long( v * 255 ) * 65536;
}
i = floor(h);
f = h - i;
if ( !(i&1) ) {
f = 1 - f; // if i is even
}
m = v * (1 - s);
n = v * (1 - s * f);
switch (i) {
case 6:
case 0: // RETURN_RGB(v, n, m)
return long(v * 255 ) * 65536 + long( n * 255 ) * 256 + long( m * 255);
case 1: // RETURN_RGB(n, v, m)
return long(n * 255 ) * 65536 + long( v * 255 ) * 256 + long( m * 255);
case 2: // RETURN_RGB(m, v, n)
return long(m * 255 ) * 65536 + long( v * 255 ) * 256 + long( n * 255);
case 3: // RETURN_RGB(m, n, v)
return long(m * 255 ) * 65536 + long( n * 255 ) * 256 + long( v * 255);
case 4: // RETURN_RGB(n, m, v)
return long(n * 255 ) * 65536 + long( m * 255 ) * 256 + long( v * 255);
case 5: // RETURN_RGB(v, m, n)
return long(v * 255 ) * 65536 + long( m * 255 ) * 256 + long( n * 255);
}
}