Wireless 3D printing with Raspberry Pi - part 2

This is part 2 of my guide to setting up OctoPrint on Raspberry Pi and making your 3D printer wireless. It might not make sense without checking out part 1 first.

In this part I will cover integrating a webcam, adding more Raspberry Pi control, changing the web address and making it more accessible.

 

Web Camera

First we need to install the mjpg streamer software and it's dependencies. Open a terminal and input the following lines.

wget http://lilnetwork.com/download/raspberrypi/mjpg-streamer.tar.gz

sudo tar xvzf mjpg-streamer.tar.gz
sudo apt-get install subversion
sudo apt-get install libjpeg8-dev
sudo apt-get install imagemagick
sudo apt-get install libav-tools
sudo apt-get install cmake

Next we need to configure it and start it

cd mjpg-streamer/mjpg-streamer

sudo make clean all
export LD_LIBRARY_PATH=.
sudo ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"

 

For some cameras you need to add "-y" after "input_uvc.so". For my camera (Logitech C270) this was needed. It enables YUYV mode. If possible you should use cameras that do not need this parameter as it  will put a bit more strain on the Raspberry Pi's CPU. 

**Edit
I found a patch to make Logitech C270 run natively MJPEG instead of YUVY mode. I saw a cpu load of nearly 100% with the -y command. Check the bottom of the post

If you want lower resolution you could add "-r 320x240" or similar after the "..uvc.so" in input.

If everything works it should give you the following output:

MJPG Streamer Version: svn rev:
 i: Using V4L2 device.: /dev/video0
 i: Desired Resolution: 640 x 480
 i: Frames Per Second.: 5
 i: Format............: MJPEG
[...]
 o: www-folder-path...: disabled
 o: HTTP TCP port.....: 8080
 o: username:password.: disabled
 o: commands..........: enabled

Open a browser on you computer and type in the following address:

http://<your Raspi's IP>:8080/?action=stream

You should now be able to see a live video feed from the camera.

We then need to add this to the OctoPrint config. This can be done through the settings menu in the OctoPrint browser interface or in the terminal.

For web interface go to Settings-Webcam and fill in the appropriate fields as shown in the image below.

Untick "Enable OctoPrint watermark in timelapse movies" if you don't want that.

You could also do this in the terminal if you want to.

sudo nano ~/.octoprint/config.yaml

Add this text to the end of the file

webcam:
  stream: http://<your Raspi's IP>:8080/?action=stream
  snapshot: http://127.0.0.1/?action=snapshot
  ffmpeg: /usr/bin/avconv
  watermark: false

After adding this to the config you should restart the OctoPrint service

sudo service octoprint restart

Do a hard refresh of the OctoPrint interface by pressing Shift+Ctrl+R or clear the cache.

You should now see a live image from your camera under the "Control" tab and a new tab called "Timelapse"

 

Turning camera on and off from OctoPrint

First we need to make the file that OctoPrint will execute.

sudo nano /etc/init.d/raspi_streamer

Add the following to this file

#!/bin/bash
# Start / stop streamer

### BEGIN INIT INFO
# Provides:          raspi_streamer
# Required-Start:    $local_fs networking
# Required-Stop:     $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: raspi_streamer
### END INIT INFO

case "$1" in
   start)
         sudo /usr/local/bin/mjpg_streamer -i "/usr/local/lib/input_uvc.so" -o "/usr/local/lib/output_http.so -w /usr/local/www" >/dev/null 2>&1 &
         echo "$0: started"
     ;;
   stop)
         sudo pkill -x mjpg_streamer
         echo "$0: stopped"
     ;;
   *)
         echo "Usage: $0 {start|stop}" >&2
     ;;
esac

Now we need to make it executable

sudo chmod +x /etc/init.d/raspi_streamer

And then we need to make it run on boot.

sudo update-rc.d raspi_streamer defaults

Finally let's add some buttons to the OctoPrint GUI to trigger it on and off.

sudo nano ~/.octoprint/config.yaml
system:
  actions:
  - action: streamon
    command: /etc/init.d/raspi_streamer start
    confirm: false
    name: Start video stream
  - action: streamoff
    command: /etc/init.d/raspi_streamer stop
    confirm: false
    name: Stop video stream

Now we need to restart OctoPrint service and refresh GUI

sudo service octoprint restart

You should now see two new buttons under the "System" tab in the web interface.

 

System commands in OctoPrint

If you want to be able to shutdown or restart your Raspberry Pi from the OctoPrint interface follow these steps. First we need to give the user that is running OctoPrint(for me it the default Pi) permissions to turn off or restart the Pi.

Open the terminal and write the following

sudo -s

(this should present you with the following: root@raspberrypi:/home/pi#)

Now write:

cat > /etc/sudoers.d/octoprint-shutdown
pi ALL=NOPASSWD: /sbin/shutdown

(You will not get any confirmation between these two lines)

When done press Ctrl+D(^D) to exit the command and write "exit" to go back to the terminal.

The open the config file for OctoPrint again. We need to add the actions.

sudo nano ~/.octoprint/config.yaml

Add the following to the end of the file(merge it with the "system" from last part):

system:
  actions:
  - name: Shutdown
    command: sudo shutdown -h now
    action: shutdown
    confirm: You are about to shutdown the system.
  - name: Reboot
    command: sudo shutdown -r now
    action: reboot
    confirm: You are about to reboot the system

After adding this to the config you should restart the OctoPrint service

sudo service octoprint restart

Do a hard refresh of the OctoPrint interface by pressing Shift+Ctrl+R or clear the cache.

You should now have a new menu item called "System" with the two options you just added.

 

Make it more accessible

sudo apt-get install haproxy

We need to change the config file

sudo nano /etc/haproxy/haproxy.cfg

Replace the content with this

frontend public
    bind *:80
    use_backend webcam if { path_beg /webcam/ }
    use_backend octoprint_socket if { path_beg /sockjs/ }
    default_backend octoprint

backend octoprint
    reqrep ^([^\ :]*)\ /(.*)     \1\ /\2
    server octoprint1 127.0.0.1:5000
    acl AuthOkay http_auth(L1)
    http-request auth realm octoprint if !AuthOkay

backend octoprint_socket
    reqrep ^([^\ :]*)\ /(.*)     \1\ /\2
    server octoprint1 127.0.0.1:5000

backend webcam
    reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
    server webcam1  127.0.0.1:8080

userlist L1
    group G1

    user <your username> insecure-password <your password> groups G1

This is the standard config file for OctoPrint with a web camera. Just replace all the existing content with this, and change <your username> and <your password> to what you want.

Alternatively you can use this config if you do not want user authentication.

global
        maxconn 4096
        user haproxy
        group haproxy
        daemon
        log 127.0.0.1 local0 debug

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option redispatch
        option http-server-close
        option forwardfor
        maxconn 2000
        timeout connect 5s
        timeout client  15min
        timeout server  15min

frontend public
        bind *:80
        use_backend webcam if { path_beg /webcam/ }
        default_backend octoprint

backend octoprint
        reqrep ^([^\ :]*)\ /(.*)     \1\ /\2
        option forwardfor
        server octoprint1 127.0.0.1:5000

backend webcam
        reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
        server webcam1  127.0.0.1:8080

Then we need to enable Haproxy

sudo nano /etc/default/haproxy

Change ENABLED to 1.

You can start the service by writing.

sudo service haproxy start

http://<your Raspi's IP> in your browser should now take you straight to the OctoPrint interface. Now we need to change the webcam settings again. Go to the settings tab and change 

http://<your Raspi's IP>:8080/?action=stream

to

/webcam/?action=stream

You should not change the snapshot URL. 

Add the following line under "server:" i ~/.octoprint/config.yaml (See further up if you don't remember how)

server:
    host: 127.0.0.1

Restart OctoPrint and refresh browser.

 

Change hostname and reach it by hostname

sudo apt-get install avahi-daemon

Change the name behind 127.0.0.1 save and close.

sudo reboot

You should now be able to reach your Pi on <YourPrinterName>.local

It is also possible to you it when with ssh. 
 

ssh <Username>@<YourPrinterName>.local


For windows you will also need to install Bonjour and allow traffic on UDP port 5353 to make this work. I will not go into more detail on this subject as I don't find it very important.

If you have any questions regarding this guide feel free to leave a comment.

 

**The patch

The patch for Logitech C270 (and maybe other cameras) to run natively MJPEG to save performance on Raspberry Pi:

sudo apt-get install libv4l-dev
sudo svn co https://svn.code.sf.net/p/mjpg-streamer/code mjpg-streamer
cd mjpg-streamer/mjpg-streamer

Now we need to make a new file with the patch

nano input_uvc_patch

Put the text under into the file and save.

--- plugins/input_uvc/input_uvc.c       (revision 174)
+++ plugins/input_uvc/input_uvc.c       (working copy)
@@ -405,9 +405,13 @@
         if(pcontext->videoIn->formatIn == V4L2_PIX_FMT_YUYV) {
             DBG("compressing frame from input: %d\n", (int)pcontext->id);
             pglobal->in[pcontext->id].size = compress_yuyv_to_jpeg(pcontext->videoIn, pglobal->in[pcontext->id].buf, pcontext->videoIn->framesizeIn, gquality);
+            /* copy this frame's timestamp to user space */
+            pglobal->in[pcontext->id].timestamp = pcontext->videoIn->buf.timestamp;
         } else {
             DBG("copying frame from input: %d\n", (int)pcontext->id);
-            pglobal->in[pcontext->id].size = memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->buf.bytesused);
+            pglobal->in[pcontext->id].size = memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->tmpbytesused);
+            /* copy this frame's timestamp to user space */
+            pglobal->in[pcontext->id].timestamp = pcontext->videoIn->tmptimestamp;
         }
 
 #if 0
@@ -418,8 +422,6 @@
         prev_size = global->size;
 #endif
 
-        /* copy this frame's timestamp to user space */
-        pglobal->in[pcontext->id].timestamp = pcontext->videoIn->buf.timestamp;
 
         /* signal fresh_frame */
         pthread_cond_broadcast(&pglobal->in[pcontext->id].db_update);
Index: plugins/input_uvc/v4l2uvc.c
===================================================================
--- plugins/input_uvc/v4l2uvc.c (revision 174)
+++ plugins/input_uvc/v4l2uvc.c (working copy)
@@ -450,6 +450,8 @@
         */
 
         memcpy(vd->tmpbuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
+        vd->tmpbytesused = vd->buf.bytesused;
+        vd->tmptimestamp = vd->buf.timestamp;
 
         if(debug)
             fprintf(stderr, "bytes in used %d \n", vd->buf.bytesused);
Index: plugins/input_uvc/v4l2uvc.h
===================================================================
--- plugins/input_uvc/v4l2uvc.h (revision 174)
+++ plugins/input_uvc/v4l2uvc.h (working copy)
@@ -28,6 +28,7 @@
 
 
 #include <stdio.h>
+#include <stdint.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -105,6 +106,8 @@
     int framecount;
     int recordstart;
     int recordtime;
+    uint32_t tmpbytesused;
+    struct timeval tmptimestamp;
 };
 

Let's run the patch

patch -p0 < input_uvc_patch
make USE_LIBV4L2=true clean all
sudo make install