Go Down

Topic: First Arduino project - Python controlled RGB LED (Read 13 times) previous topic - next topic

Jkm3141

Ever since I first saw Arduino, I knew I wanted one to play around with. Last week I finally ran across the some spare cash and picked up a Duemilanove, and after doing the obligatory Hello World and LED sketches, I wanted something a little more robust.

Anyway, I went ahead and threw together a small little sketch with some borrowed code from a few places and a lot of my own, and this is what I came up with.

Its a Python front end that controls the RGB values being displayed. However, that was boring, so I adapted the HSV loop function I saw in another thread (I'll post in there to let them know how I used that code) and now have the ability to start that loop from the Python code easily.


Anyhow, heres the sketch:
http://people.rit.edu/jkm2972/Serial_RGB.pde
Code: [Select]
int colorRGB[3];
int redPin = 11;
int greenPin = 10;
int bluePin = 9;

int delayVal = 50;
int blnFade = 0;
int h_int;
float h;
int r=0, g=0, b=0;

void h2rgb(float h, int &R, int &G, int &B);
void colorFade();

void setup() {
 
 Serial.begin(57600);
 pinMode(redPin, OUTPUT);
 pinMode(greenPin, OUTPUT);
 pinMode(bluePin, OUTPUT);

}

void loop() {
 
 if(Serial.available() >= 2){
   
   switch( byte( Serial.read() )) {
     case 'r':
       colorRGB[0] = Serial.read();
       blnFade = 0;
       break;
     case 'g':
       colorRGB[1] = Serial.read();
       blnFade = 0;
       break;  
     case 'b':
       colorRGB[2] = Serial.read();
       blnFade = 0;
       break;
     case 'c':
       Serial.flush();
       blnFade = 0;
       break;
     case 'f':
       delayVal = Serial.read();
       Serial.flush();
       colorFade();
       blnFade = 1;
     }
  }
  analogWrite(redPin, colorRGB[0]);
  analogWrite(greenPin, colorRGB[1]);
  analogWrite(bluePin, colorRGB[2]);
  delay(20);
}

void colorFade() {

while (blnFade == 1) {
if(Serial.available() ){
  blnFade = 0;
  break;
}  
for(int fadeValue = 0 ; fadeValue <= 1024; fadeValue +=5) {
   h = ((float)fadeValue)/1024;
   h_int = (int) 360*h;
   h2rgb(h,r,g,b);
   if(Serial.available() ){
     blnFade = 0;
     break;
   }
   analogWrite(redPin, r);
   analogWrite(greenPin, g);
   analogWrite(bluePin, b);
   delay(delayVal);
 }
}
}

void h2rgb(float H, int& R, int& G, int& B) {
 int var_i;
 float S=1, V=1, var_1, var_2, var_3, var_h, var_r, var_g, var_b;
 if ( S == 0 ) {
   R = V * 255;
   G = V * 255;
   B = V * 255;
 } else {
   var_h = H * 6;
   if ( var_h == 6 ) var_h = 0;
   var_i = int( var_h ) ;
   var_1 = V * ( 1 - S );
   var_2 = V * ( 1 - S * ( var_h - var_i ) );
   var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) );

   if ( var_i == 0 ) {
     var_r = V     ;
     var_g = var_3 ;
     var_b = var_1 ;
   } else if ( var_i == 1 ) {
     var_r = var_2 ;
     var_g = V     ;
     var_b = var_1 ;
   } else if ( var_i == 2 ) {
     var_r = var_1 ;
     var_g = V     ;
     var_b = var_3 ;
   } else if ( var_i == 3 ) {
     var_r = var_1 ;
     var_g = var_2 ;
     var_b = V     ;
   } else if ( var_i == 4 ) {
     var_r = var_3 ;
     var_g = var_1 ;
     var_b = V     ;
   } else {
     var_r = V     ;
     var_g = var_1 ;
     var_b = var_2 ;
   }
   R = (1-var_r) * 255;
   G = (1-var_g) * 255;
   B = (1-var_b) * 255;
 }
}


The only things that need to be set in the sketch are the pins for the RGB LED.

To display a value, the Arduino is waiting for 2 bytes to be received via serial, and it checks the first byte for an ASCII r, g, b, or f and the second value as the parameter. Obviously the r, g, and b set the respective RGB values on the LED with the byte value passed second. If you pass an ASCII f, it starts the HSV loop, with a delay value in the second byte (a good range is 5-55 if you don't want flicker).

Also, the baud rate can easily be set back to 9600 for compatibility, however the GUI likes a faster baud rate for 'smoother' fades, however I haven't really confirmed that yet.

Here are two simple python programs to demonstrate the functionality

This python code accepts user input on the terminal and sets the RGB values accordingly. Simple.

http://people.rit.edu/jkm2972/rgb.py
Code: [Select]
import serial

# Setup the Serial Object
ser = serial.Serial()
# Set the Serial Port to use
ser.setPort("COM5")
# Set the Baudrate (Arduino Sketch is expecting 57600 for smooth transitions in the GUI)
ser.baudrate = 57600
# Open the Serial Connection
ser.open()
loopVar = True

if (ser.isOpen()):
 # Start a main loop
 while (loopVar):
   # Prompt for Red value
   redVal = input('Red value:')
   ser.write("r" + chr(int(redVal)))
   # Prompt for Green value
   greenVal = input('Green value:')
   ser.write("g" + chr(int(greenVal)))
   # Prompt for Blue value
   blueVal = input('Blue value:')
   ser.write("b" + chr(int(blueVal)))
   # Check if user wants to end
   loopCheck = raw_input('Loop (y/N):')
   if (loopCheck == 'N'):
     loopVar = False
 # After loop exits, close serial connection
 ser.close()


Where as this one starts the HSV loop with a user specified delay. However, it can take one or two loop cycles in the python program to actually start the loop (stupid timing bug).

http://people.rit.edu/jkm2972/hsv.py
Code: [Select]
import serial

# Setup the Serial Object
ser = serial.Serial()
# Set the Serial Port to use
ser.setPort("COM5")
# Set the Baudrate (Arduino Sketch is expecting 57600 for smooth transitions in the GUI)
ser.baudrate = 57600
# Open the Serial Connection
ser.open()
loopVar = True

if (ser.isOpen()):
 # Start a main loop
 while (loopVar):
   # Prompt for Loop Delay  (Internal loop delay, used to control the speed of the HSV loop)
   delayVal=input('Delay value:')
   ser.write("f" + chr(int(delayVal)))
   # Check if user wants to end
   loopCheck=raw_input('Loop (y/N):')
   if (loopCheck == "N"):
     loopVar = False
 # After loop exits, close serial connection
 ser.close()


And lastly, the simple GUI code:

Code: [Select]
import gtk
import sys
import serial
import time

class PyApp(gtk.Window):

   def __init__(self):
       super(PyApp, self).__init__()

       self.serialPort = "COM5"

       self.set_title("RGB Test")
       self.set_size_request(260, 150)
       self.set_position(gtk.WIN_POS_CENTER)
       self.setup_serial()

       headerVbox = gtk.VBox(True,0)
       headerLabel1 = gtk.Label("RGB Control App for Arduino")
       headerLabel2 = gtk.Label("Written by John Meichle")
       headerVbox.pack_start(headerLabel1)
       headerVbox.pack_end(headerLabel2)


       rHbox = gtk.HBox(True,0)
       rLabel = gtk.Label("Red: ")
       rHbox.pack_start(rLabel)  
           
       rScale = gtk.HScale()
       rScale.set_name("red")
       rScale.set_range(0, 255)
       rScale.set_increments(1, 10)
       rScale.set_digits(0)
       rScale.set_size_request(160, 35)
       rScale.connect("value-changed", self.on_changed)
       rHbox.pack_end(rScale)
       
       gHbox = gtk.HBox(True,0)
       gLabel = gtk.Label("Green: ")
       gHbox.pack_start(gLabel)  
       
       gScale = gtk.HScale()
       gScale.set_name("green")
       gScale.set_range(0, 255)
       gScale.set_increments(1, 10)
       gScale.set_digits(0)
       gScale.set_size_request(160, 35)
       gScale.connect("value-changed", self.on_changed)
       gHbox.pack_end(gScale)
       
       bHbox = gtk.HBox(True,0)      
       bLabel = gtk.Label("Blue: ")
       bHbox.pack_start(bLabel)  
       
       bScale = gtk.HScale()
       bScale.set_name("blue")
       bScale.set_range(0, 255)
       bScale.set_increments(1, 10)
       bScale.set_digits(0)
       bScale.set_size_request(160, 35)
       bScale.connect("value-changed", self.on_changed)
       bHbox.pack_end(bScale)
       
       
       vbox = gtk.VBox(True,0)

       vbox.pack_start(headerVbox)
       vbox.pack_start(rHbox)
       vbox.pack_end(gHbox)
       vbox.pack_end(bHbox)
       
       

       self.add(vbox)

       self.connect("destroy", lambda w: gtk.main_quit())
       self.show_all()
       


   def on_changed(self, widget):
       
       val = widget.get_value()
       name = widget.get_name()
     
       if name == "red":
           self.ser.write("r" + chr(int(val)))
       elif name == "green":
           self.ser.write("g" + chr(int(val)))
       elif name == "blue":
           self.ser.write("b" + chr(int(val)))
       else:
           print "ERROR: Invalid widget name, in on_changed function"
   
               
               
   def setup_serial(self):
     self.ser = serial.Serial()
     self.ser.setPort(self.serialPort)
     self.ser.baudrate = 57600
     self.ser.open()
     if (self.ser.isOpen()):
       print "Serial Open"
     else:
       print "Serial Closed"

PyApp()
gtk.main()


I wrote a better GUI app, however it is way to big to post. You can download that http://people.rit.edu/jkm2972/arduino-rgb.py

The Python code requires the PySerial module to be installed, as far as I know. that can be downloaded from http://pyserial.sourceforge.net/

Also, the GUI apps rely on GTK, so if you are a Windows/OSX user, you will need the GTK runtime http://sourceforge.net/projects/gtk-win/.

All of the posted python is expecting the serial port to be COM5. That can be adjusted by editing code, its rather simple to find

Ill Mill

Do you have any pictures or videos of this in operation? It sounds like a really cool project!

Jkm3141

Sure.

http://www.youtube.com/watch?v=KLVR8xU-tJQ




- John

Ill Mill

Slick looking interface... I need to spend some time looking at GUI coding.

mako

wow, thats great,
tried it in linux and worked perfect,,
great for learning gui,
thanks!!

Jkm3141


Hiddenbanana

#6
Apr 01, 2010, 01:45 am Last Edit: Apr 01, 2010, 01:45 am by hiddenbanana Reason: 1
Cheers Jkm3141, just thinking of modding this a little and instead of using a slider could use a pinwheel



only issue atm is tring to figure out the RGB format thats being sent via tty

as i can see it uses a form like so

(r255g255b255) yet that don't work but if i send the tty query "f 4(delay number) i get somthing... could you document how you have python talking to the arduino??? :-/

Jkm3141

The Arduino is waiting for two byte values to be passed via serial to it. The first byte would be the ASCII values for r, g, or b (lowercase, 114, 103, and 98 respectively), and the second byte (0-255) indicates the intensity of that color value.

the HSV sweep feature just waits for an ASCII 'f' (102) and the second byte is used to control the loop speed, and is the same number from my python gui.

Let me know if u need any other help.

- John

Hiddenbanana

#8
Apr 01, 2010, 05:31 pm Last Edit: Apr 01, 2010, 05:37 pm by hiddenbanana Reason: 1
Hmmm intresting, :) well lets see what i get, as i am using this for a momo ambiant mod lmao, using 3 300mm acrylic tubing and 6 RGB anode leds got a good effect already just hoping to improve :)  ;D

could you give an example of how it is printed

IE, 98255

thanks :)

Jkm3141

If you print the string "98255" over serial to the Arduino it will not work. It must be a byte value of 98 and a byte value of 255, which I cannot represent in ASCII values on this page. Think binary values (Binary IO) instead of ASCII.

Hiddenbanana

hmmm well it looks like ima have to start writing up a scetch again lol, thanks anyway, if i get anywhere on the color picker i will post it up on here :)

Jkm3141

I was thinking about it agian, if you are having trouble with the serial communication with my sketch, this sample code should help.


Code: [Select]

# Setup the serial object in Python
ser = serial.Serial()
ser.setPort("COM5")
ser.baudrate = 57600

# Open the Serial Connection
ser.open()

# brightness is the intensity value (ranging from 0-255) for each color)
brightness = 255;
color = "r"
serial.write(color + chr(int(brightness)))


That 'should' set the LED to red at full brightness, however I am not home to test it with. The code that you need to understand to make my sketch talk properly is this line,

serial.write(color + chr(int(brightness)))

The brightness value is converted into integer, and then into a char data type and written out with the color (either an ascii r, g, or b).

That will take care of converting from an integer value to a char that the sketch expects.

- John

Hiddenbanana

Right i have got the color picker to work but i think a main issue i have is i am using anode LEDs and its reversing the decimal factor, so for full on is 0 ( instead of off) and vice versa...

well anyway i cannot beleave it took me so long to figure it out but heres the code :)


Code: [Select]
import gtk, sys, serial

#--\\ Serial Config //--#
ser = serial.Serial()
ser.setPort("COM3")
ser.baudrate = 57600
#ser.baudrate = 115200
ser.open()
#--\\ GTK Config //--#
w = gtk.Window()
c = gtk.ColorSelection()
w.add(c)
w.show_all()

#--\\ Main Code //--#
def callback(*args):
   color=c.get_current_color()
   ser.write("r" + chr(int(color.red/257)))
   ser.write("g" + chr(int(color.green/257)))
   ser.write("b" + chr(int(color.blue/257)))

c.connect('color-changed', callback)
w.connect('destroy', sys.exit)

gtk.main()


osfd

Hi there,
Very interesting... Something I don't understand though. How do you connect your board on the serial port ?? I mean there is only a usb port ?
Maybe you could post a picture of the setup or a little sketch..??

Thank you very much for your example.



Jkm3141

Its connected via the USB to Serial adapter built into the arduino. If the arduino driver is setup correctly, your computer will have a new virtual serial port.

John

Go Up