Almost MPCNC - my mostly printed CNC with 12mm rails

Background story: I want to build a largish MPCNC in my outdoors shop, a rectangular one that needs to conform to the available space, needs to cut 60x60 cm parts out of HDF/MDF (or whatever the really strong wooden laminate thing is called) and needs to cut small parts out of aluminium. Right now here in Romania we’ve got temperatures in the -10 to -5 Celsius range (Google says that’s 14 to 23 F) and I’ve got no heating out there, so that will only happen when Spring comes.

Since I can’t have my big machine right now, I decided to build a small machine that would allow me to learn CAM and work on software. I ended up building a machine that sits on a 450mm x 500mm table using 12 mm diameter piping!

I wanted a machine small enough to keep indoors near my desk. I decided to build one using stuff I can buy locally. Went to the hardware store, looked around for metal piping, found absolutely nothing useful, so I bought some length of 12mm outer diameter stainless steel piping. You heard that right, 12mm… Next I went to a different hardware store and asked for bearings: I requested bearings with outer diameter in the range of 15-20mm, whatever size they have that’s both cheap and in stock. I got 60 bearings with an outer diameter of 19mm and an inner diameter of 6 mm. Then I spent an awful amount of time redrawing every singe part in Fusion 360, because you can’t scale STL’s from 23.5mm down to 12mm and end up with useful parts.

Now let me tell you what I’ve learned while redrawing those parts: Ryan is a genius. This machine is fantastic and it all comes down to geometry. It’s strength and rigidity comes from the way parts brace each other, not from the might strength of the PLA plastic. This machine is rigid despite the flex in the rails and the variability in printed parts.

Next I bought a few kilograms of nuts and bolts, because I was too lazy to include all the hardware in the CAD design. I obviously made mistakes, occasionally needing screws shorter or longer then the ones I already bought. I also had to cut chamfers in some of the printed parts in order to use sunken scrwes. I could have re-printed those parts, but decided it’s not worth the lost time and plastic.

I designed the thing to take NEMA 14 motors - in theme with the “small” concept. Then I modified the parts to use NEMA 17 OR NEMA 14 parts (I have two sets of holes for the screws, with the NEMA 14 set rotated at 45 degrees to maintain some separation). In the end I installed NEMA 17 motors, the ones I bought for the future large machine, becuase my NEMA 14 motors have not arrived. I’ve ordered them 1 month ago…

I uses a RAMPS 1.4 card with 5 stepper drivers using Marlin’s X/Y_DUAL_STEPPER_DRIVERS; fixed a bug in Marlin that prevented the 2nd “X” stepper to enable. Only noticed this because I was playing with the firmware with no belts on, I had cable-ties tied to the steppers to make motion obvious.

For the spindle I used the mighty-powerful 30W (yup, thirty watt) Proxxon rotary tool. I made a mistake with this tool: it’s marketed as a 12V tool, I thought I could run it from the same power supply as the rest of the “toy”. Unfortunately it’s not exactly 12V and it’s definitely not 12V DC. I had to buy the corresponding power supply. As I understood it’s an unrectified 12V transformer that allowed simple/cheap RPM control. Not that it’s RPM control would be of much use: it simply doesn’t have enough power to spin fast when it hits wood. It did have one very good thing going for it: I learned to control depth-of-cut in Fusion 360’s CAM module. That is, after accidentally plunging the bit 1mm deep into the MDF spoil board: the bit stopped spinning then it broke!

As for the future of this machine, I’m now printing a tool support for a different spindle: a brushless 480W Chinese thingy. This should allow me to use larger bits or take deeper cuts. With the 30W Proxxon I was taking 0,5mm deep cuts and by the sound of it it was too much. I’ll play with the larger spindle until I build the larger machine, then I’ll reinstall the Proxxon and repurpose the small machine for cutting PCB’s.

On the software front I’m now working on a Marlin firmware feature for Jogging the toolhead. Moving the toolhead with G0 commands is painful and moving the toolhead by hand means no endstops and no help with tool changes. (moving the toolhead with the LCD pannel is equivalent to using G0 commands).

And finally, have a nice day and enjoy the pictures. I’ve got a few videos of the thing cutting the Batman/Victor sign, I’ll have to post them to Youtube in order to share them here.

1 Like

That looks like a lot of fun.

That looks like good work for such a tiny spindle. What speed were you using with the 0.5mm DOC?

I’d be interested to hear the bug you found in Marlin. Do you have a link?

The moving seems good too. Are you planning on using just some aux io or some kind of controller?

It would be cool to be able to use the endstop min/max for jogging…

Well I know how much work it takes to redesign the parts, for me. It can assume it was even less fun for you. Nice work. You learned a lot along the way, that is the most satisfying part for me.

The sign looks great.

Definitely want to hear about that bug.

Here’s the commit for the Marlin bug. Really simple stuff, 3 words total:

Here are a couple of videos of the machine cutting that sign:

The end mill was a 2mm ball-end mill with (I think) 8 flutes? I think it’s a tool made for manually working wood. Since I couldn’t easily change tools I ran the whole job with the same mill: adaptive clearing, finishing pass and 2D contour to cut the part out of the wood. It was spinning at 20K RPM (at least that’s what I dialed, but it wasn’t able to maintain that speed in wood). The machine’s speed was around 100mm/s, but I changed adjusted the feed rate in Repetier Host as it was working.

I liked the look of the cut in wood so I set up the final 3D finishing pass to leave those marks in the wood.

@Ryan, redrawing the parts was fun for me: I had a model! I looked at lots of MPCNC pictures and lots of videos. I remember an a-ha moment when I understood how the long screw behind the middle assembly connects and aligns all the parts. By the way, there are very few pictures of the MPCNC from behind, everyone takes pictures from the front. Also a STL of the fully assembled machine would have been grate, but there’s no such thing.

@Jeffeb3, about jogging and movement. The mechanism for controlling the jogging is the simplest part. I can imagine a Pololu-style joystick as an input method, or (in my case) a virtual joystick on the Raspberry Pi touchscreen.

The difficult part is making this work in the firmware. Marlin wants all the moves to go through it’s motion planner, and this planner has a buffer. When you issue a move command you don’t know if it’ll start executing immediately or there are 10 other commands pending! Then there’s the interrupt routine that actually generates the pulses that move the stepper. Marlin’s been through a lot, and you can see this in the code. The ISR that generates the pulses also reads the end-stops. The code that translates kinematic movements (think Delta printer) and the code that handles bed leveling has hooks in the planner module. Proper jogging needs to be under the firmware control (ie: I can’t just skip the ISR and the planner) so that all those extras get the chance to do what they need to do.

My plan is to implement a G-Code that takes as parameter a move intent vector: direction+speed, not actual destination coordinates. This code would then generate planner-based short moves “just-in-time”, based on current coordinates and current move intent, in a way that maintains the planner’s buffer almost empty at all times. Hopefully the time between changing the move intent vector (moving the joystick from forward to left) and actually seeing the tool change direction would be very short.

Holy cow, I swear I tested both versions and the offsets worked both axes and both versions. I even tested on two machines, dang. I did most of the testing in V2 but still, I really hope we didn’t leave that out. Thanks for the fix!



Hmmm. What happens if it gets disconnected? Will it just drive forever? What about changing directions? If you are moving in the positive X at 10mm/s and you send a direction in +y at 10mm/s, the machine needs to slow down, and make the turn, then speed up. That’s quite a contradiction to the current movement scheme.

After you think these things through, I wonder if you can do the same thing or similar with some relative coordinates, just keeping the buffer small. IDK. Maybe you can’t.

It’s an interesting problem. Let me know if I can help (although, I’m very short on time at the moment). I think an octoprint plugin would be pretty awesome for this, possibly. Or a CNC.js plugin. They would have a lot of machine information and installation/lifecycle stuff figured out already.

That’s a really fast speed. I do something more like 8mm/s with a deeper cut, like 5mm. I don’t have an 8 flute bit though either. That is very much in the outside of typical though.

Maybe it was 100/min? I don’t know. The bit is really tiny, it’s a 2mm ball on a stick. The contact surface is very small. This is the bit I used:

I’ve made some progress on the “jog” thing. Here’s how it works. The Marlin firmware uses a planner to plan all moves, and this planner works on “blocks”. For each block certain parameters are calculated (entry/exit speed, time, acceleration, etc). I’ve designed this jog feature to work with the planner, not against it. When the jog command is first issued it puts a first move into the planner. I’ve made a change in the planner to set a flag when a new block is requested and the block ring buffer has less then 3 blocks available. The jog method will then provide a new block “just-in-time”. The new move would always take into account the previous move and the requested move, making sure the rate-of-change doesn’t exceed configured acceleration. The planner makes sure the transition is smooth. It works so far, but now I need to focus on the controller.

The controller needs to be near the machine, can’t be a web-based controller at a distance. I’ll order two pololu joysticks (one for XY moves and one for Z) and see how I can interface it with the machine. Directly connecting those to the board running Marlin would require 3 analog pins. On a pure CNC machine I can repurpose the temperature sensor pins, but those are not available on a dual-purpose machine (CNC and printer). I’m also considering building a I2C (or SPI) based controller, but I’ll need to do the math on response-time: a move of the joystick should ideally translate in a change of direction for the toolhead in 0.1 seconds or less. That’s not going to happen, but if math says the reaction time is 1 second then that would be useless.

I’ll have a couple of weeks to think about it (and for the joysticks to arrive). My kids are having the inter-semestrial vacation so we’ll be away from home, putting the cold weather to good use (skiing).

This is what I think you’re saying. You’ve got a gcode that sends a direct command (speed and direction) but the command doesn’t go into the planner, it gets stored. Only when the planner is ready to use the command, it gets sent into the planner. When another command comes in, it gets replaced.

Really, the difference is that these commands don’t have to be achieved. They are replaced by the most recent goal if there’s an update. I would not be surprised at all if there are many other creative uses of this.

This definitely sounds good. I’d like to help. I wouldn’t mind seeing the code. Sometimes I listen better in C than in English (English is my first language, but… :slight_smile: ).

A typical USB joystick (Xbox controller) reports at about 10Hz, so 0.1s is a reasonable goal. I wouldn’t expect I2C to be worse than 100ms on an embedded board.

I have a raspberry pi attached to the machine (mine is a low rider, so it literally moves with the machine). I was thinking the process on the pi that is serving the web page would also read in from an Xbox controller and send the gcode. You would configure the controller and any settings through the web page, but the process would read and send gcode locally. Taking octopi as an example. The octopi process would read from /dev/usbjoy0 or whatever, interpret the buttons, and send gcode if the state is X and the controller trigger is pressed or something. It’s all very easy to do in Linux/Python.

But, I also am in the room with a computer to run the webpage. So I have the client machine present too. There’s no reason the controller couldn’t be configured to be read from the client and stream the xbox controls to the server. That connection would generally be fast enough (20ms or so). My only resistance to this method is that I only run Linux as a client and most people don’t do that. Maybe there’s a JavaScript package that will handle the joystick connection. It would be more work than doing it all in the server.

Several good options. They are all dependent on your firmware changes.