Modifying Marlin Firmware to work with a TandemB

In previous posts I built a prototype ‘TandemB’  which can be thought of as a 2D Delta printer in terms of its x and y-axis movement. This prototype could be moved about under its own power but that had no compatible firmware to drive it. In this post I cover the firmware modifications required to make the Tandem move using normal g-code commands. You can find a copy of this modified firmware here.

Modifying Marlin

Note: If you are considering doing any major modifications to firmware beyond just editing the config files I highly recommend exploring options other than the standard arduino code editor. I have been using the free community edition Visual Studio 2013 to view, modify and upload the firmware and it is a big improvement. Visual studio actively monitors the #define statements and then greys out any code that is not defined. ie: definitions that have been commented out. This makes skimming through the code much quicker as around 80% of what’s there is not in use for any one setup and so is greyed out. The tabs are also much easier to view, search and organise and I find the the compiling errors are simpler to find and diagnose. If you would like to use VS then you will also need VisualMicro for arduino support.

Marlin firmware was modified to work with my Tandem setup due to its compartmentalised nature and the fact that Craig Bossard had already started to make the very changes I needed. Craig would like to use scavenged flatbed scanner parts to build a 3D printer in the same manner as the Tandem and so had already been modifying Marlin to this end before I even started working on the Tandem idea. So thanks Craig for sending me a copy of you modified Marlin as it gave me a great starting point!

Craig’s idea was that rather than  re-writing a lot of code it would be far simpler to modify the existing Delta reverse kinematics. The required changes all take place in marlin.main.cpp.

The original Delta kinematic equations from marlin.main are shown below:

void recalc_delta_settings(float radius, float diagonal_rod)
delta_tower1_x= -SIN_60*radius; // front left tower
delta_tower1_y= -COS_60*radius;
delta_tower2_x= SIN_60*radius; // front right tower
delta_tower2_y= -COS_60*radius;
delta_tower3_x= 0.0; // back middle tower
delta_tower3_y= radius;
delta_diagonal_rod_2= sq(diagonal_rod);

void calculate_delta(float cartesian[3])
delta[X_AXIS] = sqrt(delta_diagonal_rod_2
– sq(delta_tower1_x-cartesian[X_AXIS])
– sq(delta_tower1_y-cartesian[Y_AXIS])
) + cartesian[Z_AXIS];
delta[Y_AXIS] = sqrt(delta_diagonal_rod_2
– sq(delta_tower2_x-cartesian[X_AXIS])
– sq(delta_tower2_y-cartesian[Y_AXIS])
) + cartesian[Z_AXIS];
delta[Z_AXIS] = sqrt(delta_diagonal_rod_2
– sq(delta_tower3_x-cartesian[X_AXIS])
– sq(delta_tower3_y-cartesian[Y_AXIS])
) + cartesian[Z_AXIS];

This code works off the delta tower definitions which is also located in marlin.main.cpp and is shown below:

#ifdef DELTA
float delta[3] = { 0, 0, 0 };
#define SIN_60 0.8660254037844386
#define COS_60 0.5
// these are the default values, can be overriden with M665
float delta_radius = DELTA_RADIUS;
float delta_tower1_x = -SIN_60 * delta_radius; // front left tower
float delta_tower1_y = -COS_60 * delta_radius;
float delta_tower2_x = SIN_60 * delta_radius; // front right tower
float delta_tower2_y = -COS_60 * delta_radius;
float delta_tower3_x = 0; // back middle tower
float delta_tower3_y = delta_radius;
float delta_diagonal_rod = DELTA_DIAGONAL_ROD;
float delta_diagonal_rod_2 = sq(delta_diagonal_rod);
float delta_segments_per_second = DELTA_SEGMENTS_PER_SECOND;

Using the code above as a basis I constructed an excel spreadsheet that modeled the delta kinematics (copy here) so as to allow me to make changes to the equations and see the results in real time. Note that excel does not allow you to plot an xyz three dimensional scatter plot and so if you want to visualise the results you will need to use a third party program such as Origin Pro.

By removing one tower completely, and then redefining the z-axis to behave as the y-axis the changes were complete. A spreadsheet that models the tandem kinematics and plots the required carriage position can be found here.

The new kinematic equations in marlin for the tandem setup are shown below:

void recalc_delta_settings(float radius, float diagonal_rod)
delta_tower1_x = -radius; // front left tower
delta_tower2_x = radius; // front right tower
delta_diagonal_rod_2 = sq(diagonal_rod);

void calculate_delta(float cartesian[2])
delta[X_AXIS] = (sqrt(delta_diagonal_rod_2 – sq(delta_tower1_x – cartesian[X_AXIS])) + cartesian[Y_AXIS]);
delta[Y_AXIS] = sqrt(delta_diagonal_rod_2 – sq(delta_tower2_x – cartesian[X_AXIS])) + cartesian[Y_AXIS];
delta[Z_AXIS] = cartesian[Z_AXIS];

and the new tower definitions:

#ifdef DELTA
float delta[3] = { 0, 0, 0 };
#define SIN_60 0.8660254037844386
#define COS_60 0.5
// these are the default values, can be overriden with M665
float delta_radius = DELTA_RADIUS;
float delta_tower1_x = -delta_radius; // front left tower
float delta_tower2_x = delta_radius; // front right tower
float delta_diagonal_rod = DELTA_DIAGONAL_ROD;
float delta_diagonal_rod_2 = sq(delta_diagonal_rod);
float delta_segments_per_second = DELTA_SEGMENTS_PER_SECOND;

For now I have left the homing code unchanged as it seems to be working fine. The only other changes were to configuration.h. Here the normal delta definitions are as follows:

// Center-to-center distance of the holes in the diagonal push rods.
#define DELTA_DIAGONAL_ROD 175.0 // mm

// Horizontal offset from middle of printer to smooth rod center. Adjust this to calibrate x-axis distance for tandem setup.
#define DELTA_SMOOTH_ROD_OFFSET 118.0 // mm

// Horizontal offset of the universal joints on the end effector.
#define DELTA_EFFECTOR_OFFSET 0.0 // mm

// Horizontal offset of the universal joints on the carriages.
#define DELTA_CARRIAGE_OFFSET 0.0 // mm

As there is no effector or carriage offset both of these values were set to zero. The DELTA_DIAGONAL_ROD distance was set to the length of the arms (175.0mm) and the DELTA_SMOOTH_ROD_OFFSET was originally set to half half the distance separating the smooth rods, just as you would in a normal delta. The result of these setting is shown below where an upscaled 40mm cube print is being dry run with a pen acting as a pen plotter.


Its clear that the cube outline is neither symmetrical or square.

In a norma Delta configuration each carriage has two parallel arms connecting it to the end effector in such a way that the resulting  parallelogram prevents the tool head from moving in any direction other than parallel to the print surface. For the TandemB setup only two arms in total are used and therefore no such parallelogram exists. It is because of this, and the fact the tool head is not directly positioned where the centre pivot is located, that we are seeing this curvature. The solution turned out to be as simple as adjusting the DELTA_SMOOTH_ROD_OFFSET from 118.0 down to 89.5 which was determined by trial and error but is something that should also be derivable mathematically. After this adjustment I can now trace cubes that are almost square.


Hopefully some fine tuning of the carriage homing positions and the tool head location is all thats now needed to make it perfect.

In the video below you can again see an upscaled ’40mm’ cube that has been stretched so as to fill the available build area and had the first layers removed so that the infill is now being drawn first.

Its far from perfect, but its a good start.

As expected, there is quite a lot of play in the z-axis (vertical) direction during rapid direction changes. This is not a problem when using a pen, as the pen acts to support itself as it pushes against the surface its drawing on. However, it will be a massive problem for a 3d printer and so needs to be addressed. I have a few ideas of how to solve this problem without reverting to the potentially much slower ‘TandemC’ design shown here and these ideas may even remove the need for any smooth rods in the whole design. More to come soon.

About Richard

I am a Materials Engineering working in the field of Magnetic Materials in Melbourne, Australia. This blog covers my personal interest in all things CNC.
This entry was posted in Tandem and tagged , , , , , , , , , . Bookmark the permalink.

4 Responses to Modifying Marlin Firmware to work with a TandemB

  1. Jacob Fisher says:

    To remove the dependence of the end effector on the pivot, couldn’t you recreate the parallelogram setup such that the the location of the end effector doesn’t matter, like this?

    • Richard says:

      Hi Jacob

      You could definitely add two extra arms like that shown in your image but this would come at the expense of build complexity and additional weight. At the moment I am trying to keep the moving mass as low as possible to allow for maximum acceleration. If I were going to add the two extra arms I would probably go with something like the ‘TandemC’ shown here as it also stabilizes the tool head in the z-axis. If I am unable to make this current setup work with only two arms due to vibration problems then this will be the next step.


  2. rel says:

    Actively build 3D printer from old inkjet printers )
    I have a Chinese Arduino Mega and RAMPS 1.4, DRV8825 drivers (purple, or notebook which), the firmware is Marlin
    In test mode all twisted (SM) works
    But the main problem is the following -> I have on the z-axis of SM c two gears of different number of steps and straps of different pitch, those physically not able to connect two of these SM’s on a single driver the z axis.
    RAMPS 1.4 Board has 3 channels for stepper motor 3 axes X, Y, Z and two extruders for E0 and E1. The extruder I have one, so it remains a free driver E2. To it just I want to connect a second stepper motor Z-axis different from the first parameters.
    But here is just Zapara – motor control of the extruder differs from the management of SM axis, and plus and myself configuring Marlin to work with 2 extruders are not simple and little information.
    The settings of the axes are stored in the header Configuration.h
    #define DEFAULT_AXIS_STEPS_PER_UNIT {80,(100*1)/(1.05*40),(100*1)/(1.25*20),(3200 * 39.0)/(11.0 * 6.75 * 3.45)}// X Y Z E0 E1 100 – the number of steps per revolution stepper motor, 1.05 – step belt, 40 is the number of gear teeth
    Each Driver receives 4 parameters: MS1, MS2 – microstepping mode(1, 1/2, 1/4,1/8,1/16,1/32); STEP, DIR as I understood – step (pulse) and direction of rotation. However, I do not understand why the code is MS1, MS2 although in the diagram to the controller is not connected
    I guess the easiest option would be to find where in the code the firmware sends commands to the outputs of Arduini -> driver-E1 and write down the values for Z, but given its parameters, naturally there will be several edits on the way from DEFAULT_AXIS_STEPS_PER_UNIT to the programming outputs of the mega.
    Here’s what I can’t find where it is set to output mega for STEP, DIR
    Well, maybe somebody is already doing? or well versed in the code?

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s