back to index

3D printer server and dev environment


Problem
Solution
Bare system installation
      Install the basics
      Install Python and pip
            Minor security detour
            What would probably work outright
OctoPrint installation
      Make the user
      Install and set octoprint
mjpg-streamer installation
            Minor installation detour
            What would probably work outright
      Installing and getting it to run
Installing OctoPrint plugins
Partial attempt to install Cura
Filament error encountered
Set OctoPrint to run on startup
Set up streaming video to be enabled/disabled from OctoPrint
octocmd
More detailed API control
Controller reset script, port configuration
      Controller reset from octoprint
gmessage, gbeep - python g-code directly to port
octoprint REST API
Arduino dev environment and avrdude install
      Getting avrdude to communicate with the controller
      arduino dev tools, compiling controller firmware
Compiling the controller firmware
            The scenic route
            What would likely run outright
locale error solving
GPIO control, ATX PSU control
      Running stuff on shutdown:
      ATX power control hardware
      USB hub power split
Set up printer device as symlink, as /dev/3dprinter
Install tool to monitor http requests
      httpry
      justnuffer
            install on intel machine:
            on raspi:
            Some interesting intercepted data:
Install tool to play with websockets
            The scenic route
            What would probably work outright
            Testing websocket communication between two machines
octoPrint config.yaml notes
      System menu configuration for streaming, board reset, and power
      Events section, for displaying status

Problem

A 3D printer was obtained and built. Printing however required either a laptop tethered to the machine, or printing from SD card which is difficult to supervise. The panel control is also annoying.

Some functionality also requires running scripts directly communicating with the printer controller. Additional sensors and tools also may have to be attached, and easy changes of tools are needed.

A standalone server, capable of feeding G-code to the printer, and also running all sorts of scripts directly communicating with the controller, was desired.


Solution

A Raspberry Pi board was sacrificed to the 3D printing gods. Minibian distro was used as the basis of the system, with OctoPrint as the printer control software.


Bare system installation

The Minibian image was downloaded and written to a 8GB SD card (4GB would be enough, though). The board with the card was attached to the Ethernet network and powered up. Default password "raspberry" was used to log in as root.

The card was repartitioned to this result:

        Device Boot      Start         End      Blocks   Id  System
 /dev/mmcblk0p1              16       97727       48856    b  W95 FAT32
 /dev/mmcblk0p2           97728    14900000     7401136+  83  Linux
 /dev/mmcblk0p3        14900001    15759359      429679+  82  Linux swap / Solaris

 fdisk /dev/mmcblk0

...and then issue commands

d 2
n
<cr>
2
<cr>
14900000
n
<cr>
3
<cr>
<cr>
t
3
82
w
q
(delete partition #2, create new partition #2, primary, starting just after #1, ending at 14900000 blocks, then create a new #3, starting just after #2 and ending at the end, then change its type to 82 (swap), and write the changes and quit)

The computer was rebooted with the new partition table, and the filesystem was resized to its full size:

 resize2fs /dev/mmcblk0p2
(takes few seconds)

The SSH key was added to the card.

 mkdir .ssh
 cd .ssh;wget http://<wherever_they_are>/authorized_keys; chmod 600 authorized_keys;cd
...and we can connect without having to enter the password.

Install the basics

Now set up the basics:

apt-get update
apt-get install mc strace usbutils sudo lsof psmisc raspi-gpio wiringpi mlocate setserial apt-file tcpdump tshark

Install Python and pip

apt-get install python git

install pip: https://pip.pypa.io/en/latest/installing/

 wget https://bootstrap.pypa.io/get-pip.py
 python get-pip.py

(Newer systems do not require installing pip separately.)

Minor security detour

got:

/tmp/tmpTgcbsP/pip.zip/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
did:
pip install requests[security]

got failure after a while with gcc not available, so...

apt-get install gcc

tried again,

pip install requests[security]

and got

gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -DUSE__THREAD -I/usr/include/ffi -I/usr/include/libffi -I/usr/include/python2.7 -c c/_cffi_backend.c -o build/temp.linux-armv6l-2.7/c/_cffi_backend.o
  c/_cffi_backend.c:2:20: fatal error: Python.h: No such file or directory
  compilation terminated.
  error: command 'gcc' failed with exit status 1

so did

apt-get install python-dev
...and, again...
pip install requests[security]

...aaaand got

    building '_cffi_backend' extension
    gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -DUSE__THREAD -I/usr/include/ffi -I/usr/include/libffi -I/usr/include/python2.7 -c c/_cffi_backend.c -o build/temp.linux-armv6l-2.7/c/_cffi_backend.o
    c/_cffi_backend.c:13:17: fatal error: ffi.h: No such file or directory
    compilation terminated.
    error: command 'gcc' failed with exit status 1

...soooo,

apt-get install libffi-dev

...and, again...

pip install requests[security]
...this time it runs for LONG time and gcc eats a lot of cpu

What would probably work outright

 apt-get install python git gcc python-dev libffi-dev
 wget https://bootstrap.pypa.io/get-pip.py
 python get-pip.py
 pip install requests[security]

OctoPrint installation

Make the user

We have to set the user for the OctoPrint; it doesn't like to run as root.

 mkdir /home/pi
 useradd -d /home/pi -m -s /bin/bash -U pi
 chown pi:pi /home/pi

Set the user to the groups for devices access.

 usermod -a -G tty pi
 usermod -a -G dialout pi
 usermod -a -G video pi

Prepare the tools we will need in a moment.

 apt-get install python-pip python-setuptools python-virtualenv git libyaml-dev build-essential

Install and set octoprint

https://github.com/foosel/OctoPrint/wiki/Setup-on-a-Raspberry-Pi-running-Raspbian

 su pi
 cd
 git clone https://github.com/foosel/OctoPrint.git
 cd OctoPrint
 virtualenv --system-site-packages venv
 ./venv/bin/python setup.py install

...and OctoPrint is installed. Now, let's run it...

 ~/OctoPrint/venv/bin/octoprint

...and it works. So,

 exit
...and we're the root again.


mjpg-streamer installation

Now, to allow supervising the printouts, install mjpg-streamer...

 apt-get install subversion libjpeg8-dev imagemagick libav-tools cmake
(lots of files, 152 megabytes of space, tons of x11 libraries)

Minor installation detour

and let's do this as the pi user, so...

 su pi
 cd
 git clone https://github.com/jacksonliam/mjpg-streamer.git
 cd mjpg-streamer/mjpg-streamer-experimental
 make
...and, fail:
 /home/pi/mjpg-streamer/mjpg-streamer-experimental/plugins/input_raspicam/input_raspicam.c:44:22: fatal error: bcm_host.h: No such file or directory
 compilation terminated.
...so...
 apt-get install libfreeimage-dev
...retry and fail again so...
 apt-get install libraspberrypi-dev
...took a while... and did not work, because as it turned out later we installed it on a wrong raspi, blame the bottle of creme de cassis ...so let's install the file lookup utility for the repositories, we didn't have it in the stock preinstall then...
 apt-get install apt-file
 apt-file update
 apt-file search bcm_host
...results in...
 libraspberrypi-dev: /opt/vc/include/bcm_host.h
 libraspberrypi0: /opt/vc/lib/libbcm_host.so
 python-picamera: /usr/lib/python2.7/dist-packages/picamera/bcm_host.py
 python-picamera: /usr/share/pyshared/picamera/bcm_host.py
 python3-picamera: /usr/lib/python3/dist-packages/picamera/bcm_host.py

...so retrying...

 apt-get install libraspberrypi-dev
...and the /opt/vc/include/ magically appears.
...and we installed it by mistake to the Raspi1 supervisory machine, so we have to test if it reboots.
...and we ran it on the correct machine and it appears there.
...and retrying
 make
...and it works.

What would probably work outright

 apt-get install subversion libjpeg8-dev imagemagick libav-tools cmake libraspbrerrypi-dev
 su pi
 cd
 git clone https://github.com/jacksonliam/mjpg-streamer.git
 cd mjpg-streamer/mjpg-streamer-experimental
 make

Installing and getting it to run

So, as root:

 make install

...and streamer works:

 ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so"
...but there is no video, http://octoprint:8080/?action=snapshot yields white screen, manual connection to port gives a content-type: image/jpeg header but no data in the body.

So, to test the webcam, fswebcam was installed:

 apt-get install fswebcam
and tested
 fswebcam -v -r 640x480 -d /dev/video0 /tmp/output.jpeg
and it worked well.

edited file plugins/input_uvc/input_uvc.c replaced line

    int width = 640, height = 480, fps = -1, format = V4L2_PIX_FMT_MJPEG, i;
with
    int width = 640, height = 480, fps = -1, format = V4L2_PIX_FMT_YUYV, i;
tried to recompile
 make clean
 make
...and it started working. But while streaming the process takes 50% of the cpu. too much. So trying
    int width = 352, height = 288, fps = 4, format = V4L2_PIX_FMT_YUYV, i;
...and it fails, so trying instead running the commandline
 ./mjpg_streamer -i ./input_uvc.so --resolution=QVGA -o ./output_http.so
...and it runs on nice 12% of the CPU.


Installing OctoPrint plugins

These may be useful:


Partial attempt to install Cura

Installing Cura: https://github.com/foosel/OctoPrint/wiki/Cura-Integration

We need to upgrade stock gcc to version 4.8, from the v4.6:

edit /etc/apt/sources.list, add line

 deb http://mirrordirector.raspbian.org/raspbian/ jessie main contrib non-free rpi

create file /etc/apt/preferences.d/0Jessie with content:

Package: *
 Pin: release n=wheezy
 Pin-Priority: 900

Package: * Pin: release n=jessie Pin-Priority: 300

Package: * Pin: release o=Raspbian Pin-Priority: -10

...then...

 apt-get update
...takes a while, and after this package list reading takes much longer, though luckily just once;
now we install new gcc from the Jessie repository...
 apt-get install -t jessie gcc g++

...and decided against continuing as raspi slicer would take too long time, not worth it, better run it on a way stronger machine


Filament error encountered

If the hot-end fan fails, filament softens too early, then fold and jams in the upper part of the hot end, like a pushed rope.

Note: Running the workpiece cooling fan on full overcools the hot end and trips thermal runaway!


Set OctoPrint to run on startup

as root:

 cp scripts/octoprint.init /etc/init.d/octoprint
 chmod +x /etc/init.d/octoprint
 cp scripts/octoprint.default /etc/default/octoprint

edit /etc/default/octoprint:

 NICELEVEL=-2
change to
 NICELEVEL=-18
...to not be bothered with other running crap, and added line
 DAEMON=/home/pi/OctoPrint/venv/bin/octoprint
and run:
 update-rc.d octoprint defaults


Set up streaming video to be enabled/disabled from OctoPrint

Set up mjpg streamer:

 /home/pi/scripts/raspi_streamer
with content:
 #!/bin/bash
 # from here: https://github.com/foosel/OctoPrint/issues/441
 RES=QVGA
 PlugPath=/usr/local/lib
 Daemon=mjpg_streamer
 DaemonBase=/usr/local
 DaemonArgs="-i \"./input_raspicam.so\" -o \"./output_http.so\""
 case "$1" in
    start)
          mjpg_streamer -b -i "$PlugPath/input_uvc.so --resolution=QVGA" -o "$PlugPath/output_http.so"
          echo "$0: started"
      ;;
    stop)
          pkill -x ${Daemon}
          echo "$0: stopped"
      ;;
    *)
          echo "Usage: $0 {start|stop}" >&2
      ;;
 esac

Shut down octoprint (or it would overwrite its configuration file we just edited):

 service octoprint stop

and edit /home/pi/.octoprint/config.yaml:

 system: {}

change to:

 system:
   actions:
   - action: streamon
     command: /home/pi/scripts/raspi_streamer start
     confirm: false
     name: Start video stream
   - action: streamoff
     command: /home/pi/scripts/raspi_streamer stop
     confirm: false
     name: Stop video stream
   -action: divider

and

 temperature: {}

change to:

 temperature:
   profiles:
   - name: ABS
     extruder: 210
     bed: 100
   - name: PLA
     extruder: 180
     bed: 60


octocmd

Setting up Octocmd, octoprint commandline control: http://octocmd.readthedocs.org/en/latest/

 pip install https://github.com/vishnubob/octocmd/archive/master.zip
...and it ran and it did not work, octocmd nowhere to be found. Apparently setup.py mysteriously failed. (On another computer it worked A-OK.)

So,

 cd /home/pi
 wget https://github.com/vishnubob/octocmd/archive/master.zip
 unzip master.zip
...octocmd-master directory apears, so we get rid of the archive...
 rm master.zip
 chmod 755 /home/pi/octocmd-master/scripts/octocmd
 ln -s /home/pi/octocmd-master/scripts/octocmd /usr/bin/

initialize:

 octocmd init
 > Octorpint URL: http://127.0.0.1:5000/
 > Octoprint API key: <key>

Uploaded gcode files are at /home/pi/.octoprint/uploads


More detailed API control

It was decided to build a more functional API control. curl and bash were used instead of python the octocmd is written in, due to much faster start.

The whole project was move to its own page, sw_8control.


Controller reset script, port configuration

The controller needs to be reset by hardware. Examples are aborted print, tripped thermal protection, or upload of new firmware.

Install script for pulsing DTR to reset arduino, from https://github.com/brandonedens/jukebox/blob/master/bin/arduino-pulse-dtr

 #!/usr/bin/python
 """
 Script to pulse the DTR line prior to firmware upload.
 """
 import serial
 import time
 import sys
 ser = serial.Serial('/dev/ttyACM0', 115200, timeout=1)
 ser.setDTR(1)
 time.sleep(0.5)
 ser.setDTR(0)
 ser.close()

put it to /home/pi/scripts/ardureset, then

 chmod 755 /home/pi/scripts/ardureset
 ln -s /home/pi/scripts/ardureset /usr/bin/

...and it fails,

 Traceback (most recent call last):
   File "/usr/bin/ardureset", line 6, in <module>
     import serial
 ImportError: No module named serial
...so...
 pip install pyserial
...and now it works...

And because stty often does not support 250000 bps, here is a setup script, /home/pi/scripts/gsetport

 #!/usr/bin/python
 import serial
 import sys
 ser = serial.Serial('/dev/ttyACM0', 250000, timeout=1)
 ser.close()

add symlinks...

 ln -s /home/pi/scripts/gsetport /usr/bin/

According to https://github.com/foosel/OctoPrint/wiki/OctoPrint-support-for-250000-baud-rate-on-Raspbian there can be a problem with some older pyserial libraries with this speed.

The octoprint distro has a working version.

For other versions, there is a patch for the serialposix.py file, located at /usr/local/lib/python2.7/dist-packages/serial/serialposix.py and the patch is here: http://sourceforge.net/p/pyserial/patches/_discuss/thread/ed3fb0de/f4fd/attachment/pyserial.patch

Controller reset from octoprint

add to /home/pi/.octoprint/config.yaml, to the System: section:

  - action: divider
  - action: ardureset
    command: /home/pi/scripts/ardureset
    confirm: "Are you sure you want to reset the controller?"
    name: Reset controller board


gmessage, gbeep - python g-code directly to port

created script for showing messages on the display via the M117 g-code: /home/pi/scripts/gmessage

 #!/usr/bin/python
 """
 Script to show messages on attached display via g-code
 Do not use during active print jobs!
 Todo: add checks.
 """
 import serial
 import time
 import sys
 if len(sys.argv) < 2 :
   print 'Shows messages on display via g-code'
   print 'Usage: ',sys.argv[0],' <message>'
   exit()
 ser = serial.Serial('/dev/ttyACM0', 250000, timeout=1)
 ser.write('\nM117 '+sys.argv[1]+'\n')
 ser.close()

and to beep via g-code: /home/pi/scripts/gbeep

 #!/usr/bin/python
 """
 Script to beep via g-code
 Do not use during active print jobs!
 Todo: add checks.
 """
 import serial
 import sys
 ser = serial.Serial('/dev/ttyACM0', 250000, timeout=1)
 ser.write('\nM300\n')
 ser.close()

add symlinks...

 ln -s /home/pi/scripts/gmessage /usr/bin/
 ln -s /home/pi/scripts/gbeep /usr/bin/


octoprint REST API

octoprint API: http://docs.octoprint.org/en/master/api/index.html

 export API=CAFEBABE....
 export OCTOHOST=http://127.0.0.1:5000

connection:

get API version:

 curl -H "X-Api-Key: $API" http://127.0.0.1:5000/api/version

get status:

 curl -H "X-Api-Key: $API" http://127.0.0.1:5000/api/connection
connect:
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "connect" }' http://127.0.0.1:5000/api/connection
disconnect:
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "disconnect" }' http://127.0.0.1:5000/api/connection

execute arbitrary g-code command (here, a beep):

 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "M300" }' http://127.0.0.1:5000/api/printer/command
execute more commands as list (here, two beeps and a message):
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "commands": ["M300","M117 test","M300"] }' http://127.0.0.1:5000/api/printer/command

get print job status:

 curl -H "X-Api-Key: $API" http://127.0.0.1:5000/api/job

start, restart, pause/unpause, cancel job: (also, it is /api/job, not /api/control/job)

 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "start" }' http://127.0.0.1:5000/api/job
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "restart" }' http://127.0.0.1:5000/api/job
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "pause" }' http://127.0.0.1:5000/api/job
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "cancel" }' http://127.0.0.1:5000/api/job

home the tool head:

 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "home" }' http://127.0.0.1:5000/api/printer/printhead
relative move of the tool head:
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "jog", "x": 10, "y": 20, "z": -30 }' http://127.0.0.1:5000/api/printer/printhead

set tool head temperature for tool 0 and 1 (here, 180 and 210 'C):

 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "target", "targets": { "tool0": 180, "tool1": 210" } }' http://127.0.0.1:5000/api/printer/tool
select tool head (here, tool0):
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "select", "tool": "tool0" }' http://127.0.0.1:5000/api/printer/tool
extrude/retract filament (by 5mm, retract with negative number):
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "extrude", "amount": 5 }' http://127.0.0.1:5000/api/printer/tool
set bed temperature (here, to 60 deg):
 curl -H "X-Api-Key: $API" -H "Content-Type: application/json" -X POST -d '{ "command": "target", "target": 60 }' http://127.0.0.1:5000/api/printer/bed

get bed temperature/status:

 curl -H "X-Api-Key: $API" http://127.0.0.1:5000/api/printer/bed
get tool status:
 curl -H "X-Api-Key: $API" http://127.0.0.1:5000/api/printer/tool

REVERSE-ENGINEERED:
run tshark -x port 5000|tee x.txt, run example commands; the justniffer tool is better here though

streaming on and off (streamon, streamoff)

 curl -H "X-Api-Key: $API" -H "Content-Type: application/x-www-form-urlencoded" -X POST -d 'action=streamon' http://127.0.0.1:5000/api/system
 curl -H "X-Api-Key: $API" -H "Content-Type: application/x-www-form-urlencoded" -X POST -d 'action=streamoff' http://127.0.0.1:5000/api/system

attempt to get interactive command: failed, will need to go through log files (monitored a parametric command)

   - name: Reporting
     type: section
     children:
     - command: M114
       name: Get Position
       type: feedback_command
       regex: "X:([0-9.]+) Y:([0-9.]+) Z:([0-9.]+) E:([0-9.]+)"
       template: "Position: X={0}, Y={1}, Z={2}, E={3}"

result: a /api/printer/command request is done, with 204 no-data response. Apparently the response data are taken from logs streamed via websocket.

The printer can have several values of its status:


Arduino dev environment and avrdude install

Installing arduino for development of firmware:

 apt-get install arduino-core arduino-mk

installs stuff to /usr/share/arduino

Also, install picocom to test stuff directly over serial line:

 apt-get install picocom

But this one does not work at 250000 bps speed, so maybe better use the sw_picocom-shad one.

Getting avrdude to communicate with the controller

Controller board runs on ATmega2560.

 avrdude -p atmega2560 -P /dev/ttyACM0 -b 115200 -c arduino
...and it keeps failing on
 avrdude: stk500_recv(): programmer is not responding
 avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x00

So let's try something different:

 ardureset; avrdude -p atmega2560 -P /dev/ttyACM0 -b 115200 -c avrispv2
(it has to be preceded by /home/pi/scripts/ardureset script) ...and it is getting errors:
 avrdude: stk500v2_ReceiveMessage(): timeout
 avrdude: stk500v2_getsync(): found AVRISP programmer
 avrdude: ser_recv(): programmer is not responding
...but runs, s-l-o-w-l-y.
...and it turned out that it works a-ok when the octoprint service is stopped, when nothing else listens on the port.

Use lsof -n|grep ttyACM0 to find the culprit.

After killing the offending process it will start running smoothly.

(todo: automate this in arduread/arduwrite scripts; use killall to kill the processes, or an API call to disconnect the printer daemon)

arduino dev tools, compiling controller firmware

Install arduino dev tools, namely ino:

 apt-get install python-dev&amp;&amp;python-setuptools
...python-dev is newest, python-setuptools also newest, cannot run command python-setuptools
 cd /home/pi
 git clone git://github.com/amperka/ino.git
 cd ino
 python setup.py install


Compiling the controller firmware

Now where we have the arduino dev env installed, let's use it.

The scenic route

 cd /home/pi

scp MarlinDev... directory from the other machine (or wget it from its site, or git clone the directory)

 cd /home/pi/MarlinDev-dev-20150910-ShadConfig/Marlin

ino list-models: indicates we should use mega2560

 ino build -m mega2560
...and we got...
 No project found in this directory.
...strace shown it looks for src subdirectory, which was not in the Marlin distro... so let's make it...
 cd ..
 ln -s Marlin src
 ino build -m mega2560
...aaaaand, now it runs!
...aaaaand, we fail.
 .build/mega2560/src/Marlin.cpp:5:35: fatal error: LiquidCrystal_I2C.h: No such file or directory
 compilation terminated.
 .build/mega2560/Makefile:367: recipe for target '.build/mega2560/src/Marlin.o' failed
 make: *** [.build/mega2560/src/Marlin.o] Error 1
 Make failed with code 2

...so,

 cd /usr/share/arduino/libraries/
 git clone git://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
...and the files are fetched into a new directory. so, let's go back and retry...
 cd -
 ino build -m mega2560
...aaand, we got it wrong. grep source for LiquidCrystal_I2C.h, and we get where we should get the files from...
 cd -
 git clone git://github.com/kiyoshigawa/LiquidCrystal_I2C
 cd -
 ino build -m mega2560
...and it failed again. let's look where is the problem...
 strace -f -o /tmp/x ino build -m mega2560
 grep LiquidCrystal_I2C.h /tmp/x
...and we find where it is looking, and that it is actually checking the directory and does not find the file. oops, why?
...so we look and we find that the git clone process created this directory with the files: /usr/share/arduino/libraries/LiquidCrystal_I2C/LiquidCrystal_I2C/
...so...
 cd /usr/share/arduino/libraries/LiquidCrystal_I2C/LiquidCrystal_I2C
 mv * ..
 cd ..
 rmdir LiquidCrystal_I2C
 cd /home/pi/MarlinDev-dev-20150910-ShadConfig/
 ino build -m mega2560
...and fail again. so let's try copying the library to local...
 cp -av /usr/share/arduino/libraries/LiquidCrystal_I2C lib/
...and fail again.

strace shows that it looks in the /usr/share/arduino/libraries/LiquidCrystal/ so let's put it there (don't actually do this, because the missing step is the ino clean step):

 mv /usr/share/arduino/libraries/LiquidCrystal_I2C/* /usr/share/arduino/libraries/LiquidCrystal/
 ino build -m mega2560
...and, crap...
 .build/mega2560/src/Marlin.cpp:7:28: fatal error: LiquidTWI2.h: No such file or directory
 compilation terminated.
 .build/mega2560/Makefile:373: recipe for target '.build/mega2560/src/Marlin.o' failed
 make: *** [.build/mega2560/src/Marlin.o] Error 1
...so, grep -r LiquidTWI *
...and we need this: https://github.com/lincomatic/LiquidTWI2
 git clone git://github.com/lincomatic/LiquidTWI2
 mv LiquidTWI2 /usr/share/arduino/libraries/
 ino build -m mega2560
...and fails again. so...
 strace -f -o /tmp/x ino build -m mega2560
 grep LiquidTWI2.h /tmp/x
...and it is still looking in weird directories
...but one process found it, another did not... the first was the ino binary, the other one was gcc, with -I subdirectories that missed the LiquidTWI2 one...
...so we're missing parts, so, let's try to rebuild the files...
 ino clean
 ino build -m mega2560
...aaand, we got through, we now got another error:
 .build/mega2560/src/Marlin.cpp:8:101: fatal error: U8glib.h: No such file or directory
 compilation terminated.
 .build/mega2560/Makefile:396: recipe for target '.build/mega2560/src/Marlin.o' failed
 make: *** [.build/mega2560/src/Marlin.o] Error 1
 Make failed with code 2
...we have this one locally, in the marlin distro, so...
 cp -av ./ArduinoAddons/Arduino_1.6.x/hardware/marlin/avr/libraries/U8glib /usr/share/arduino/libraries/
 ino clean
 ino build -m mega2560
...and two and half minutes later we get...
 .build/mega2560/src/Marlin.cpp:13:29: fatal error: TMC26XStepper.h: No such file or directory
 compilation terminated.
 .build/mega2560/Makefile:1097: recipe for target '.build/mega2560/src/Marlin.o' failed
 make: *** [.build/mega2560/src/Marlin.o] Error 1
 Make failed with code 2
...which is good news, so let's add the other two from the ArduinoAddons...
 cp -av ./ArduinoAddons/Arduino_1.6.x/libraries/TMC26XStepper/ /usr/share/arduino/libraries/
 cp -av ./ArduinoAddons/Arduino_1.6.x/libraries/L6470/ /usr/share/arduino/libraries/
...and retry...
 ino clean
 ino build -m mega2560
...and it got through the src directory and now is going through libraries and taking hopeful amount of time, so fingers crossed... ...and 7 minutes 18 seconds later it finished...
 .....
 arduino/Tone.cpp
 arduino/HID.cpp
 Linking libarduino.a
 Linking firmware.elf
 Converting to firmware.hex
...so, where it is?
 find .|grep firmware.hex
...and it is in ./.build/mega2560/firmware.hex

So, let's try to flash it...

 service octoprint stop
 ardureset; avrdude -p atmega2560 -P /dev/ttyACM0 -b 115200 -c avrispv2 -U flash:w:./.build/mega2560/firmware.hex
...and we got
 avrdude: Device signature = 0x1e9801
 avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
          To disable this feature, specify the -D option.
 avrdude: erasing chip
 avrdude: stk500v2_command(): command failed
...so let's try to disable the erasing...
 ardureset; avrdude -p atmega2560 -P /dev/ttyACM0 -b 115200 -c stk500 -D -U flash:w:./.build/mega2560/firmware.hex
...and we got through! the file is uploaded and the flash is verified. so let's test the printer now...
 service octoprint start
...and connect from browser and try to move around...
...and it works! a frame for a raspi board printed well.
...so let's try change and recompile, rename the printer in the Configuration.h file...
 <edit file>
 ino build -m mega2560
...and 1min41sec later the compilation finished, so the mods and reconfigs will be hopefully easy.

What would likely run outright

 cd /home/pi

scp MarlinDev... directory from the other machine (or wget it from its site, or git clone the directory)

 cd /home/pi/MarlinDev-dev-20150910-ShadConfig
 ln -s Marlin src

 git clone git://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
 mv Arduino-LiquidCrystal-I2C-library /usr/share/arduino/libraries/
 git clone git://github.com/lincomatic/LiquidTWI2
 mv LiquidTWI2 /usr/share/arduino/libraries/
 cp -av ./ArduinoAddons/Arduino_1.6.x/hardware/marlin/avr/libraries/U8glib /usr/share/arduino/libraries/
 cp -av ./ArduinoAddons/Arduino_1.6.x/libraries/TMC26XStepper/ /usr/share/arduino/libraries/
 cp -av ./ArduinoAddons/Arduino_1.6.x/libraries/L6470/ /usr/share/arduino/libraries/
 ino build -m mega2560

locale error solving

Getting rid of an annoyance that appeared sometime along installing the packages.

 perl: warning: Setting locale failed.
 perl: warning: Please check that your locale settings:
         LANGUAGE = (unset),
         LC_ALL = (unset),
         LANG = "en_GB.UTF-8"
     are supported and installed on your system.
 perl: warning: Falling back to the standard locale ("C").

put this to /etc/default/locale:

 LANG=en_US.UTF-8
 LANGUAGE=en_US
 LC_ALL=en_US

...but doesn't work.

tried apt-get install locales, and failed:

 Reading package lists... Done
 Building dependency tree
 Reading state information... Done
 Some packages could not be installed. This may mean that you have
 requested an impossible situation or if you are using the unstable
 distribution that some required packages have not yet been created
 or been moved out of Incoming.
 The following information may help to resolve the situation:
 The following packages have unmet dependencies:
  locales : Depends: glibc-2.13-1
            Depends: debconf (>= 0.5) but it is not going to be installed or
                     debconf-2.0
 E: Unable to correct problems, you have held broken packages.

Decided to not bother, put "C" as local language: /etc/default/locale:

 LANG=C


GPIO control, ATX PSU control

Set up GPIOs on the Raspi board:

 apt-get install wiringpi

create directory for scripts:

 mkdir /home/pi/scripts/gpio

create pin list /home/pi/scripts/gpio/gpio_defs.sh, with content

 PIN_ATXPWR=23

create script for setting the pins and setting output permissions for the pi user, /home/pi/scripts/gpio/setpins.sh:

 #!/bin/bash
 . /home/pi/scripts/gpio/gpio_defs.sh
 setinput (){
   echo $1 > /sys/class/gpio/export
   echo "in" > /sys/class/gpio/gpio$1/direction
   gpio -g mode $1 in
   gpio -g mode $1 up
 }
 setoutput (){
   echo $1 > /sys/class/gpio/export
   echo "out" > /sys/class/gpio/gpio$1/direction
   chown -R pi:pi /sys/class/gpio/gpio$1/value
 }
 setoutput $PIN_ATXPWR

Create script for powering up the ATX PSU, disabling reset on connect, and connecting controller to server, /home/pi/scripts/gpio/atx_on.sh:

 #!/bin/bash
 . /home/pi/scripts/gpio/gpio_defs.sh
 setpin() {
   echo $2 > /sys/class/gpio/gpio$1/value
 }
 setpin $PIN_ATXPWR 1
 sleep 2
 stty -F /dev/ttyACM0 hupcl-
 8connect

Create script for powering down the ATX PSU, /home/pi/scripts/gpio/atx_on.sh:

 #!/bin/bash
 . /home/pi/scripts/gpio/gpio_defs.sh
 setpin() {
   echo $2 > /sys/class/gpio/gpio$1/value
 }
 8msg Disconnecting...
 8disconnect
 sleep 2
 setpin $PIN_ATXPWR 0

Running stuff on shutdown:

name script to start with Kxx, put to /etc/rc6.d

as described here: http://ccm.net/faq/3348-ubuntu-executing-a-script-at-startup-and-shutdown

ATX power control hardware

One of the GPIOs of the host computer board was connecte to the base of a NPN transistor. The transistor was connected between the power-control line of the ATX PSU connector (the green wire) and ground. High level on the GPIO now powers up the ATX PSU.

USB hub power split

The controller and the webcam aren't needed when the printer is depowered. The host computer would however feed them via the USB. A USB hub was therefore used; its USB power wire was cut and attached instead to the non-standby 5V rail of the ATX PSU. This automatically powers and depowers the controller and all the auxiliary equipment when the printer is powered on or off..

A switch is added to allow selection between the standby and non-standby power, to facilitate development without having to power the whole machine.


Set up printer device as symlink, as /dev/3dprinter

This may be handier than using /dev/ttyACM0, for the cases when there are more than one Arduino-based serial controller attached to the machine.

In /etc/rc.local, add

 ln -s /dev/serial/by-id/usb-Arduino__www.arduino.cc_<your-serial-number>-if00 /dev/3dprinter


Install tool to monitor http requests

httpry

apt-get install httpry

...and httpry works, but shows just the requests, but for debugging the API the whole requests and headers are needed. So...

justnuffer

So let's try justniffer, http://justniffer.sourceforge.net/

The way via apt repository using ppa:oreste-notelli/ppa repo did not work.

install on intel machine:

 cd /usr/src
 git clone git://github.com/onotelli/justniffer
 cd justniffer
 ./configure

...and it failed, on inability to run config.sub
...which points to /usr/share/automake-1.14/config.sub
...but there's no such version, we have /usr/share/automake-1.11/config.sub, so relink for that and for config.guess
...and retry...

 checking for Boost headers version >= 1.46.0... no
 configure: error: cannot find Boost headers version >= 1.46.0
...so...
 apt-get install libboost-iostreams1.49 libboost-iostreams1.49-dev
...and we got over 100 megs installed... ...retry configure, it continues past the point above and fails...
 checking for boost/program_options.hpp... yes
 checking for the Boost program_options library... no
 configure: error: cannot find the flags to link with Boost program_options
...so, look what we need...
 apt-file find libboost-program-options
...and install it...
 apt-get install libboost-program-options1.49.0 libboost-program-options1.49-dev
...and retry... and it fails on libpcap, so...
 apt-get install libpcap0.8-dev
 ./configure
...and it finished... but
 make
 CDPATH="${ZSH_VERSION+.}:" && cd . && aclocal-1.14
 /bin/bash: aclocal-1.14: command not found
 make: *** [aclocal.m4] Error 127
so edit Makefile, rewrite aclocal and automake versions from 1.14 to 1.11
...and we got
 In file included from /usr/include/boost/python/detail/prefix.hpp:13:0,
                  from /usr/include/boost/python/args.hpp:8,
                  from /usr/include/boost/python.hpp:11,
                  from python.h:15,
                  from formatter.cpp:19:
 /usr/include/boost/python/detail/wrap_python.hpp:50:23: fatal error: pyconfig.h: No such file or directory
...so... in /usr/include/boost/python/detail/wrap_python.hpp replace
 # include <pyconfig.h>
with
 # include <python3.2/pyconfig.h>
and
 #include <patchlevel.h>
with
 #include <python3.2/patchlevel.h>
and
 # include <Python.h>
with
 # include <python3.2/Python.h>
...and it fails on python3.2 patchlevel.h, so
 apt-get install python3.2-dev
 make
...and it compiles and then linker fails...
 make[1]: Entering directory `/usr/src/justniffer/src'
 /bin/bash ../libtool --tag=CXX   --mode=link g++  -g -O2   -o justniffer justniffer-main.o justniffer-formatter.o justniffer-utilities.o justniffer-regex.o justniffer-prog_read_file.o -L../lib/libnids-1.21_patched/src -lnids2 -lpcap     -lboost_regex-mt -lboost_program_options-mt   -l
 libtool: link: g++ -g -O2 -o justniffer justniffer-main.o justniffer-formatter.o justniffer-utilities.o justniffer-regex.o justniffer-prog_read_file.o  -L../lib/libnids-1.21_patched/src -lnids2 -lpcap -lboost_regex-mt -lboost_program_options-mt -l
 g++: error: missing argument to '-l'
 make[1]: *** [justniffer] Error 1
 make[1]: Leaving directory `/usr/src/justniffer/src'
 make: *** [all-recursive] Error 1
...set in src/Makefile this:
 PYTHON_LIB = python3.2mu
and try
 make
...and we're done! so
 make install

on raspi:

 cd /home/pi
 git clone git://github.com/onotelli/justniffer
 cd  justniffer
 apt-get install libboost-iostreams1.49 libboost-iostreams1.49-dev libboost-program-options1.49.0 libboost-program-options1.49-dev libpcap0.8-dev
 apt-get install automake
 ./configure

...failed on config.sub symlinked to automake 1.14, replaced with 1.11:

 rm config.sub config.guess;ln -s /usr/share/automake-1.11/config.sub .;ln -s /usr/share/automake-1.11/config.guess .
 ./configure
...and we're done, so...
 make
 CDPATH="${ZSH_VERSION+.}:" && cd . && aclocal-1.14
 /bin/bash: aclocal-1.14: command not found
 Makefile:409: recipe for target 'aclocal.m4' failed
 make: *** [aclocal.m4] Error 127
...so, edit Makefile, rewrite aclocal and automake versions from 1.14 to 1.11
...and we got
 configure.ac:54: error: possibly undefined macro: AC_PROG_LIBTOOL
...so...
 apt-get install libtool
 make
...fails

...so for now just intercept the comm and analyze it elsewhere...

 tcpdump -w /tmp/file.cap -s0 port 5000

...and let's continue...

 ./configure: line 5435: AC_PROG_LIBTOOL: command not found
...and an error: libext variable is empty, did you invoke Libtool?

...so, let's try again, this time from a clean configuration...
 cd ..
 rm -rvf justniffer
 git clone git://github.com/onotelli/justniffer
 cd justniffer
 rm config.sub config.guess;ln -s /usr/share/automake-1.11/config.sub .;ln -s /usr/share/automake-1.11/config.guess .
 ./configure

...and, we're successful, so...

 make
...runs for fairly long time... and succeeds
 make install
...and we're done!

Some interesting intercepted data:

GET /api/plugin/status_line ==> {"status_line": "OctoPrint connected"}

GET /plugin/printhistory/history ==> {"history": {"0": {"filamentLength": 2004.615320000079, "filamentVolume": 4.821664896354753, "fileName": "RaspiB1frame.gcode", "note": ""}, "1446143472159": {"filamentLength"...

GET /api/timelapse ==> {"config": {"type": "off"}, "files": []}

GET /plugin/softwareupdate/check ==> {"information": {"customcontrol": {"check": {"current": "0.2.1", "displayName": "Custom Control Editor Plugin", "displayVersion": "0.2.1", "pip": "https://git..."

GET /api/logs ==> {"files": [{"date": 1446710839, "name": "octoprint.log", "refs":...

GET /api/plugin/pluginmanager ==> {"octoprint": "1.2.7", "os": "linux", "pip": {"additional_args": null, "available": true, "command": "/home/pi/OctoPrint/venv/bin/pip", "use_sudo": false, "version": "1.1"},...

POST /api/files/local/KosselXL-tubeholder.gcode

GET /api/printer/command/custom ==> {"controls": [{"children": [{"command": "M106 S%(speed)s", "input": [{"default": 255, "name": "Speed (0-255)", "parameter": "speed"}], "name": "Enable Fan", "type": "parametric_command"}, {"command": "M107", "name": "Disable Fan", "type": "command"}], "name": "Fan", "type": "section"}, {"children": [{"command": "M114", "key": "94e9bd395ef599fbe33e42c344ae9ced", "name": "Get Position", "regex": "X:([0-9.]+) Y:([0-9.]+) Z:([0-9.]+) E:([0-9.]+)", "template": "Position: X={0}, Y={1}, Z={2}, E={3}", "template_key": "5d54d9c91f6e9cf73978708bfa400d3c", "type": "feedback_command"}], "name": "Reporting", "type": "section"}]}

GET /api/settings ==> {"api": {"allowCrossOrigin": false, "enabled": true, "key":...

GET /api/printerprofiles ==> {"profiles": {"_default": {"axes": {"e": {"inverted": false, "speed": 300}, "x": ...

GET /sockjs/213/_3zjc4ry/websocket HTTP/1.1 Host: 10.0.0.110:5000 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket Origin: http://10.0.0.110:5000 Sec-WebSocket-Version: 13 DNT: 1 User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8,cs;q=0.6,sk;q=0.4,fi;q=0.2,it;q=0.2,ru;q=0.2 Cookie: session=.eJyrVorPTFGyqlZSSFKyUvJ1ca2MDPct9zUKyvHLci2PyvU18s31rfALCTX2M_I1jArxyvFzcTSICg-1VarVUcpMSc0rySyp1EssLcmIL6ksSFWyyivNyUGSQTI9yiOsJCkiG6izFgDnTSbT.CRyisQ.mlVLY d27OMDgk127y171dY_VE6c Sec-WebSocket-Key: TA37E8MJqIDd6SrbQjLjZQ== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: xp/dciJo2gSfFoK5itfcmvRK1SU=

<81>^Ao<81>~^@a[{"connected":{"display_version":"1.2.7 (master branch)","apikey":"D567207981A64C368074EC43E9117E95","version":"1.2.7","branch":"master","plugin_hash":"3d63630e210bb671f2dc2e8e16fd0a1d"}}]<81>~7^Da[{"history":{"logs":["Connecting to: /dev/3dprinter","Changing monitoring state from 'Offline' to 'Opening serial port'","Connected to: Serial(port='/dev/3dprinter', baudrate=250000, bytesize=8, parity='N', stopbits=1, timeout=10.0, xonxoff=False, rtscts=False, dsrdtr=False), starting monitor","Changing monitoring state from 'Opening serial port' to 'Connecting'","Send: M110"

Install tool to play with websockets

The octoprint user interface uses the websocket protocol. So tools for connecting to those are needed. There are several, the wssh one was chosen as it does not rely on a monstrous framework like node.js nor it does require some obscure language like go.

The scenic route

 git clone git://github.com/progrium/wssh
 cd wssh
 python setup.py install

...and it fails on gcc not finding event.h, so we used...

 apt-file find include/event.h
...we found a candidate and tried
 apt-get install libevent-dev
...and then again,
 python setup.py install
...and we ran and failed at
 Searching for ws4py==0.2.4
 Reading https://pypi.python.org/simple/ws4py/
 Reading http://www.defuze.org/oss/ws4py/
 Reading https://github.com/Lawouach/WebSocket-for-Python
 Reading https://pypi.python.org/pypi/ws4py
 No local packages or download links found for ws4py==0.2.4
 error: Could not find suitable distribution for Requirement.parse('ws4py==0.2.4')
...so,
 pip install ws4py
...installed, rerun setup... and it fails with the same error So, try to edit setup.py from
     install_requires=['ws4py==0.2.4','gevent==0.13.6'],
to
     install_requires=['ws4py==0.3.4','gevent==0.13.6'],
...and the install script ran, but then wssh failed:
 Traceback (most recent call last):
   File "/usr/local/bin/wssh", line 9, in <module>
     load_entry_point('wssh==0.1.0', 'console_scripts', 'wssh')()
   File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 558, in load_entry_point
     return get_distribution(dist).load_entry_point(group, name)
   File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2682, in load_entry_point
     return ep.load()
   File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2355, in load
     return self.resolve()
   File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2361, in resolve
     module = __import__(self.module_name, fromlist=['__name__'], level=0)
   File "build/bdist.linux-armv6l/egg/wssh/__init__.py", line 9, in <module>
   File "build/bdist.linux-armv6l/egg/wssh/server.py", line 6, in <module>
 ImportError: cannot import name UpgradableWSGIHandler

...which is because that bloody ws4pi requiring the 0.2.4 version. fuck!

...so,

 apt-get install python-ws4py=0.2.4
 Reading package lists... Done
 Building dependency tree
 Reading state information... Done
 E: Version '0.2.4' for 'python-ws4py' was not found

...no dice.

...so let's pip it right from the git, using the painfully found @version trick...

 pip install git+git://github.com/Lawouach/WebSocket-for-Python/@v0.2.4
...and it runs! so, now...
 python setup.py install
...and it runs and wssh is there.

What would probably work outright

 apt-get install libevent-dev
 pip install git+git://github.com/Lawouach/WebSocket-for-Python/@v0.2.4
 git clone git://github.com/progrium/wssh
 cd wssh
 python setup.py install

Testing websocket communication between two machines

so, run on two machines:

 wssh -l 0.0.0.0:8888/websocket
 wssh -v octo:8888/websocket
...and we are connected together.

Now, how to connect to the octoprint...

 wssh 127.0.0.1:5000/sockjs/x/y/websocket
...and it works


octoPrint config.yaml notes

System menu configuration for streaming, board reset, and power

System section in /home/pi/.octoprint/config.yaml:

system:
  actions:
  - action: stream
    command: /home/pi/scripts/raspi_streamer start
    confirm: false
    name: Start video stream 320x240
  - action: streamvga
    command: /home/pi/scripts/raspi_streamer start VGA
    confirm: false
    name: Start video stream 640x480
  - action: streamoff
    command: /home/pi/scripts/raspi_streamer stop
    confirm: false
    name: Stop video stream
  - action: divider
  - action: "on"
    command: /home/pi/scripts/gpio/atx_on.sh
    confirm: false
    name: Power ON the printer
  - action: "off"
    command: /home/pi/scripts/gpio/atx_off.sh
    confirm: Power off the printer - are you sure?
    name: Power OFF the printer
  - action: divider
  - action: reset
    command: /home/pi/scripts/ardureset
    confirm: Are you sure you want to reset the controller?
    name: Reset controller board

"on" and "off" have to be quoted, otherwise yaml parser converts them to true and false.

Events section, for displaying status

events: enabled: true subscriptions: - command: echo "OctoPrint disconnected" > /dev/3dprinter event: Disconnected type: system - command: - M117 OctoPrint connected event: Connected type: gcode - command: - M117 Upload:{file} event: Upload type: gcode - command: - M117 do:{filename} event: FileSelected type: gcode - command: - M117 Start:{filename} event: PrintStarted type: gcode - command: - M117 FAIL:{filename} event: PrintFailed type: gcode - command: - M117 Printing finished! event: PrintDone type: gcode - command: - M117 Cancelled event: PrintCancelled type: gcode - command: - M117 --PAUSED-- event: PrintPaused type: gcode - command: - M117 Resumed:{filename} event: PrintResumed type: gcode - command: - M117 --Waiting-- event: Waiting type: gcode


If you have any comments or questions about the topic, please let me know here:
Your name:
Your email:
Spambait
Leave this empty!
Only spambots enter stuff here.
Feedback: