Portada! Creando un Magic Monitor

unreal4u

I solve problems.
Miembro del Equipo
ADMIN
1587342919217.png
Algo ya les había adelantado el otro día acerca de este proyecto, pero quise hacer un thread aparte detallando todo el teje y maneje del cual tengo fotos, ya que ese thread mostró sólo el casi resultado final.

Resulta que hace como un mes atrás di con un video en YouTube el cual mostraba todas las formas en que uno podía re-ocupar las piezas de un notebook viejo, así que recordando que el pasado siempre fue mejor, revolví cielo mar y tierra hasta poder encontrar ese notebook que hacía años se había echado a perder pero que nunca lo llegué a botar pensando en que algún día me iba a poder servir. Ja, de algo que me sirva ser cachurero! Yo sabía perfectamente hace 7 años atrás que lo iba a necesitar para deshuesarlo!

La cosa es que lo desarmé por completo y terminé sacando la pantalla completa que aún estaba intacto. Sin poder probar si todavía funcionaba, me fijé que el monitor era un Samsung LTN154X3-L01 así que tío AliExpress me ayudó a encontrar un driver para ese monitor en particular. Suerte la mía, sólo costaba €19, así que lo compré inmediatamente junto con una fuente de 12v/5A para poder alimentarlo. La verdad el monitor sólo necesita una fuente de 3A, pero más adelante pretendo utilizar esa misma fuente para alimentar algunas otras cosas. (Música de suspenso maestro!)

También rescaté la webcam del fallecido notebook para de esa forma tener ojos digitales en el living que era el lugar donde quería poner mi monitor.

Acto seguido fue ver de qué tamaño era el monitor para poder hacerle un frame de madera. Luego de medir todo, me dispuse a cortar diversos palos en 45° y expuse todas las piezas para ver qué iba a quedar donde:



Ya teniendo una idea más certera de cómo iba a ser la construcción, hice los slots para que el monitor quedara embebido en el frame:



A su vez, también me dispuse a probar la cámara, la verdad no sabía muy bien si esto iba a funcionar, pero por lo general las webcam son todas USB... así que si lo conectaba a un cable USB era probable que funcionara... y así fue! Lo único sí es que tuve que dar vuelta los cables de datos pq lamentablemente el estándar USB no define los colores de los cables de datos, así que su experiencia puede ser distinta:



Teniendo claro el tamaño y ubicación de la webcam me dispuse a crear un slot para que cupiera. Había hecho algunas pruebas y la cámara funcionaba de lo más bien metiéndolo en un hoyo de 6mm de profundidad, así que eso hice, primero taladré el hoyo y luego me dispuse a sacar material para poder embeber la cámara:



Terminado esos dos pasos, la idea final fue surgiendo:



Ahora empezaba la espera... del coronavirus el primer caso se presentó en Francia hacía sólo 5 días, el movimiento social en Chile estaba en una calma típica antes de la tormenta y el 14 de febrero se iba a celebrar en 14 días más... Sin embargo, recién el 14 de marzo pude seguir con el proyecto ya que por fin aparecieron todas las piezas que necesitaba.

Como ya tenía gran parte del proyecto listo, me dispuse a probar que el monitor funcionara y para mi tranquilidad funcionó bien a la primera! Así que ya con eso confirmado, me dispuse a hacer mierda la puerta donde iba a colgar el monitor. La idea era que la controladora quedara afirmada por el reverso de la puerta mientras que el monitor quedara al anverso.



Por el reverso:



Así y todo, así es como quedó aquel primer día:



Finalmente llegó la hora de prender el monitor y por supuesto apreciar el booteo en pleno:



Ahora bien, me imagino que querrán saber qué cresta hay al otro lado de esa puerta: pues muy convenientemente, llegan los cables eléctricos y de la compañía de tv e internet, así que es un lugar ideal para colgar una raspberry pi 4 que había adquirido hace un tiempo (favor omitir el mar de cables, algún día lo ordenaré como se debe):



Aquí venía la parte divertida: configurar la rpi para que pudiera levantar un entorno gráfico (cuando lo instalé lo hice sin entorno gráfico pq no pensé que lo iría a ocupar algún día) e iniciara un navegador en modo kiosk mostrando cierta página web. Para esto, ejecuté lo siguiente en la rpi:

Código:
sudo apt install raspberrypi-ui-mods
sudo raspi-config
En raspi-config es muy importante ir a "7 Advanced Options" y activar el soporte experimental para el GL Driver, de otra forma les va a ser imposible poder rotar la pantalla 90° o 270° y aumentar la resolución a la resolución nativa del monitor.
Aprovechando que están ahí, para que bootee en modo gráfico, vayan a "3 Boot Options" y en B1 eligen la opción B4 para que se logee automáticamente a la cuenta pi del sistema mientras está en el modo gráfico.

Nótese que en la rpi4 y con el último build de raspbian, cambiaron bastante la forma en que los gráficos que se manejan, así que les dejo el dato que a partir de la rpi4 esta es la forma oficial de rotar la pantalla via CLI:

Código:
DISPLAY=:0 xrandr --output HDMI-1 --rotate left
Obviamente tienen que ajustar ahí los parámetros correspondientes.

Una vez rotada la pantalla, llegó la hora de poder iniciar el browser. Lo primero que hice fue instalar Chromium ya que soporta el modo kiosko de forma nativa, pero dp de un rato me empezó a salir el mensaje de que tenía que actualizarlo y no encontré una forma fácil de deshabilitar eso, así que terminé instalando firefox-esr con dos plugins: una para establecer el zoom predeterminado del navegador y el otro para iniciar firefox en pantalla completa. Para que ejecutara el programa creé el siguiente script:

Código:
#!/bin/bash

sleep 45
DISPLAY=:0 /usr/bin/firefox-esr https://mm.home.unreal4u.com/ &
El sleep está ahí para que docker tenga tiempo de levantar la imagen antes de que firefox se active, de otra forma tendré en pantalla un lindo error 502 (Según me contó un amigo). Este script lo llamo en mi /etc/xdg/lxsession/LXDE-pi/autostart:

Código:
@/home/pi/start-kiosk.sh
También hice un script que reiniciara firefox cada 24 horas en caso de problemas o actualización, este simplemente mata cualquier proceso con nombre firefox-esr y ejecuta el script de arriba. Convenientemente, le da 45 segundos a firefox para salir de su sesión.

De ahí lo otro que hice fue "instalar" Magic Mirror... en realidad simplemente lo hice con Docker, mi docker-compose.yml se ve así:

Código:
version: "3"
services:
  nodered:
  container_name: nodered
  image: "nodered/node-red:1.0.3-2-12-minimal-arm32v7"
  restart: unless-stopped
  environment:
  - TZ=Europe/Amsterdam
  ports:
  - 1880:1880
  volumes:
  - ~/iot-services/services/nodered:/data
  magicmirror:
  container_name: magicmirror
  image: "bastilimbach/docker-magicmirror"
  restart: unless-stopped
  ports:
  - 8099:8080
  volumes:
  - /etc/localtime:/etc/localtime:ro
  - ~/iot-services/services/magicmirror/config:/opt/magic_mirror/config
  - ~/iot-services/services/magicmirror/modules:/opt/magic_mirror/modules
  - ~/iot-services/services/magicmirror/custom.css:/opt/magic_mirror/css/custom.css
Eso instala dos imágenes de Docker: una es node-red (del cual hablaré muy brevemente en este post, pero estoy preparando otro donde se tocará en mayor profundidad) y la otra es MagicMirror.

Lo otro que configuré fue un proxy transparente en nginx para tener un fácil soporte para https, ya que tengo un certificado wildcard mediante LetsEncrypt para que de esa forma pueda tener cualquier subdominio habilitado en mi casa: con un poco de magia en pfsense, otro poco de magia en CloudFlare y tener una pi siempre prendido y conectado esto se hace realmente fácil.

Una vez iniciadas las imágenes me puse a jugar un poco con algunos de los módulos tanto internos como externos que provee MagicMirror, hasta lograr el siguiente resultado:



Ahora ya había llegado la hora de dejar de probar cosas y sacar todo para pintarlo y dejarlo bonito, con todos los detalles hechos.
Sin embargo; antes de hacer eso; en un día particularmente flojo acá en la casa me puse a jugar con node-red para que hiciera unas cuantas cosas:
  1. Que prendiera la pantalla a las 8 de la mañana
  2. Que la apagara a las 21 horas
  3. Que la apague cuando detecte que ni yo ni mi señora están en la casa
  4. Que la prenda cuando detecte que llegamos de vuelta
Terminé haciendo dos flow's que se ven de la siguiente manera:

Detectar si estamos en la casa:


Que envié el comando para apagar o prender la pantalla:


Detectar si estamos en la casa:
Código:
[{"id":"4902c068.3cf1f8","type":"tab","label":"Empty house","disabled":false,"info":""},{"id":"4004dd79.2dc08c","type":"mqtt in","z":"4902c068.3cf1f8","name":"Is Camilo home","topic":"sensors/whoishome/camilo","qos":"2","datatype":"utf8","broker":"16d5a874.d300a","x":180,"y":220,"wires":[["bcd8b923.f03698"]]},{"id":"292dbc71.d14644","type":"mqtt in","z":"4902c068.3cf1f8","name":"Is Eva home","topic":"sensors/whoishome/eva","qos":"2","datatype":"utf8","broker":"16d5a874.d300a","x":170,"y":260,"wires":[["bcd8b923.f03698"]]},{"id":"bcd8b923.f03698","type":"join","z":"4902c068.3cf1f8","name":"Combine","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":340,"y":240,"wires":[["a0272ae3.d08f2","a30c524d.37227"]]},{"id":"f6ac59b3.ca7da8","type":"change","z":"4902c068.3cf1f8","name":"People not present","rules":[{"t":"set","p":"payload","pt":"msg","to":"no","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":260,"wires":[["eb7cead1.e670e","46b0392a.e202b"]]},{"id":"c8163e42.e92868","type":"change","z":"4902c068.3cf1f8","name":"People present","rules":[{"t":"set","p":"payload","pt":"msg","to":"yes","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":220,"wires":[["eb7cead1.e670e","46b0392a.e202b"]]},{"id":"eb7cead1.e670e","type":"mqtt out","z":"4902c068.3cf1f8","name":"MQTT Command out","topic":"sensors/whoishome/peoplepresent","qos":"0","retain":"true","broker":"16d5a874.d300a","x":1000,"y":200,"wires":[]},{"id":"46b0392a.e202b","type":"debug","z":"4902c068.3cf1f8","name":"People present in house?","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1010,"y":260,"wires":[]},{"id":"a0272ae3.d08f2","type":"debug","z":"4902c068.3cf1f8","name":"Combine output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":580,"y":360,"wires":[]},{"id":"3dc133bc.d0336c","type":"debug","z":"4902c068.3cf1f8","name":"Contains home","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":740,"y":180,"wires":[]},{"id":"a7719dea.fe04d8","type":"debug","z":"4902c068.3cf1f8","name":"Does not contain home","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":770,"y":300,"wires":[]},{"id":"2f49da32.6d874e","type":"inject","z":"4902c068.3cf1f8","name":"Test both not home => no","topic":"","payload":"[\"not_home\",\"not_home\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":140,"wires":[["a30c524d.37227"]]},{"id":"1cf631b7.2d92fe","type":"inject","z":"4902c068.3cf1f8","name":"Test one not home (1) => yes","topic":"","payload":"[\"not_home\",\"home\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":60,"wires":[["a30c524d.37227"]]},{"id":"f20777b7.b6302","type":"inject","z":"4902c068.3cf1f8","name":"Test both home => yes","topic":"","payload":"[\"home\",\"home\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":180,"wires":[["a30c524d.37227"]]},{"id":"c308e095.980cc8","type":"inject","z":"4902c068.3cf1f8","name":"Test one not home (2) => yes","topic":"","payload":"[\"home\",\"not_home\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":100,"wires":[["a30c524d.37227"]]},{"id":"a30c524d.37227","type":"function","z":"4902c068.3cf1f8","name":"find home","func":"const isHome = msg.payload.find(element => element == \"home\");\n\n//node.warn(isHome);\n//node.warn(msg.payload);\n\nif (isHome === undefined) {\n  // If it does NOT contain home, use second output\n  return [null, msg];\n}\n\n// If it does contain home, use first output\nreturn [msg, null];\n","outputs":2,"noerr":0,"x":520,"y":240,"wires":[["3dc133bc.d0336c","c8163e42.e92868"],["a7719dea.fe04d8","f6ac59b3.ca7da8"]]},{"id":"16d5a874.d300a","type":"mqtt-broker","z":"","name":"MQTT Broker","broker":"XXXXX","port":"1883","clientid":"nodered-mqtt-client","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
Enviar el comando para apagar o prender la pantalla:
Código:
[{"id":"768a9d7d.7438a4","type":"tab","label":"MMonitor","disabled":false,"info":""},{"id":"ee7687e4.2319","type":"inject","z":"768a9d7d.7438a4","name":"Timed MM off","topic":"commands/mainmonitor","payload":"off","payloadType":"str","repeat":"","crontab":"00 21 * * *","once":false,"onceDelay":0.1,"x":513,"y":320,"wires":[["5178386f.558f78"]]},{"id":"7a814ebd.8d7ef","type":"inject","z":"768a9d7d.7438a4","name":"Timed MM on","topic":"commands/mainmonitor","payload":"on","payloadType":"str","repeat":"","crontab":"00 08 * * 1,2,3,4,5","once":false,"onceDelay":0.1,"x":513,"y":200,"wires":[["5178386f.558f78"]]},{"id":"8cf919e7.a48748","type":"switch","z":"768a9d7d.7438a4","name":"Anybody home?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"yes","vt":"str"},{"t":"eq","v":"no","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":300,"y":260,"wires":[["87e9d74a.953028"],["ffc8c1e1.0639f"]]},{"id":"5178386f.558f78","type":"mqtt out","z":"768a9d7d.7438a4","name":"MQTT Command out","topic":"commands/mainmonitor","qos":"0","retain":"true","broker":"16d5a874.d300a","x":760,"y":260,"wires":[]},{"id":"87e9d74a.953028","type":"change","z":"768a9d7d.7438a4","name":"MM on","rules":[{"t":"set","p":"payload","pt":"msg","to":"on","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":493,"y":240,"wires":[["5178386f.558f78","21428ff.87136f"]]},{"id":"ffc8c1e1.0639f","type":"change","z":"768a9d7d.7438a4","name":"MM off","rules":[{"t":"set","p":"payload","pt":"msg","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":493,"y":280,"wires":[["5178386f.558f78","21428ff.87136f"]]},{"id":"21428ff.87136f","type":"debug","z":"768a9d7d.7438a4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":710,"y":320,"wires":[]},{"id":"2ee07f48.c203f8","type":"mqtt in","z":"768a9d7d.7438a4","name":"People present","topic":"sensors/whoishome/peoplepresent","qos":"2","datatype":"auto","broker":"16d5a874.d300a","x":100,"y":260,"wires":[["8cf919e7.a48748"]]},{"id":"16d5a874.d300a","type":"mqtt-broker","z":"","name":"MQTT Broker","broker":"XXXXX","port":"1883","clientid":"nodered-mqtt-client","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

El script que efectivamente prende o apaga la pantalla es bastante simple: se subscribe a un tópico en particular, en este caso commands/mainmonitor y ejecuta lo siguiente cuando entra un mensaje a ese tópico:

Código:
# Para apagarlo: (mensaje "off")
/usr/bin/vcgencmd display_power 0
# Para prenderlo: (mensaje "on")
/usr/bin/vcgencmd display_power 1
Con respecto a la forma en que se hacía en una rpi3 se simplificó bastante, lo cual se agradece harto!

Volviendo al monitor, llegó la hora de los toques finales. Lo desarmé nuevamente para que mi señora eligiera el color y lo pintara acorde:



Finalmente llegó la hora de instalar la cámara (soldé los cables sueltos al conector USB) la cual cupo bastante bien:



Y así es el resultado en el frontis:


Finalmente, le apliqué un poco de pegamento y lo dejé secando:



Una vez seco, llegó la hora de hacer la instalación final! Ya era de noche pq no me pude aguantar así que disculpen la calidad de las fotos, pero aquí van:



Y de cerca:


Y eso sería todo amigos! Cualquier duda o consulta, por favor háganla!

Saludos.
 
Última modificación por un moderador:

Sago7

Gold Member
Buenísimo proyecto.
yo pensé en el magic mirror, pero no me he dado el trabajo de ver donde puedo comprar el componente principal acá, el espejo.
 

unreal4u

I solve problems.
Miembro del Equipo
ADMIN
Buenísimo proyecto.
yo pensé en el magic mirror, pero no me he dado el trabajo de ver donde puedo comprar el componente principal acá, el espejo.
Ah wena! Yo al ppio igual había pensado en un magic mirror, pero dp de leer varios blogs y explicaciones de locos que la habían hecho, desistí de la idea: es un culo ya que tienes que prácticamente sellar el monitor pq el backlight se escapa por el fondo (en este proyecto igual pero no es gran problema, pero en un mirror te jode todo el borde) y al tener que sellarlo, tienes que preocuparte de instalar un sistema de ventilación, la gran mayoría son pasivos, pero de todas formas es un punto a tener en consideración.

Además, el ángulo de visión es más webeado ya que, aunque en ppio no es nada más que un vidrio con un film que deja pasar la luz por un lado pero no por el otro, disminuye mucho el brillo del monitor mismo entonces se hace más difícil poder ver lo que está en pantalla.

Por eso mismo... preferí la vía fácil e implementé un magic monitor, que era harto más rápido que hacer y con menos webeo xD

Saludos.

EDIT: Si de todas formas te tiras por un magic mirror, este es el tipo de film a ocupar: https://www.magicmirrorcentral.com/product/bdf-s05-window-film-daytime-privacy-mirror-film-silver-5/

Ni idea eso sí dónde se puede comprar, ya que como expliqué, desistí de la idea xD
 
felicitaciones, me encanto el proyecto, la verdad es que ya había visto el tema del driver que venden en ali, incluso le pregunte al vendedor si era compatible con la pantalla de mi note, nunca lo concrete pero mi idea era utilizar la pantalla de mi note muerto como monitor portable, he visto varios videos de youtube, incluso algunos utilizan pantallas de tablet, pero al final no me anime, lo que me tiro para atrás fue el hecho de fabricar el soporte, ya que soy muy malo para trabajar con distintos materiales y no tengo las herramientas, pero voy a ver si algún día me animo, en todo caso me gusto mucho la idea, ya que se pueden aprovechar las pantallas de los equipos viejos. salu2
 

unreal4u

I solve problems.
Miembro del Equipo
ADMIN
Pequeño tip: terminé cambiando el color base de magicmirror de negro a blanco, pq sobretodo durante la mañana cuando entra todo el sol por la ventana no se alcanzaba a leer nada, con blanco anda harto mejor y además me permite ponerle más colores.

Lo más bkn sería poder cambiar el esquema de colores de acuerdo a si el sol está puesto o no, pero no sé cómo hacer eso todavía. Además, está llegando el verano acá así que cambiaría de esquema de colores cuando ya la pantalla estaría apagada así que no tiene apuro jajajja

Saludos.
 
una consulta, yo quiero utilizar un monitor viejo de note, el controlador debe ser de alguna "calidad" me preocupa que si compro el mas barato (aunque es para mas adelante) pudiera haber riesgo que se queme el HDMI o no pasa?
 

unreal4u

I solve problems.
Miembro del Equipo
ADMIN
una consulta, yo quiero utilizar un monitor viejo de note, el controlador debe ser de alguna "calidad" me preocupa que si compro el mas barato (aunque es para mas adelante) pudiera haber riesgo que se queme el HDMI o no pasa?
yo ni me preocupé de eso: el driver me costó €18 desde China, así que si caga compro uno nuevo. Además, las pantallas de notebook son tan específicos que lo más probable es que el driver más caro que compres sea exactamente el mismo que el chino, sólo a un precio distinto.

Yo revisé temperatura así como a la rápida la otra vez y no lo encontré caliente el chip. Eso sí, no tengo termómetro laser y no me atreví a tocarlo pq era un día particularmente húmedo y vez que tocaba a mi esposa o hija andaba descargando electricidad estática, así que preferí no tocar un chip en caliente aajajaj

Saludos.
 
yo ni me preocupé de eso: el driver me costó €18 desde China, así que si caga compro uno nuevo. Además, las pantallas de notebook son tan específicos que lo más probable es que el driver más caro que compres sea exactamente el mismo que el chino, sólo a un precio distinto.

Yo revisé temperatura así como a la rápida la otra vez y no lo encontré caliente el chip. Eso sí, no tengo termómetro laser y no me atreví a tocarlo pq era un día particularmente húmedo y vez que tocaba a mi esposa o hija andaba descargando electricidad estática, así que preferí no tocar un chip en caliente aajajaj

Saludos.
No pero, me refiero al hdmi del equipo que conecto no al del driver osea al hdmi macho, no quiero que se me queme el hdmi del note por ejemplo o no pasa nada ?
 

unreal4u

I solve problems.
Miembro del Equipo
ADMIN
No pero, me refiero al hdmi del equipo que conecto no al del driver osea al hdmi macho, no quiero que se me queme el hdmi del note por ejemplo o no pasa nada ?
El HDMI del driver es HDMI in, no pongo las manos al fuego pero creo que eso limita bastante la posibilidad de que se te queme la salida HDMI de tu notebook.

Saludos.
 
Pulento el proyecto, tengo al menos 3 Notebook con las pantallas impecables como para intentar copiarte... Peeeeero soy medio negado para la programación y de frustrado terminaría todo en la basura.... Felicitaciones!!!
por ultimo transfórmalos en simples monitores HDMI, es lo que voy a hacer yo mas adelante
 
Subir