Cómo hacer un informador muy notable desde un módulo LED para publicidad exterior y Arduino

Cómo hacer un informador muy notable desde el módulo LED P10 y Arduino.


Propósito : Conecte rápidamente una gran matriz LED P10 (16x32cm) a una PC u otro dispositivo, convirtiendo todo esto en un informador muy notable y brillante con un cambio dinámico de la información mostrada. Hay muchas aplicaciones para tal cosa, y como muestra la práctica, llama la atención. ¡Imagínese, ahora todos sabrán con certeza que su aire acondicionado está funcionando y se supone que la puerta está cerrada!



La luz de fondo no proviene de la ventana, sino del panel;)


Una vez que miré la matriz opaca de 8x8 LED para Arduino y a su costo me sentí triste. Se decidió gotear hacia líneas preparadas para la publicidad exterior y cuál fue mi sorpresa cuando todas resultaron ser estándar y todo porque uno no sabía nada sobre la actualización dinámica de la información a través de un puerto externo. Profundizando, se descubrió que en todos estos productos se utilizan módulos LED típicos (matrices LED).


Todos vieron las brillantes pantallas de publicidad exterior, desde simples líneas rastreras hasta enormes televisores. Estas pantallas se ensamblan a partir de una cadena de dichos módulos LED y se controlan mediante controladores especiales, cuyo precio aumenta en proporción directa al tamaño de la pantalla.




Estos módulos LED son bastante baratos. Alrededor de $ 6, el precio depende del tamaño y el color. Es más complicado con los controladores. Los precios más simples son comparables a un solo panel LED. Sin embargo, la mayoría de ellos están "adaptados" para trabajar en el modo de demostrar "presentaciones" preparadas previamente y no pueden cambiar dinámicamente la información mostrada. Admito que solo me familiaricé brevemente con la funcionalidad de los controladores más simples, pero resultó ser suficiente para entender: es más barato y más rápido hacer las cosas necesarias en el controlador universal Arduino. Esto le permitirá conectar con bastante calma varios módulos. Pero comenzaremos con uno.


El módulo LED (P10) se parece a esto:



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


De hecho, esto es solo una matriz de LED que están soldados a las salidas de los registros de desplazamiento, no entraremos en la jungla de circuitos, los módulos son todos estándar y difieren solo en tamaño. P10 significa que entre dos diodos adyacentes 10 mm. Hay paneles monocromáticos, bicolores e incluso RGB. Para aquellos interesados ​​en los detalles, ya existe un artículo similar, pero con un sesgo a un nivel inferior.


Hardware


Y para aquellos que desean obtener rápidamente un resultado que puedan "sentir", necesitarán:


  1. Una matriz de LED.
  2. Arduino (Usé mini, pero sería más conveniente nano no usar adaptadores adicionales para la comunicación con una PC).
  3. Fuente de alimentación para 5V / 3A. La matriz es voraz, si enciende todos los diodos, entonces necesita mucha energía.
  4. Cable de conexión (Por lo general, viene con una matriz).
  5. El deseo de terminar el trabajo.

Haremos un diseño monolítico que solo se conecta a una toma de corriente y una PC para mostrar nuestra valiosa información (por ejemplo, tasa de Bitcoin) .


Es necesario tomar el bucle de la matriz, cortándolo en media soldadura en un circuito simple al Arduino.



Si usa Arduino Mini o UNO, entonces necesita soldar a los pines correspondientes por analogía.


Si por alguna razón no tenía un cable, puede reemplazarlo con un cable MIDI que conecte el conector MIDI (opción para los antiguos) a la placa en las tarjetas de sonido antiguas o reemplazarlo con dos enchufes Dupont (madre) con 8 pines. En principio, el conector es estándar, se ve con bastante facilidad.



Pero en 10 minutos obtuve el siguiente diseño, cuelga un adaptador USBtoUART adicional que no tendrá si no usa el Arduino Mini.




El hardware está listo, solo necesita conectar una fuente de alimentación adicional de 5V / 3A para alimentar la matriz. Para hacer esto, tiene un conector separado, más a más, menos a menos. Por el contrario, puede, pero no funcionará. Aunque es extraño, ya que todos saben que los electrones, como siempre, fluyen de más a menos. Y si tenemos en cuenta que los electrones están cargados negativamente, entonces no está nada claro por qué fluyen hacia el polo negativo ...;) Pensaron cosas más cortas.



Espero que conecte la fuente de alimentación de manera más confiable, y mi método solo es adecuado si tiene muchas fuentes de alimentación de repuesto. Arduin se puede atornillar ( ¡No es un tornillo autorroscante! ) al panel mismo, el panel tiene orificios roscados de montaje.


Eso es todo, que los dioses DIY me perdonen, pero hoy no había cinta azul. No vale la pena gastar un recurso tan valioso en cosas tan inútiles.


Parte de software


Si pensabas que sería necesario entender cómo programar esto desde tres letras (interfaz SPI), entonces puedo decepcionarte, todo es mucho más simple. Como siempre, todas las bicicletas se inventan antes que nosotros y más de una vez. En realidad, como este artículo . Hay bibliotecas DMD y DMD2 listas para usar, y el dibujo se reduce al hecho de que en el boceto solo necesita indicar qué, cómo y dónde generar, ¡todo es ganancia!


Después de jugar un poco con la forma cuadrada de estas bibliotecas, llegué a la conclusión de que el desplazamiento de texto en DMD2 parece "triste", aunque hay más opciones y control de brillo allí. Como resultado, se decidió por el primero y recordó que "usted es un programador" le agregó un control de brillo primitivo. Aquí vale la pena mencionar que estas matrices brillan mucho. Al final de la depuración, quemará la córnea.


En general, todo lo que necesita hacer es cargar el boceto en el tablero, pero es desde ese directorio donde desempaquetará mi archivo, ya que lo modifiqué un poco archivo Algunas bibliotecas que están en el mismo lugar. Control de brillo agregado y SPI ligeramente overclockeado, tal vez algo más, no lo recuerdo. Rabatet? ¡No tocar!


Para mayor claridad, agregué un sensor de temperatura de 18v20. Si no conecta uno, la temperatura simplemente no se mostrará. Según mi idea, allí se mostrará un cierto peso en gramos, pero puede mostrar solo un número. Para cambiar el texto que se desplaza desde abajo, solo necesita "cargarlo" en el puerto COM de Arduino utilizando un formato especial, una especie de secuencia ESC. Se adjunta un script de Python que simplemente envía la hora actual al panel.


Formato de comando:


  1. 0x1B (ESC) + 0x40 ("@") // Identificación, devuelve la ID de cadena del dispositivo "P10_DISPLAY_V01 \ r \ n"
  2. 0xOC (CLR) // Borrar la pantalla.
  3. 0x1F (EE. UU.) + 0x58 ("X") + 0x80 // Establece el brillo de la pantalla, brillo máximo 0x80.
  4. 0x1F (US) + 0x52 ("B") + Cadena // Establezca la línea inferior que se desplazará.
  5. 0x1F (EE. UU.) + 0x0A ("LF") + 0x31, 0x32, 0x33, 0x34, 0x35 // Pase el número de cinco dígitos que se muestra en la parte superior "12345"

No encontré el acabado y tuve que dibujar la fuente rusa 5 * 7. Probablemente una bicicleta, pero ¿hacemos siempre todo racionalmente?


Bosquejo:


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); } 

Operación de brillo completo:



Trabajar con brillo humano:



Trabajando con carga dinámica de tiempo desde una PC usando un script Python:



Todas las irregularidades con el desplazamiento se deben al disparo en la cámara, para el ojo desde una distancia normal, todo se ve bastante suave y legible.


Vale la pena señalar que es completamente gratuito conectar varios módulos en serie y solo indicar esto en el boceto para obtener una pantalla más grande. Las limitaciones comienzan solo después de un cierto número de paneles y están asociadas con el rendimiento limitado de Arduino y su interfaz SPI. ¡Gracias ElectricFromUfa por una descripción detallada y detallada de los paneles y más!


Quizás en el futuro haga lo mismo en el STM32F103C8T6 y en el ESP-12F, todo debería moverse más rápido allí.


Referencias


1. Enlace al archivo con un boceto y relacionado.
2. Otro enlace a BitBucket


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


All Articles