最近我开始使用 Arduino 开发板和 Java 来构建一些有用的东西,我想与大家分享我的最后一个项目。
简而言之,我决定为我的 PC 构建非常简单的流光溢彩,以便在我看电影或玩游戏时实时更改显示器后面的颜色。
我们需要什么:
-
Arduino开发板
-
新像素条
-
15分钟
您可以找到许多不同的 NeoPixel 灯带,但我们需要像 这样的 简单 30 LED RGB 灯带。由于能耗低,我们不需要额外的电源。如果您尝试以最大亮度点亮所有 LED,则可能会出现问题。
电路
neopixel strip 的使用非常简单。它只有 3 根电缆:VCC、GND 和 PWM。将它们连接到电路板上的适当 PIN,我们就设置好了。当您运行示例 Arduino 应用程序时,您将看到如下效果:
Java部分
首先,我们必须创建一个简单的应用程序,它将根据屏幕上显示的内容计算平均颜色。为此,我们将屏幕分成多个部分,计算每个部分的平均颜色,然后我们准备将数据发送到 Arduino。为了与开发板通信,我们使用 RXTX 库,可 在此处 找到。
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();
}
}
可能通过使用不同的颜色近似算法可以获得更好的性能,但也许下次......
Arduino部分
我们项目的 Arduino 部分非常简单,因为可以在 GitHub 上找到官方 NeoPixel 库。下载它,导入到 Arduino IDE,我们就可以开始了。我们唯一要做的就是从串行端口读取数据(由 java 应用程序发送的数据)并点亮灯条上的 LED。
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();
}
}
将所有东西连接在一起后,我做了一些测试:
但最好的效果是在关灯播放电影后实现的:
我希望你喜欢这篇短文。下次我会尝试从 我们的博客 (波兰语) 翻译更多的东西,比如基于 Java 和 Kinect 的虚拟方向盘。