Saturday, April 04, 2009

Lightweight and fast G-Code printing with the Arduino

For once, a post with real text and no images from the RepRap team (Philipp and Marius) in Vienna! Fear not, we'll fall back into our usual picture post at soon enough.

Our reprap setup still uses the Gen-2 hardware centered around the Arduino. So far, we've been printing using an adaptated version of the Arduino G-Code firmware, generated G-Code using Skeinforge and used ReplicatorG as host software.

This setup has got some weaknesses and this piece of scribbling will outline what these are and give a short heads-up on possible ways to easily work around these.

1) Data loss

Once in a while, we notice irregularities in the printing process. Twice, these irregularities has been serious enough that the extruder head suddenly decided to move down ca. 1 centimeter into the already printed part. Forensic analysis has shown that nothing is wrong with the G-Code file itself. As all coordinates are transferred using absolute positioning, the only explanation left is that we have lost one or more serial bytes. In the two really bad cases, we most probably lost the first digit of a Z axis movement (e.g. instead of "G1 Z15.34", we got "G1 Z5.34"). Using this theory, we probably experience data loss much more often, but we just don't notice missing bytes in most other cases. Since we're printing at 19200 baud, we can rule out any error in the serial protocol sampling (0.2% error I think).

Other causes could be noise, a bug causing a 1-byte overrun of the serial buffer, or a firmware bug causing the serial interrupt to be held back too long.

To get to the bottom of this, we've extended the built-in serial class in Arduino to support detection of data overrun and frame errors and report this back to the host whenever it happens. Time will show what we find.

2) ReplicatorG woes

While being a neat and user friendly piece of software, ReplicatorG has caused us some headache lately. It's hard to say what causes this but while running, ReplicatorG gradually slows down, slowly eating up more and more CPU time. It also appears that memory usage keep piling up, but it's hard to see through the JVM without digging deeper.

On Windows, we haven't managed to make ReplicatorG live for more that ~15-20 minutes before crawling to a halt.

Inheriting the codebase from Arduino (who inherited theirs from Wiring who again took it from Processing) enabled a very rapid development process of ReplicatorG. Unfortunately, the result is code which isn't as clean as one would have wished for. Personally, this has resulted in me not managing to muster enough motivation/courage to really dig into this and try to figure out what's wrong. Unfortunately, the fear of large code bases has also kept me from building up courage to tackle the other java host software : /
Maybe I'm just not a GUI person.

Together with Philipp, I decided to quickly toss together a cmd-line alternative. The result so far is something with the codename metahost. This is a cmd-line application written in python using pyserial. It reads G-Code and sends it to the RepRap while giving progress info. In addition it supports warmup/cooldown scripts like ReplicatorG. We might add support from reading machines.xml directly from ReplicatorG in order to collect all machine info in one place.

3) Serial speed

The reprap prints at 19200 baud. This shouldn't actually cause any problems, but we discovered something worth mentioning: The baudrate calculation on the Arduino is a bit suboptimal. Specifically, it doesn't use the U2X bit on the AVR which enables for twice the bitsampling rate. This again causes the 115200 baud mode to be useless due to a 3.5% error. The following code can be used to optimize the baudrate setting:

#define BAUD 115200
#include <util/setbaud.h>
#if USE_2X
UCSR0A |= _BV(U2X0)
UCSR0A &= ~_BV(U2X0)

We're printing at 115200 baud for now, testing how stable this is in practise.

Thanks for listening,


I have some custom firmware to run my sherline mill (acting as a repstrap) and I experienced a lot of corruption over the serial link. I did three (four) changes that eventually fixed this:

1. Each line is considered a frame, each frame ends with 2 ascii characters hex encoding a crc8 for that frame. The crc8 is appended both to frames to the microcontroller (gcode) as well as frames being returned. If a frame is bad it's discarded by the firmware and host. Once I had a checksum I could see the actual error rate and I was surprised anything had worked in the first place ..

2. I added a TCP style sliding window protocol for sending the individual line segments (I preprocess the gcode on the host using python). I found that a window size of about 10 is usually good enough. If there is frame loss the server simply resends the segments that were lost. Because the microcontroller has a buffer there is usually more than enough segments to keep the steppers running even if there is significant noise on the line.

3. pyserial would have weird issues when running for a long time, so I ended up firewalling it off in the host code and basically make it resistant to dropping the link. If the serial connection is severed or starts acting bad the python program just drops it and tries to open up the port again. The microcontroller buffer is again usually enough to keep the build going anyway.

The code is in python (host) and C (firmware) but I'm sure you could easily adopt it for use in other languages. Ping me if you are interested, I've been planning to make this open source but haven't had the time.

The serial code worked fine and I did a number of jobs using it but eventually I decided to move to ethernet for host/controller communication. I hooked up an enc28j60 and wrote a small IP stack. The performance of ethernet is far superior to the serial link and this way I can hook up the mill to my LAN and don't need to keep it tethered to my laptop. Despite a lot of extra code pyserial would also occasionally get into a state that it couldn't recover from which caused the build to be lost. Given this happened especially for long builds it was very unsatisfactory. I've kept the serial code around for RS485 needs.
@ Marius: Thanks for the interesting and amusing post. Your photoblogging often leaves me wonder how you do things, but still insired by the results.

@ Kai: I'd be interested in seeing how you made it ethernet based. A network enabled RepRap would be highly desirable in my opinion. Also, the ENC28J60 is easy to acquire and available also in DIP (which is rare for ethernet chips). I have one at home and like to make a test setup.

You wrote your own IP stack? Dude... kudos to you :)
I'd love to see an open source implementation of IP that replaces the serial link.

Thanks for a bunch a useful comments!

I'd love to take a look at your python solution.
Firmware is harder to adapt/reuse, but if it's for the AVR I'd be interested in that one as well.


@Erik: I should have said tiny IP stack.. It only supports the basics to get going. I believe Nophead did something similar and I'm sure his code is much more evolved than mine.

@Marius: The firmware is for AVR. I'm running it on a Sanguino with my little enc board hooked up over SPI. The code isn't ready for open sourcing quite yet, mail me at if you are interested in a copy of the git repository.
Post a Comment

Links to this post:

Create a Link

<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to
Posts [Atom]