All posts by admin

What I Am Learning About Functional. Some Insights.

For years now I have been working on a replacement for OpenSCAD. The developers of OpenSCAD describe it as

a functional language that lacks both variables and assignment statements.

In various discussions nobody is entirely clear if OpenSCAD is functional or declarative. I’ll leave that argument to the wiki on functional programming.

In computer sciencefunctional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions[1]

Regardless what is true and useful to us is that you can unroll OpenSCAD operations in a way that is declarative and leaves nothing behind but a core subset of basic constructive solids geometry operations. All of which are constant. They take something. They return something. New state is always made. Old state is never altered. This gives us the opportunity to shave OpenSCAD with a sharp razor.

Shave The Nightmare

Following this I am not sure of the  formal proof but I also surmise that it is then true that you can go from a series of unrolled declarative statements to a functional outcome and indeed to any language you desire. The reasoning then follows that a functional representation is by far the best way to move OpenSCAD in to another language.

Why do we want to do this?

Well I will reiterate that we want to translate OpenSCAD in to a language that is formal and sensible. We want to do that for a whole lot of reasons but let us just start with the interesting translation part for now and how it leads us in to the land of functional.

Here is an example …

difference() {
for( x = [0:10:100] ) {

for ( i = 0; i < 100; i++ ) {
stack.push( translate([x,0,0]) .cube(size=1) )

this.translate( [ 0 , 0 , 0 ] )
this.translate( [ 10 , 0 , 0 ] )
this.cube(size=1)  }
this.translate( [ 20 , 0 , 0 ] )
this.cube(size=1)  }
this.translate( [ 30 , 0 , 0 ] )
this.cube(size=1)  }
this.translate( [ 40 , 0 , 0 ] )
this.cube(size=1)  }
this.translate( [ 50 , 0 , 0 ] )

*we build a push tree which we unwind as we traverse the stack.   

Note here that we applying a greatly simplified grammar set in order to do the bare minimum to the OpenSCAD script to move it over to a new descent tree for a new target. For example to attack the loop above we might have applied the following Ternary 


|for ?  @NEXT : @COPY ?  “(”  ? @PROCESS_FORLOOPS : @COPY

I am referring to this as bare minimum grammar to achieve OpenSCAD shaving.

Now you might be wondering why we don’t use something like Antlr or YACC. Why don’t we take the existing grammar they have created for this purpose?

Having worked this out you are now up to where I am which is you could either implement your own version of the OpenSCAD grammar and parser thus making all the same mistakes again -or- write a translator that moves the quasi functional / declarative OpenSCAD language as it currently is in to a representation accessible in a broader richer more formalised language like Javascript.

Why would we want to do this. Because making up your own grammar and parser is a terrible idea. The heavy lifting has been done for you. Just pick one! They are all good! Just not Prolog. Prolog was that weird kid that sat at the back of the class and smelled funny. See him all alone over thar. Go give him a hug if your feeling sorry for him.

Pick One

Given that we know that at its most basic that the constructive geometry is a series of simple boolean operations it really isn’t a requirement to have written a whole enormous parser. We know that doing these operations well and quickly was the target problem.

What was NEVER the target problem was inventing your own parser. Avoid at all costs.

Whenever you can translate don’t spew out gigantic grammar files and bury us all in your YACC. YOU ARE NOT THAT SMART AND NEITHER ARE WE.

The answer is we could indeed do exactly that. However we have a burden of work which shifts incrementally to the right as the complexity of parsing increases. At a certain point the token consumer is so simple that the majority of our work is actually in the hooks to code. We then weigh up the extra weight and time consuming effort of making something like Antlr or YACC actually work. The OpenSCAD grammar is totally at the opposite end of the scale. I dare you to go read it.

So we reiterate once again we did not come here to write a parser. We came here to reshape as little as possible on the way to getting to a parser that ALREADY WORKS ..

It is particularly useful to understand that this is possible because it makes interpreting OpenSCAD fairly straight forward if you wish to separate the OpenSCAD language from its current parser and grammar and gigantic codebase of C++.

Those playing along may have seen a hole in the above reasoning. Because OpenSCAD is not entirely pure.

For example should a difference statement be cumulative over all children  or should it operate on an invisible but implied a,b argument. It is obvious once you have unrolled it but this is what happens in the existing parser. So this line of thinking is useful in that it makes us really thing about the language itself.

difference() {
this.translate( [ 0 , 0 , 0 ] )
difference() {
this.translate( [ 10 , 0 , 0 ] )
difference() {
this.translate( [ 20 , 0 , 0 ] )
difference() {
this.translate( [ 30 , 0 , 0 ] )
difference() {
this.translate( [ 40 , 0 , 0 ] )
difference() {
this.translate( [ 50 , 0 , 0 ] )

And that leads us to functional programming and a conversation about translating and parsing and what the code might look like in JS to get us from thar to over here ….

First some ugly code I put together in my first pass. Imperative. Bad. All bad.

// ------------------------------------------------------------
// Walk a tree
// ------------------------------------------------------------
this.walk = ( tree ) => {
let i = 0
let state = true
while ( state === true && i < tree.length ) {
if ( this.isInList(tree[i][0]) ) {
state = true
} else {
state = false

let s = '|difference |intersection |union |minkowski @NEXT @COPY => |( @NEXT @COPY => |) @NEXT @COPY => |{ @PROCESS_BOOLEANS @COPY' // grammar
let tokens = " difference ( ) { difference ( ) { union ( ) { circle ( size = 5 ) ; } } }" // set of tokens to iterate over
// -------------------------------------------------
// Flow the grammar stream past the tokens stream
// -------------------------------------------------
let flowGrammar = g => t => {
// split each tern delimited by =>
const gram = g.split("=>").map((ternaryGroup)=> {
return ternaryGroup.split(" ").filter((a)=>{ return a.length!==0 ? true : false })
// build occurence table of each instruction descent tree in token stream
const set => {
return lodash.flatten( => {
return oPeration[0] === "|" ?
t.split(" ").map( (chnk,index) => {
return oPeration.slice(1,oPeration.length) === chnk ? index : 0
}).filter( (index)=>{ return index > 0 ? true : false })
: []
// build call table
const instr => {
return lodash.flatten( => {
return oPeration[0] === "@" ? oPeration : []
console.log( set )
console.log( instr )

Tutorial 2 – Unions


Since the last tutorial the interface has had a significant update. You will now find that the editor and the main viewing area are all on the same page. You will see the interface now has two save buttons and that the editor button has been removed. The save button in the tool panel saves changes made to model through the visual interface. The save button in the editor saves changes made in the editor.


The computational solids boolean operations in the Makertron support the following results of intersection subtraction and union.


The type can be either “intersection” , “subtraction” , “union”. You may select only one root object.  Each boolean operation represents the application of one boolean statement on one root object. The boolean operation that is applied to the root object can have multiple children specified in the object field.

The resultant of the boolean operation will take the name in the name field.  If you wish to update an existing object you can specify that name in the name field and the previous state of that object will be over written.

In the example json script shown below both the ‘teapot_spout’ and the ‘teapot_handle’ are being unified with another object t named ‘clip_top’. The result of this operation will produce a new object named ‘unified_teapot’.

"type": "union",
"name": "unified_teapot",
"root": "clip_top",
"objects": "teapot_spout,teapot_handle",
"state": "teapot_body_enable"

For the teapot body in the body.jsn we have created two spheres. One named ‘teapot_body_outside’ and one named ‘teapot_body_inside’.

"type": "sphere",
"name": "teapot_body_outside",
"radius": "teapot_body_size",
"pos": "[0,0,0]",
"scale": "[1,1,1]",
"color": "teapot_body_color",
"resolution": "teapot_body_resolution",
"state": "1",
"group": "1"

"type": "sphere",
"name": "teapot_body_inside",
"radius": "teapot_body_size-teapot_body_thickness",
"pos": "[0,0,0]",
"scale": "[1,teapot_body_scale_y,1]",
"color": "teapot_body_color",
"resolution": "teapot_body_resolution",
"state": "1",
"group": "1"

For the radius of the inside sphere we set a a size that is the size of the outside sphere minus some thickness. It is important when doing boolean operations to consider what the results might be of your geometrical interactions.

The state of both these spheres could be true or  or false at this point. Once we have completed our operation with them they will be set to false. The state controls which objects will ultimately be included in our final scene for export. Note however that even if we set these objects to a state of false we can still act on them in the scene.   Likewise when we  get a new object as a result of our boolean operation we can set the state of that to. If the state of that boolean operation is set to true then this new object that is a result of the operation will be visible in the scene and exportable .  If it is set to false it will become invisible but we could still use this object as an invisible proxy to perform other operations on objects that were visible. For state you can enter true or false or 1 and 0 respectively. 1 being true and zero being false. Using an integer value for true or false is useful if you wish to set the state of something via a variable.

Now we will apply a boolean operation. This will result in a new object named ‘hollow_sphere’.

"type": "subtract",
"name": "hollow_sphere",
"root": "teapot_body_outside",
"objects": "teapot_body_inside",
"state": "1"

Next we will want to cut the top off the new object ‘hollow_sphere’ with another boolean operation.  To do this we will create a cube called ‘lid_line’.  This cube can be moved up and down on the y-axis to change how much of it will ultimately be subtracted from the ‘hollow_sphere’.

"type": "cube",
"name": "lid_line",
"radius": "[teapot_body_size*2,teapot_body_size*2,teapot_body_size*2]",
"pos": "[0,clip_height,0]",
"color": "teapot_body_color",
"state": "1",
"group": "1"

Then we will apply a subtraction between this cube called ‘lid_line’  and the ‘hollow_sphere’ object we made in the previous boolean operation. We are using the cube ‘lid_line’ as a proxy to alter our ‘hollow_sphere’ geometry. Note once again that it does not matter if it is invisible or visible in this context because after our boolean operation it will be set to false.

"type": "subtract",
"name": "clip_top",
"root": "hollow_sphere",
"objects": "lid_line",
"state": "1"

The result of this operation will be a new object named clip_top with the top sliced off that will be set to visible in our scene and exportable. This is the body of our teapot.


In the next tutorial I will cover Making the handle and the spout and the Bezier tool.

Makertron Tutorials

Makertron Teapot Tutorial 01

Interface Overview



The tool bar contains the non dynamic parts of the scene that you cannot edit. The first button saves your complete project to server. Second exports your project out to a zip containing stl files. [ Third button takes you to the editor. – depreciated ] 

*Like most things in the Makertron how an stl is exported directly depends on how you configure your model. If you completely unifiy your model you will get a single STL. If you break your model up in to parts each of those will be contained with the zip file seperately. STL files are currently exported in the STL binary format. 

Main Display

display In the centre is the main display area which contains the tool bar  and the viewing area for your project. Below this is the debugging window which will tell you if you have any errors and if the Makertron has successfully managed to process your project. 


Panel Area

accordion Panels are dynamically generated at load time based on the code you have written. A project should always begin with an ‘About’ panel.  Each panel that is stacked in the accordion will load a set of components associated with that panel in to the menu space on the right hand side of the interface. In the  default project you begin with you will see that the ‘About’ panel loads three sliders in to the right hand interaction menu when selected. These are not currently connected to anything and have no descriptions. We will demonstrate in this tutorial how to get them to do something useful to your model.

In the editor where you access your code each panel entry is defined as a panel at the start of each module.  Each module that you write will typically begin with a panel.  If you include no panels you will have no interactivity aside from the main display and no interaction menus will be selectable. The very first line in the root of your project should always be a panel definition. 

Interaction Menus


Interaction menus are also dynamically generated at load time based on the code you have written. Each interaction menu will typically contain a combination of sliders spinners and editing fields to let you tweak specific aspects of your model as you develop it. These changes can then be saved and in turn will be reflected in your code.

*Both the left hand panels and the right hand menu area are dynamically generated based on the code that you write when your model is loaded. This is what seperates the Makertron from pure computational solids generation systems like OpenSCAD. As you develop your project you are also developing an interface to interact with that project. In the coming update you wont need to switch between the editor and graphical display area. It will be in the same window.

 Associating A Slider With Code

All widgets in the right hand interaction menus are associated with code that you create in the editor. At the moment in the default teapot example you will see three sliders associated with the about panel when you select it ( select about panel interaction menu appears on right ) . They currently have no names and are not associated with anything.


If you go in to the editor edit and look at the start of the first  module you will see three lines of code each of them associated with the about  panel.  We will edit the first one.


{ “type”: “set”, “panel”: “About”, “note”: “”, “value”: “teapot_body_enable”, “operation”:”range|0|1|1″ }

Set commands will typically intialize the variable and generate a widget.  The variable and widget can be defined even if you do not use them right away.

In this case we are initializing a variable called “teapot_body_enable” and associating it with the panel “About”. However we have not assigned it a note yet or associated it with anything in our project.  Change the code to the following

{ “type”: “set”, “panel”: “About”, “note”: “This Enables Teapot“, “value”: “teapot_body_enable”, “operation”:”range|0|1|1″ }

A range slider enables you to slide across a number range. This number range can either be  an interger range or floating point value. At the moment the range slider is set as a toggle between 0-1 and defaults to 1.   That is “range|0|1|1.

*The operation field in a set is where you define what sort of variable you want to set and also describes what widget will be defined for that variable. You can set spinners. Array fields. Bezier fields. Menu fields. Color Fields. More of these fields will become available as the interface develops. Controls for Chamfers. Complex organic surfaces. 

Now we want to get this slider to do something useful.  Scroll down to the bottom of the default. jsn module.  The final function is a union. It unifies together the body of the teapot and the handle. It currently has a state of 1 which means it is enabled.  Change that state to “teapot_body_enable”.


Then click save. When you return to the main window from the editor you should now see the first slider in the interaction menu is labled.


Drag it to the left and the teapot and handle should both disable. Drag it to the right and it should be enabled again. You will also see that when you disable the unified teapot body and handle that it is no longer exported in the STL.

In the next tutorial I will demonstrate how to union the teapot body and handle with the spout.


Axis Problem

So I have an axis problem on the X-axis. Not sure if it is backlash / worn lead screw or a problem with the axis cable or controller. I swapped over the X/Y plugs on the controller and still got the same problem in the same cable. This leads me to think it is not the controller but either the axis leadscrew itself or the cable. Here is a picture of the part. Note that the walking back and fourth on the x-axis has happened in a thin part of the model aligned with the X-axis where it had to step quickly back and fourth over a short distance.



Here is what the part is supposed to look like for reference.



This turned out to be a classic problem with the end-stops causing interference. Disconnected the end-stop on the X-axis and the problem went away. Moving right along !

Bracket mounted on the mill ..



This is the 3040 engraver / mill. These things are cheap generic ubiquitous and can have long lives.


I have just re-purposed one in to a 3D printer. Why would you want to do this. Well to me it is really an argument about belts vs lead screws. Belts are fast and can be accurate. Some debate over belts having more/less backlash than a leads screw arrangement. As it is I am going to be purchasing a 6040 to my milling tasks so it made sense to take this gantry and convert it to a printer. The conversion of the mill itself was easy. It was the conversion of the controller that was time consuming.

3040 3D printing


Most of this centres around the controller. This is the original blue YOOCNC controller that came with the mill back in 2010. Quite a few variations of this box exist and it may even have been easier just to buy a new enclosure and to start from scratch. I chose to modify it given that the axis driver boards were already mounted in it and it has plenty of space for the replacement control board and other modifications required.



I will cover the changes to the mill itself in another post. These were not extensive. Primarily a mount to hold the extruder and the addition of limit switches. Redoing all the cables was probably the most time consuming part.


The original controller had a YOOCNC X4JKB controller board that is parallel port driven.


This actually worked just fine in the CNC application for which it was designed. Parallel port driven via Mach3. To turn a mill in to an FDM style printer you are going to need another axis for the extruder and heat control for the heater in the extruder. You will also need inputs for the thermistors. In short you really need a whole new brain.

To upgrade the controller to run a 3D printer I had to get rid of the X4JKB and the spindle motor control unit inside the controller to make space for the new brain. This made sense as if I was to swap the 3040 back to a spindle it will be a VFD kind with an external spindle control drive anyway. The Rambo board will fit where the original spindle control board was. You will have to drill out new holes in the base for the stand off’s.


I’d like to say something here about boards that come with the connection plugs pre-soldered. Initially I wanted a smoothie board. They have a faster processor and you can get them without the connectors soldered on so you can select your own better quality industrial connectors.

But since these seem to be forever out of stock I decided to go with one of the Arduino based controllers based on the RAMPS style architecture running Repetier. The Rambo manual recommends Marlin by default but getting it to work with the external stepper boards boards proved impossible to figure out ( more on that further on ).


Rambo Board

The connectors on the Rambo are okay but I could have chosen better. You know on a board like this where they are trying to keep down the cost that the quality of things like connectors is always going to suffer. If I had my way I would have populated only the connectors that I needed with high quality heavier duty Molex style connectors. The little snap push type plugs are I feel just a bit dinky.

It is possible to find really nice quality reasonably priced surplus Amphenol Aerospace type connectors and Molex connectors quite cheaply.


When I first did this I ended up with a big mess of XLR 4 pin microphone style connectors.


The thing to realize when you go from a simple CNC machine with a spindle is that you are going to be spending a long time sorting out cables and where to run them. An endless amount of them. You wont be able to skimp either. You are going to need to add end-stops. Unlike a simple router you just wont be able to get by without them. The software just wont work. If I had done this again I think I would have gone for a couple of single big aerospace type connectors like these

Aerospace Connectors

And some multi-core with a lot more wires. By the time I was done getting the cable and extra 4 pin XLR microphone plugs and Molex connectors for the Stepper ends of each cable I had spent about another 110 dollars on top of the cost of the RAMBO board and the cost of the extruder :( We live and we learn!

  • 3 x XLR for each axis as you have 4 pins per motor (::)
  • 1 x XLR for the extruder stepper. For this I used the mounting hole in case for the spindle connector (::)
  • 1 x XLR for both the heater fan and for the thermistor for heater ( ;: )
  • 1 x XLR for X min/MAX ganged together in a single loop ( two pins ) and Z min
  • 1 x XLR for the thermistor on the heater bed and the heater bed pcb
  • 1 x XLR for the z Max and the Y min/Max ( ganged together in single loop like the X )
  • 1 x cable for the heater supply in to the Rambo heater loop =>
  • 1 x cable in for the heater bed loop =>
  • 24 volt internal PSU to the stepper driver boards

  • All the extra wiring hanging out of the case is ugly and noise prone. Basically it turned in to a custerfluck of cables. I’ve now got some multi pin aerospace connectors sitting on my desk with which I will banish all the extra plugs and if I had considered this at the start it is exactly what I would have done to begin with !



    The controller has a 6A 24 volt supply in it. This is plenty for the steppers but not nearly enough for the steppers and the control board and the heater and the heater bed all at once. For awhile I ended up with two extra external power supplies stacked up to get everything to go. I was able to ditch one after I chucked out the heater bed though.

    I will be replacing the 24 volt supply in the case with a much larger power supply or maybe I will just stack another 12 volt supply in the case on top of it for the heater. Note that on the Rambo the heater circuit has its own power loop and this is on a 5A fuse. While the heater will run fine on 24 volts and the board will run fine on 24 volts at 24 volts a 12 volt heater will draw a lot more than 5A. Duh! I learned this the hard way after I blew the 5A fuse and had to wait a week to get some more 5A nano fuses in the post.

    I’ve seen people change to a larger fuse but it isn’t recommended. Instead I am running the heater off an external 12 volt power supply. Doing it this way I see my 60W heater never pulls more than about 4A and all is happy.


    The heated bed loop on the Rambo is on a much larger blade fuse. You can run a heated bed that does 24 volts off the Rambo board no problems. Heated beds are just a terrible idea in this conversion though. Not only will they not give you a good flat surface to print on and force you to spend even more hours wiring up yet more -stuff- but all the extra wiring and configuration makes them a royal pain. Note that on an aluminium bed you also have to insulate the heater bed from the deck with a sheet of heat resistant plastic. I used one of plastic kitchen surface sheets you can buy in packs. Basically if you are going to go with a heater bed good luck! Worst idea ever :/

    Instead I have switched to first putting down a layer of painters tape on the glass and then a layer of double sided tape. This takes less time than heating up the stupid heater bed and works brilliantly. Peels right off PLA. Can print without rafts no problems. No worries with burning your fingers on the hot surface. A lot easier.


    Next up I had to decide to do about the stepper control. The Rambo has 5 stepper driver chips on it. These have no heat sinks or mounting for heat sinks and will really only handle up to a 1.5A current. They say the board itself functions as a heat sink but I am a bit dubious about this. These stepper chips will be dead on the board if you manage to fry one as they are not modular like the RAMPS board. Wave soldered on. I do feel that it would be rather hard to kill one of the A4982 chips as they are current sensing and will shut down if in over current.

    The CNC3040 controller with its YOOCNC components already has three TB6560AHQ stepper driver boards hooked up and so the plan was to connect straight to those for the X/Y/Z. These boards do not have a good reputation but they seem to work fine to me.


    These have the typical +CP/-CP/+DIR/-DIR/+ENABLE/-ENABLE pins. They also have massive heat sinks and were already nicely wired to the 4 pin XLR style connector plugs on the back of the chassis and power to them is already supplied off the 24 volt supply in the controller.

    To hook these up to a controller like the RAMBO which provides a single pin for CP ( step ) and a single pin for direction and a single pin for enable I went for common +5 like this. You need to make up a cable like this for each axis. To save myself some soldering and crimping I recycled some of the IDC header cables from an old PC that ran between the audio and the USB on the case. These were 5×2 and all I had to do was pull out some plastic inserts and shift some pins and I had all 12 pins ( MX1/MX2/MX3 ) sorted at the Rambo end. I then grafted these to the original cables that had plugged in to the original control board. Note also that the ground to pin two is implied. I just left the 2nd pin on each MX disconnected and left it floating.


    Then I used one of the on board stepper chips for the extruder which is a much smaller Nema17 stepper. To get all this to work and to use the off board stepper boards used up pretty much every available pin and I had to be careful to ensure none of the pins clashed. You will want to look at the section in the Rambo manual on the MX headers to suss out what is going on

    This is my pin configuration for the Rambo board for Repetier.

    #if MOTHERBOARD == 301
    #define KNOWN_BOARD
    * RAMBo Pin Assignments

    #ifndef __AVR_ATmega2560__
    #error Oops! Make sure you have 'Arduino Mega 2560' selected from the 'Tools -> Boards' menu.

    #define ORIG_X_STEP_PIN 22
    #define ORIG_X_DIR_PIN 32
    #define ORIG_X_ENABLE_PIN 44
    #define ORIG_X_MIN_PIN 12 // Multiplexed together due to pin limit ..
    #define ORIG_X_MAX_PIN 12 // Which is how I damn wired em .... sigh
    #define X_MS1_PIN -1
    #define X_MS2_PIN -1

    #define ORIG_Y_STEP_PIN 23
    #define ORIG_Y_DIR_PIN 31
    #define ORIG_Y_ENABLE_PIN 45
    #define ORIG_Y_MIN_PIN -1
    #define ORIG_Y_MAX_PIN -1
    #define Y_MS1_PIN -1
    #define Y_MS2_PIN -1

    #define ORIG_Z_STEP_PIN 24
    #define ORIG_Z_DIR_PIN 30
    #define ORIG_Z_ENABLE_PIN 46
    #define ORIG_Z_MIN_PIN 10
    #define ORIG_Z_MAX_PIN 11 // Set this to YMIN since not shared with the MX port ....
    #define Z_MS1_PIN -1
    #define Z_MS2_PIN -1

    #define HEATER_0_PIN 9
    #define TEMP_0_PIN 0

    #define HEATER_1_PIN 3
    #define TEMP_1_PIN 1

    #define HEATER_2_PIN 7
    #define TEMP_2_PIN 1

    #define TEMP_3_PIN -1

    #define ORIG_E0_STEP_PIN 34
    #define ORIG_E0_DIR_PIN 43
    #define ORIG_E0_ENABLE_PIN 26
    #define E0_MS1_PIN 65
    #define E0_MS2_PIN 66

    #define ORIG_E1_STEP_PIN 33
    #define ORIG_E1_DIR_PIN 42
    #define ORIG_E1_ENABLE_PIN 25
    #define E1_MS1_PIN 63
    #define E1_MS2_PIN 64

    #define DIGIPOTSS_PIN 38
    #define DIGIPOT_CHANNELS {4,5,3,0,1} // X Y Z E0 E1 digipot channels to stepper driver mapping

    #define SDPOWER -1
    #define SDSS 53
    #define LED_PIN 13
    #define ORIG_FAN_PIN 8
    #define ORIG_FAN2_PIN 6
    #define ORIG_FAN3_PIN 2
    #define ORIG_PS_ON_PIN 4
    #define SUICIDE_PIN -1 //PIN that has to be turned on right after start, to keep power flowing.

    #define E1_PINS

    #define SCK_PIN 52
    #define MISO_PIN 50
    #define MOSI_PIN 51
    #define MAX6675_SS 53



    As usual the hell began with the software. I began with Marlin as this is what the Rambo manual recommends. Reconfiguring the X/Y/Z axis pins to clock out to the MX1 / MX2 / MX3 headers on the RAMBO board is fairly straight forward and well documented in the manual. I was pretty sure I had wired up the drivers correctly but no matter what I did I couldn’t get them to run smoothly. The drivers have optocouplers on them and it would seem no matter what I messed with I couldn’t get Marlin to really deal with the stepper drivers in a real PWM type mode as the YOOCNC + Mach3 setup had done. I was beginning to think this might have all been a terrible idea till I tried Repetier. For a start it has a really nice on-line configuration tool. I can’t think of a nice way to say this but Marlin is #@T#$^$! After two days of wasted effort with Marlin I had my stepper drivers running perfectly in about 60 minutes with Repetier. The ability to walk back and fourth through the configuration in the web interface is awesome. Thanks Repetier for writing something that -works. Having said this once I got the hardware working it took the longest time for me to fine tune everything and to get it all going smoothing. Adapting an existing gantry mill to a 3D printer is definitely not a plug and play option. Once you have done it though and you have a clear idea of what you are doing it really isn’t hard.

    This file is part of Repetier-Firmware.

    Repetier-Firmware is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Repetier-Firmware is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Repetier-Firmware. If not, see .



    /**************** READ FIRST ************************

    This configuration file was created with the configuration tool. For that
    reason, it does not contain the same informations as the original Configuration.h file.
    It misses the comments and unused parts. Open this file file in the config tool
    to see and change the data. You can also upload it to newer/older versions. The system
    will silently add new options, so compilation continues to work.

    This file is optimized for version 0.92

    If you are in doubt which named functions use which pins on your board, please check the
    pins.h for the used name->pin assignments and your board documentation to verify it is
    as you expect.


    #define NUM_EXTRUDER 1
    #define MOTHERBOARD 301
    #include "pins.h"

    // ################## EDIT THESE SETTINGS MANUALLY ################
    // Microstepping mod eof your RAMO board
    #define MICROSTEP_MODES { 8,8,8,8,8 } // [1,2,4,8,16]
    // Motor Current setting (Only functional when motor driver current ref pins are connected to a digital trimpot on supported boards)
    #define MOTOR_CURRENT_PERCENT { 55,55,55,55,55 }

    // ################ END MANUAL SETTINGS ##########################

    #define FAN_BOARD_PIN -1
    #define X_MIN_PIN ORIG_X_MAX_PIN
    #define Y_MIN_PIN -1
    #define Y_MAX_PIN -1

    //#define EXTERNALSERIAL use Arduino serial library instead of build in. Requires more ram, has only 63 byte input buffer.
    // Uncomment the following line if you are using arduino compatible firmware made for Arduino version earlier then 1.0
    // If it is incompatible you will get compiler errors about write functions not beeing compatible!
    //#define COMPAT_PRE1
    #define BLUETOOTH_SERIAL -1
    #define BLUETOOTH_BAUD 115200
    #define MIXING_EXTRUDER 0

    #define DRIVE_SYSTEM 0
    #define XAXIS_STEPS_PER_MM 400
    #define YAXIS_STEPS_PER_MM 400
    #define ZAXIS_STEPS_PER_MM 400
    #define PDM_FOR_EXTRUDER 0
    #define PDM_FOR_COOLER 0
    #define RETRACT_ON_PAUSE 2
    #define PAUSE_END_COMMANDS ""
    #define EXT0_X_OFFSET 0
    #define EXT0_Y_OFFSET 0
    #define EXT0_Z_OFFSET 0
    #define EXT0_STEPS_PER_MM 73.03
    #define EXT0_TEMPSENSOR_TYPE 3
    #define EXT0_DIR_PIN ORIG_E1_DIR_PIN
    #define EXT0_INVERSE 0
    #define EXT0_ENABLE_ON 0
    #define EXT0_MAX_FEEDRATE 50
    #define EXT0_MAX_START_FEEDRATE 20
    #define EXT0_MAX_ACCELERATION 100
    #define EXT0_HEAT_MANAGER 1
    #define EXT0_WATCHPERIOD 1
    #define EXT0_PID_I 2
    #define EXT0_PID_D 40
    #define EXT0_PID_MAX 255
    #define EXT0_ADVANCE_K 0
    #define EXT0_ADVANCE_L 0
    #define EXT0_WAIT_RETRACT_TEMP 150
    #define EXT0_SELECT_COMMANDS ""
    #define EXT0_DECOUPLE_TEST_PERIOD 12000
    #define EXT0_JAM_PIN -1
    #define EXT0_JAM_PULLUP 0

    #define RETRACTION_SPEED 40
    #define RETRACTION_Z_LIFT 0
    #define JAM_STEPS 220
    #define JAM_SLOWDOWN_STEPS 320
    #define JAM_SLOWDOWN_TO 70
    #define JAM_ERROR_STEPS 500
    #define JAM_MIN_STEPS 10
    #define JAM_ACTION 0

    #define RETRACT_DURING_HEATUP true
    #define PID_CONTROL_RANGE 20
    #define SKIP_M109_IF_WITHIN 2
    #define SCALE_PID_TO_MAX 0
    #define TEMP_HYSTERESIS 0
    #define EXTRUDE_MAXLENGTH 160
    #define GENERIC_THERM_VREF 5
    #define HEATER_PWM_SPEED 0

    // ############# Heated bed configuration ########################

    #define HAVE_HEATED_BED 0
    #define HEATED_BED_MAX_TEMP 100
    #define SKIP_M190_IF_WITHIN 3
    #define HEATED_BED_SET_INTERVAL 5000
    #define HEATED_BED_PID_IGAIN 2
    #define HEATED_BED_PID_DGAIN 40
    #define HEATED_BED_PID_MAX 255
    #define MIN_EXTRUDER_TEMP 150
    #define MAXTEMP 220

    // ################ Endstop configuration #####################

    #define ENDSTOP_PULLUP_X_MIN true
    #define ENDSTOP_X_MIN_INVERTING true
    #define MIN_HARDWARE_ENDSTOP_X true
    #define ENDSTOP_PULLUP_Y_MIN true
    #define ENDSTOP_Y_MIN_INVERTING false
    #define MIN_HARDWARE_ENDSTOP_Y false
    #define ENDSTOP_PULLUP_Z_MIN true
    #define ENDSTOP_Z_MIN_INVERTING true
    #define MIN_HARDWARE_ENDSTOP_Z true
    #define ENDSTOP_PULLUP_X_MAX true
    #define ENDSTOP_X_MAX_INVERTING true
    #define MAX_HARDWARE_ENDSTOP_X true
    #define ENDSTOP_PULLUP_Y_MAX true
    #define ENDSTOP_Y_MAX_INVERTING false
    #define MAX_HARDWARE_ENDSTOP_Y false
    #define ENDSTOP_PULLUP_Z_MAX true
    #define ENDSTOP_Z_MAX_INVERTING true
    #define MAX_HARDWARE_ENDSTOP_Z true
    #define max_software_endstop_r true

    #define min_software_endstop_x false
    #define min_software_endstop_y false
    #define min_software_endstop_z false
    #define max_software_endstop_x false
    #define max_software_endstop_y false
    #define max_software_endstop_z false
    #define ENDSTOP_X_BACK_MOVE 5
    #define ENDSTOP_Y_BACK_MOVE 5
    #define ENDSTOP_Z_BACK_MOVE 2
    #define ENDSTOP_X_BACK_ON_HOME 1
    #define ENDSTOP_Y_BACK_ON_HOME 1
    #define ENDSTOP_Z_BACK_ON_HOME 0

    // ################# XYZ movements ###################

    #define X_ENABLE_ON 1
    #define Y_ENABLE_ON 1
    #define Z_ENABLE_ON 1
    #define DISABLE_X 1
    #define DISABLE_Y 1
    #define DISABLE_Z 1
    #define DISABLE_E 0
    #define INVERT_X_DIR 0
    #define INVERT_Y_DIR 0
    #define INVERT_Z_DIR 1
    #define X_HOME_DIR 1
    #define Y_HOME_DIR 1
    #define Z_HOME_DIR -1
    #define X_MAX_LENGTH 5000
    #define Y_MAX_LENGTH 5000
    #define Z_MAX_LENGTH 5000
    #define X_MIN_POS 0
    #define Y_MIN_POS 0
    #define Z_MIN_POS 0

    // ##########################################################################################
    // ## Movement settings ##
    // ##########################################################################################


    #define DELTA_SEGMENTS_PER_SECOND_PRINT 180 // Move accurate setting for print moves
    #define DELTA_SEGMENTS_PER_SECOND_MOVE 70 // Less accurate setting for other moves

    // Delta settings
    #define DELTA_HOME_ON_POWER 0

    #define MAX_INACTIVE_TIME 0L
    #define MAX_FEEDRATE_X 200
    #define MAX_FEEDRATE_Y 200
    #define MAX_FEEDRATE_Z 200
    #define HOMING_FEEDRATE_X 50
    #define HOMING_FEEDRATE_Y 50
    #define HOMING_FEEDRATE_Z 50
    #define ZHOME_HEAT_ALL 1
    #define ZHOME_HEAT_HEIGHT 20
    #define ZHOME_X_POS 999999
    #define ZHOME_Y_POS 999999
    #define X_BACKLASH 0
    #define Y_BACKLASH 0
    #define Z_BACKLASH 0
    #define STEPPER_HIGH_DELAY 23
    #define DIRECTION_DELAY 3
    #define STEP_DOUBLER_FREQUENCY 12000
    #define DOUBLE_STEP_DELAY 24 // time in microseconds
    #define MAX_JERK 5
    #define MAX_ZJERK 0.3
    #define MOVE_CACHE_LOW 10
    #define LOW_TICKS_PER_MOVE 250000
    #define X2_STEP_PIN ORIG_E1_STEP_PIN
    #define X2_DIR_PIN ORIG_E1_DIR_PIN
    #define Y2_STEP_PIN ORIG_E1_STEP_PIN
    #define Y2_DIR_PIN ORIG_E1_DIR_PIN
    #define Z2_STEP_PIN ORIG_E1_STEP_PIN
    #define Z2_DIR_PIN ORIG_E1_DIR_PIN
    #define USE_ADVANCE 0

    // ################# Misc. settings ##################

    #define BAUDRATE 115200
    #define POWER_INVERTING 0
    #define KILL_METHOD 1
    #define ECHO_ON_EXECUTE 1
    #define EEPROM_MODE 0
    #define PS_ON_PIN ORIG_PS_ON_PIN

    /* ======== Servos =======
    Control the servos with
    M340 P S / ServoID = 0..3 pulseInUs = 500..2500
    Servos are controlled by a pulse width normally between 500 and 2500 with 1500ms in center position. 0 turns servo off.
    WARNING: Servos can draw a considerable amount of current. Make sure your system can handle this or you may risk your hardware!
    #define FEATURE_SERVO 0
    #define SERVO0_PIN 11
    #define SERVO1_PIN -1
    #define SERVO2_PIN -1
    #define SERVO3_PIN -1
    #define SERVO0_NEUTRAL_POS -1
    #define SERVO1_NEUTRAL_POS -1
    #define SERVO2_NEUTRAL_POS -1
    #define SERVO3_NEUTRAL_POS -1
    #define UI_SERVO_CONTROL 0
    #define FAN_KICKSTART_TIME 200

    #define FEATURE_WATCHDOG 1

    // #################### Z-Probing #####################

    #define FEATURE_Z_PROBE 0
    #define Z_PROBE_BED_DISTANCE 10
    #define Z_PROBE_PIN -1
    #define Z_PROBE_PULLUP 0
    #define Z_PROBE_ON_HIGH 0
    #define Z_PROBE_X_OFFSET 0
    #define Z_PROBE_Y_OFFSET 0
    #define Z_PROBE_SPEED 2
    #define Z_PROBE_XY_SPEED 150
    #define Z_PROBE_HEIGHT 40
    #define Z_PROBE_START_SCRIPT ""
    #define Z_PROBE_X1 20
    #define Z_PROBE_Y1 20
    #define Z_PROBE_X2 160
    #define Z_PROBE_Y2 20
    #define Z_PROBE_X3 100
    #define Z_PROBE_Y3 160
    #define FEATURE_AXISCOMP 0
    #define AXISCOMP_TANXY 0
    #define AXISCOMP_TANYZ 0
    #define AXISCOMP_TANXZ 0

    #ifndef SDSUPPORT // Some boards have sd support on board. These define the values already in pins.h
    #define SDSUPPORT 0
    #define SDCARDDETECT -1
    #define SD_EXTENDED_DIR 0 /** Show extended directory including file length. Don't use this with Pronterface! */
    #define SD_RUN_ON_STOP ""
    #define ARC_SUPPORT 1
    #define UI_LANGUAGE 0
    #define UI_PRINTER_NAME "RepRap"
    #define UI_PRINTER_COMPANY "Home made"
    #define UI_PAGES_DURATION 4000
    #define UI_ANIMATION 1
    #define FEATURE_UI_KEYS 0
    #define UI_ENCODER_SPEED 1
    #define UI_REVERSE_ENCODER 0
    #define UI_KEY_BOUNCETIME 10
    #define UI_KEY_FIRST_REPEAT 500
    #define UI_KEY_REDUCE_REPEAT 50
    #define UI_KEY_MIN_REPEAT 50
    #define FEATURE_BEEPER 0
    #define CASE_LIGHTS_PIN -1
    #define UI_START_SCREEN_DELAY 1000
    Beeper sound definitions for short beeps during key actions
    and longer beeps for important actions.
    Parameter is delay in microseconds and the secons is the number of repetitions.
    Values must be in range 1..255
    #define BEEPER_LONG_SEQUENCE 8,8
    #define UI_SET_MAX_HEATED_BED_TEMP 120
    #define UI_SET_MIN_EXTRUDER_TEMP 170
    #define UI_SET_MAX_EXTRUDER_TEMP 260

    #define NUM_MOTOR_DRIVERS 0


    /* Below you will find the configuration string, that created this Configuration.h

    ========== Start configuration string ==========
    "editMode": 1,
    "processor": 0,
    "baudrate": 115200,
    "bluetoothSerial": -1,
    "bluetoothBaudrate": 115200,
    "xStepsPerMM": 400,
    "yStepsPerMM": 400,
    "zStepsPerMM": 400,
    "xInvert": 0,
    "xInvertEnable": "1",
    "eepromMode": 0,
    "yInvert": 0,
    "yInvertEnable": "1",
    "zInvert": "1",
    "zInvertEnable": "1",
    "extruder": [
    "id": 0,
    "heatManager": 1,
    "pidDriveMin": 40,
    "pidDriveMax": 255,
    "pidMax": 255,
    "sensorType": 3,
    "sensorPin": "TEMP_0_PIN",
    "heaterPin": "HEATER_0_PIN",
    "maxFeedrate": 50,
    "startFeedrate": 20,
    "invert": "0",
    "invertEnable": "0",
    "acceleration": 100,
    "watchPeriod": 1,
    "pidP": 7,
    "pidI": 2,
    "pidD": 40,
    "advanceK": 0,
    "advanceL": 0,
    "waitRetractTemp": 150,
    "waitRetractUnits": 0,
    "waitRetract": 0,
    "stepsPerMM": 73.03,
    "coolerPin": "ORIG_FAN_PIN",
    "coolerSpeed": 100,
    "selectCommands": "",
    "deselectCommands": "",
    "xOffset": 0,
    "yOffset": 0,
    "zOffset": 0,
    "xOffsetSteps": 0,
    "yOffsetSteps": 0,
    "zOffsetSteps": 0,
    "stepper": {
    "name": "Extruder 1",
    "step": "ORIG_E1_STEP_PIN",
    "dir": "ORIG_E1_DIR_PIN",
    "enable": "E1_ENABLE_PIN"
    "advanceBacklashSteps": 0,
    "decoupleTestPeriod": 12,
    "jamPin": -1,
    "jamPullup": "0"
    "uiLanguage": 0,
    "uiController": 0,
    "xMinEndstop": 1,
    "yMinEndstop": 0,
    "zMinEndstop": 1,
    "xMaxEndstop": 1,
    "yMaxEndstop": 0,
    "zMaxEndstop": 1,
    "motherboard": 301,
    "driveSystem": 0,
    "xMaxSpeed": 200,
    "xHomingSpeed": 50,
    "xTravelAcceleration": 50,
    "xPrintAcceleration": 50,
    "yMaxSpeed": 200,
    "yHomingSpeed": 50,
    "yTravelAcceleration": 50,
    "yPrintAcceleration": 50,
    "zMaxSpeed": 200,
    "zHomingSpeed": 50,
    "zTravelAcceleration": 400,
    "zPrintAcceleration": 400,
    "xMotor": {
    "name": "X motor",
    "step": "ORIG_X_STEP_PIN",
    "dir": "ORIG_X_DIR_PIN",
    "enable": "ORIG_X_ENABLE_PIN"
    "yMotor": {
    "name": "Y motor",
    "step": "ORIG_Y_STEP_PIN",
    "dir": "ORIG_Y_DIR_PIN",
    "enable": "ORIG_Y_ENABLE_PIN"
    "zMotor": {
    "name": "Z motor",
    "step": "ORIG_Z_STEP_PIN",
    "dir": "ORIG_Z_DIR_PIN",
    "enable": "ORIG_Z_ENABLE_PIN"
    "enableBacklash": "0",
    "backlashX": 0,
    "backlashY": 0,
    "backlashZ": 0,
    "stepperInactiveTime": 360,
    "maxInactiveTime": 0,
    "xMinPos": 0,
    "yMinPos": 0,
    "zMinPos": 0,
    "xLength": 5000,
    "yLength": 5000,
    "zLength": 5000,
    "alwaysCheckEndstops": "1",
    "disableX": "1",
    "disableY": "1",
    "disableZ": "1",
    "disableE": "0",
    "xHomeDir": "1",
    "yHomeDir": "1",
    "zHomeDir": "-1",
    "xEndstopBack": 1,
    "yEndstopBack": 1,
    "zEndstopBack": 0,
    "deltaSegmentsPerSecondPrint": 180,
    "deltaSegmentsPerSecondTravel": 70,
    "deltaDiagonalRod": 445,
    "deltaHorizontalRadius": 209.25,
    "deltaAlphaA": 210,
    "deltaAlphaB": 330,
    "deltaAlphaC": 90,
    "deltaDiagonalCorrA": 0,
    "deltaDiagonalCorrB": 0,
    "deltaDiagonalCorrC": 0,
    "deltaMaxRadius": 150,
    "deltaFloorSafetyMarginMM": 15,
    "deltaRadiusCorrA": 0,
    "deltaRadiusCorrB": 0,
    "deltaRadiusCorrC": 0,
    "deltaXOffsetSteps": 0,
    "deltaYOffsetSteps": 0,
    "deltaZOffsetSteps": 0,
    "deltaSegmentsPerLine": 24,
    "stepperHighDelay": 23,
    "directionDelay": 3,
    "stepDoublerFrequency": 12000,
    "allowQuadstepping": "0",
    "doubleStepDelay": 24,
    "maxJerk": 0.3,
    "maxZJerk": 0.3,
    "moveCacheSize": 16,
    "moveCacheLow": 10,
    "lowTicksPerMove": 250000,
    "enablePowerOnStartup": "1",
    "echoOnExecute": "1",
    "sendWaits": "0",
    "ackWithLineNumber": "1",
    "killMethod": 1,
    "useAdvance": "0",
    "useQuadraticAdvance": "0",
    "powerInverting": 0,
    "mirrorX": 0,
    "mirrorXMotor": {
    "name": "Extruder 1",
    "step": "ORIG_E1_STEP_PIN",
    "dir": "ORIG_E1_DIR_PIN",
    "enable": "E1_ENABLE_PIN"
    "mirrorY": 0,
    "mirrorYMotor": {
    "name": "Extruder 1",
    "step": "ORIG_E1_STEP_PIN",
    "dir": "ORIG_E1_DIR_PIN",
    "enable": "E1_ENABLE_PIN"
    "mirrorZ": 0,
    "mirrorZMotor": {
    "name": "Extruder 1",
    "step": "ORIG_E1_STEP_PIN",
    "dir": "ORIG_E1_DIR_PIN",
    "enable": "E1_ENABLE_PIN"
    "dittoPrinting": "0",
    "featureServos": "0",
    "servo0Pin": 11,
    "servo1Pin": -1,
    "servo2Pin": -1,
    "servo3Pin": -1,
    "featureWatchdog": "1",
    "hasHeatedBed": "0",
    "enableZProbing": "0",
    "extrudeMaxLength": 160,
    "homeOrder": "HOME_ORDER_ZXY",
    "featureController": 0,
    "uiPrinterName": "RepRap",
    "uiPrinterCompany": "Home made",
    "uiPagesDuration": 4000,
    "uiAnimation": "1",
    "uiDisablePageswitch": "1",
    "uiAutoReturnAfter": 30000,
    "featureKeys": "0",
    "uiEncoderSpeed": 1,
    "uiReverseEncoder": "0",
    "uiKeyBouncetime": 10,
    "uiKeyFirstRepeat": 500,
    "uiKeyReduceRepeat": 50,
    "uiKeyMinRepeat": 50,
    "featureBeeper": "0",
    "uiPresetBedTempPLA": 60,
    "uiPresetBedABS": 110,
    "uiPresetExtruderPLA": 190,
    "uiPresetExtruderABS": 240,
    "uiMinHeatedBed": 30,
    "uiMaxHeatedBed": 120,
    "uiMinEtxruderTemp": 170,
    "uiMaxExtruderTemp": 260,
    "uiExtruderFeedrate": 2,
    "uiExtruderRetractDistance": 3,
    "uiSpeeddependentPositioning": "0",
    "maxBedTemperature": 100,
    "bedSensorType": 3,
    "bedSensorPin": "TEMP_1_PIN",
    "bedHeaterPin": "HEATER_1_PIN",
    "bedHeatManager": 1,
    "bedUpdateInterval": 5000,
    "bedPidDriveMin": 40,
    "bedPidDriveMax": 255,
    "bedPidP": 7,
    "bedPidI": 2,
    "bedPidD": 40,
    "bedPidMax": 255,
    "bedDecoupleTestPeriod": 300,
    "caseLightPin": -1,
    "caseLightDefaultOn": "1",
    "bedSkipIfWithin": 3,
    "gen1T0": 25,
    "gen1R0": 100000,
    "gen1Beta": 4036,
    "gen1MinTemp": -20,
    "gen1MaxTemp": 300,
    "gen1R1": 0,
    "gen1R2": 4700,
    "gen2T0": 25,
    "gen2R0": 100000,
    "gen2Beta": 4036,
    "gen2MinTemp": -20,
    "gen2MaxTemp": 300,
    "gen2R1": 0,
    "gen2R2": 4700,
    "gen3T0": 25,
    "gen3R0": 100000,
    "gen3Beta": 4036,
    "gen3MinTemp": -20,
    "gen3MaxTemp": 300,
    "gen3R1": 0,
    "gen3R2": 4700,
    "userTable0": {
    "r1": 0,
    "r2": 4700,
    "temps": [
    "t": 235,
    "r": 292.9,
    "adc": 240.22622123415
    "t": 250,
    "r": 227.8,
    "adc": 189.30171679045
    "t": 10,
    "r": 10,
    "adc": 8.6942675159236
    "t": 125,
    "r": 125,
    "adc": 106.08808290155
    "t": 135,
    "r": 2404,
    "adc": 1385.7516891892
    "t": 145,
    "r": 1869,
    "adc": 1165.1019942153
    "t": 195,
    "r": 615.8,
    "adc": 474.37845667632
    "t": 185,
    "r": 755.4,
    "adc": 567.02771565788
    "t": 175,
    "r": 934.5,
    "adc": 679.16895909131
    "t": 215,
    "r": 418.8,
    "adc": 335.03672735797
    "t": 165,
    "r": 1166,
    "adc": 813.97374701671
    "t": 205,
    "r": 505.9,
    "adc": 397.94473578056
    "t": 225,
    "r": 349.1,
    "adc": 283.1325384722
    "t": 245,
    "r": 247.3,
    "adc": 204.69619792614
    "t": 155,
    "r": 1469,
    "adc": 975.12643864484
    "t": 45,
    "r": 42951,
    "adc": 3691.0945205767
    "t": 115,
    "r": 4115,
    "adc": 1911.6193987521
    "t": 105,
    "r": 5487,
    "adc": 2205.6802787867
    "t": 95,
    "r": 7419,
    "adc": 2506.8739169899
    "t": 85,
    "r": 10181,
    "adc": 2801.6393387541
    "t": 75,
    "r": 14198,
    "adc": 3076.5588951212
    "t": 65,
    "r": 20144,
    "adc": 3320.3059088714
    "t": 55,
    "r": 29119,
    "adc": 3525.8968331411
    "t": 35,
    "r": 64759,
    "adc": 3817.9084783829
    "t": 25,
    "r": 100000,
    "adc": 3911.1747851003
    "t": 0,
    "r": 333960,
    "adc": 4038.1686647375
    "t": -55,
    "r": 9877500,
    "adc": 4093.0524073587
    "numEntries": 27
    "userTable1": {
    "r1": 0,
    "r2": 4700,
    "temps": []
    "userTable2": {
    "r1": 0,
    "r2": 4700,
    "temps": []
    "tempHysteresis": 0,
    "pidControlRange": 20,
    "skipM109Within": 2,
    "extruderFanCoolTemp": 50,
    "minTemp": 150,
    "maxTemp": 220,
    "minDefectTemp": -10,
    "maxDefectTemp": 230,
    "arcSupport": "1",
    "featureMemoryPositionWatchdog": "1",
    "forceChecksum": "0",
    "sdExtendedDir": "0",
    "featureFanControl": "0",
    "fanPin": "ORIG_FAN_PIN",
    "scalePidToMax": 0,
    "zProbePin": -1,
    "zProbeBedDistance": 10,
    "zProbePullup": "0",
    "zProbeOnHigh": "0",
    "zProbeXOffset": 0,
    "zProbeYOffset": 0,
    "zProbeWaitBeforeTest": "0",
    "zProbeSpeed": 2,
    "zProbeXYSpeed": 150,
    "zProbeHeight": 40,
    "zProbeStartScript": "",
    "zProbeFinishedScript": "",
    "featureAutolevel": "1",
    "zProbeX1": 20,
    "zProbeY1": 20,
    "zProbeX2": 160,
    "zProbeY2": 20,
    "zProbeX3": 100,
    "zProbeY3": 160,
    "zProbeSwitchingDistance": 1,
    "zProbeRepetitions": 1,
    "sdSupport": "0",
    "sdCardDetectPin": -1,
    "sdCardDetectInverted": "0",
    "uiStartScreenDelay": 1000,
    "xEndstopBackMove": 5,
    "yEndstopBackMove": 5,
    "zEndstopBackMove": 2,
    "xEndstopRetestFactor": 3,
    "yEndstopRetestFactor": 3,
    "zEndstopRetestFactor": 3,
    "xMinPin": "ORIG_X_MAX_PIN",
    "yMinPin": -1,
    "zMinPin": "ORIG_Z_MIN_PIN",
    "xMaxPin": "ORIG_X_MAX_PIN",
    "yMaxPin": -1,
    "zMaxPin": "ORIG_Z_MAX_PIN",
    "deltaHomeOnPower": "0",
    "fanBoardPin": -1,
    "heaterPWMSpeed": 0,
    "featureBabystepping": "0",
    "babystepMultiplicator": 1,
    "pdmForHeater": "0",
    "pdmForCooler": "0",
    "psOn": "ORIG_PS_ON_PIN",
    "mixingExtruder": "0",
    "decouplingTestMaxHoldVariance": 20,
    "decouplingTestMinTempRise": 0.5,
    "featureAxisComp": "0",
    "axisCompTanXY": 0,
    "axisCompTanXZ": 0,
    "axisCompTanYZ": 0,
    "retractOnPause": 2,
    "pauseStartCommands": "",
    "pauseEndCommands": "",
    "distortionCorrection": "0",
    "distortionCorrectionPoints": 5,
    "distortionCorrectionR": 100,
    "distortionPermanent": "1",
    "distortionUpdateFrequency": 15,
    "distortionStartDegrade": 0.5,
    "distortionEndDegrade": 1,
    "distortionExtrapolateCorners": "0",
    "sdRunOnStop": "",
    "sdStopHeaterMotorsOnStop": "0",
    "featureRetraction": "0",
    "autoretractEnabled": "0",
    "retractionLength": 3,
    "retractionLongLength": 13,
    "retractionSpeed": 40,
    "retractionZLift": 0,
    "retractionUndoExtraLength": 0,
    "retractionUndoExtraLongLength": 0,
    "retractionUndoSpeed": 20,
    "filamentChangeXPos": 0,
    "filamentChangeYPos": 0,
    "filamentChangeZAdd": 2,
    "filamentChangeRehome": 1,
    "filamentChangeShortRetract": 5,
    "filamentChangeLongRetract": 50,
    "fanKickstart": 200,
    "servo0StartPos": -1,
    "servo1StartPos": -1,
    "servo2StartPos": -1,
    "servo3StartPos": -1,
    "uiDynamicEncoderSpeed": "1",
    "uiServoControl": 0,
    "killIfSensorDefect": "0",
    "jamSteps": 220,
    "jamSlowdownSteps": 320,
    "jamSlowdownTo": 70,
    "jamErrorSteps": 500,
    "jamMinSteps": 10,
    "jamAction": 0,
    "primaryPort": 0,
    "numMotorDrivers": 0,
    "motorDrivers": [
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "t": "None",
    "s": "",
    "invertEnable": "0",
    "invertDirection": "0",
    "stepsPerMM": 100,
    "speed": 10,
    "dirPin": -1,
    "stepPin": -1,
    "enablePin": -1
    "manualConfig": "",
    "zHomeMinTemperature": 0,
    "zHomeXPos": 999999,
    "zHomeYPos": 999999,
    "zHomeHeatHeight": 20,
    "zHomeHeatAll": "1",
    "maxHalfstepInterval": 1999,
    "hasMAX6675": false,
    "hasMAX31855": false,
    "hasGeneric1": false,
    "hasGeneric2": false,
    "hasGeneric3": false,
    "hasUser0": false,
    "hasUser1": false,
    "hasUser2": false,
    "numExtruder": 1,
    "version": 92.3,
    "primaryPortName": ""
    ========== End configuration string ==========


    I’d like to take a moment to give a plug to the guys at Micron 3DP from Israel. The extruder they have sent me is a jewel! It is a beautifully made thing and the cad models they sent me so I could get on with designing the mountings were excellent.



    On face selection pipes and beziers …

    General :

    Sometimes the things that seem easy turn out to take a long time. In this particular instance drawing nice lofted tapering pipes that follow Bézier curves. Wont go in to much detail as it is long and tedious but it took about 3 days to get that working properly. Problems with non convex polygons and other issues are not as simple as it would seem to resolve. This is a bit of a long post and I have been putting bits in to it for a few days.


    Yes you can make your own objects now via the editor window but no documentation is available yet! Yes you can save your projects to. No you cannot do user registration just yet. Accounts however can be requested.

    At a few presentations I have done now people have asked me what the difference is between a parametric system and a procedural system. Here is the definition for those who asked at my last one. My answer is that one of these is a system and the other is a way of addressing that system.

    “Procedural modeling is an umbrella term for a number of techniques in computer graphics to create 3D models and textures from sets of rules. L-Systems, fractals, and generative modeling are procedural modeling techniques since they apply algorithms for producing scenes.”

    That is what we are doing here. Using small bundles of rules to build things!

    Now a procedural modelling system may present either a classic in-scene interface involving sketches and drawing tools -or- it may present a parametric interface that consists of primarily skeuomorphic tools that are geared towards specific higher level tasks. The design pressure tends to lead to a bit of both but seldom just one or the other dominates the user experience completely.


    When developing a procedural modelling system the temptation is always to step in to the scene with tools and to eschew the parametric approach. Last week I was thinking about individual face selection. Consider two objects in a 3D scene. Say a tea pot spout and the surface of the body of a tea pot. You want to join the two together by selecting a face on the teapot and attaching the spout to that point. In a traditional cad tool you would define a sketch plane. Then you would draw a profile in that sketch plane. Constrain it. You might draw other planes that contain a curve. Then you might apply a lofting function of some kind. In a good well developed tool with a decent legacy behind it this can all work very nicely. This is the kind of task that naturally leads to scene driven in-scene tools. While it is easy to procedurally define for example a bicycle frame. It would appear at first glance to be not so easy to deal with objects on a point by point basis or on an implied solid basis.

    So what might we do in a procedural fashion that performs the same task?

    What might we do that doesn’t involve entering the scene at all?

    One way is to provide dynamic projection through a model on to a 2D plane so that you can interact with the points projected in to that plane in a canvas.

    Consider a sphere with a plane cutting through the Centre like so.


    Your projection will ultimately consist of a point where every single polygon passes a line through the plane.

    In your canvas you now in essence have a 2D drawing task easily addressable by various widget approaches. You can move points. Bind other points to them. Track points as they move. Then when you have performed your operations you can transport that association back out in to changes in the scene. Note how this begins to sound like you are just reinventing a part of the scene itself in another scene. You have merely taken a slice of the scene. That starts to sound like sketches. Note that it is implied that you would reach in to the scene in some way and move the plane or perhaps interact with it with parametric inputs. As you did so wherever objects in the scene passed through that plane would project in to your 2D canvas and that it would update accordingly. Following on from this you could then interpolate between points to dock to points on the model that were sub vertex and select those to. You could also do interesting operations with unions and subtractions and intersections this way.

    So to explore this idea I wrote a plane intersection parser that is now available in the Makertron. If you define a plane within the json code it will give you back a 2D canvas to manipulate in various ways. You can overlay it with a bezier as a point guide. Bind a point so it will be followed. You can pick projected vectors out of a canvas.

    And you know what. Once I had written this tool I realized it was the wrong way. This is the wrong approach to these problems. Wrong. Deeply wrong!!!

    This is at the core of choosing a different way. Pretty much everything you might want to do is in the scene or involves interaction in the scene. Or does it really? The reality is pretty much every interaction you have with a scene also involves an interaction with a set of widgets that drive that scene. This is the elephant in the room with all scene based modelers. Draw a circle in a sketch in Solidworks. Behold the sudden appearance of the parametric dialog majesty! Since the 1970’s CAD tools have followed a drafting legacy and rightly so. The metaphors are all here. Dimensions. Sketches. Constraints. The purpose of these tools was not to assemble complete finished models for output to production but to provide clear drawings and dimensional information for translation in to DRAWINGS for production. Drafting is a skilled and time consuming job. The tools are heavy. The learning long.

    So what does the procedural looking alternative look like?

    Firstly you don’t have to reach in to the scene at all. The thinking changes here. What is a collection of points on a model?

    It is a list. As a list you can select locations on a model in any number of ways. Distance based or simple indexing. To find things in a model it really isn’t necessary to understand the solid nature of a model. What it intersects with. What it does not intersect with. All you need to understand is that it has another nature and that it can be addressed that way. In a procedural world everything has an abstraction beyond the scene. Everything is data and everything is code and is bound by rules. Everything is ultimately approachable as a structure. As a list.

    So for example the parametric alternative here is simple and elegant. Two thumb wheels. One thumb wheel moves up through each segment of the sphere highlighting as you go. The other rotates radially picking a point on that segment. You can find any point on the sphere in a second. No clicking or hunting required with the mouse.

    And then as we go down this path it gets even more interesting. Do we really need to always bind objects at all. Well not always. Unlike the bicycle example the tea pot demo has no bindings at all. Objects merely float and where they union each other intersections or joins are created as appropriate.

    Things just keep getting simpler and simpler. Oh wait no they do not! And in further adventures this happened NEXT …

    —————— The Ungodly Bezier Of Doom ———————-

    Here are some images from the past few weeks of a long bug filled csg nightmare saga that has ultimately concluded with something solid.





    Drawing smooth Bezier path pipes.

    Makertron And The Manufacturing Renaissance


    I have a theory. If an idea is good it will occur in what seems like a vacuum and then one day you will find it by accident but it will be somewhere else and it will probably predate whatever you thought of by some considerable period of time.


    Remember those toys that were about design and actually building things. What if those toys grew up in to series tools and manufacturing processes.


    Maketron is likewise design ( AnD ) patterns ( AnD ) nodes in software for the world of real things and the real world.

    In the kind of system I am talking about the user asks for objects

    • Tubes
    •  Brackets
    •  Wheels
    • Motors
    • Seats
    • Gears
    • Nuts
    • Bolts
    • Electronics

    These objects themselves generate constraints widgets and interaction with which they can be managed.

    Design possibilities exist as arrangements of these  complex object definitions held together  with constraints that have captured expertise.

    In many instances this looks like inverse kinematics but the intent is different.

    When engaging with such a system you always have somewhere to begin and something to choose from and answers to your design questions.

    You may have heard of this before as ECAD or EDA.

    “Electronic design automation (EDA or ECAD) is a category of software tools for designing electronic systems such as printed circuit boards and integrated circuits. The tools work together in a design flow that chip designers use to design and analyze entire semiconductor chips.”

    That seems to me a pretty limited definition of ECAD which to my mind is really just about solving paths as is inverse kinematics.

    This definition is not to be confused with an expert system

    In artificial intelligence, an expert system is a computer system that emulates the decision-making ability of a human expert. Expert systems are designed to solve complex problems by reasoning about knowledge, represented primarily as if–then rules rather than through conventional procedural code. The first expert systems were created in the 1970s and then proliferated in the 1980s. Expert systems were among the first truly successful forms of AI software

    In the Makertron the more complex the object the more sophisticated the interaction that it asks for.

    You can get some feeling for this in the Makertron interface itself.


    You can also see how tools can gradually scale in complexity to meet the requirements of components.

    For example the bezier curve tool for the handle bars.

    Each bezier curve controls a portion of a standard bicycle drop bar geometry.


    It captures the sort of expertise you might find in an article like this

    Drop Bars

    When a node editor is added you begin to get a system that perhaps looks something like this ,


    This is completely the inverse of a third generation cad tool which typically has a work flow that will consist of

    • Sketches
    •  Solids Editor
    •  Assembly Tool

    It takes on average two semester modules at University to learn the basics of Autodesk Inventor.

    Four semesters to learn Catia.

    Add another year to be even vaugely proficient and actually start doing something useful. Then add another year to that before you can begin to even consider trying to manufacture the things you have drawn.

    While your at it add an engineering degree and a life time of workshop experience to build something safe / useable competent.

    Cad is complicated. It is a world of tiny annoying things and by the time your done the creative impulse will be long gone and you will have an enormous grey beard and the world of real things will be long forgotten.

    This is I feel one of the largest obstacles to changing how we design things and ultimately how we manufacture them.

    Quite a bit of difference exists between the capture of expertise and giving people somewhere to start and the notion of capturing ideas from the crowd and honing those down to a series of winners  Quirky

    Quite a bit of difference exists between a box full of tools and an empty work bench Tinkercad