OctoPrint con múltiples impresoras 3D
Una de las herramientas más útiles a la hora de gestionar una impresora 3D es OctoPrint, ya que entre varias cosas permite administrar tu impresora desde una interfaz web, así como agregar un montón de funcionalidades (por ejemplo, monitoreo y gestión de la impresora a través de Telegram, plugins para generar los videos timelapse de las impresiones, o incluso detectar cuando hay problemas tipo espagueti en nuestra impresiones usando otro plugin con AI).
Múltiples impresoras
¿Pero qué pasa si tenemos más de una impresora? ¿Puede OctoPrint gestionar mas de una impresora?
La respuesta corta es: NO! El software en sí no está diseñado para múltiples impresoras, pero se puede buscar formas alternativas para tener más de una instancia de OctoPrint corriendo en nuestro dispositivo o computadora.
Existen varias formas para esto, por ejemplo se puede modificar los scripts de OctoPi para agregar más instancias, crear scripts que lancen varias instancias del mismo programa , pero la opción que terminé usando fue la de contenedores Docker.
Raspberry Pi + Ubuntu
En mi caso tengo una Raspberry Pi 4, de 8 GB de RAM, más que suficiente para correr múltiples instancias de OctoPrint.
En principio tenía instalado en mi Raspberry un sistema operativo específico llamado OctoPi, un derivado de Debian con OctoPrint como el programa principal que se lanza cuando se enciende el dispositivo, pero al querer tener contenedores pensé en usar Ubuntu que es una distribución menos especializada y donde seguramente se puede encontrar todo lo necesario para trabajar con contenedores.
Requisitos y componentes:
Para esta configuración usé lo siguiente:
- Impresora 3D - Artillery Genius Pro
- Impresora 3D - Artillery SideWinder X2
- 2x WebCam - Logitech C270 (una por impresora)
- Raspberry Pi 4 - 8G RAM
- SD Card 64 GB
- Ubuntu 22.04 LTS ARM 64 bits
- Docker 20.10.17
- OctoPrint lastest (1.8.3)
Instalación de Ubuntu
Para instalar Ubuntu en la Raspberry Pi ahora es más sencillo que nunca, solo tienes que descargar Raspberry Pi Imager, el cual generará la imagen en tu tarjeta SD, seleccionar el sistema operativo, que en este caso es Ubuntu 22.04 LTS y listo. Hay versiones de Raspberry Pi Imager para Linux, Mac y Windows, así que podrás generar la imagen en el sistema operativo que más te convenga.
Desde este mismo software vas a tener la posibilidad de configurar la contraseña de la red WiFi en caso de que la estés usando la Raspberry de forma inalámbrica y que no quieras usar un televisor y un teclado (headless), o tocar la SD para configurar la red. Como yo lo estoy usando headless, fue lo que hice y luego me conecté por ssh.
Para la configuración headless el truco es dejar que corran los scripts de inicialización de Ubuntu y luego conectarte por ssh. Ahora si ves que no puedes conectarse por ssh puedes buscar un cable HDMI y un teclado para revisar qué pueda estar pasando.
Instalación del software en Ubuntu
Aparte de Docker vamos a necesitar, docker-compose, y los utilitarios que nos ayudarán a instalarlos.
Docker y docker-compose
Para instalar docker debemos descargarlo usando el script. Necesitaremos entrar como usuario administrador con la cuenta root:
pi@octopi:~$ sudo su
root@octopi:~#
root@octopi:~# apt install curl
root@octopi:~# curl -sSL https://get.docker.com | sh
root@octopi:~# apt update
root@octopi:~# apt install docker-compose
OctoPrint con una impresora
Lo primero que haremos es crear un carpeta para OctoPrint, y luego entrar en ella:
root@octopi:~# mkdir octoprint
root@octopi:~#
root@octopi:~# cd octoprint/
root@octopi:~/octoprint#
Vamos a empezar con una impresora, en este caso empezaré con la Artillery Genius Pro
. Para esto creamos una carpeta que identifique a esta impresora:
root@octopi:~# mkdir geniuspro
root@octopi:~# chmod -R 777 geniuspro/
Es importante saber la ruta actual porque luego la usaremos en la definición del contenedor junto con el nombre de esta carpeta. Para ello ejecutamos la siguiente orden:
root@octopi:~# pwd
/root/octoprint
Ya con esto podemos indicar luego que los archivos de esta primera instancia de OctoPrint residirán en /root/octoprint/geniuspro
.
Ahora para configurar la impresora vamos a usar docker-compose
por lo que creamos el archivo docker-compose.yml
con la siguiente información:
version: '2.4'
services:
geniuspro:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5000:5000
- 8080:8080
devices:
- /dev/ttyACM0:/dev/ttyACM0
- /dev/video0:/dev/video0
volumes:
- /root/octoprint/geniuspro:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
Este archivo define varias cosas relacionadas a OctoPrint, como la cámara web y la impresora como tal. Por ejemplo, con image
indicamos cuál es la imagen Docker a usar para el servicio geniuspro
.
En esta definición hay que destacar los puertos, los cuales serán usados para acceder a los servicios, siendo el 5000
para la interfaz web y el 8080
para el streaming de video de la cámara web.
Por otro lado se define como dispositivo la impresora en /dev/ttyACM0
, y la cámara web en /dev/video0
Algo a tener en cuenta y que se diferencia del ejemplo en cuál me basé, es que el streamer ahora es parte de la imagen Docker y es atendido en el puerto 8080
, por los que no hace falta tener una definición aparte a otra imagen Docker apuntando al streamer.
Luego en volumes
es donde se hace la referencia al directorio de la impresora que habíamos creado en la ruta /root/octoprint/geniuspro
, que será mapeada dentro del contenedor en la carpeta /octoprint
. Es decir, cuando levantemos el contenedor todos los archivos de OctoPrint para esta impresora quedarán en la ruta /root/octoprint/geniuspro
del sistema operativo.
Por último, en environment
definimos los parámetros para el streamer, pasando la información del dispositivo de la cámara web referido desde el contenedor, que en este caso coincide con el del sistema operativo afitrión, es decir, /dev/video0
. También le proporcionamos los parámetros de la cámara, en mi caso le indico que la resolución es de 1280x960 a 30 frames por segundos, que es lo que corresponde com mi Logitech C270. Para otros modelos puedes consultar el listado desde la página de cámaras soportadas.
Una vez creado el archivo docker-compose.yml
con la información mostrada arriba, solo basta con levantar el servicio con Docker:
root@octopi:~/octoprint# docker-compose up -d
Creating octoprint_geniuspro_1 ... done
Ya con esto podemos acceder a la interfaz web de OctoPrint desde la ip de la Raspberry en el puerto que le habíamos indicado, que en mi caso la url es http://192.168.68.102:5000:
Agregar una segunda impresora
Para agregar una segunda impresora solo hay que replicar la configuración que teníamos antes, pero especificando la nueva impresora y la cámara de video correspondiente. Entonces para mi Artillery Sidewinder X2 esto sería:
version: '2.4'
services:
geniuspro:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5000:5000
- 8080:8080
devices:
- /dev/ttyACM0:/dev/ttyACM0
- /dev/video0:/dev/video0
volumes:
- /root/octoprint/geniuspro:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
sidewinder_x2:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5000:5000
- 8080:8080
devices:
- /dev/ttyACM1:/dev/ttyACM0
- /dev/video2:/dev/video0
volumes:
- /root/octoprint/geniuspro:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
Aquí hay que aclarar que en el apartado devices físicamente la impresora está identificada en la Raspberry como /dev/ttyACM1
, pero al crear el contenedor internamente se identificará como /dev/ttyACM0
. Lo mismo pasa con la cámara de video, donde físicamente se identifca como /dev/video2
pero internamente el contenedor la verá como /dev/video0
.
Una vez creada la configuración se debe levantar el contendor de la siguiente manera:
root@octopi:~/octoprint# docker-compose up -d
octoprint_geniuspro_1 is up-to-date
Creating octoprint_sidewinderx2_1 ... done
Con esto ya se podrá acceder a la segunda instancia de OctoPrint en el puerto 5001:
¿Cómo diferenciar las impresoras?
En el anterior ejemplo usé los dipositivos /dev/ttyACM0
y /dev/ttyACM1
para hacer referencia a mi primera impresora, la Artillery Genius Pro, y a la segunda impresora la Artillery SideWinder X2, pero el sistema operativo no siempre las tomará en ese orden, ya que dependerá de cuál encienda primero . Para poder identificar las impresoras sin importar el orden de conexión podemos crear un enlace simbólico usando udev
con la información del fabricante y del producto.
Para esto primero hay que listar los dispositivos USB:
root@octopi:~/octoprint# lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 003: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 015: ID 0483:5740 STMicroelectronics Virtual COM Port
Bus 001 Device 016: ID 0483:5740 STMicroelectronics Virtual COM Port
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Acá hay dos dispositivos de STMicroelectronics Virtual COM Port, que corresponden a las dos impresoras, pero cuál es cuál? Vamos a pedir el detalle de estos dispositivos:
root@octopi:~/octoprint# lsusb -v -d 0483:5740
Bus 001 Device 015: ID 0483:5740 STMicroelectronics Virtual COM Port
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 2 Abstract (modem)
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0483 STMicroelectronics
idProduct 0x5740 Virtual COM Port
bcdDevice 0.00
iManufacturer 1 STMicroelectronics
iProduct 2 ARTILLERY_RUBY CDC in FS Mode
iSerial 3 386F39543538
...
Bus 001 Device 016: ID 0483:5740 STMicroelectronics Virtual COM Port
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 2 Communications
bDeviceSubClass 2 Abstract (modem)
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0483 STMicroelectronics
idProduct 0x5740 Virtual COM Port
bcdDevice 0.00
iManufacturer 1 STMicroelectronics
iProduct 2 ARTILLERY_RUBY CDC in FS Mode
iSerial 3 3594398F3538
...
Lo que diferenica una impresora de la otra es el número serial del dispositivo. Como inicialmente la Artillery Genius Pro estaba mapeada en /dev/ttyACM0
, podemos consultar cuál es su número de serial:
root@octopi:~/octoprint# udevadm info -a -n /dev/ttyACM0 | grep serial
ATTRS{serial}=="3594398F3538"
ATTRS{serial}=="0000:01:00.0"
Ahora para la ver el código serial de la Artillery Sidewinder X2 se debe consultar por el dispositivo /dev/ttyACM1
:
root@octopi:~/octoprint# udevadm info -a -n /dev/ttyACM1 | grep serial
ATTRS{serial}=="386F39543538"
ATTRS{serial}=="0000:01:00.0"
Para que udev
reconozca estas reglas debemos crear el siguiente archivo /etc/udev/rules.d/40-printers.rules
, con este contenido:
# Genius Pro
KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", ATTRS{serial}=="3594398F3538", SYMLINK="ttyGeniusPro"
# Sidewinder X2
KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", ATTRS{serial}=="386F39543538", SYMLINK="ttySidewinderX2"
Luego se debe reiniciar udev
para que tome estos cambios:
root@octopi:~/octoprint# udevadm control --reload-rules && udevadm trigger
De esta manera quedarán las impresoras mapeadas de la siguiente forma:
root@octopi:~/octoprint# ls -l /dev/tty{GeniusPro,Sidewinder}*
lrwxrwxrwx 1 root root 7 Aug 22 21:10 /dev/ttyGeniusPro -> ttyACM0
lrwxrwxrwx 1 root root 7 Aug 22 21:10 /dev/ttySidewinderX2 -> ttyACM1
Por lo que la configuración de Docker podemos modificarla para reflejar estos cambios:
version: '2.4'
services:
geniuspro:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5000:5000
- 8080:8080
devices:
- /dev/ttyGeniusPro:/dev/ttyACM0
- /dev/video0:/dev/video0
volumes:
- /root/octoprint/geniuspro:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
sidewinderx2:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5001:5000
- 8081:8080
devices:
- /dev/ttySidewinderX2:/dev/ttyACM0
- /dev/video2:/dev/video0
volumes:
- /root/octoprint/sidewinderx2:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
Cámaras de video
A diferencia de las impresoras las cuales tienen seriales distintos, tengo dos cámaras de video Logitech C270 que son exaxtamente iguales. ¿Cómo hacemos para diferenciarlas? ¿Cómo podemos hacer que siempre tome la cámara correcta?
Nuevamente hacemos el análisis con lsusb
:
root@octopi:~/octoprint# lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 003: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 015: ID 0483:5740 STMicroelectronics Virtual COM Port
Bus 001 Device 016: ID 0483:5740 STMicroelectronics Virtual COM Port
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Ambas cámaras están en el mismos bus pero identificadas como dispositivos USB diferentes, lo cual podemos aprovechar. En este caso no usaremos el id del producto para diferenciarlas sino el id de dispositivo USB. Tomaremos como referencia el subsystem video4linux
en vez de tty
, por lo que estaremos usando KERNELS
en vez de KERNEL
para diferenciar las cámaras. Por ejemplo, para saber en qué dispositivo USB está la cámara en /dev/video0
podemos ejecutar lo siguiente:
root@octopi:~/octoprint# udevadm info -a -p $(udevadm info -q path -n /dev/video0) | grep KERNELS
KERNELS=="1-1.4:1.0"
KERNELS=="1-1.4"
KERNELS=="1-1"
KERNELS=="usb1"
KERNELS=="0000:01:00.0"
KERNELS=="0000:00:00.0"
KERNELS=="pci0000:00"
KERNELS=="fd500000.pcie"
KERNELS=="scb"
KERNELS=="platform"
Para la otra cámara hacemos lo mismo:
root@octopi:~/octoprint# udevadm info -a -p $(udevadm info -q path -n /dev/video2) | grep KERNELS
KERNELS=="1-1.3:1.0"
KERNELS=="1-1.3"
KERNELS=="1-1"
KERNELS=="usb1"
KERNELS=="0000:01:00.0"
KERNELS=="0000:00:00.0"
KERNELS=="pci0000:00"
KERNELS=="fd500000.pcie"
KERNELS=="scb"
KERNELS=="platform"
Ya con esta información podemos modificar las reglas de udev
de la siguiente manera:
# Genius Pro
KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", ATTRS{serial}=="3594398F3538", SYMLINK="ttyGeniusPro"
SUBSYSTEM=="video4linux", KERNELS=="1-1.3", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", SYMLINK="videoGeniusPro"
# Sidewinder X2
KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", ATTRS{serial}=="386F39543538", SYMLINK="ttySidewinderX2"
SUBSYSTEM=="video4linux", KERNELS=="1-1.4", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", SYMLINK="videoSidewinderX2"
Recuerden reiniciar las reglas de udev con:
root@octopi:~/octoprint# udevadm control --reload-rules && udevadm trigger
De esta forma ahora tenemos las cámaras mapeadas como /dev/videoGeniusPro
y /dev/videoSidewinderX2
respectivamente. Ahora podemos modificar el archivo docker-compose.yml para que tome esta nueva configuración:
version: '2.4'
services:
geniuspro:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5000:5000
- 8080:8080
devices:
- /dev/ttyGeniusPro:/dev/ttyACM0 # use `python -m serial.tools.miniterm` to see what the name is of the printer, this requires pyserial
- /dev/videoGeniusPro:/dev/video0
volumes:
- /root/octoprint/geniuspro:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
sidewinderx2:
image: octoprint/octoprint
restart: unless-stopped
ports:
- 5001:5000
- 8081:8080
devices:
- /dev/ttySidewinderX2:/dev/ttyACM0 # use `python -m serial.tools.miniterm` to see what the name is of the printer, this requires pyserial
- /dev/videoSidewinderX2:/dev/video0
volumes:
- /root/octoprint/sidewinderx2:/octoprint
# uncomment the lines below to ensure camera streaming is enabled when
# you add a video device
environment:
- ENABLE_MJPG_STREAMER=true
- CAMERA_DEV=/dev/video0
- MJPG_STREAMER_INPUT="-y -n -r 1280x960 -f 30
Desempeño con dos instancias de OctoPrint
Seguramente te estarás preguntando sobre el consumo de estos contenedores en el sistema operativo y si las Raspberry puede manejarlo. Aquí te dejo una imagen de htop
con los dos contenedores arriba imprimiendo en cada una de las impresoras a la vez.
Como pordás notar la Raspberry está holgada, ya que de los 8GB de RAM apenas está consumiendo 0.5 GB incluyendo el sistema operativo. De esto podemos sacar dos conclusiones: la primera es que se pueden agregar más impresoras sin problemas, el único límite es la cantidad de puertos USB que ofrece las Raspberry (4 en mi modelo), pero podrías agregar un hub USB para agregar puertos; y lo segundo es que para dos impresoras se podría usar una Raspberry con menos RAM, por ejemplo de 2 GB o 4 GB, y así economizar un poco.
¿Cómo luce las impresoras y la Raspberry Pi?
Para cerrar este artículo les dejo una foto de mis impresoras conectadas a la RaspBerry Pi:
En la impresora más chica puede verse una caja pequeña gris que es la Raspberry Pi, junto a las dos cámaras web.
Referencias
- OctoPrint
- OctoPi
- Setting up OctoPrint on a Raspberry Pi for multiple printers
- Installing OctoPrint using Docker in Linux (video tutorial)
- Installing OctoPrint using Docker in Linux (instructions)
- USB webcams known to work with mjpg-streamer.
- How to distinguish between identical USB-to-serial adapters?
- Udev Rule to discern 2 identical webcams on Linux
Leave a Comment