Sunday, January 18, 2009

Arduino LED Color Picker



After experimenting with an RGB LED for a while, I became interested in the idea of using a color gradient to control the LED. I search around a bit for a color gradiant library and finally settled on colorpicker, a subproject of colorchooser (scroll down the page for colorpicker). This library is surprisingly easy to integrate with and allows you to receive color events in real-time, as you drag the mouse around the gradient.

Since this solution is written in Java and uses the same serial library (RXTX) as Arduino, it will run on any platform that Arduino supports (Windows/Mac/Linux etc.). I have included RXTX libraries in the download so it will run on any of these platforms, out-of-the-box (box not included).

Here's a quick video of the application in action:



The application consists of Java application that serves as the colorpicker interface and an Arduino Sketch that receives RGB colors and applies them to the PWM pins

The basic process flow of the Java application is as follows:

- Open serial port to Arduino
- Initialize colorpicker component and register an event handler
- (In forever loop)
- Wait for colorpicker event.
- Send color to Arduino
- Wait for reply from Arduino (ACK)

The colorpicker runs in a separate thread and sends color events to the main thread.

The Java application sends the RGB colors in a sequence of four bytes: red, green, blue and a EOT byte. The EOT byte signals the end of a color sequence and instructs the Arduino to process the color. Since a byte has 256 values and we need all of them for the color values, I created a escape byte (0x2) to distinguish the EOT byte (0x1) from the color value (0x1). Any time a color value of 0x1 or 0x2 is sent, it is preceded by a escape byte and XOR'd with 0x20. When the Arduino receives the data via Serial.read(), it performs a corresponding XOR operation to un-escape the bytes.

Once the Arduino processes the color, it sends an ACK byte back to the Java application, so that it can send the next color. Without the ACK byte, we run the chance of overflowing the buffer by sending color events faster than the Arduino can process them.

Here's the Arduino sketch:


// Adjust as necessary to match your RGB LED. Remember to only use the PWM pins
int redPin = 11;
int greenPin = 10;
int bluePin = 9;

// Indicates what color we are reading next; 0 = red, 1 = green, 2 = blue
int pos = 0;

// red PWM value
int red = 0;
// green PWM value
int green = 0;
// blue PWM value
int blue = 0;

// indicates if next byte should be unescaped
boolean escape = false;

void setup() {
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);

Serial.begin(9600);

// turn on LED on low brightness
analogWrite(redPin, 16);
analogWrite(greenPin, 16);
analogWrite(bluePin, 16);
}

void loop () {

while (Serial.available()) {
int rgb = Serial.read();

if (rgb == 1) {
// end of RGB sequence byte
// reset pos
pos = 0;

// process this color
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);

// Send ACK byte so Java app can send the next color
Serial.print("k");
Serial.flush();

// get next byte
continue;
} else if (rgb == 2) {
// escape byte
escape = true;
// discard byte and read next byte
continue;
}

if (escape) {
// unescape byte
rgb = 0x20 ^ rgb;
// reset escape
escape = false;
}

switch (pos++) {
case 0:
red = rgb;
break;
case 1:
green = rgb;
break;
case 2:
blue = rgb;
break;
}
}
}

To run the app, first download the project from my Google Code project. I've included the full Java source code and everything necessary to run the app, sans Java.

Next, connect your RGB LED to the Arduino PWM pins, as specified in the Arduino sketch. Be sure to use appropriate resistors so that you do not draw more current than the Arduino can supply. If you need a RGB LED, here's a basic one offered by SparkFun . You could also use separate Red, Green and Blue LEDs and diffuse the colors with something like a paper box.

Now upload the sketch to your Arduino.

If you don't have Java 1.5 or later, now would be a good time to get it. You can find out by opening a command prompt/shell and typing "java -version" If you see something like this then you are all set:

java version "1.5.0_16"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_16-b06-284)
Java HotSpot(TM) Client VM (build 1.5.0_16-133, mixed mode, sharing)


Before running the Java application you need to open the run script for your platform and specify the COM port of your Arduino:

Windows: ledcolorpicker.cmd
Mac/Linux: ledcolorpicker.sh

The COM port should be the last argument on the line that starts with "java"

Now run the script for your platform:

Windows: double-click on ledcolorpicker.cmd (note: if the window closes immediately, open a command prompt and run the script again to get the error)

Mac/Linux: First you probably need to make it executable: "chmod u+x ledcolorpicker.sh". Now run the script "./ledcolorpicker.sh"

The LedColorPicker application should now start up and be ready to go.

Based on my results, the Arduino processes colors in about 16ms -- that is time it takes to send a color to the Arduino and receive an ACK @ 9600 baud. I've found that the Arduino can process color events almost as fast as the colorpicker can generate events. In my limited testing, only less than 2% of color events were discarded because the Arduino was busy processing a previous color. You could use a higher baud rate but with this level of performance it's not necessary. This translates to the Arduino processing about 30 colors changes per second -- not bad.

You may have noticed that we are tethered to our computer in this configuration. The good news is it's possible to make this solution wireless with minimal effort, using with XBee radios. Using the Arduino XBee Shield configured in transparent mode, you could put your LED across the room or anywhere within range and control it from your PC. In this configuration you would need two XBee radios, 1 Arduino XBee Shield and 1 USB-Serial device with XBee socket, such as the SparkFun's XBee Explorer.

13 comments:

  1. Hey!

    Very nice, I wanted to do exactly the same thing when I stumbled over your project.
    I want to extend it: The colour change seems to be too slow to make smooth gradient-like transitions from one color to the next, I would like to have the Arduino do smooth transitions in between the colors that are sent from the computer.
    This will prevent instant changes of course, but I would like the change to be slow for an Abient Orb style device.

    Cheers,
    Till

    ReplyDelete
  2. When trying to run ledcolorpicker.jar I am getting an error message saying 'Check the Console for possible error messages' Any advice?

    ReplyDelete
  3. Hi, very nice project, can You tell me, what should I do to colors would change by using random() automatically? Thanks very much, Tom!

    ReplyDelete
  4. Nice write up! Thanks!

    ReplyDelete
  5. you might want to consider doing a color value to exponential PWM value mapping. LED light output (and human eye perception) is non-linear.

    You will need more bits of precision on the output PWM counter to give you a full 256 different output levels.

    I don't know if Atmels support 16 bit PWMs...It probably has enough CPU horsepower to do the PWMs yourself in an interrupt service routine.

    ReplyDelete
  6. I am working on alternative firmware for the BlinkM RGB LED controller that is based on AVR -- the firmware is called cyz_rgb. The project is located here: http://code.google.com/p/codalyze/wiki/CyzRgb The normal firmware, I understand, has some bits under a commercial license, so working with an alternative helps give these little "smart LEDs" hopefully some new functionality.

    I am not the original developer, but I just did a port to the BlinkM MaxM (ATTiny44) hardware, and I'm focusing now on improving the PWM resolution and supporting logarithmic color response.

    In response to Eightway, the AVRs used on the arduino (at least current boards) only have 1 16 bit timer that has PWM control of only 2 pins. There isnt any "friendly" support for using it directly in the arduino libraries, but in any case you still have to do some crazy tricks to get an extra 16 bit output. IMO at least with the aruduino it's better to dump this off to another chip, be it a dedicated LED driver or another AVR.

    Related to this project, I currently have an arduino sketch that implements a graphical ajax web-based color picker very similar to this using the ethernet shield and 12 connected BlinkM MaxM's. It can drive around 300W of RGB LED's due to the BlinkM hardware; no java needed. It also fades smoothly between colors -- update interval is approximately 250ms, but due to the fading it looks much smoother. I'll try to release some basic version of it when I get some better functionality into cyz_rgb.

    ReplyDelete
  7. Win. The Mac shell script needs a correction, though: for Snow Leopard you need to add the -d32 flag to the .sh script because the serial library is compiled for 32-bit.

    The script is written with an implicit assumption of a common cathode LED, but that's easy enough to fix.

    ReplyDelete
  8. I am new to microcontrollers and ran across this code, I want to recreate this project but tried to specify the COM port but am unable to specify, I am using the uno. Can someone help me with this very small issue!

    ReplyDelete
  9. I tried running the code on a mac and got this message:
    Exception in thread "main" java.lang.NoClassDefFoundError: com/rapplogic/ledcolorpicker/LedColorPicker
    Caused by: java.lang.ClassNotFoundException: com.rapplogic.ledcolorpicker.LedColorPicker
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

    ReplyDelete
    Replies
    1. I am running Windows XP Pro 64 bit with 32 bit Java. My Java - Version is;

      C:\Documents and Settings\marks>java -version
      java version "1.7.0_13"
      Java(TM) SE Runtime Environment (build 1.7.0_13-b20)
      Java HotSpot(TM) Client VM (build 23.7-b01, mixed mode, sharing)

      I get this error in the dos cmd window;

      C:\Documents and Settings\marks>P:\Arduino\MyArduino\LedColorPicker\ledcolorpick
      er.cmd

      C:\Documents and Settings\marks># this script requires java 1.5 in your path
      '#' is not recognized as an internal or external command,
      operable program or batch file.

      C:\Documents and Settings\marks># replace the last argument with the COM port of
      your Arduino
      '#' is not recognized as an internal or external command,
      operable program or batch file.

      C:\Documents and Settings\marks>java -classpath lib\colorpicker.jar;lib\log4j.ja
      r;lib\RXTXcomm.jar;ledcolorpicker.jar -Djava.library.path=. com.rapplogic.ledcol
      orpicker.LedColorPicker COM3
      Error: Could not find or load main class com.rapplogic.ledcolorpicker.LedColorPi
      cker

      Thanks.

      Delete
  10. HI, I came across your project and hacked it up a bit to work with Adafruit's neopixels. Mind if I post my hacked up version of your sketch on their forum? I ask as you listed the sketch as copyrighted.

    Thanks,

    -Chris

    ReplyDelete