Exploring the question of using a Raspberry Pi 3 to drive a 3D printer.

Exploring the question of using a Raspberry Pi 3 to drive a 3D printer.

By this I do not mean something like OctoPrint, which chats with a 3D printer’s existing controller (usually Arduino-based). When a Pi 3 is ~$35, it is a bit silly to use anything less.

Heard this was attempted before, with limited success. The problem is that Linux is not really meant for hard/fast real-time usage. There are options to and variants of Linux that are bit better at real-time.

But I am not interested in the entire general domain of real-time applications. I am interested in updating the output pins (GPIO) on a Raspberry Pi 3 in an efficient, predictable manner.

(Keep in mind, the following is what I gathered while waiting on an eight-hour print. I might be badly wrong about any of this.)

First, seems two output pins are needed to provide the signals needed to control a stepper motor. Those control signals are in turn amplified in voltage and power by an external circuit, to the levels needed to directly drive a stepper motor. (I got a bit lost trying to sort through the various options. Will return to this topic, later.)

As a starting point, note this guy collected measures for various means of updating the GPIO pins on a Raspberry Pi.


He managed 185 kHz on a Pi B (which has a slower CPU than the Pi 3) using WiringPi. So we should be able to manage at least that.

Seems the GPIO pins are visible to the CPU as memory-mapped device registers. Libraries that offer functions to set the state of an individual pin follow the usual convention of reading the register, masking out the present value of the pin, OR-ing in the new state, and writing the updated word back to the register.

Yeh. I am not going to do that. :slight_smile:

At any given point in time, I know the exact values to be output on all the GPIO pins. That means I can update all the GPIO pins with a single-word write to memory. Writes to memory are very, very fast - though the hardware may introduce wait-states for writes to device registers.

Well, now I need a regular event from which to perform those writes.

The Linux kernel gets regular interrupts from the hardware clock. The amount of processing I need to add is small. The rate at which kernel receives clock interrupts is configurable - though I do not know the practical limits on a Pi 3.

This does not quite work as other interrupts can mask the clock interrupt, introducing jitter. As a Pi 3 has a quad CPUs, I can set the CPU affinity for the clock to one CPU, and all other interrupts to other CPUs. That should eliminate the jitter. (Again, barring hardware oddities.)

The remainder of the problem is “soft” real-time.

There needs to be a task running to plan the updates to the GPIO pins. The “plan” is really just a buffer containing a list of delay/value pairs. Need to figure out how to pass this buffer from userspace to the kernel.

More reading to do… :slight_smile:

Can one CPU core do GPIO firing while another does stuff like reading gcode and motion planning?

@Ryan_Carlyle Should be possible.

To be clear, “CPU affinity” associates (locks) a task to a CPU. In userspace, this means a process executes only on that CPU. For interrupts, this means the handler will run on the specified CPU.

This can be a huge win for single-threaded tasks when innermost cache is local to a CPU core.

Hardware has long had the ability to steer individual interrupts to specific CPUs … though I do not know specifically about this particular ARM chip.

Even if we have to mostly saturate one CPU to do the clock interrupt / GPIO update … this is a dedicated device and we have three more CPUs for the soft real-time (and applications like OctoPrint).

So … yes, so far this looks to be possible.

I should reiterate since the post didn’t actually load when I commented. Linux is a no go because you’d want your software to run with a higher CPU priority than the kernel, which isn’t possible or worth it. If you’re adventurous, you could write your own software that would run on the Pi without Linux. It’d take way more work than needed, and at that point you could just pick up a controller board off Amazon for the same price as a Pi with the stepper drivers included.

I don’t see a huge benefit to this, but I’m commenting for updates.

@_Spice You are assuming that the “hard” real-time code is running in userspace. What I wrote above was about putting the “hard” real-time code into the kernel.

Yes, I expect to modify the Linux kernel. :slight_smile:

In that case, I suppose it could work as a kernel extension. It’s just a matter of implementation.

@Justin_Nesselrotte Well, you are partly right. :slight_smile:

As I was mucking about online, looking for a board that could drive at least five steppers (XYZ and two extruders), noticed that boards with that many stepper-drivers tended to have an onboard CPU - usually(?) Arduino-based - for controlling the steppers.

Still need to sort - am I stuck with a bunch of postage-stamp boards (and a mess of wire) or is the a single five driver board, somewhere?

The old mainframe guys will like the model of outboard I/O processors. Except … the Pi is not remotely overloaded.

As a software guy, that suddenly means I might need the Arduino tool chain. That does not sound like fun. More complex build environments are not usually a win. (And I have built rather complex stuff in past.)

Also those boards with outboard processors are several times as expensive as the Pi. My guess is the cost is not the drivers.

So … still trying to sort stepper-driver hardware.

@_Spice Er, “just a matter of implementation”…

Are you volunteering? :slight_smile:

Regarding GPIO speed: I have put together a comparison of different ways to send things to GPIO on the Raspberry Pi (search for hzeller/rpi-gpio-dma-demo on github) - you can get up to 60Mhz by directly writing data, but you want to do timer gated DMA, which brings you in the 2Mhz range, but that is sufficient for all intents of stepmotor driving.

Why DMA ? Because you then can actually generate somewhat predictable timing, because you can gate the speed things are sent out ( Check out richardghirst/PiBits on github). So that can bring you in the realtime area, because the timing is done by hardware; you just need to feed the data fast enough. I have not tried to drive stepper motors like that, but it should be doable.

There are various ways to improve the realtime performance of the Raspberry Pi (PREEMPT RT realtime patch; tickless kernel; using isolcpus to confine things to one processor).

Regarding the “amplified in voltage” part, you just need an off-the-shelf stepmotor driver (search for Pololu driver to get started, but you can then as well built a Raspberry Pi hat by using the chips directly). This is actually the most expensive part to hardware interface step motors, which you need in any case.

If you want to use a different hardware, such as the Beaglebone Green (around $40), you can as well use something like BeagleG (preferred, but I am biased :); There is also MachineKit); these systems are using the Programmable Realtime Unit of the Beaglebone.

Protoneer is a hat with standard driver sockets. Is that what you want?

Why don’t you look at the existing realtime Linux variants? PREEMPT_RT or one of the others. Seriously, this is a solved problem,
both Redeem and MachineKit run 3d printers on Linux SBCs.

Another approach we haven’t discussed yet is to use a stepper driver chip that accepts full ramp commands via SPI, I2c, or whatever protocol you want. That largely eliminates the hard realtime requirement, bit-banging lots of GPIOs at high frequencies, etc. I really think that’s going to be a more promising approach.

In any case, it is a 100% non-negotiable certainty that you’ll need a hat for the RPi with MOSFETs and stepper drivers and such. (I’m not aware of any existing hats offhand but there might be one out there.)

Incidentally, if you try to buffer the entire step pulse stream, you’re going to have memory problems. A single large 3d print is routinely billions of step pulses, and they happen faster than you can reliably talk to an SD card. Thus why the step timing is calculated in realtime from buffered motion commands.

Now, if you want to pre-calculate all the acceleration calculations so the printer is merely playing back a series of ramps and coasts, that is very doable. That de-bottlenecks some really intensive iterative
motion planning math out of the printer controller and means you’ll never suffer underruns because the motion planner can’t keep up with the commanded motion speed. (This is a big issue in AVR systems, less so on ARMs.)

@Henner_Zeller For some reason, Google marked your reply as spam. Had manually force for others could see.

Timer-gated DMA? Interesting. I have no idea what that means.

Yet. :slight_smile:

Then again, I have not programmed hardware directly for a couple of decades. I have no notion (yet) what the chipset in the Pi can do in that area. Would not be surprised by fancier hardware.

@Ryan_Carlyle OK … pick a side. Pretty sure you said prior this was not a well-solved problem. :slight_smile:

I did look at the existing real-time variants you mentioned. Thing is … these are “fat” solutions, that aim to solve a more general problem.

From experience, I am wary of “fat” solutions. Too many variables that can muck up the problem. I have a very specific focus, and want an efficient, reliable solution. (Though a niche problem that could drive every 3D printer on the planet is pretty deep.)

Dunno about I2C or SPI or … if there is existing cheap and effective hardware that can drive stepper motors and talks those protocols, that would be interesting. Pointers would be welcome.

@Alex_Hayden Thanks for the link.

As to the Protoneer, the embedded CPU makes me wary. Seems it takes up to four Pololu daughter boards … and I need five?

And then I need software to talk to whatever API the board’s CPU wants to offer. Yeh. About that.

I will do more reading.

Thing is, I think there is a lesson not yet learned by the present 3D printing hardware community, that was learned a long time back in the general microprocessor community.

The first concern is not about your (possibly) clever hardware. The first priority is software. If your clever hardware asks changes of existing software, you lose.

In the small computer marketplace, hardware vendors learned long ago and accepted that their hardware is useless without matching software (drivers) that work with existing software. This lesson was so long ago, we may have a generation of hardware engineers that missed the lesson.

I am a software guy. You want me to write special drivers for every different maybe-clever hardware variant? Not going to happen.

I would like to use the specific board above, but.

@Preston_Bannister the existing Linux-based 3d printing solutions offload step pulse generation to dedicated PRU processors. The Beaglebone Black has two PRUs and only costs a little more than a RPi so it has been a popular choice.

Redeem and MachineKit are quite different and rely on different cape hardware designs to work but they’re both effective.

Interestingly, the Redeem guys started out trying to put the whole thing in Python for legibility and ability to do cool runtime stuff, but ended up switching to C to get enough performance for high-criticality tasks such as motion planning. Even with a GHz level processor offloading step pulse generation to a PRU, under-optimized code couldn’t keep up. So it’s good you’re not planning on doing that :slight_smile:

My big question is, if you have to add 4-6 big honking stepper driver ICs and a bunch of giant MOSFETs to your hat/cape anyway, why not just put a PRU or coprocessor on there to handle all the timing-sensitive stuff? The marginal cost is low, the performance gains are huge. Pairing a Linux processor with a bare-metal firmware coprocessor (or FPGA or whatever) is completely routine and widely accepted in automation and motion control.

Redeem and MachineKit was mentioned, but do not forget BeagleG that can drive up to 8 steppers with up to 1Mhz step frequency using the PRU and has a clean GCode implementation.

@Preston_Bannister Everybody uses dedicated hardware for CNC motion control, because you have to. All the way from multimillion dollar machining centers to $100 3d printers. General-purpose computers can’t drive motors or heaters. It’s about the rock-hard inescapable physics of amperage flowing through semiconductors, not lack of clever engineering. …Unless you know a way to switch 12 amps through an RPi that we don’t.

Once custom hardware is required, you might as well optimize the hardware for the application.

I seriously think MachineKit is going to be the answer at the end of your research. It’s a platform-agnostic controller that can run any kind of machine that consumes gcode. You can put it on an x86 PC with a Mesa card. You can put it on a Beaglebone with a cape. It could run on an RPi if anyone cared enough to port it, and people try sometimes, and typically give up because of the severe lack of GPIO on the RPi.

@Henner_Zeller I haven’t seen BeagleG, is it a TinyG port/derivative or something different?