Rainbow calculator

Hi!
I'd like to be able to calculate on the fly the RGB values at any point of the color spectrum. So like by using for example a potentiometer, one can sweep the rainbow and generate RGB values. I've tried using the SIN function but would there be a way to do it without using floats to keep the code small. Can the SIN functin be used without using floats? or can someone give me a hint on how to code a simili SIN function using linear integer values (like triangle waves instead of sine waves)?

Thank you.

I suspect that code can be done with dot products, but if you aren't in a matrix math mood you could use a lookup table with the first quarter cycle of the sine function. That serves both sine and cosine by flopping it about different directions.

Here is one with 256 entries scaled for a byte. Don't sweat the flat spot at the end, it belongs there.

  0,   1,   3,   4,   6,   7,   9,  10,  12,  14,  15,  17,  18,  20,  21,  23, 
 25,  26,  28,  29,  31,  32,  34,  36,  37,  39,  40,  42,  43,  45,  46,  48, 
 49,  51,  53,  54,  56,  57,  59,  60,  62,  63,  65,  66,  68,  69,  71,  72, 
 74,  75,  77,  78,  80,  81,  83,  84,  86,  87,  89,  90,  92,  93,  95,  96, 
 97,  99, 100, 102, 103, 105, 106, 108, 109, 110, 112, 113, 115, 116, 117, 119, 
120, 122, 123, 124, 126, 127, 128, 130, 131, 132, 134, 135, 136, 138, 139, 140, 
142, 143, 144, 146, 147, 148, 149, 151, 152, 153, 155, 156, 157, 158, 159, 161, 
162, 163, 164, 166, 167, 168, 169, 170, 171, 173, 174, 175, 176, 177, 178, 179, 
181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 
197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 211, 
212, 213, 214, 215, 216, 217, 217, 218, 219, 220, 221, 221, 222, 223, 224, 225, 
225, 226, 227, 227, 228, 229, 230, 230, 231, 232, 232, 233, 234, 234, 235, 235, 
236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, 244, 244, 
244, 245, 245, 246, 246, 247, 247, 247, 248, 248, 249, 249, 249, 250, 250, 250, 
251, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 
254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,

Hi Jims,
Altough I'm unfamiliar with dot products and matrix math (I studied math in french, so names and techniques are little bit different at times, vive le Wikipedia!), thanks for this lookup table, I'll try it and comment on it soon. Thanks

Hmm... if you want to have the arduino look at light and analyze the rgb values, you could probably use an rgb led or individual r g and b leds and use them as light sensors. I'm not sure if this is what you mean, but I'll just throw this out there.

To me, the best way to make that is to use the HSV color mode (Hue Saturation Value)
Hue = Varies from 0 to 360°. It defines the point in the spectrum, 0 = 360 = red, 10 is like a purplish color, etc... This way, you have an easy way to find all the colors in the spectrum, just by varying the angle.
Saturation = "Purity" of the color. Varies from 0 to 100. The lower the value, the more the colour will be dull.
Value = Brightness of the color.

Exemple : to have a pure RGB red (255,0,0), HSV value will be (360 or 0, 100,100)

You can then convert the value to RGB by using this kind of code

http://www.easyrgb.com/math.php?MATH=M21#text21

It seems that maximal value for H is 256 on this code, haven't tried it myself yet, but i gave the same advice to a guy on a french electronic forum and here's his result ^^

I was working on a HSV -> RGB function for the Arduino. In the code field below the result.
It seems the work when I watch the serial output, however I didn't test it with an RGB led.
This might be enough for a Rainbow ( change the hue ).

Any suggestions for improvement are welcome ( i'm more a designer than a programmer ).

// based on <http://www.codeproject.com/miscctrl/CPicker.asp>
// I use 2 analog inputs
// Didn't test it with an RGB led, but the serial output seems alright. 

long base;
long hue;
long sat;
long val; 

int r;
int g;
int b;

int ana_0 = 0; // used for analog inputs 
int ana_1 = 0; // used for analog inputs 

void getRGB()
{ 
      if (sat == 0) // Acromatic color (gray). Hue doesn't mind.
      {   r = val;
          g = val;
          b = val;  
      }
      else
      {   base = ((255 - sat) * val)>>8; // >>8 same as dividing by 255
          
            switch(hue/60)
            {
            case 0:
                  r = val;
                  g = (((val-base)*hue)/60)+base;
                  b = base;
            break;

            case 1:
                  r = (((val-base)*(60-(hue%60)))/60)+base;
                  g = val;
                  b = base;
            break;

            case 2:
                  r = base;
                  g = val;
                  b = (((val-base)*(hue%60))/60)+base;
            break;

            case 3:
                  r = base;
                  g = (((val-base)*(60-(hue%60)))/60)+base;
                  b = val;
            break;

            case 4:
                  r = (((val-base)*(hue%60))/60)+base;
                  g = base;
                  b = val;
            break;

            case 5:
                  r = val;
                  g = base;
                  b = (((val-base)*(60-(hue%60)))/60)+base;
            break;
            }
            colorsRGB[0]=r;
            colorsRGB[1]=g;
            colorsRGB[2]=b; 
      }   
}

void setup() 
{ digitalWrite(13, HIGH);
  Serial.begin(9600);  
}

void loop()  
{ ana_0 = analogRead(0); // # between 0-1023
  ana_1 = analogRead(1); // # between 0-1023
    
  hue=(360*(long)ana_0)>>10; //0-360; //shift bits = /1023
  sat=ana_1>>2;                    //0-255;     
  val=255;                            //0-255; 
  
  getRGB();
  
  Serial.println(colorsRGB[0]);
  Serial.println(colorsRGB[1]);
  Serial.println(colorsRGB[2]);   
  delay(100);
}

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