Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Create ambilight with Java and Arduino

DZone's Guide to

Create ambilight with Java and Arduino

·
Free Resource

Recently I have started to play with Arduino board and Java to build some useful stuff and I wanted to share my last project with you.

In a nutshell I've decided to build very simple ambilight for my PC to change the colors behind the monitor in real time when I watch the movie or play the games.

What we will need:

  • Arduino board

  • NeoPixel strip

  • 15 minutes

You can find many different NeoPixel strips, but we will need simple 30 LED RGB one like this. Because of low energy consumption we will not need additional power supply. The problems may appear if You will try to light up all LEDs with maximum brightness.

Circuit

Usage of neopixel strip is very simple. It has only 3 cables: VCC, GND and PWM. Connect them to appropriate PINs on a board and we are set. When You will run example Arduino application You will see effect like this:

 

Java Part

First we have to create simple application that will calculate average colors based on what is displayed on the screen. To do that we split the screen to sections, calculate average color for each and then we are ready to send the data to Arduino. To communicate with the board we use RXTX library, that can be found here.


import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;

public class Ambilight {

//Arduino communication config
public static final int DATA_RATE = 9600;
public static final int TIMEOUT = 2000;
//delay between next color calculations in [ms]
private static final long DELAY = 10;
//leds number on strip
public static final int LEDS_NUM = 30;
//number of leds per section
public static final int LEDS_PER_SECTION = 3;
//we split strip to sections because of performance reasons
public static final int SECTIONS = LEDS_NUM / LEDS_PER_SECTION;

//screen resolution
public static final int X_RES = 1920;
public static final int Y_RES = 1080;
//sections width and height
public static final int SECT_WIDTH = X_RES / SECTIONS;
public static final int SECT_HEIGHT = Y_RES;
//for better performance we do not calculate every pixel,
//but skip some of them
public static final int SECT_SKIP = 10;

// robot to read the data from the screen
private Robot robot;

// arduino communication
private SerialPort serial;
private OutputStream output;

/**
 * init arduino communication
 */
private void initSerial() {
// find the port where teh arduino is connected
CommPortIdentifier serialPortId = null;
Enumeration enumComm = CommPortIdentifier.getPortIdentifiers();
while (enumComm.hasMoreElements() && serialPortId == null) {
serialPortId = (CommPortIdentifier) enumComm.nextElement();
}

try {
serial = (SerialPort) serialPortId.open(this.getClass().getName(),
TIMEOUT);
serial.setSerialPortParams(DATA_RATE, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
} catch (PortInUseException | UnsupportedCommOperationException e) {
e.printStackTrace();
}
}

/**
 * init the robot
 */
private void initRobot() {
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
}

/**
 * init arduino output
 */
private void initOutputStream() {
try {
output = serial.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
 * read colors from the screen
 * 
 * @return array with colors that will be send to arduino
 */
private Color[] getColors() {
BufferedImage screen = robot.createScreenCapture(new Rectangle(
new Dimension(X_RES, Y_RES)));
Color[] leds = new Color[SECTIONS];

for (int led = 0; led < SECTIONS; led++) {
BufferedImage section = screen.getSubimage(led * SECT_WIDTH, 0, SECT_WIDTH, SECT_HEIGHT);
Color sectionAvgColor = getAvgColor(section);
leds[led] = sectionAvgColor;
}
return leds;
}

/**
 * calculate average color for section
 */
private Color getAvgColor(BufferedImage imgSection) {
int width = imgSection.getWidth();
int height = imgSection.getHeight();
int r = 0, g = 0, b = 0;
int loops = 0;
for (int x = 0; x < width; x += SECT_SKIP) {
for (int y = 0; y < height; y += SECT_SKIP) {
int rgb = imgSection.getRGB(x, y);
Color color = new Color(rgb);
r += color.getRed();
g += color.getGreen();
b += color.getBlue();
loops++;
}
}
r = r / loops;
g = g / loops;
b = b / loops;
return new Color(r, g, b);
}

/**
 * Send the data to Arduino
 */
private void sendColors(Color[] leds) {
try {
output.write(0xff);
for (int i = 0; i < SECTIONS; i++) {
output.write(leds[i].getRed());
output.write(leds[i].getGreen());
output.write(leds[i].getBlue());
}
} catch (IOException e) {
e.printStackTrace();
}
}

//Main Loop
private void loop() {
while (true) {
Color[] leds = getColors();
sendColors(leds);
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
Ambilight ambi = new Ambilight();
ambi.initRobot();
ambi.initSerial();
ambi.initOutputStream();
ambi.loop();
}
}

Probably it is possible to achieve better performance by using different color approximation algorithm but maybe next time...

 

Arduino Part

The Arduino part of our project is very simple because of official NeoPixel library that can be found on GitHub. Download it, import to Arduino IDE and we are ready to go. The only thing we have to do is to read the data from serial port(data send by java application) and light up the LEDs on strip.


#include <Adafruit_NeoPixel.h>
#define PIN 6 //PWM pin number
#define LEDS 30 //number of LEDs
#define SECTIONS 10 //number of sections
#define LEDS_PER_SECTION 3 //LEDs per section
#define DELAY 10 //delay for performance reasons
//strip configuration
Adafruit_NeoPixel strip = Adafruit_NeoPixel(LEDS, PIN, NEO_GRB + NEO_KHZ800);
//color arrays
int r[SECTIONS];
int g[SECTIONS];
int b[SECTIONS];

void setup() {
  Serial.begin(9600);
  strip.begin();
  //reduce the brightness if You don't have
  //additional power supply
  strip.setBrightness(128);
  strip.show();
}

void loop() {
  //read the data from serial port
  if(Serial.available() > 30) {
    if(Serial.read() == 0xff) {
      for(int i = 0; i<SECTIONS; i++) {
        r[i] = Serial.read();
        g[i] = Serial.read();
        b[i] = Serial.read();
      }
    }
  }

  //light up next sections
  for(int i=0; i<SECTIONS; i++) {
    strip.setPixelColor(i*LEDS_PER_SECTION, r[i], g[i], b[i]);
    strip.setPixelColor(i*LEDS_PER_SECTION+1, r[i], g[i], b[i]);
    strip.setPixelColor(i*LEDS_PER_SECTION+2, r[i], g[i], b[i]);
  }
  strip.show();
  delay(DELAY);
}

After connecting all things up together I did some tests:


but the best effect is achieved after turning off the lights and playing some movie:

 

I hope You enjoy this short article. Next time I will try to translate more things from our blog(Polish), like virtual steering wheel based on Java and Kinect.

Topics:
java ,arduino ,neopixel

Published at DZone with permission of Slawek Ludwiczak. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}