Ambilight with Arduino and Processing

This idea started long ago, when I saw someone else having made a program to capture the image on the screen, and process it into something an Arduino micro controller could understand.

I then started to make an application in C#, and got it working pretty well (over 25 fps with 8% cpu load). After some time, I got bored with it and didn’t touch it a few months.

Not long ago I saw a post on the arduino forum, about someone else having made ambilight too, but used Processing instead.

This gave me some interests for ambilight again, and I started to look at how I had done it last time. The C# application I made back then was still working, and the Processing application provided by the creator of the new ambilight project, only used the entire screen.

After a bit poking around in the code, and adding some new options, and also dividing the screen up in custom fields, I kinda made the Processing code do exactly the same as my C# code.

This one got four rectangles where it gets the average color within. This makes the processing of the picture a bit faster, because it skips all the pixels outside of the boxes.

The form size is based on the screen (1/5th of the real size) and the rectangles inside are in scale to the settings with border (there are definable borders on all four sides of the screen), and to the size of each box.

So here it is! My first attempt with Processing and Arduino to make ambilight! 🙂

//Developed by Rajarshi Roy heavily modified by bld @ http://captain-slow.dk/
import java.awt.Robot; //java library that lets us take screenshots
import java.awt.AWTException;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
import java.awt.Rectangle;
import java.awt.Dimension;
import processing.serial.*; //library for serial communication

Serial port; //creates object "port" of serial class
Robot robby; //creates object "robby" of robot class

//IMPORTANT!!!
//Put the right screen size in here, if isn't 100% right, the code might give some unexpected results.
int screenW     = 1920;
int screenH     = 1200;

//Define a border on each side of the screen
int borderLeft  = 10;
int borderRight = 10;
int borderTop   = 20;
int borderBot   = 20;

//Size of top left box
int topLeftW    = 350;
int topLeftH    = 450;

//Size of top right box
int topRightW   = 350;
int topRightH   = 450;

//Size of bottom left box
int botLeftW    = 350;
int botLeftH    = 450;

//Size of bottom right box
int botRightW   = 350;
int botRightH   = 450;

//Color adjustments, use this to adjust the color values to match your LEDs
int maxRed      = 255;
int maxGreen    = 255;
int maxBlue     = 125;

//How many pixels to skip while reading
int pixelSpread = 2;

void setup()
{
  port = new Serial(this, Serial.list()[0],115200); //set baud rate
  size(screenW/5, screenH/5); //window size

  try //standard Robot class error check
  {
    robby = new Robot();
  }
  catch (AWTException e)
  {
    println("Robot class not supported by your system!");
    exit();
  }
}

void draw()
{
  int pixel; //ARGB variable with 32 int bytes where
  //sets of 8 bytes are: Alpha, Red, Green, Blue
  float r=0;
  float g=0;
  float b=0;

  //get screenshot into object "screenshot" of class BufferedImage
  BufferedImage screenshot = robby.createScreenCapture(new Rectangle(new Dimension(screenW,screenH)));

  //Calculate top left rectangle
  for(int i = borderLeft; i < (topLeftW + borderLeft); i += pixelSpread)
  {
    for(int j = borderTop; j < (topLeftH + borderTop); j += pixelSpread)     {       pixel = screenshot.getRGB(i,j); //the ARGB integer has the colors of pixel (i,j)       r = r+(int)(maxRed&(pixel>>16)); //add up reds
      g = g+(int)(maxGreen&(pixel>>8)); //add up greens
      b = b+(int)(maxBlue&(pixel)); //add up blues
    }
  }
  r = r / ( (topLeftW / pixelSpread) * (topLeftH / pixelSpread) ); //average red
  g = g / ( (topLeftW / pixelSpread) * (topLeftH / pixelSpread) ); //average green
  b = b / ( (topLeftW / pixelSpread) * (topLeftH / pixelSpread) ); //average blue

  port.write(0xC1); //sync
  port.write((byte)(r)); //red
  port.write((byte)(g)); //green
  port.write((byte)(b)); //blue

  color topL = color(r, g, b);
  fill(topL);
  rect(borderLeft/5, borderTop/5, topLeftW/5, topLeftH/5);

  //Calculate top right rectangle
  for(int i = screenW - (borderRight + topRightW); i < (screenW-borderRight); i += pixelSpread)
  {
    for(int j = borderTop; j < (topRightH + borderBot); j += pixelSpread)     {       pixel = screenshot.getRGB(i,j); //the ARGB integer has the colors of pixel (i,j)       r = r+(int)(maxRed&(pixel>>16)); //add up reds
      g = g+(int)(maxGreen&(pixel>>8)); //add up greens
      b = b+(int)(maxBlue&(pixel)); //add up blues
    }
  }
  r = r / ( (topRightW / pixelSpread) * (topRightH / pixelSpread) ); //average red
  g = g / ( (topRightW / pixelSpread) * (topRightH / pixelSpread) ); //average green
  b = b / ( (topRightW / pixelSpread) * (topRightH / pixelSpread) ); //average blue

  port.write(0xC2); //sync
  port.write((byte)(r)); //red
  port.write((byte)(g)); //green
  port.write((byte)(b)); // blue

  color topR = color(r, g, b);
  fill(topR);
  rect(screenW/5 - ((topRightW/5)+(borderRight/5)), borderTop/5, topRightW/5, topRightH/5);

  //Calculate bottom left rectangle
  for(int i = borderLeft; i < (botLeftW + borderLeft); i += pixelSpread)
  {
    for(int j = screenH - (botLeftH + borderBot); j < (screenH - borderBot); j += pixelSpread)     {       pixel = screenshot.getRGB(i,j); //the ARGB integer has the colors of pixel (i,j)       r = r+(int)(maxRed&(pixel>>16)); //add up reds
      g = g+(int)(maxGreen&(pixel>>8)); //add up greens
      b = b+(int)(maxBlue&(pixel)); //add up blues
    }
  }
  r = r / ( (botLeftW / pixelSpread) * (botLeftH / pixelSpread) ); //average red
  g = g / ( (botLeftW / pixelSpread) * (botLeftH / pixelSpread) ); //average green
  b = b / ( (botLeftW / pixelSpread) * (botLeftH / pixelSpread) ); //average blue

  port.write(0xC3); //sync
  port.write((byte)(r)); //red
  port.write((byte)(g)); //green
  port.write((byte)(b)); //blue

  color botL = color(r, g, b);
  fill(botL);
  rect(borderLeft/5, screenH/5 - (botLeftH/5 + borderBot/5), botLeftW/5, botLeftH/5);

  //Calculate bottom right rectangle
  for(int i = screenW - (borderRight + botRightW); i < (screenW-borderRight); i += pixelSpread)
  {
    for(int j = screenH - (botRightH + borderBot); j < (screenH - borderBot); j += pixelSpread)     {       pixel = screenshot.getRGB(i,j); //the ARGB integer has the colors of pixel (i,j)       r = r+(int)(maxRed&(pixel>>16)); //add up reds
      g = g+(int)(maxGreen&(pixel>>8)); //add up greens
      b = b+(int)(maxBlue&(pixel)); //add up blues
    }
  }
  r = r / ( (botRightW / pixelSpread) * (botRightH / pixelSpread) ); //average red
  g = g / ( (botRightW / pixelSpread) * (botRightH / pixelSpread) ); //average green
  b = b / ( (botRightW / pixelSpread) * (botRightH / pixelSpread) ); //average blue

  port.write(0xC4); //sync
  port.write((byte)(r)); //red
  port.write((byte)(g)); //green
  port.write((byte)(b)); //blue

  color botR = color(r, g, b);
  fill(botR);
  rect(screenW/5 - ((topRightW/5)+(borderRight/5)), screenH/5 - (botLeftH/5 + borderBot/5), botRightW/5, botRightH/5);
}

And the Arduino code, this example will use the values from the box in the top right corner.

int red, green, blue; //red, green and blue values

#define RedPin 9 //Red pin 9
#define GreenPin 10 //Green pin 10
#define BluePin 11 //Blue pin 11

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  if (Serial.available() >= 4)
  {
    if(Serial.read() == 0xC2)
    {
      red = Serial.read();
      green= Serial.read();
      blue = Serial.read();

      analogWrite(RedPin, red);
      analogWrite(GreenPin, green);
      analogWrite(BluePin, blue);
    }
  }
}

Which box is used, is defined by 0xC3, the options here are 0xC1, 0xC2, 0xC3, and 0xC4. If you use an Arduino UNO (or any other with the 328 chip), you can show two channels at the same time. With a MEGA you can show all four channels at the same time, because of more PWM capable pins.

 

A newer version also got a top center, bottom center, and FPS counter.

8 comments

  • Hi!!

    Nice work!!

    I get here from the arduino forum.
    I am REALLY interested in this project. I am going to make my own ambilight.

    I have some questions I hope you can solve:
    – Arduino MEGA is very expensive, but I want more than 2 channels… is there a way I can get mora than 2 channels EASYLY with the Arduino UNO?
    – Why don’t you just make a main in java so you have an easier app?
    – Isn’t it too much processor-cost to be doing the loop all the time?

    Thanks a lot!!

    • No, there are no way of doing it easy with the uno.

      Because I can do C# and Processing, so that is why I did it in those languages.

      And it is taking around 8% cpu on one thread of my i7 to run it 25 times pr second, so it isn’t that big a deal.

  • Hi!!

    I just made it, but finally, I made a system with 25 RGB leds. I bought an Arduino Mega, but I used SPI with some shift registers with PWM.

    I used processing too, I asked about why not using a java app since processing uses java, and the code you write in processing would work as a java main modifying it just a little bit.

    Right now I have everything a bit “hardcoded” I need to clean my code and improve it, I have seen some libraries I could use to improve my speed. I just have an Intel core 2 duo Q6600, and getting the average color of 25 areas is more processor burning than averaging just 4 areas.

    My main problem is that I have no time!! So I go slowly

  • Hi,
    you really did some great work!
    I’m experimenting with a RGB LED for a few days but couldn’t get the red one working.
    Looks like you disabled it by a comment (e.g. line 78 in processing code – again a few lines later).
    Just wanted to let you know 😉

  • i just loved your code! would you please post the code for 2 channels (left and right)?

  • Hi,
    Does the robot class capture hardware accelerated content as well? I.e. games, visualizations of audio players,…
    The system I am currently using for ambilight does not, which is why I am looking for something else.
    Thanks!

  • Hi,

    can you tell me how to modifiy the Arduino code to use it with more boxes of the processing code ?
    I tried it by myself, it worked, but with big delay in the botom left and botom right Box, i can´t explain why?!
    Also i would apreciate if you upload the new code with 5 boxes and fps counter.

    Thanks!
    Its perfectly what i searched for!

Leave a Reply