Prusa Mini+ WiFi and virtual USB

September 9, 2021 16:02

What?

The Prusa Mini+ has a problem: It has no WiFi. Also the software does not support any wifi module until now - that feature is planned, but not implemented. Additionally the web interface is, let’s say, not really working, as the printer struggles with serving the static contents (like .css and .js), which are required to render the site. This issue is also known since release 4.3.0 but also not resolved (#1285).

This article describes how I used an Orange Pi Zero LTS to get the printer on my WiFi, add an wirelessly accessible storage and “fix” the webinterface. For the first point I follow this gerneral idea and for the second we will take a look into NGINX and its caching abilities.

What will you need for this?

  • Orange Pi zero LTS with Armbian and a case
  • Sandisk class 10 32GB
  • LAN & USB cable
  • Prusa Mini+

Getting the Orange Pi ready

But before we start some more general words about the Orange Pi: The Orange Pi uses not too much power! It is completly satisfied by using an 5V 2A power supply (or even less, as it works fine on my USB 3.0 - which serves about 5V 500mA), but (at least for me) you have to ensure the utilized cable does also work fine. A not lit up green led on the PCB or missing serial output over the 3-pin header beside the LAN port are indicating critical power issues. Also random crashes or freezes during the boot stage commonly point to an brown-out on the board - I fixed both issues, by just using an other (higher quality) cable. Finally you should make sure to use the correct image (I used the Armbian Buster image, as Bullseye was not stable (user-space) during my experiments) for your specific Orange Pi Model - ask me how I know :)

For further debugging (also down the road) I strongly recommend you to install this Systemd service unit, which reconfigures the Orange Pis leds to blink periodically and also shine during activity. This is very helpful to detect black- or brown-outs during later use (check your power cables, the printers USB port is powerful enough!). Note that any stop of the leds blinking is only “useful” after the red led started to blink initially.

[Unit]
Description=LED control service
Before=basic.target
After=local-fs.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes

# Set the green LED twice, as it seems to be ignored on the first try. Mostly.
ExecStartPre=/usr/bin/bash -c '/usr/bin/echo activity > /sys/class/leds/orangepi\:red\:status/trigger'
ExecStartPre=/usr/bin/bash -c '/usr/bin/echo heartbeat > /sys/class/leds/orangepi\:green\:pwr/trigger'
ExecStart=/usr/bin/bash -c '/usr/bin/echo heartbeat > /sys/class/leds/orangepi\:green\:pwr/trigger'
ExecStop=/usr/bin/bash -c '/usr/bin/echo none > /sys/class/leds/orangepi\:green\:pwr/trigger'
ExecStopPost=/usr/bin/bash -c '/usr/bin/echo heartbeat > /sys/class/leds/orangepi\:red\:status/trigger'

[Install]
WantedBy=basic.target

Configure the LAN port

Set the Orange Pis LAN interface IP to a static one (as there is no DHCP on this site). I recommend to use nmtui for that and to either create a new profile or to modify the existing one.

Make it a router - setup DHCP!

We will use dnsmasq for that - make sure to install it beforehand. Then add those lines to the configuration:

port=0
interface=eth0
dhcp-range=192.168.10.50,192.168.10.250,12h
dhcp-host=xx:xx:xx:xx:xx:xx,PrusaMiniPlus,192.168.10.2

Insert the MAC address of your printer on xx:xx:xx:xx:xx:xx. The config disables any DNS functionality (see here), locks it only to the LAN interface, configures a “static” IP for the printer and configures a dynamic port range (just in case).

Serve the webinterface

I’ve used NGINX for the caching of the webinterface, as it is small, fast and simple (?). Add this (based on that) into your NGINX configuration (into the html block).

    proxy_cache_path  /srv/nginx/cache levels=1:2    keys_zone=STATIC:10m  max_size=1g;
    server {
        listen 80 default_server;
        location / {
            proxy_pass             http://192.168.10.2;
            proxy_set_header       Host $host;
            proxy_buffering        on;
            proxy_cache            STATIC;
            proxy_cache_valid      200;
            proxy_cache_use_stale  error timeout invalid_header updating
                                   http_500 http_502 http_503 http_504;
            proxy_read_timeout 600s;
        }
        error_page 502 /502.html;
        location = /502.html {
            alias /srv/502.html;
        }
    }

And then install this as the error page to /srv/502.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Whooops!</title>
    <meta http-equiv="refresh" content="60">
    <style>
        body {
            margin:auto;
            text-align:center;
        }
    </style>
</head>
<body>
    <h1>Whoops!</h1>
    <iframe src="https://giphy.com/embed/pcC2u7rl89b44" width="480" height="270" frameBorder="0" class="giphy-embed" allowFullScreen></iframe>
    <p>
        I am very sorry to inform you that NGINX failed to communicate with the printer.
        This is commonly solved by restarting the printer and making sure it gets the correct ip address.
        You may need to make sure to re-apply the <i>lan_settings.ini</i> from the attached virtual USB storage.
    </p>
    <p>Or maybe the printer is just not running. In that case - did you try to turn it off and on again?</p>
    <p>Good luck,</p>
    <p>IT Guy</p>
</body>
</html>

Make sure the cache path exist (e.g. /srv/nginx/cache -> mkdir -p /srv/nginx/cache) and to also use very high timeout values (again, the printer is messing up the responses)! Then restart NGINX to apply the changed config - the web interface should now load (give it time, reload it multiple times until it works) under port 80.

Simulate an USB stick

Now we let the orange Pi simulate a generic mass storage device with its own web interface.

Force the USB into “peripheral” mode

This step depends on which OS you use. Armbian is already preconfigured to serve a virtual serial console using the USB OTG port, but other systems may not. If your system is not using the USB port as peripheral but as otg or host you won’t be able to load and activate the kernel module (g_mass_storage) during the next steps. You may need to modify your device tree (which is compiled into your kernel) or create an appropiate overlay. Take a look into your /sys/class/firmware/devicetree/[USB_PORT_DRIVER]/dr_mode to determine the state of your usb port mode. Further references about device trees, overlays and the Orange Pis processor are here, here and here.

Armbian: Disable virtual serial console

For that modify /etc/modules-load.d/modules.conf and remove or comment (by prepending a #) g_serial.

Create the USB image

Let’s create the piusb.img as FAT32 stick.

sudo fallocate -l 4G /piusb.img
sudo mkdosfs /piusb.img -F 32 -I

Mount that img on boot

So you can add files yourself - extend /etc/fstab with (make sure the target directory exist and to use the sync option to make any file modifications instantly visible to the printer):

/piusb.img  /mnt/vstick   auto    defaults,rw,sync 0   0

Start the virtual USB on boot

Add g_mass_storage into /etc/modules-load.d/modules.conf and configure the loading options inside /etc/modprobe.d/g_mass_storage.conf by appending:

options g_mass_storage file=/piusb.img stall=0 ro=1 removable=1

In case you want to know more about those options, use modinfo g_mass_storage. Or maybe you want to emulate other virtual devices? Try this documentation!

Add an other webinterface

Let’s use Docker (-Compose) for hosting an other web interface with the files from the virtual USB image on port 8080:

version: '3'
services:
    fm:
        image: filebrowser/filebrowser
        restart: always
        ports:
            - 8080:80
        volumes:
            - /mnt/vstick:/srv
            - ./database.db:/database.db
            - ./docker.json:/.filebrowser.json

Make sure to create the database.db and docker.json before you start it, as empty files / directories created by Docker will be contra-productive! To disable any authentication run this after the initial start via docker:

docker run --rm -it -v $(pwd)/database.db:/database.db filebrowser/filebrowser config set --auth.method=noauth

You may set the instance name to “Prusa Mini+” now.

Auto-Import USB files

You may implement this to auto-import any *.gcode files from connected USB sticks automatically (primary reference is here).

  1. sudo apt-get install autofs
  2. Insert into /etc/auto.master: /mnt/auto /etc/auto.automnt --timeout=5 --ghost
  3. sudo mkdir /mnt/auto
  4. Now configure what should be mounted into this folder - append into /etc/auto.automnt that: stick -fstype=auto,sync :/dev/sda1
  5. Activate: sudo systemctl reload autofs
  6. Add those cronjobs (root user) to import files and leave a warning for every user to use the web interface instead the USB stick:
    * * * * * find /mnt/auto/stick -name "*.gcode" -print0 | xargs -0 mv -v --target-directory=/mnt/loop
    0 * * * * echo -e "DO NOT USE THIS STICK\nUse the webinterface to view the progress: http://[ORANGE_PI_IP]/\nAnd also to upload the files: http://[ORANGE_PI_IP]:8080/" > /mnt/auto/stick/README.txt
    

Prusa Mini+ Setup

Make sure to use a static IP configuration (I strongly recommend that, as it is way more stable), otherwise you have to implement the Dnsmasq part of the Orange Pi setup. This is the lan_settings.ini which must be installed on your printer using any USB stick (or the virtual stick later on).

[lan_ip4]
type=STATIC
hostname=PrusaMINI
address=192.168.10.2
mask=255.255.255.0
gateway=192.168.10.1