I can't resist a project that makes pretty colors. I wired up an 3 potentiometer, 3 LED light and programmed it for HSV controls from the pots. I have two suggestions for improvement, one coding and one visual.
In your code you repeat a number of calculations in the switch statement with enough differences that they don't get optimized into one evaluation, I implemented them once to keep the code size down.
More importantly... Human eyes are not linear. As a very rough estimate, if you were to divide brightness up from 1 to 7, you have to double the number of photons with each step to make them look uniform. If you just run 'value' into the analog to digital converters you get a 'value' knob that is very sensitive at the low end and nearly flat at the high end.
This code addresses both of those issues. It still isn't great, the 'value' and 'saturation' knobs feel about right, but the 'hue' knob does not feel smooth. I must study color perception more deeply. It also ignores differences in brightness of the red, green, and blue LEDs, but that is ok if you handle it in the current limiting resistor.
#include <avr/pgmspace.h>
// This is is an exponential function, scaled and shifted so 0..255 maps onto 0..255
// For i from 0 to 255... round( pow( 2.0, i/32.0) - 1)
// We are going to use it to compensate for the nonlinearity of human vision.
// The PROGMEM keeps it in flash instead of SRAM, but we must use
// special functions to access it.
static const PROGMEM uint8_t delog[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
15, 15, 16, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21,
22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30,
31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 58, 59, 60, 62,
63, 64, 66, 67, 69, 70, 72, 73, 75, 77, 78, 80, 82, 84, 86, 88,
90, 91, 94, 96, 98, 100, 102, 104, 107, 109, 111, 114, 116, 119, 122, 124,
127, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 165, 169, 172, 176,
180, 184, 188, 192, 196, 201, 205, 210, 214, 219, 224, 229, 234, 239, 244, 250,
};
void getRGB(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b)
{
// I feel I should explain fixed point arithmetic in more detail, but this is
// is not the place.
uint8_t sector = h/60U;
uint8_t remainder = (h - sector*60U) * 64U / 15U; // 64/15 is really 256/60, but lets stay clear of overflows
uint8_t p = v * (255U-s) / 255U; // s and v are (0.0..1.0) --> (0..255), p is (0..255^2)
uint8_t q = v * (255UL*255UL-((long)s)*remainder) / (255UL*255UL); // look, fixed point arithmetic in varying encodings
uint8_t t = v * (255UL*255UL-((long)s)*(255U -remainder)) / (255UL*255UL);
switch( sector ) {
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;
break;
}
}
void setup()
{
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
}
void loop()
{
uint8_t r,g,b;
int hue,sat,val;
int ana_0 = analogRead(0); // # between 0-1023
int ana_1 = analogRead(1); // # between 0-1023
int ana_2 = analogRead(2);
hue=(360*(long)ana_0)>>10; //0-360; //shift bits = /1023
sat=ana_1>>2; //0-255;
val=ana_2>>2; //0-255;
getRGB(hue,sat,val, &r, &g, &b);
// pgm_read_byte(x+k) is very similar to x[k] for arrays of bytes that are
// stored in flash with the PROGMEM attribute
analogWrite(9,pgm_read_byte( delog + r));
analogWrite(10,pgm_read_byte( delog + g));
analogWrite(11,pgm_read_byte( delog + b));
delay(20);
}