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
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
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
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:
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 GTK+2 for Windows Runtime Environment download | SourceForge.net.
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