Skip to content

Instantly share code, notes, and snippets.

@pyr0ball
Last active December 24, 2021 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pyr0ball/f72afaed995c066610cac3b42fbedf65 to your computer and use it in GitHub Desktop.
Save pyr0ball/f72afaed995c066610cac3b42fbedf65 to your computer and use it in GitHub Desktop.
Dual-Wielding Octoprint Instances, a Tutorial

Dual-Wielding 3D-Printers with Octoprint

Foreword

Before taking on this tutorial, the author assumes that:

  • The end-user can comfortably install and configure their Linux distribution of choice. (For the sake of clarity, I'll be using Ubuntu 18.04 for this tutorial. Other distros, YMMV)
  • The end user can install, upgrade, downgrade and resolve both conflicts and dependency resolution of packages on his/her distribution's package manager.
  • That the user is comfortable with the Linux terminal, and can navigate through it.
  • Basic competence on the shell, such as reading man files, using a text editor of choice, manipulating file operations on the same, etc is assumed.
  • Some advanced features require specific hardware (High-performance hardware encoding utilizes Intel's QuickSync, requires a relatively new Intel CPU with integrated graphics)

Many of the commands and groups of commands in this guide are using generic names and addresses that can be customized. I tried to use defaults that make sense, but if you want to make changes, feel free. Setup guides for nginx, ffmpeg, ffserver, etc, are readily available onliine and can be referenced to make this easier.

You can also add as many octoprint instances as your hardware is capable of handling. Simply add an additional iteration with a unique name and port.

Install and Configure Ubuntu 18.04

Recommended setup option:

  • When creating a user, there is a box to specify the name of the server. Choose something easy to remember, as you can use the system's name in an address like server-name.local
  • If you plan to just copy and paste the commands here, simply name your server 'octoprint'
  • This local address scheme also requires a router/modem that is capable of utilizing mDNS / avahi / bonjour. This is also sometimes called uPNP

Install required packages

# Update package info
sudo apt update && \
# Upgrade any packages with available updates
sudo apt upgrade -y && \
# Update kernel modules with available updates
sudo apt dist-upgrade -y && \
sudo apt install -y \
# git protocol for pulling from github and other repos
git \
# octoprint and many other applicatios run on this interpreter
python3 \
# python's in-built package manager
python3-pip \
# SSH server, allows remote terminal control over network
openssh-server \
# Webserver, used in this case for reverse-proxy (octoprint.local/printer0 instead of 192.168.1.123:5000)
nginx \
 # Video encoding application. Needed for webcam streams
ffmpeg \
# AKA Bonjour|mDNS, allows local networks to utilize the server's self-configured hostname in place of an IP address
avahi-daemon

Optional packages

sudo apt install -y \
# Allows remote desktop view of the server's GUI
x11vnc \
# My personal preferred terminal text editor
vim \
# Allows users to search for files by name or regex
locate \
# Allows users to view the thermal sensor and voltage readings from hardware
lm-sensors \
# For non-ubuntu debian-based installs, this is used primarily in MOTD login notifications
update-notifier-common \
# Package contains binaries like ifconfig, which were removed between Ubuntu 16 and 18
net-tools \
# Allows other systems to access NFS shares on this server
nfs-kernel-server \
# Allows this server to access NFS shares on other systems
nfs-common \
# Allows other systems to access Windows SMB protocol shared folders
samba \
# Allows this server to mount Windows SMB network shared folders
cifs-utils

sudo sensors-detect --auto # first-time setup for lm-sensors

sudo updatedb # Initial database creation for locate

Users and Permissions

Create Users

# You will be prompted to make a password and supply user data. Only the password is required, all other fields can be left blank

sudo adduser octoprint0
sudo adduser octoprint1

Change users and install octoprint for each

su octoprint0       # You will be prompted to enter the password for this user
cd ~
pip install --upgrade --user pip    # installs latest pip version to user's local environment
        # DO NOT OMIT THE --user FLAG! This can cause serious issues with your system's default pip instance!
~/.local/bin/pip install --user https://get.octoprint.org/latest
exit
su octoprint1       # You will be prompted to enter the password for this user
cd ~
pip install --upgrade --user pip
~/.local/bin/pip install --user https://get.octoprint.org/latest
exit

Create a sudoers.d file

Edit sudoer's to allow octoprint to restart itself and reboot the server

echo -e \
"octoprint0 ALL = NOPASSWD: /bin/systemctl start octoprint.service
octoprint0 ALL = NOPASSWD: /bin/systemctl stop octoprint.service
octoprint0 ALL = NOPASSWD: /bin/systemctl restart octoprint.service
octoprint0 ALL = NOPASSWD: /bin/systemctl reboot
octoprint0 ALL = NOPASSWD: /bin/systemctl poweroff
octoprint0 ALL = NOPASSWD: /usr/sbin/service octoprint restart
octoprint0 ALL = NOPASSWD: /sbin/reboot
octoprint0 ALL = NOPASSWD: /sbin/shutdown -P 0
octoprint1 ALL = NOPASSWD: /bin/systemctl start octoprint.service
octoprint1 ALL = NOPASSWD: /bin/systemctl stop octoprint.service
octoprint1 ALL = NOPASSWD: /bin/systemctl restart octoprint.service
octoprint1 ALL = NOPASSWD: /bin/systemctl reboot
octoprint1 ALL = NOPASSWD: /bin/systemctl poweroff
octoprint1 ALL = NOPASSWD: /usr/sbin/service octoprint restart
octoprint1 ALL = NOPASSWD: /sbin/reboot
octoprint1 ALL = NOPASSWD: /sbin/shutdown -P 0" > /etc/sudoers.d/octoprint

Video Streaming Setup

RTSP -> MJPEG using ffmpeg

Set up ffmpeg RTSP restreaming (This example is using a Wyze camera with RTSP enabled)

sudo mv /etc/ffserver.conf /etc/ffserver.original   # Backup original configuration file
# Writes a new config file using contents below
sudo echo -e \
"#################################################################
# Global Server Config
HTTPPort 8090
HTTPBindAddress 0.0.0.0
MaxHTTPConnections 2000
MaxClients 1000
MaxBandwidth 100000
CustomLog /var/log/ffserver.log

##################################################################
# Definition of the live feeds. Each live feed contains one video

<Feed printer0.ffm>
File /tmp/printer0.ffm
FileMaxSize 1G
ACL allow 127.0.0.1
</Feed>

#################################################################

<Feed printer1.ffm>
File /tmp/printer1.ffm
FileMaxSize 1G
ACL allow 127.0.0.1
</Feed>

##################################################################
# Stream definitions

<Stream printer0.mjpeg>
Feed printer0.ffm
Format mjpeg
VideoBitRate 6140
VideoBufferSize 8192
VideoFrameRate 15
VideoSize hd1080
VideoGopSize 12
VideoCodec mjpeg
NoAudio
VideoQMin 3
VideoQMax 31
</Stream>

<Stream printer0_snap.jpg>
Feed printer0.ffm
Format jpeg
VideoFrameRate 2
VideoIntraOnly
VideoSize hd1080
NoAudio
Strict -1
</Stream>

##################################################################

<Stream printer1.mjpeg>
Feed printer1.ffm
Format mjpeg
VideoBitRate 6140
VideoBufferSize 8192
VideoFrameRate 15
VideoSize hd1080
VideoGopSize 12
VideoCodec mjpeg
NoAudio
VideoQMin 3
VideoQMax 31
</Stream>

<Stream printer1_snap.jpg>
Feed printer1.ffm
Format jpeg
VideoFrameRate 2
VideoIntraOnly
VideoSize hd1080
NoAudio
Strict -1
</Stream>" > /etc/ffserver.conf # Edit this file if any changes are needed

Running everything On-Boot

Creating systemd unit files

Octoprint server instances

We need separate Octoprint instances for each printer (Each one uses it's own port)

# Writes a new webserver config file to nginx for each octoprint instance
sudo echo -e \
"[Unit]
Description=Octoprint - Open Source Printing Interface for 3D printers
Documentation=https://docs.octoprint.org
After=network.target

[Service]
User=octoprint0
Environment=HOME=/home/octoprint0
WorkingDirectory=/home/octoprint0
ExecStart=/usr/bin/python2 /home/octoprint/.local/bin/octoprint --port=5000 serve
Restart=always

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/octoprint0.service

sudo echo -e "[Unit]
Description=Octoprint - Open Source Printing Interface for 3D printers
Documentation=https://docs.octoprint.org
After=network.target

[Service]
User=octoprint1
Environment=HOME=/home/octoprint1
WorkingDirectory=/home/octoprint1
ExecStart=/usr/bin/python2 /home/octoprint/.local/bin/octoprint --port=5001 serve
Restart=always

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/octoprint1.service

# Now we enable the new services
sudo systemctl enable octoprint0.service octoprint1.service

# And finally start the octoprint servers using systemd
sudo service octoprint0 start
sudo service octoprint1 start

ffmpeg and ffserver systemd services

sudo echo -e \
"[Unit]
Description=FFMPEG streaming server service
After=network.target

[Service]
ExecStart=/usr/bin/ffserver

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/ffserver.service

sudo echo -e \
"[Unit]
Description=ffmpeg Recast for RTSP                                                                                                            After=ffserver.service

[Service]
Type=simple                                                                                                                                   WorkingDirectory=/usr/bin/
ExecStart=/usr/bin/ffmpeg -i \"rtsp://<username>:<password>@<rtsp_stream_address>\" http://localhost:8090/printer0.ffm
Restart=always

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/printer0-ffmpeg.service

sudo echo -e \
"[Unit]
Description=FFMPEG streaming server service
After=network.target

[Service]
ExecStart=/usr/bin/ffserver

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/ffserver.service

sudo echo -e \
"[Unit]
Description=ffmpeg Recast for RTSP                                                                                                            After=ffserver.service

[Service]
Type=simple                                                                                                                                   WorkingDirectory=/usr/bin/
ExecStart=/usr/bin/ffmpeg -i \"rtsp://<username>:<password>@<rtsp_stream_address>\" http://localhost:8090/printer1.ffm
Restart=always

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/printer1-ffmpeg.service

echo -e "Please be sure to edit the systemd unit files with the correct rtsp information before starting the services."

Before continuing, be sure to edit the new systemd unit files before enabling and starting them

# If using nano as your text editor:
sudo nano /etc/systemd/system/printer0-ffmpeg.service
sudo nano /etc/systemd/system/printer1-ffmpeg.service

# If using vim as your text editor:
sudo vim /etc/systemd/system/printer0-ffmpeg.service
sudo vim /etc/systemd/system/printer1-ffmpeg.service

# And make sure to reload the systemd daemon
sudo systemctl daemon-reload

Then enable and start the services in order

sudo systemctl enable ffserver.service printer0-ffmpeg.service printer1-ffmpeg.service
sudo service ffserver start
sudo service printer0-ffmpeg.service start
sudo service printer1-ffmpeg.service start

Creating user friendly URL's

nginx config (change the name of the location to your own taste)

sudo echo -e \
"server {
    listen      80;
    server_name  octoprint.lan octoprint-nuc.local localhost;

    location /printer0/ {
        proxy_pass http://127.0.0.1:5000/;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header X-Script-Name /printer0;
        proxy_http_version 1.1;

        client_max_body_size 0;
    }

    location /printer0_stream/ {
        proxy_pass http://127.0.0.1:8090/printer0.mjpeg;
    }

    location /printer0_snap/ {
        proxy_pass http://127.0.0.1:8090/printer0_snap.jpg;
    }

    location /printer1/ {
        proxy_pass http://127.0.0.1:5001/;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header X-Script-Name /printer1;
        proxy_http_version 1.1;

        client_max_body_size 0;
    }

    location /printer1_stream/ {
        proxy_pass http://127.0.0.1:8090/printer1.mjpeg;
    }

    location /printer1_snap/ {
        proxy_pass http://127.0.0.1:8090/printer1_snap.jpg;
    }


    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}" > /etc/nginx/sites-available/octoprint.conf

sudo ln -s /etc/nginx/sites-available/octoprint.conf /etc/nginx/sites-enabled/octoprint.conf
sudo service nginx reload
sudo service nginx restart

You should now have two separate octoprint instances running, along with two RTSP ip cameras streams.

Configuring Octoprint

Server Commands

Timelapse and Snapshot

Extras

Network Adapter Name Fix

Most network adapters get assigned some weird and hard to remember designations these days. To make them more "friendly" you can edit the startup config:

Edit your /etc/default/grub changing the line from

GRUB_CMDLINE_LINUX=""

to

GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"

and, finally:

sudo update-grub

and reboot your system:

sudo reboot

Samba Shares

Exposing a writable folder for dropping in printable files

If you installed the Optional Packages earlier, you can skip this:

sudo apt install -y samba

There are a number of lines in the samba configuration file that need to be edited:

 sudo vim /etc/samba/smb.conf

# Change this to the network name you want to use
  netbios name = octoprint
#------------------------------------------------------------------#
# Allow users who've been granted usershare privileges to create
# public shares, not just authenticated ones
  usershare allow guests = yes
#------------------------------------------------------------------#
[Printer 0 FileStore]
  path = /home/octoprint0/.octoprint/uploads
  writeable = yes
  browseable = yes
  guest ok = yes

[Printer 0 Watched]
  path = /home/octoprint0/.octoprint/watched
  writeable = yes
  browseable = yes
  guest ok = yes

[Printer 1 FileStore]
  path = /home/octoprint1/.octoprint/uploads
  writeable = yes
  browseable = yes
  guest ok = yes

[Printer 1 Watched]
  path = /home/octoprint1/.octoprint/watched
  writeable = yes
  browseable = yes
  guest ok = yes

Lastly, we need to make sure the folders are writable by everyone:

sudo chmod -R 777 /home/octoprint0/.octoprint/uploads
sudo chmod -R 777 /home/octoprint0/.octoprint/watched
sudo chmod -R 777 /home/octoprint1/.octoprint/uploads
sudo chmod -R 777 /home/octoprint1/.octoprint/watched

On a Windows system, you would be able to access these shares by opening a file exporer window, and typing into the address bar \\octoprint.local


Accessing a Shared Folder on a locally networked Windows system

If you installed the Optional Packages earlier, you can skip this:

sudo apt install -y cifs-utils

First we need to create an empty folder that will "contain" the files hosted on the windows share

sudo mkdir -p /home/octoprint0/.octoprint/uploads/shared
sudo chown -R octoprint0:octoprint0 /home/octoprint0/.octoprint/uploads/shared

Next, we must create a new auto-mount under the filesystem table

sudo vim /etc/fstab

Example (last line is the only one we've added):

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda2 during installation
UUID=[redacted] /               ext4    errors=remount-ro 0       1
## /boot/efi was on /dev/sda1 during installation
UUID=[redacted]  /boot/efi       vfat    umask=0077      0       1
# swap was on /dev/sda3 during installation
UUID=[redacted] none            swap    sw              0       0

//windows-share.local/STL_Files /home/octoprint0/.octoprint/uploads/shared  cifs    uid=octoprint0,gid=octoprint0,password=,dir_mode=0777,file_mode=0666,iocharset=utf8,_netdev   0       0

Now lets test the mount

sudo mount -a

If everything works, the cursor should advance to the next line without any output


NFS Shares

Exposing a writable folder over NFS

If you installed the Optional Packages earlier, you can skip this:

sudo apt install -y nfs-kernel-server

First we need to define the shared folder:

sudo vim /etc/export

An example NFS exports config file:

# /etc/exports: the access control list for filesystems which may be exported
#   to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/octoprint0/.octoprint 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check,no_acl)

Now we need to start the NFS share

sudo exportfs -a
sudo service nfs-kernel-server restart

This share can be accessed on other Linux/Unix systems by following the NFS Client instructions below


Accessing a NFS share on another local server

If you installed the Optional Packages earlier, you can skip this:

sudo apt install -y nfs-common

First we need to create an empty folder to mount the share

sudo mkdir -p /home/octoprint0/.octoprint/uploads/shared
sudo chown -R octoprint0:octoprint0 /home/octoprint0/.octoprint/uploads/shared

Now we add the NFS share to the filesystem table

sudo vim /etc/fstab

Example (last line is the only one we've added):

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda2 during installation
UUID=[redacted] /               ext4    errors=remount-ro 0       1
# /boot/efi was on /dev/sda1 during installation
UUID=[redacted]  /boot/efi       vfat    umask=0077      0       1
# swap was on /dev/sda3 during installation
UUID=[redacted] none            swap    sw              0       0

nfs-share.local:/path/to/shared/folder /home/octoprint0/.octoprint/uploads/shared nfs  auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0

Now let's test the mount:

sudo mount -a

Here Be Dragons! Advanced Transcoding and Reverse Proxy

Hardware Video Transcoding with ffmpeg

This portion of the guide involves building an application from source, which may be difficult for less experienced linux users. I personally don't have a ton of experience with the backend of ffmpeg, so there won't be a lot of help here if something goes wrong

I initially started building this portion of the guide while combining features from this tutorial and this tutorial for building ffmpeg to be able to use Intel's hardware transcoding to offload most of the heavy processing involved with getting a video feed from an RTSP source, however many of the configuration options applied here are either depreciated, out of date, or were completely uneccessary, so it took quite a while to get the sources built in a way that would work for this application.

Also this is still a work in progress. I've got one method mostly working, but there are a number of other ways to do this that I'm still exploring.

One important thing to know is that H264 and other such video streams cannot be transcoded directly to mjpeg using Intel's hardware transcoder. Instead they must be served as a web-enabled stream format like HLS/m3u8 or DASH/webm. This requires extra steps to get it working with Octoprint, namely using a modified plugin that I'm currently developing

Intel QuickSync Transcoding to HLS

Install packages required to build ffmpeg and nginx from source

sudo apt update && \        # Update package info
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt upgrade -y && \    # Upgrade any packages with available updates
sudo apt dist-upgrade -y \  # Update kernel modules with available updates
sudo apt install -y \
build-essential gcc libgd-dev \
libgd3 \
python-pip \
openssh-server \
nginx \
ffmpeg \
avahi-daemon

Intel Quicksync Transcoding to DASH

Work in Progress

Nvidia NVENC Transcoding to HLS

Work in Progress

Nvidia NVENC Transcoding to DASH

Work in Progress

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment