Tuesday, October 11, 2011

Ambient Lighting using an RGB LED and Arduino UNO

I had some spare time today and managed to put up an ambient lighting using a single RGB LED and an Arduino UNO Micro controller.

There are many implementations of Arduino Ambient light setups like

http://siliconrepublic.blogspot.com/2011/02/arduino-based-pc-ambient-lighting.html


The above implementation is a really cool one which uses a +RGB LED Strip to create the ambient light effect from the PC's averaged screen color.

But sadly I didn't have a RGB LED Strip but only one RGB LED. So I tried to create the ambient light effect using the single RGB LED. So the setup is as following








The RGB LED is a common anode (positive) one so the 5v is connected to the common anode via a 1k resistor. And I use pin 9, 10, 11 with Pulse Width Modulation (PWM) for Blue, Green and Red cathodes (negative) respectively.  I drive the PWM pins using the Arduino analogWrite(). But since the RGB pins are cathodes it negates the driving value. Meaning if I give a high value (255) it will dim the corresponding color and if I give a low value (0) it will increase the brightness of the corresponding color.

Sketch Code for Arduino UNO


int bluePin = 9;
int greenPin = 10;
int redPin = 11;
int blueBrightness = 0;
int greenBrightness = 0;
int redBrightness = 0;

void setup() {
  // Setting Up the COM Port
  Serial.begin(9600);
  // Changing PIN modes to OUTPUT
  pinMode(bluePin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(redPin, OUTPUT);
}

void loop() {
  if(Serial.available() >= 4) {
   if(Serial.read() == 0xff) {
    // 0, 0, 0 is Black
    // 255, 255, 255 is White
    redBrightness = Serial.read();
    greenBrightness = Serial.read();
    blueBrightness = Serial.read();
   } 
  }
  
  /*
   Since the RGB LED has cathode pins for 
   RGB we need to deduct value from 255 
   meaning if the brightness is 255 from the 
   PC for a color we need to give 0 so that it
   will eluminate brightly
  */
  analogWrite(bluePin, 255 - blueBrightness);
  analogWrite(greenPin, 255 - greenBrightness);
  analogWrite(redPin, 255 - redBrightness);
  delay(10); 
}



In order to capture the Screen Averaged color I used the implementation from the above mentioned post yet I used Java instead of Processing because I am familiar with it. I used RXTX Library for COM port writing.

The Java Code is below


package com.shazin.ambientrgb;

/**
 *
 * @author Shazin Sadakath
 */
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import java.awt.AWTException;
import java.awt.Robot; 
import java.awt.image.BufferedImage;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AmbientRGB implements Runnable {

    private static Robot robot; 
    private static SerialPort port = null;
    private static CommPortIdentifier cpi = null;

    public static void main(String[] args) throws AWTException {
        Enumeration enums = CommPortIdentifier.getPortIdentifiers();
        robot = new Robot();
        while (enums.hasMoreElements()) {
            cpi = (CommPortIdentifier) enums.nextElement();
            if ("COM27".equals(cpi.getName())) {
                break;
            }
        }

        if (cpi != null) {
            try {
                port = (SerialPort) cpi.open("ArduinoJavaBridge", 1000);
                if (port != null) {
                    port.setSerialPortParams(9600,
                            SerialPort.DATABITS_8,
                            SerialPort.STOPBITS_1,
                            SerialPort.PARITY_NONE);
                }

                System.out.println("Ready!");
                new Thread(new AmbientRGB()).start();

            } catch (Exception e) {
                Logger.getLogger(AmbientRGB.class.getName()).log(Level.SEVERE, null, e);
            }


        }
    }

    public void run() {
        OutputStream os = null;
        try {
            os = port.getOutputStream();
        } catch (IOException ex) {
            Logger.getLogger(AmbientRGB.class.getName()).log(Level.SEVERE, null, ex);
        }
        while (true && os != null) {
            int pixel;
            float r = 0;
            float g = 0;
            float b = 0;

            Rectangle rectangle = new Rectangle(new Dimension(1366, 768));
            BufferedImage screenshot = robot.createScreenCapture(rectangle);


            int i = 0;
            int j = 0;

            for (i = 0; i < rectangle.getWidth(); i = i + 2) {
                for (j = 0; j < rectangle.getHeight(); j = j + 2) {
                    pixel = screenshot.getRGB(i, j); 
                    r = r + (int) (255 & (pixel >> 16)); 
                    g = g + (int) (255 & (pixel >> 8)); 
                    b = b + (int) (255 & (pixel)); 
                }
            }
            int totalPixels = (683 * 384); 
            r = r / totalPixels; 
            g = g / totalPixels; 
            b = b / totalPixels;


            try {

                os.write(0xff); 
                os.write((byte) (r)); 
                os.write((byte) (g)); 
                os.write((byte) (b)); 
                os.flush();
                Thread.sleep(10);
            } catch (Exception ex) {
                Logger.getLogger(AmbientRGB.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }
}


I used a White Paper Scroll to cover the RGB LED so that the light is evenly visible.