Archive

Archive for April, 2009

HSL to RGB tutorial + Updated Rotary Encoder Library

April 22nd, 2009

Background:

I am in the design process of a new project that uses a lot of RGB LEDs. In the project I want to allow the user to select various colours and I was looking for a fairly intuitive way to do this.

If one has ever worked with RGB LEDs you know you have to PWM the values of each channel (R, G or B). Therefore for Red, I would PWM (255,0,0), for Green – (0,255,0), etc.

Now since each LED has 3 channels, you have to control three outputs. Therefore if I wanted to allow the user to be able to control the colour of an LED, I would have to have him control three sensors. That is a lot of inputs for one output (3 inputs per output). For an application like this I would normally consider three potentiometer’s to control the LED

But I wanted to be able to control the LED with one sensor. Doing some research I found that the HSL colour scheme was exactly what I need. I stumbled across this Rotary Encoder on Seeedstudio and it fit my price range. The reason I chose an encoder is you can use it to act like a pot but it allows continuous rotation.

My solution was to use an HSL Color Schemer. If I set the luminance and saturation to a suitable value, I can then use the rotary encoder to cycle through the Hue spectrum. Now while this does not give me every single RGB combination, it does give me 360 combinations across all the RGB colour channels.

Integration:

The major stumbling block I had was the Rotary Encoder. The reason it was so cheap was because it was a Mechanical Encoder. Therefore I has some serious debounce factor to consider – either through software or hardware. I found a little Rotary Encoder library written by SunBox on the Arduino Forums. However, the way it worked was you had to set a max and a min and once either extreme was reached, it no longer counted forward or backward.

I added a restart function to the library. Setting restart to a value of 1, tells the library to start counting from the min value.

restart(1);

I’ve attached the library below:
Rotary Encoder Library

Note: the library currently counts up by 20 on every edge. I used this cause I had a 20 detent encoder and that means a full rotation goes just over the whole hue range. I will add a function that allows you to choose how much to count by on each edge

Here is some sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "RotaryEncoder.h"
 
RotaryEncoder rotary(3, 2, 4);
int Rpin=10;
int Gpin=9;
int Bpin=11;
float H,S,L,Rval,Gval,Bval;
 
void HSL(float H, float S, float L, float& Rval, float& Gval, float& Bval);
float Hue_2_RGB( float v1, float v2, float vH );
 
void setup()
{
  Serial.begin(9600);
 
  //set up encoder with rotary library
  rotary.minimum(0);
  rotary.maximum(360);
  rotary.position(320);
  rotary.restart(1);
  pinMode(12, OUTPUT); 
}
 
 
void loop()
{
  H=rotary.position();
  S=1;
  L=.5;
  Rval=0;
  Gval=0;
  Bval=0;
  HSL(float(rotary.position())/360,S,L,Rval,Gval,Bval);
 
  //common anode configuration
  //analogWrite(Rpin, 255-Rval);
  //analogWrite(Gpin, 255-Gval);
  //analogWrite(Bpin, 255-Bval);
  //digitalWrite(12,HIGH);
 
  //common cathode configuration
  analogWrite(Rpin, Rval);
  analogWrite(Gpin, Gval);
  analogWrite(Bpin, Bval);
  digitalWrite(12,LOW);
 
  //print statements for debug
  Serial.print("position:");
  Serial.print(H);
  Serial.print(" R:");
  Serial.print(Rval);
  Serial.print(" G:");
  Serial.print(Gval);
  Serial.print(" B:");
  Serial.println(Bval);
  delay(1000);
}
 
void HSL(float H, float S, float L, float& Rval, float& Gval, float& Bval)
{
  float var_1;
  float var_2;
  float Hu=H+.33;
  float Hd=H-.33;
  if ( S == 0 )                       //HSL from 0 to 1
  {
     Rval = L * 255;                      //RGB results from 0 to 255
     Gval = L * 255;
     Bval = L * 255;
  }
  else
  {
     if ( L < 0.5 ) 
       var_2 = L * ( 1 + S );
     else           
       var_2 = ( L + S ) - ( S * L );
 
     var_1 = 2 * L - var_2;
 
     Rval = round(255 * Hue_2_RGB( var_1, var_2, Hu ));
     Serial.print("Rval:");
     Serial.println(Hue_2_RGB( var_1, var_2, Hu ));
     Gval = round(255 * Hue_2_RGB( var_1, var_2, H ));
     Bval = round(255 * Hue_2_RGB( var_1, var_2, Hd ));
  }
 
}
float Hue_2_RGB( float v1, float v2, float vH )             //Function Hue_2_RGB
{
   if ( vH < 0 ) 
     vH += 1;
   if ( vH > 1 ) 
     vH -= 1;
   if ( ( 6 * vH ) < 1 ) 
     return ( v1 + ( v2 - v1 ) * 6 * vH );
   if ( ( 2 * vH ) < 1 ) 
     return ( v2 );
   if ( ( 3 * vH ) < 2 ) 
     return ( v1 + ( v2 - v1 ) * (.66-vH) * 6 );
   return ( v1 );
}

Hope this helps someone, I will be adding pictures soon. If you need any other modifications made to the library don’t hesitate to ask.

Darius Gai Arduino

Seeedstudio Issues Part II (Resolved!!)

April 21st, 2009

The issues have been resolved.
It did take some back and forth with Eric (the owner of seeedstudio) over email. Eric tried to help me through various options that would let me salvage the anode LEDs, but all the ideas we put together would cost me additional money and take up more space in my eventual design. Eric finally conceded that cathode LEDs would be the right way to go and then we went through arrangements for them to send me the cathode LEDs over. Initally they wanted me to pay for the new LEDs, but I stayed strong to my arguement that the mistake was their fault and Eric finally did concede.

Altogether the back and forth of emails took about 2 days, and the LEDs came in today (18 days later) at the cost of seeedstudio. Through out the process Eric was extremely polite, and replied promptly to my emails.

Thanks seeedstudio!!

Darius Gai Arduino