使用 Java 和 Arduino 创建流光溢彩

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 63w+ 字,讲解图 2808+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2200+ 小伙伴加入学习 ,欢迎点击围观

最近我开始使用 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 的虚拟方向盘。

相关文章