如何通过用于户外广告和Arduino的LED模块制作非常醒目的告密者

如何从P10和Arduino LED模块制作非常引人注目的告密者。


目的 :快速将大型P10 LED矩阵(16x32厘米)连接到PC或其他设备,通过动态改变显示的信息,将所有这些变成非常引人注目的明亮提示器。 这种事情有很多应用,并且如实践所示,它引起了人们的注意。 试想一下,现在每个人都可以肯定地知道您的空调正在工作并且门应该关闭了!



背光不是从窗户而是从面板;)


一旦查看了Arduino 8x8 LED的沉闷矩阵,并付出了代价,我感到很难过。 决定投入现成的户外广告运行线,当它们全部成为标准且所有人都不了解通过外部端口动态更新信息时,我感到惊讶。 深入研究发现,在所有此类产品中都使用了典型的LED模块(LED矩阵)。


从简单的爬行线到大型电视,所有人都看到了户外广告的亮屏。 这些屏幕由一系列此类LED模块组装而成,并由特殊控制器控制,其价格与屏幕尺寸成正比。




这些LED模块非常便宜。 大约6美元,价格取决于大小和颜色。 使用控制器更加复杂。 最简单的价格可与单个LED面板媲美。 但是,它们中的大多数都是“量身定制”的,以演示预先准备的“演示”的方式工作,并且无法动态更改显示的信息。 我承认我只是简要地了解了最简单的控制器的功能,但是事实证明这足以理解-在通用Arduino控制器上执行必要的操作会更便宜,更快捷。 这将使您可以相当平静地连接多个模块。 但是我们将从一个开始。


LED模块(P10)看起来像这样:



http://digital-wizard.net/avr_projects/p10_led_display_panel_interface


实际上,这只是焊接到移位寄存器输出的LED矩阵,我们不会进入电路的丛林,模块都是标准的,只是尺寸不同。 P10表示两个相邻二极管之间的10mm。 有单色,二色甚至RGB面板。 对于那些对细节感兴趣的人,已经有一篇类似的文章,但是偏向较低的水平。


硬体


对于那些想要快速获得“感觉”结果的人,您将需要:


  1. 一个LED矩阵。
  2. Arduino的 (我使用了mini,但是在不使用额外的适配器与PC进行通信的情况下,它将更加方便,nano)。
  3. 5V / 3A电源。 矩阵非常虚幻,如果您点亮所有二极管,则需要大量功率。
  4. 连接电缆 (通常它带有一个矩阵。)
  5. 渴望完成工作。

我们将进行整体设计,只需将其插入电源插座和PC即可显示我们的宝贵信息 (例如比特币汇率)


有必要从矩阵中取出回路,并通过一条简单的电路将其切成一半,然后焊接到Arduino。



如果您使用Arduino Mini或UNO,则需要类推焊接至相应的引脚。


如果由于某种原因没有电缆,则可以用MIDI电缆代替它,该电缆将MIDI连接器(旧计时器的选件)连接到旧声卡中的板上,或者用两个8针的杜邦插头(母亲)代替。 原则上,连接器是标准的,看起来很容易。



但是在十分钟之内,我得到了以下设计,另外一个USBtoUART适配器悬挂在不使用Arduino Mini的情况下将没有。




硬件已准备就绪,您只需要连接一个额外的5V / 3A PSU即可为矩阵供电。 为此,它有一个单独的连接器,加号为正,减号为负。 相反,它可以,但是不起作用。 尽管很奇怪,但是由于每个人都知道电子像往常一样从正流向负。 而且,如果我们考虑到电子带负电,那么根本不知道为什么电子流向负极...;)他们认为事情变短了。



我希望您能更可靠地连接电源,并且我的方法仅在有大量备用电源时才适用。 Arduin可以用螺栓固定( 不是自攻螺丝! )到面板本身,面板上有安装螺纹孔。


就是这样,让众神DIY原谅我,但是今天没有繁文tape节。 在如此毫无价值的事情上花费如此宝贵的资源是不值得的。


软件部分


如果您认为有必要从三个字母(SPI接口)中了解如何编写此程序,那么我会让您失望的-一切都简单得多。 与往常一样,所有自行车都是在我们面前发明的,而且不止一次。 其实就像这篇文章 。 有现成的库DMDDMD2,而图纸归结为一个事实,您只需在草图中指出要输出的内容,方式和位置,那么一切都是利润!


这些库的方形形状进行了一些测试之后,我得出的结论是,尽管那里有更多的选项和亮度控制,但是在DMD2中滚动文本看起来“很糟糕”。 结果,他选择了第一个,并记住“您是程序员”向其添加了原始的亮度控制。 这里值得一提的是,这些矩阵非常明亮。 调试结束时,它将烧毁角膜。


通常,您需要做的就是将草图上传到板上,但是它是从该目录中解压缩档案的,因为我做了一点修改 档案 某些库位于同一位置。 我不记得添加了亮度控制和稍微超频的SPI,也许还有其他东西。 拉巴特? 请勿触摸!


为了更加清晰,我添加了一个18v20温度传感器。 如果您不连接,则不会显示温度。 根据我的想法,此处将显示以克为单位的一定重量,但您只能显示一个数字。 要更改从下面滚动的文本,您只需要使用一种特殊的格式(一种ESC序列)将其“加载”到Arduino COM端口即可。 随附的是一个Python脚本,该脚本仅将当前时间发送到面板。


命令格式:


  1. 0x1B(ESC)+ 0x40(“ @”)//标识,返回设备ID字符串“ P10_DISPLAY_V01 \ r \ n”
  2. 0xOC(CLR)//清除屏幕。
  3. 0x1F(US)+ 0x58(“ X”)+ 0x80 /​​/设置屏幕的亮度,最大亮度为0x80。
  4. 0x1F(美国)+ 0x52(“ B”)+字符串//设置要滚动的底行。
  5. 0x1F(美国)+ 0x0A(“ LF”)+ 0x31、0x32、0x33、0x34、0x35 //传递显示在顶部“ 12345”的五位数

我没有找到完成的字体,因此不得不绘制俄语字体5 * 7。 也许是自行车,但我们是否总是理性地做每一件事?


草图:


代号
//====================== OneWire ==================================== #include <OneWire.h> OneWire ds2 (2); String res = ""; byte type_s; byte data[12]; byte addr_ds2[8]; bool temp_sensor_2_found = false; float temp1 = 999; //=================================================================== #include <SPI.h> #include "DMD.h" #include "TimerOne.h" #include "SystemFont5x7rus.h" #define CLR 0x0C #define US 0x1F #define ESC 0x1B #define LF 0x0A static const char ID[] = "P10_DISPLAY_V01\r\n"; // Device ID #define DISPLAYS_ACROSS 1 #define DISPLAYS_DOWN 1 DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN); //  ,     . #define max_char1 6 #define max_char2 176 unsigned char message1[max_char1] = {0x20,0x30,0x30,0x30,0x30,0x00}; // stores you message unsigned char message2[max_char2]; // stores you message unsigned char r_char; byte index1 = 4; byte index2 = 176; int i; long scrollSpeed = 25; char test[] = { 0x84,0x80,0x20,0x87,0x84,0x90,0x80,0x82,0x91,0x92,0x82,0x93,0x85,0x92,0x20,0x92, 0x8E,0x20,0x81,0x8B,0x80,0x83,0x8E,0x84,0x80,0x90,0x9F,0x20,0x97,0x85,0x8C,0x93, 0x20,0x8C,0x9B,0x20,0x8D,0x85,0x91,0x8C,0x8E,0x92,0x90,0x9F,0x20,0x8D,0x88,0x20, 0x8D,0x80,0x20,0x97,0x92,0x8E,0x20,0x88,0x20,0x82,0x8E,0x8F,0x90,0x85,0x8A,0x88, 0x20,0x82,0x91,0x85,0x8C,0x93,0x21,0x00}; //============================================================================= bool get_sensor(byte addr[8], OneWire ds) { //    if (! ds.search (addr)) { ds.reset_search (); delay (250); return false; } //        if (OneWire::crc8(addr, 7) != addr[7]) { Serial.println("get_sensor:CRC is not valid!"); return false; } //    switch (addr[0]) { case 0x10: //Chip = DS18S20 type_s = 1; break; case 0x28: //Chip = DS18B20 type_s = 0; break; case 0x22: //Chip = DS1822 type_s = 0; break; default: Serial.println("get_sensor:Device is not a DS18x20 family device."); return false; } return true; } //============================================================================= double get_temp(byte addr[8], OneWire ds) { double celsius; ds.reset (); ds.select (addr); //   ds.write (0x44, 1); //  ,     delay (750); ds.reset (); ds.select (addr); ds.write (0xBE); //     for (int i = 0; i < 9; i++) { //     data[i] = ds.read (); } //        ,           int raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); if (cfg == 0x00) raw = raw << 3; else if (cfg == 0x20) raw = raw << 2; else if (cfg == 0x40) raw = raw << 1; } celsius = (double)raw / 16.0; return celsius; }; //============================================================================= void massage2BufClear () { for(i=0; i<max_char2; i++) { message2[i] = '\0'; } index2=0; } //-------------------------------------------------------------- void massage1BufClear () { message1[0] = 0x20; message1[1] = 0x30; message1[2] = 0x30; message1[3] = 0x30; message1[4] = 0x30; message1[5] = 0x00; index1=0; } //-------------------------------------------------------------- void massage1Normalization () { char buf[5]; buf[0] = 0x20; buf[1] = 0x30; buf[2] = 0x30; buf[3] = 0x30; buf[4] = 0x30; int char_count = index1; for(i = char_count - 1; i >= 0; i--) { buf[i] = message1[i]; } massage1BufClear(); for(i = char_count - 1; i >= 0; i--) { message1[i+5-char_count] = buf[i]; } } //------------------------------------------------------------------ void ScanDMD() { dmd.scanDisplayBySPI(); } //------------------------------------------------------------------ void setup(void) { Timer1.initialize( 500 ); Timer1.attachInterrupt( ScanDMD ); dmd.clearScreen( true ); Serial.begin(9600); strcpy(message2,test); dmd.brightness = 70; temp_sensor_2_found = get_sensor(addr_ds2, ds2); dmd.selectFont(SystemFont5x7); } //------------------------------------------------------------------ void loop(void) { dmd.drawMarquee(message2 ,index2,(32*DISPLAYS_ACROSS)-1 ,8); if (temp_sensor_2_found) // !!!!!!! { // !!!!!!! res = String(get_temp(addr_ds2, ds2)); // !!!!!!! res.toCharArray(message1, 5); // !!!!!!! message1[4] = 0xF8; // !!!!!!! } // !!!!!!! long start=millis(); long timer=start; boolean ret=false; while(!ret) { if ((timer + scrollSpeed) < millis()) { dmd.drawFilledBox( 0,0,31,7, GRAPHICS_INVERSE); ret=dmd.stepMarquee(-1,0); timer=millis(); dmd.drawString( 1, 0, message1, 5, GRAPHICS_NORMAL ); if(Serial.available()) break; } } } //----------------------------------------------------------------------------------- void serialEvent() { delay(25); // Wait all data r_char = Serial.read(); if(r_char < 0x20) { switch (r_char) { case ESC: r_char = Serial.read(); if(r_char=='@') { Serial.write(ID); } break; case CLR: massage1BufClear(); massage2BufClear(); break; case US: r_char = Serial.read(); if(r_char=='X') { dmd.brightness = Serial.read(); } if(r_char=='B'){ massage2BufClear(); while(Serial.available() > 0) { r_char = Serial.read(); if(index2 < (max_char2-1)) { if(r_char > 0x1F) { message2[index2] = r_char; index2++; } } } } if(r_char==LF){ massage1BufClear(); while(Serial.available() > 0) { r_char = Serial.read(); if(index1 < (max_char1-1)) { if((r_char > 0x29) and (r_char < 0x3A)) { message1[index1] = r_char; index1++; } } } massage1Normalization(); } dmd.drawFilledBox( 0,8,31,15, GRAPHICS_INVERSE); break; default: break; } } while (Serial.available()) Serial.read(); dmd.writePixel(0,0,GRAPHICS_NORMAL,1); } 

全亮度操作:



以人类的亮度工作:



使用Python脚本从PC动态加载时间:



滚动的所有不规则都是由于在相机上拍摄而引起的,对于正常距离的眼睛来说,一切看起来都非常平滑且可读。


值得注意的是,完全可以自由串联几个模块 ,只需在草图中指出即可得到更大的屏幕。 限制仅在一定数量的面板之后才开始并且它们与Arduino及其SPI接口的有限性能有关。 感谢ElectricFromUfa对面板的详细介绍以及更多信息!


也许将来我会在STM32F103C8T6和ESP-12F上做同样的事情,那里的一切应该都更快。


参考文献:


1.使用草图和相关内容链接到档案。
2.另一个链接到BitBucket


Source: https://habr.com/ru/post/zh-CN404867/


All Articles