做网站步骤社群营销方案
点矩阵LED显示屏很有趣。 哪个开发人员不喜欢与空白的灯光一起玩? 我最近将Freetronics的32 x 16点矩阵显示器与Node.js 配对 ,并使其能够显示黑白PNG图像。 在本文中,我将解释其全部工作原理。
点矩阵LED显示器(也称为点矩阵显示器或DMD)是带有LED灯网格的显示器,您可以打开和关闭它们以显示文本和形状。 其中一些具有多种颜色,而另一些仅是一种颜色。 我们将在此演示中使用的颜色只有一种,因此我们仅限于黑白图像。 重要的是要注意– LED显示屏与LCD显示屏完全不同。 LCD使用花式晶体,并用于VCR,时钟,计算器等的显示。 几周前,我写了一篇文章, 关于使用Node.js在Arduino LCD上显示Web API 。 如果您想将两者进行比较,请看一下。
此特定演示需要Freetronics 32×16点矩阵显示器,因为它依赖于Freetronics DMD库。
演示代码
如果您热衷于获取代码并自己尝试一下,可以在GitHub上找到它。
Freetronics DMD库
通过Freetronics DMD库可以在我们的LED点矩阵显示器上绘制线条,形状和文本。 要使用它,请完成以下步骤:
- 从其GitHub存储库下载DMD库。
- 将这些文件复制到您自己的文件夹名称下的
/Arduino/libraries/
文件夹中。 对于我的Mac,我将其放在/Users/username/Documents/Arduino/libraries/DMD-master
的文件夹中。 - 下载TimerOne库 ,并将其也放入您的
/Arduino/libraries/
文件夹中。 例如,对于Mac用户,/Users/username/Documents/Arduino/libraries/TimerOne-r11
。
我们的Arduino素描
在DMD上显示元素的大多数功能将在Arduino草图代码中实现。 草图代码将密切注意串行端口上的消息,并根据这些消息更改显示的显示。
草图从我们的包含和常量开始。 我们包含SoftwareSerial.h
以允许我们访问串行端口并定义DMD的宽度和高度(在本例中为32×16)。 BUFLENGTH
存储我们拥有的灯的数量,因为这是我们要发送给Arduino的消息的最大大小。 在我们的例子中,它是32乘以16(即512)。
#include <SoftwareSerial.h>#define SCREEN_WIDTH 32#define SCREEN_HEIGHT 16#define BUFLENGTH 512
接下来,我们有针对Freetronics DMD的配件。 这些都应该可以从我们之前复制到Arduino库文件夹中的文件中获得。
#include <SPI.h>#include <DMD.h>#include <TimerOne.h>
然后,我们有两个常量DISPLAYS_ACROSS
和DISPLAYS_DOWN
,用于定义我们连接在一起的LED显示屏的数量。 我将假设您与我处于相同的情况,并且只显示一个,因此这两个都等于一个。 然后,将其传递到DMD库中,使用DMD dmd()
使其运行。
#define DISPLAYS_ACROSS 1#define DISPLAYS_DOWN 1DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
下一部分代码包含在DMD样本中。 我们调用此函数来获取DMD库,以按设定的时间间隔刷新显示。 我们将该间隔定义得更远一些。
void ScanDMD() { dmd.scanDisplayBySPI();}
然后,我们定义最后两个变量。 这两个与通过串行端口接收消息有关。 首先, buf[BUFLENGTH]
存储应打开和关闭LED的串行端口消息的缓冲区。 其次, bufCount
将用于存储该缓冲区中还有多少字节需要读出。
char buf[BUFLENGTH];int bufCount;
我们的setup()
函数使用常量和已定义的库开始整个过程。 首先在端口57600上侦听串行端口消息。
void setup() {Serial.begin(57600);
然后,我们使用前面包含的TimerOne库初始化计时器。 我们告诉它从四个毫秒开始倒数。 在Freetronics的示例中,他们建议将其设置为不超过5毫秒,以避免在我们的显示器上闪烁。
Timer1.initialize(4000);
然后,我们将其设置为在计时器到期时运行ScanDMD()
函数,从而刷新显示。
Timer1.attachInterrupt(ScanDMD);
最后,在setup()
函数中,通过将true
传递给dmd.clearScreen()
函数来清除显示器上的所有像素。 如果将false传递给此函数,则每个像素都会打开!
dmd.clearScreen(true);
在Arduino的loop()
函数中,我们要注意串行端口上的所有消息。 我们看着看到有多少字节可用于从串行端口读取。 如果有可用的字节,那么将有一条消息流通过,然后运行serialParse()
函数。
void loop() {if (Serial.available() > 0) {serialParse();}}
在serialParse()
内部,我们将bufCount
设置为-1
以重置计数值。 然后,我们使用BUFLENGTH
Serial.readBytesUntil()
从该数组(我们的BUFLENGTH
)中读取512个元素。 如果有一个\n
字符,它也会停止读取数组。 此处的主要目的是使串行消息保持在我们LED光栅的长度之内。
void serialParse(void) {bufCount = -1;bufCount = Serial.readBytesUntil('\n', buf, BUFLENGTH);
如果缓冲区中确实有一条消息,则将其发送到parseBuffer()
,它将进行解析并将其显示在屏幕上。
if (bufCount > 0) {String message = String(buf);parseBuffer(message);}}
在parseBuffer()
函数中,我们先清除屏幕,以准备parseBuffer()
新的图形将其点亮。 然后,我们创建一个整数i
来跟踪正在读取的数组中的哪个位置。
然后,我们通过x
循环直到SCREEN_WIDTH
从左到右遍历缓冲区中的每个字符,并通过y
向下循环直到SCREEN_HEIGHT
。 这会将我们的一维数组读出到DMD的二维显示中。 对于每个字符,我们检查它是否为'1'
。 如果是这样,那么我们将在x
和y
处将显示器插入该LED。 这将用于我们图像的黑色部分。 如果它不是'1'
,那么我们继续下一个位置,依此类推。 最终,绘制出我们的整个图像。
void parseBuffer(char* buf) {dmd.clearScreen(true);int i = 0;for (byte y = 0; y < SCREEN_HEIGHT; y++) {for (byte x = 0; x < SCREEN_WIDTH; x++) {if ((char)buf[i] == '1') {dmd.drawFilledBox(x, y, x, y, GRAPHICS_NORMAL);}i++;}}}
这涵盖了Arduino的工作原理–如果现在在连接了LED的情况下在Arduino上运行该代码,那么它什么也不显示。 要在点矩阵显示器上显示任何内容,我们需要节点代码通过串行端口发送消息。
我们的节点代码
我们的JavaScript首先需要两个重要的npm模块。 serialport
是允许我们通过串行端口向Arduino发送消息的工具,而png-js
是在PNG图像中读取的信息。
var SerialPort = require('serialport').SerialPort,PNG = require('png-js'),
然后,我们设置串行端口消息传递。 我们在变量serialPort
设置了SerialPort
对象,并设置了Arduino连接到的端口以及将在其上侦听串行端口消息的波特率。
serialPort = new SerialPort('/dev/tty.usbmodem1431', {baudrate: 57600}),
如果不确定Arduino连接到哪个端口(例如,我有'/dev/tty.usbmodem1431'
),请将其连接到PC,打开Arduino IDE,转到“工具”>“端口”,然后查看选择了哪个端口。
波特率可以是个人喜好,如果您真的不关心它使用的是哪种波特率,请继续使用示例中已经获得的波特率。
然后,我们初始化一个名为serialMessage
的字符串变量,该变量将存储将通过串行端口发送的完整的1和0字符串。
serialMessage = '';
我们的serialPort
对象的事件侦听器为'open'
,当定义的串行端口打开并且可以从我们的JavaScript访问时,它将响应。 在这种情况下,我们运行console.log
以便可以确定串行端口消息传递一切正常。
serialPort.on('open', function() {console.log('Serial port open');
一旦知道串行端口已准备好发送消息,就可以运行PNG.decode()
函数以读取PNG图像文件。 在我们的演示中,我们在名为sitepointlogo-withsmile.png
Node文件的同一文件夹中有一个PNG图像,因此我们传入该文件名。 然后,我们有了回调函数,该函数通过data
变量为我们提供PNG文件的数据。
PNG.decode('sitepointlogo-withsmile.png', function(data) {// We'll read in data here
从我们的PNG.decode()
函数返回的data
将是一个从0到255的值的数组。它们对每个像素进行迭代,每个像素有一系列四个项-红色,绿色,蓝色和alpha值。 我们不会在演示中使用alpha值,因为我们只处理黑白图像,但是理论上您可以这样做。 示例数组如下所示:
[255,255,255,255,0,0,0,255]
上面的数组代表一个白色像素255,255,255,255
和一个黑色像素0,0,0,255
。 对于每个像素,这一过程不断重复,直到我们代表了整个图像为止。
在回调函数中,我们将serialMessage
重置为空字符串,然后开始以四个为一组循环遍历data
数组。 我们设置red
, green
和blue
的局部变量以匹配每个像素的各自值。
serialMessage = '';for (i = 0; i < data.length; i+=4) {var red = data[i],green = data[i+1],blue = data[i+2],
为了能够处理不完全是黑色或白色的灰度值,我们还进行了亮度检查。 下面的函数确定像素颜色的暗或亮:
luminance = ((red * 299) + (green * 587) + (blue * 114)) / 1000;
如果该值大于150,则假定它是一种漂亮的浅色,并将其设置为0
(白色)。 否则,我们将其设置为1
并将其设为黑色。 我们将任一值附加到serialMessage
字符串。
if (luminance > 150) {serialMessage += '0';} else {serialMessage += '1';}}
一旦我们遍历了每个像素并分配了一个零或一个来表示它,我们就使用serialPort.write()
通过串行端口发送该消息。 实际上,读取图像并进行迭代的整个过程似乎比显示器准备好接收图像所需的时间要快,因此我将其放在setTimeout
以使其在运行之前等待两秒钟。
setTimeout(function() {serialPort.write(serialMessage);}, 2000);
运行我们的演示
如果您上传草图,将显示器连接到Arduino并通过node serialDMD.js
运行节点服务器代码(请记住首先要npm install
所有内容),您应该看到它像PNG文件一样亮起:
结论
您可以通过很多方法对此进行扩展。 它是一个节点服务器,因此您可以将其连接到API并显示通过它的图像。 您可以根据一天中的时间,家里的互联网连接设备的状态,天气或其他许多因素,使它显示不同的图像!
如果您将这个想法扩展为真正简洁的内容,请在评论中让我知道,或者在Twitter( @thatpatrickguy )上与我联系,我想看看!
From: https://www.sitepoint.com/displaying-images-on-a-dot-matrix-led-display-with-node-js/