much improved HSV colour cycling LED on Arduino

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);
  }
} 

One thought on “much improved HSV colour cycling LED on Arduino”

Leave a Reply

Your email address will not be published. Required fields are marked *