HSL to RGB tutorial + Updated Rotary Encoder Library
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.