Como criar um informante muito visível a partir de um módulo LED para publicidade ao ar livre e Arduino

Como criar um informante muito notável do módulo LED P10 e Arduino.


Objetivo : Conecte rapidamente uma grande matriz de LEDs P10 (16x32cm) a um PC ou outro dispositivo, transformando tudo isso em um informador muito perceptível e brilhante com uma alteração dinâmica das informações exibidas. Existem muitas aplicações para isso e, como mostra a prática, atrai a atenção. Imagine, agora todos saberão com certeza que o seu ar condicionado está funcionando e a porta deve estar fechada!



A luz de fundo não é da janela, mas do painel;)


Uma vez, olhei para a matriz opaca de LEDs 8x8 do Arduino e, ao custo deles, fiquei triste. Decidiu-se pular para linhas de corrida prontas para publicidade ao ar livre e qual foi a minha surpresa quando todas se mostraram padrão e tudo como se não soubesse nada sobre a atualização dinâmica de informações através de uma porta externa. Indo mais fundo, verificou-se que em todos esses produtos são utilizados módulos típicos de LED (matrizes de LED).


Todo mundo viu as telas brilhantes de publicidade ao ar livre, de simples linhas rasteiras a enormes TVs. Essas telas são montadas a partir de uma cadeia de módulos LED e controladas por controladores especiais, cujo preço aumenta em proporção direta ao tamanho da tela.




Esses módulos de LED são bem baratos. Cerca de US $ 6, o preço depende do tamanho e cor. É mais complicado com controladores. O preço mais simples é comparável a um único painel de LED. No entanto, a maioria deles é "adaptada" para funcionar no modo de demonstração de "apresentações" pré-preparadas e não é capaz de alterar dinamicamente as informações exibidas. Admito que me familiarizei brevemente com a funcionalidade dos controladores mais simples, mas isso foi o suficiente para entender - é mais barato e mais rápido fazer as coisas necessárias no controlador universal do Arduino. Isso permitirá que você conecte com bastante calma vários módulos. Mas vamos começar com um.


O módulo LED (P10) é mais ou menos assim:



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


De fato, isso é apenas uma matriz de LEDs que são soldados às saídas dos registros de turnos; não entraremos na selva de circuitos; os módulos são todos padrão e diferem apenas em tamanho. P10 significa que entre dois diodos adjacentes 10 mm. Existem painéis monocromáticos, bicolores e até RGB. Para os interessados ​​em detalhes ,existe um artigo semelhante, mas com um viés para um nível inferior.


Hardware


E para aqueles que desejam obter rapidamente um resultado que você possa "sentir", será necessário:


  1. Uma matriz de LED.
  2. Arduino (Usei mini, mas será mais conveniente nano não usar adaptadores adicionais para comunicação com um PC).
  3. Fonte de alimentação para 5V / 3A. A matriz é voraz, se você iluminar todos os diodos, precisará de muita energia.
  4. Cabo de conexão (Geralmente, ele vem com uma matriz.)
  5. O desejo de terminar o trabalho.

Faremos um design monolítico que é apenas conectado a uma tomada e a um PC para exibir nossas informações preciosas (por exemplo, taxa de Bitcoin) .


É necessário retirar o loop da matriz, cortando-o em meia solda em um circuito simples para o Arduino.



Se você usar o Arduino Mini ou o UNO, precisará soldar os pinos correspondentes por analogia.


Se, por algum motivo, você não tiver um cabo, substitua-o por um cabo MIDI que conecte o conector MIDI (opção para veteranos) à placa em placas de som antigas ou substitua-o por dois plugues Dupont (mãe) com 8 pinos. Em princípio, o conector é padrão, parece bastante fácil.



Mas em 10 minutos obtive o design a seguir, um adaptador USBtoUART adicional oscila que você não terá se não usar o Arduino Mini.




O hardware está pronto, você só precisa conectar um PSU 5V / 3A adicional para alimentar a matriz. Para fazer isso, ele possui um conector separado, mais para mais, menos para menos. Pelo contrário, pode, mas não vai funcionar. Embora seja estranho, já que todo mundo sabe que os elétrons, como sempre, fluem de mais para menos. E se levarmos em conta que os elétrons estão carregados negativamente, não está claro por que eles fluem para o pólo negativo ...;) Eles pensaram em coisas mais curtas.



Espero que você conecte a fonte de alimentação de maneira mais confiável, e meu método só é adequado se você tiver muitas fontes de alimentação sobressalentes. O Arduin pode ser parafusado ( Não é um parafuso autorroscante! ) para o próprio painel, o painel possui orifícios roscados de montagem.


Isso é tudo, deixe os deuses DIY me perdoarem, mas hoje não havia fita azul. Não vale a pena gastar um recurso tão valioso em coisas sem valor.


Parte do software


Se você pensou que seria necessário entender como programar essa coisa a partir de três letras (interface SPI), posso desapontá-lo - tudo é muito mais simples. Como sempre, todas as bicicletas são inventadas diante de nós e mais de uma vez. Na verdade, como este artigo . Existem bibliotecas prontas, DMD e DMD2, e o desenho se resume ao fato de que no esboço você precisa apenas indicar o que, como e onde produzir, tudo é lucro!


Tendo brincado um pouco com a forma quadrada dessas bibliotecas, cheguei à conclusão de que a rolagem de texto no DMD2 parece "triste", embora haja mais opções e controle de brilho por lá. Como resultado, ele decidiu o primeiro e lembrou que "você é um programador" adicionou um controle primitivo de brilho a ele. Aqui vale ressaltar que essas matrizes brilham muito intensamente. Ao final da depuração, ele queimará a córnea.


Em geral, tudo o que você precisa fazer é carregar o esboço no quadro, mas é nesse diretório que você descompactará meu arquivo, pois eu o modifiquei um pouco arquivo algumas bibliotecas que estão no mesmo lugar. Adicionado controle de brilho e SPI ligeiramente com overclock, talvez outra coisa, não me lembro. Rabatet? Não toque!


Para maior clareza, adicionei um sensor de temperatura 18v20. Se você não conectar um, a temperatura simplesmente não será exibida. De acordo com a minha ideia, um certo peso em gramas será exibido lá, mas você pode exibir apenas um número. Para alterar o texto exibido abaixo, basta carregá-lo na porta COM do Arduino usando um formato especial, um tipo de sequência ESC. Em anexo está um script Python que simplesmente envia a hora atual para o painel.


Formato de comando:


  1. 0x1B (ESC) + 0x40 ("@") // Identificação, retorna o ID da string do dispositivo "P10_DISPLAY_V01 \ r \ n"
  2. 0xOC (CLR) // Limpe a tela.
  3. 0x1F (EUA) + 0x58 ("X") + 0x80 // Defina o brilho da tela, brilho máximo de 0x80.
  4. 0x1F (US) + 0x52 ("B") + String // Defina a linha inferior que será rolada.
  5. 0x1F (EUA) + 0x0A ("LF") + 0x31, 0x32, 0x33, 0x34, 0x35 // Passe o número de cinco dígitos exibido na parte superior "12345"

Não encontrei o finalizado e tive que desenhar a fonte russa 5 * 7. Provavelmente uma bicicleta, mas sempre fazemos tudo racionalmente?


Esboço:


Código
//====================== 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); } 

Operação de brilho total:



Trabalhe com brilho humano:



Trabalhando com carregamento dinâmico de tempo de um PC usando um script Python:



Todas as irregularidades na rolagem ocorrem devido ao disparo na câmera; para os olhos a uma distância normal, tudo parece bem suave e legível.


Vale ressaltar que é totalmente gratuito conectar vários módulos em série e apenas indicar isso no esboço para obter uma tela maior. As limitações começam apenas após um certo número de painéis e estão associadas ao desempenho limitado do Arduino e sua interface SPI. Obrigado ElectricFromUfa por uma visão geral detalhada e detalhada dos painéis e muito mais!


Talvez no futuro eu faça o mesmo no STM32F103C8T6 e no ESP-12F, tudo deve se mover mais rápido lá.


Referências:


1. Link para o arquivo morto com um esboço e relacionados.
2. Outro link para o BitBucket


Source: https://habr.com/ru/post/pt404867/


All Articles