Introduction: How to Draw Pictures With a 3D Printer

Tired of always extruding filament with your 3D-printer? No problem, let's downgrade it to a 2D-printer to draw images much slower than a traditional printer! Why did I do it? Well I wanted a project where I control the movement from my printer troughout Python, I tried some tutorials on drawing with a 3D printer before and they all used a slicer with a lot of changed setting or Inkscape and required some postprocessing of the Gcode. So I tried making it a bit easier to achieve.

Step 1: Some Required Knowledge

Before we could draw anything we should understand how a 3D printer works. Well they do consist of some motors in order to move the head in a 3D space. There is a pcb attached and on there is a microcontroller (actually often an Arduino microcontroller). On the microcontroller runs some code. For most cheap 3Dprinters this code is often Marlin (Marlin website). This code does two main things: It listens to commands that it gets and according to those commands it does things like moving the head to a certain position. Those commands follow a certain standard which is Gcode. A lot of commands start with the letter 'G' hence the name. A command that is often used is 'G0' and it will result in a lineair movement for example:

G0 Z3;

This will result in the printerhead going upwards 3 mm. You could also move in more dimensions simultaneously:

G0 X200 Y100;

Note that a semicolon is required after each command.
There are a lot of commands available, there is also a homing command for example and many more. If you would like to know more I suggest this website for a good introduction.

Step 2: Sending Commands Through Python

The commands for the microcontroller can come from two sources: the first one is from a sdcard. Then it reads a file with a '.gcode' extension and that file consist of sequential commands that the printer will execute. You can just open such a file with a text-editor and see for yourself. The second source is a serial port on the board.
To connect your laptop to the printer you just need a USB-A to USB-B cable. When a serial connection is made the microcontroller starts with sending some firmware (Marlin) information. If you have putty/Arduino IDE/Termite installed you could select the COM-port that just poped up and connect to it. If you see some garbage on the screen than you are probably using the wrong baudrate. You can change it untill you get some readable text. By doing so I discovered that my printer uses baudrate 115200. Another common baudrate for printers seems to be 250000.
To make the serial connection through Python you can just use the package 'serial' and the code will look something like this:

import serial

s = serial.Serial('COM4', 115200)

time.sleep(5) # Wait for marlin to initialize
s.flushInput()
s.write(str.encode('G28\n'))

Note the 'str.encode' is only required for python3.

Step 3: Making a Penholder and Attaching It to Your Printer

Now we should find a way to attach a pen to our printerhead. Luckily since everybody that is reading this has probably a 3D-printer we could just print a piece that we can attach to our head. For my printer (Anet A6) there were some screwholes to the side of my printhead so I used them to attach my print. Some things you should keep in mind while designing the holder is that you could change the height of the pen easily and if possibe you should attach your pen as closely to the nozzle of your printer because the further the pen is from the nozzle, the smaller the available drawarea will be.
My design and gcode for the holder is available on my github.

Step 4: Transforming a Picture Into Gcode

For this step I won't explain the whole code but I will try to explain the used ideas.
First of all there is this amazing package for python/c 'Opencv' which is handy for performing tasks on images.
I use this to read the picture I want to draw and transform it into a black and white picture since we won't be able to draw different colors.

Then I defined some variables:

#everything in mm
STYLOS_X_OFFSET = 20

STYLOS_Y_OFFSET = 50

DRAW_X_OFFSET = 0

DRAW_Y_OFSSET = 0

PRINTER_DIM = 250

BACKUP_HEIGHT = 5

STYLUS_REL_NOZZLE_HEIGHT = 2

SPEED = 3600

desired_img_width = 100

STYLOS_X_OFFSET and STYLOS_Y_OFFSET define the distance for the point of your pen to the nozzle. Otherwise you could end up drawing aside your printbed.

DRAW_X_OFFSET and DRAW_Y_OFSSET define the offset from the left-bottom corner from which your drawing should start.

PRINTER_DIM defines the size of my printbed for me it's 250x250 mm.

STYLUS_REL_NOZZLE_HEIGHT this defines the height at which the pen will be drawing on the paper. You probably don't want this to be 0 because then while drawing your nozzle would probably scrape the paper.

BACKUP_HEIGHT defines the height to use between drawing different lines.

SPEED, the higher the faster your printer will draw.

desired_img_width: this will define the width to which your photo will be scaled to and drawn on the paper.

The script will show you a representation of you're printbedarea, you're effective drawarea and the place and size of the drawing on your board according to your settings. A screenshot of this representation can be seen above.

I wrote two methods for transforming a picture into Gcode.

The first one is 'line_by_line'. This is the most mainstream approach. Here the printer will draw your image from left to right and for each black pixel that is in the picture the head will go down and draw.

The second one is 'by_contour'. Here contour lines of the image get defined and than the printer will just draw the lines and not print 'pixel by pixel'.

Which one to choose:

'line_by_line' is better than 'by_contour' for real images (no sketches). Parts of the image will be filled as it should be while 'by_contour' only draws the contour of course.

'by_contour' is better than 'line_by_line' because it is muuuuch faster. For my spongebob drawing this was probably a 30x faster. If you have a problem with the double lines and no infill in between you could try a marker with a big point so the lines would overlap.

Note: sorry for the unclean structure/variables of my code. I kind off rushed making the program and didn't took the time to clean it up afterwards.

Step 5: Drawing a Picture

When you run convert_image_to_gcode.py a '.gcode' file will be generated. You could either copy this file onto a SDcard and play this file for your menu on the printer or you could stream these commands from your pc to the printer using the 'playfile.py' script.

Note: when playing the file the printer will first home its position and afterwards move to (100,100) and wait for 30 seconds. During this moment you should change the height of your pen so that it touches the paper on your printbed.

That's it, your printer will now start drawing!

Step 6: Results

In the above pictures you see the result of a Spongebob drawing.

The first one is with drawn by contour. The diminishing intensity from left to right is due to my bed that wasn't leveled. The second one is drawn line by line. The straight line at his foot was already there before the print.
A short video of the printer drawing each method can also be found on my github.

Github: https://github.com/Sfeeen/3Dprinter2drawer

Robots Contest

Participated in the
Robots Contest