Control por computadora a través de control remoto desde un amplificador usando Arduino y Node.js

Prólogo


La historia comienza con el hecho de que hace medio año compré un amplificador Yamaha A-S501.


Yamaha A-S501


Se incluía un control remoto que podía controlar tanto el amplificador como el reproductor de CD Yamaha, que naturalmente no tenía. Por lo tanto, la mayoría de los botones del control remoto simplemente no se usaron. Y, en general, la consola en sí no era necesaria, y siempre se encontraba en un estante.


Sin embargo, al mirarlo, me persiguió la idea de usar el control remoto al máximo. Por ejemplo, sería conveniente acostarse en el sofá y ver una película, rebobinar con un movimiento rápido de la mano, pausarlo, etc. Por supuesto, para estos propósitos, solía usar aplicaciones en mi teléfono inteligente para controlar los programas MPC-HC, Foobar2000, pero el control remoto sería más rápido y más conveniente.


Como dicen, los ojos tienen miedo y las manos están haciendo. Con la elección de la tecnología, todo quedó inmediatamente claro. Arduino: Hace mucho que quería jugar con ella, y esta es solo una gran oportunidad. Para el manejador de botones, Node.js, porque Me especializo en javascript y no quería cambiar de contexto.


Y entonces, vamos ...


Soluciones confeccionadas


Uno de los análogos existentes que pude encontrar es Flirc . Al usarlo, puede emular las pulsaciones de teclas en el teclado físico de una computadora.



Tal receptor infrarrojo cuesta 100 zlotys (≈ $ 28). Mirando hacia el futuro, es el doble de caro que lo que obtuve. Además, en términos de funcionalidad, resultó aún mejor (subjetivamente).


Compra de piezas



Yo necesitaba:


  • En realidad, la placa Arduino Uno en sí. Vale la pena señalar que este no es el tablero original, sino algún tipo de clon polaco. Según la descripción, es completamente similar al original. (27,90 zł)
  • Receptor de infrarrojos VS1838B HX1838 (voltaje: 3.3–5 V, frecuencia: 38 kHz, ángulo: 90 °) (1.30 zł)
  • Tablero de prototipos + cables (13,90 zł)
  • Una placa vacía para soldar todo (2,10 zł)
  • Conectores para placas de conexión (2,51 zł)

Total: 47,71 zł (≈ $ 14)


Software


Mientras esperaba la entrega, comencé a escribir un "controlador", que debería leer los datos del puerto serie de Arduino y realizar ciertas acciones para el botón presionado en el control remoto.


La idea era poder personalizar todo y todo. A cada botón del control remoto se le pueden asignar ciertas acciones de varios tipos:



{ "key": "space" } 

  • Ejecutar un programa arbitrario con parámetros:

 { "exec": ["c:\\Program Files (x86)\\foobar2000\\foobar2000.exe", "/play"] } 


 { "if": { "running": "mpc-hc.exe" }, "then": [ ... ], "else": [ ... ] } 

Cada tipo tiene su propio controlador, que tiene la misma API y, por lo tanto, fue posible contraer todo en un bucle simple que inicia todos los controladores en secuencia.


 const runHandlers = require('./handlers') module.exports = async function run(actions) { if (!Array.isArray(actions)) { actions = [actions] } for (const act of actions) { await runHandlers(act) } } 

En lugar de mil palabras de documentación, las pruebas te dicen todo:


 run when "exec" action √ executes the specified file without args (as array) (4ms) √ executes the specified file without args (as string) (1ms) √ executes the specified file with args √ rejects if "exec" has wrong type (5ms) when "key" action √ sends the specified key press if passed string (1ms) √ sends the specified key combination if passed array √ rejects if "key" has wrong type (1ms) when "if" action √ rejects if no "then" (1ms) √ rejects if operator is not supported when operator if "running" √ runs "then" actions if the condition is true (1ms) √ runs "else" actions if the condition is false √ does not run anything if the condition is false and no "else" statement (1ms) when multiple actions √ executes all actions (1ms) when multiple actions are mixed into one √ runs only first one alphabetically 

Queda por esperar los preciados detalles.


Hierro


Lo admito, no inventé nada nuevo, todo ya se ha hecho antes que yo. Acabo de usar el diagrama listo del artículo Cómo configurar un control remoto y receptor IR en un Arduino .


El esquema es bastante simple:



En la práctica:




Firmware


Honestamente también tomé prestado el firmware del artículo , para su trabajo necesitaré la Biblioteca IRremote Arduino .


Reemplacé los códigos de botón con los códigos reales de mi control remoto:


 void loop() { if (irrecv.decode(&results)) { if (results.value == 0xFFFFFFFF) { results.value = key_value; } switch (results.value) { case 0x9E6140BF: Serial.println("play"); break; case 0x9E61AA55: Serial.println("pause"); break; /* ...*/ case 0x5EA1A857: Serial.println("cd"); break; default: Serial.println(results.value, HEX); break; } key_value = results.value; irrecv.resume(); } } 


Tan pronto como aparecieron los nombres de los botones presionados en la ventana Monitor de puerto en el IDE de Arduino, fue necesario agregar un componente para trabajar con el puerto serie al controlador.


El resultado fue un contenedor sobre la biblioteca de puerto serie y, de hecho, el flujo de datos desde el puerto:


 const SerialPort = require('serialport') module.exports = class SerialPortReader { constructor(port) { const serialPort = new SerialPort(port) this.lineStream = serialPort.pipe(new SerialPort.parsers.Readline()) } start(handler) { this.lineStream.on('readable', () => { const data = this.lineStream.read().trim() handler(data) }) } } 

Más tarde, hubo una necesidad de convertir los controladores en una función de "rebote", porque el control remoto recibe una señal que se repite rápidamente y que, incluso con una breve presión sobre el botón, se apaga varias veces. Sin embargo, eliminar esa opción para todos los botones tampoco es del todo apropiado, por ejemplo, para el volumen.


El código final se ve así:


 const debounce = require('debounce') const settings = require('./lib/settings') const run = require('./lib/run') const SerialPortReader = require('./lib/SerialPortReader') const simpleHandle = async button => { const actions = settings.mappings[button] if (!actions) { console.warn(`Action not found for remote control button "${button}"`) return } try { await run(actions) } catch (e) { console.error(e.message) process.exit(1) } } const debouncedHandle = debounce(simpleHandle, settings.debounceDelay, true) const callHandleFn = button => { return (settings.noDebounce.includes(button) ? simpleHandle : debouncedHandle)(button) } const reader = new SerialPortReader(settings.serialPort) reader.start(callHandleFn) 

Creando una Junta Independiente


Después de asegurarme de que el prototipo funciona, procedí a crear un tablero. Vale la pena señalar, para mí esta es la primera experiencia en estos asuntos. No tenía un soldador adecuado con una aguja pequeña, solo el viejo soviético grande con un cable apretado.


Con pena por la mitad, logré soldar las "patas" (de los dos conectores grandes de 8 pines, solo sobrevivieron 2 pines). Todo lo demás fue más fácil.



(Torcido. Probablemente debido al clon Arduino. Los nidos son desiguales entre sí).



Coloqué intencionalmente el receptor infrarrojo entre los tableros. Por lo tanto, el dispositivo se ajusta fácilmente debajo del amplificador. Decidí hacer las pistas conectando los agujeros en el tablero con estaño, en lugar de conectarlos con cables. Y finalmente, pegué la cinta para que la placa no se cerrara en la caja metálica del amplificador.




Como resultado: un dispositivo y un software totalmente funcionales por 14 $ 14. ¡La experiencia adquirida y la alegría del trabajo realizado y el resultado son invaluables! :-)


Gracias por su atencion!




Demo:



Fuentes en Github .




PD Gracias ramanchik 'u por la consulta :)

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


All Articles