RepRap 3D Printer - Prusa i3 - Build Log: Part 5

Software and Firmware

In order to control the 3D printer there are pieces of software that are required - Firmware, which runs on the Arduino to control the printer movements; a Printer control, which provides an interface from a PC; and slicing software, which takes a 3D files and determines the tool paths.
Pronterface software for controlling the printer.


The firmware runs on the Arduino Mega on the actual printer, and is basically the printers operating system. There are several different options for Firmware including Marlin, Sprinter, Teacup, and many more. I am using Marlin as it is very popular and feature rich.

Download the latest version of Marlin here. 

To get the firmware onto the Arduino you need the Arduino IDE, which can be downloaded here.

Marlin requires quite a bit of customisation before it will work and I will go through the changes I made for my i3 below. There are a couple of good resources for setting up the firmware such as these ones-

When you open the marlin.ino file in the Arduino IDE, you can see the related files as tabs. The changes that need to be made are all in the Configuration.h tab.

Change the date, time and author of the firmware to keep track of when changes were last made, and who made them.

#define STRING_VERSION_CONFIG_H __DATE__ "18/06/2014 1830h" __TIME__ // build date and time
#define STRING_CONFIG_H_AUTHOR "D Hansen" // Who made the changes.

The next two options, #define SERIAL_PORT, and #define BAUDRATE do not require changes. #define SERIAL_PORT is used when you have another serial port on your arduino other than USB (such as bluetooth or wireless). #define BAUDRATE can be left as default, but keep note of the baud rate, you will need to use this in Pronterface.

Next you must define your motherboard from the list of available boards in the firmware. Since I am running RAMPS 1.4 with one extruder and a heated bed, my motherboard is number 33.

#define MOTHERBOARD 33

If you wish you can name your printer, and the name will appear on the LCD screen. Don't make the name too long, or it won't fit on the screen, and likely crash the firmware.

// Define this to set a custom name for your generic Mendel,
#define CUSTOM_MENDEL_NAME "DH 3D Printer"

Next define the number of extruders on the printer. For my printer this is one.

// This defines the number of extruders
#define EXTRUDERS 1

The #define POWER_SUPPLY 1 option can be left as default, as well as // #define PS_DEFAULT_OFF.

Next are the thermal settings. It is important to know what kind of thermocouple you have - On my Hot End I have a 100k ATC Semitec 104GT-2 (which is standard for J-Head hot ends) and my Heated Bed uses a generic EPCOS 100k thermistor. Define which thermistor is in use for each sensor. On RAMPS 1.4 TEMP_SENSOR_0 corresponds to the T0 thermistor (the Hot End thermistor), TEMP_SENSOR_BED corresponds to the T1 thermistor (the Heated Bed thermistor), and TEMP_SENSOR_1 corresponds to the T2 pin. TEMP_SENSOR_2 is not used if you have RAMPS and should be left as 0. My final code is -

#define TEMP_SENSOR_0 5
#define TEMP_SENSOR_1 0
#define TEMP_SENSOR_2 0

The next six options can be left as default. Their effects are pretty well documented in the Marlin code. My firmware looks like this -

// This makes temp sensor 1 a redundant sensor for sensor 0. If the temperatures difference between these sensors is to high the print will be aborted.

// Actual temperature must be close to target for this long before M109 returns success
#define TEMP_RESIDENCY_TIME 10  // (seconds)
#define TEMP_HYSTERESIS 3       // (degC) range of +/- temperatures considered "close" to the target one
#define TEMP_WINDOW     1       // (degC) Window around target to start the residency timer x degC early.

// The minimal temperature defines the temperature below which the heater will not be enabled It is used
// to check that the wiring to the thermistor is not broken.
// Otherwise this would lead to the heater being powered on all the time.
#define HEATER_0_MINTEMP 5
#define HEATER_1_MINTEMP 5
#define HEATER_2_MINTEMP 5
#define BED_MINTEMP 5

// When temperature exceeds max temp, your heater will be switched off.
// This feature exists to protect your hotend from overheating accidentally, but *NOT* from thermistor short/failure!
// You should use MINTEMP for thermistor short/failure protection.
#define HEATER_0_MAXTEMP 275
#define HEATER_1_MAXTEMP 275
#define HEATER_2_MAXTEMP 275
#define BED_MAXTEMP 150

// If your bed has low resistance e.g. .6 ohm and throws the fuse you can duty cycle it to reduce the
// average current. The value should be an integer and the heat bed will be turned on for 1 interval of

// If you want the M105 heater power reported in watts, define the BED_WATTS, and (shared for all extruders) EXTRUDER_WATTS
//#define EXTRUDER_WATTS (12.0*12.0/6.7) //  P=I^2/R
//#define BED_WATTS (12.0*12.0/1.1)      // P=I^2/R

Next are the PID settings. These control how the Hot Ends are heated without under-heating or over-heating. The PID constants can be determined automatically by the firmware at the calibration stage. For the moment leave the Hot End PID setting as is.

The PID settings for the Heated Bed follow. PID control for the Heated Bed will allow a smoother temperature profile, but will cycle the current on and off quite quickly (7.689 Hz). For a 200mm square Heated Bed and RAMPS, this is not a problem. But if you're using a large heat bed and an external relay, the fast switching will break things. To turn on the Bed PID, uncomment the #define PIDTEMPBED. The PID constants will again be determined by the firmware at the calibration stage.


// This sets the max power delivered to the bed, and replaces the HEATER_BED_DUTY_CYCLE_DIVIDER option.
// all forms of bed control obey this (PID, bang-bang, bang-bang with hysteresis)
// setting this to anything other than 255 enables a form of PWM to the bed just like HEATER_BED_DUTY_CYCLE_DIVIDER did,
// so you shouldn't use it unless you are OK with PWM on your bed.  (see the comment on enabling PIDTEMPBED)
#define MAX_BED_POWER 255 // limits duty cycle to bed; 255=full current

//120v 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
//from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10)
    #define  DEFAULT_bedKp 10.00
    #define  DEFAULT_bedKi .023
    #define  DEFAULT_bedKd 305.4

//120v 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
//from pidautotune
//    #define  DEFAULT_bedKp 97.1
//    #define  DEFAULT_bedKi 1.41
//    #define  DEFAULT_bedKd 1675.16

// FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
#endif // PIDTEMPBED

The following section deals with the Mechanical setting. The first setting deals with the use of CoreXY kinematics, which I am not using. Hence it can be left as is (i.e. commented out).

Next are the setting for the endstops. Because I am using Mechanical Endstop v1.2, my endstops have internal pull-up resistors. This means I do not require the Arduino Mega's internal pull-up resistors and I can disable them by commenting out #define ENDSTOPPULLUPS. If your endstops consist of only a mechanical switch, you will require the Arduino pull ups. I also do not have maximum endstops, only minimum endstops. So I can use #define DISABLE_MAX_ENDSTOPS.

// coarse Endstop Settings
//#define ENDSTOPPULLUPS // Comment this out (using // at the start of the line) to disable the endstop pullup resistors

  // fine endstop settings: Individual pullups. will be ignored if ENDSTOPPULLUPS is defined


// The pullups are needed if you directly connect a mechanical endswitch between the signal and ground pins.
const bool X_MIN_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.
const bool Y_MIN_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.
const bool Z_MIN_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.
const bool X_MAX_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.
const bool Y_MAX_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.
const bool Z_MAX_ENDSTOP_INVERTING = true; // set to true to invert the logic of the endstop.

The Pololu A4988 drivers which I am using, use an active low state on the Enable pin, i.e. to enable the driver the Enable pin must be set to a logic state low. This means I don't need to change any of the #define X_ENABLE settings. If you are using a different stepper driver, you may need to set the logic high. My resulting code is -

// For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1
#define X_ENABLE_ON 0
#define Y_ENABLE_ON 0
#define Z_ENABLE_ON 0
#define E_ENABLE_ON 0 // For all extruders

Next is the setting to disable any of the axes, if they aren't being used while printing. Enabling this option will prevent the stepper motors from holding position while they're not being used. This option is usually disabled, but some people choose set #define DISABLE_Z true so they can make manual Z-axis height adjustments during a print, if say the Hot End was too close to the print bed.

The stepper motors can be driven in either direction, and the direction of drive can be set by either the firmware, or the hardware (by simply unplugging the stepper motor from the RAMPS board and plugging it in the other way). The Prusa i3 is a mendel-like printer and the motor directions can be set with the mendel options. The Greg's Wade extruder requires the motor direction to be inverted. All of these options can be checked and reset at the calibration stage.

#define INVERT_X_DIR false    // for Mendel set to false, for Orca set to true
#define INVERT_Y_DIR true    // for Mendel set to true, for Orca set to false
#define INVERT_Z_DIR false     // for Mendel set to false, for Orca set to true
#define INVERT_E0_DIR true   // for direct drive extruder v9 set to true, for geared extruder set to false
#define INVERT_E1_DIR false    // for direct drive extruder v9 set to true, for geared extruder set to false
#define INVERT_E2_DIR false   // for direct drive extruder v9 set to true, for geared extruder set to false

When the printer homes, each axis travels until it hits an endstop. It can travel to the minimum endstops or to the maximum endstops. I wanted my printer to home to position 0,0,0 which corresponds to X, Y, and Z minimums (also I don't have maximum endstops installed). Some people choose to have a Z-axis home to it's maximum. For my setup no firmware changes are required -

// Sets direction of endstops when homing; 1=MAX, -1=MIN
#define X_HOME_DIR -1
#define Y_HOME_DIR -1
#define Z_HOME_DIR -1

Once the printer has homed, the firmware can prevent the printer from trying to move outside of its physical limits. To do this, we need to set the physical dimensions of the printer in the firmware. The easiest way to do this is turn the printer off, physically move each axis to its home position, then use a ruler to measure the distance each axis can move before it hits something. The axis distance is set using the #define X_MAX_POS setting. The #define X_Min_POS setting can be used if your printer doesn't home to position 0,0,0 (i.e. it homes to a position where the print head is outside the actual print bed) but for most cases it will be left as 0. My code is -

// Travel limits after homing
#define X_MAX_POS 180
#define X_MIN_POS 0
#define Y_MAX_POS 180
#define Y_MIN_POS 0
#define Z_MAX_POS 195
#define Z_MIN_POS 0

The next big section of code deals with the auto-bed leveling function. As I don't (yet?) have auto-bed leveling implemented I didn't change any of this code.

Movement setting deal with how far each axis moves for a given stepper motor rotation. These setting are mostly calculated theoretically, then at the calibration stage minor adjustments can be made. Most settings can be calculated using Prusa's RepRap Calculator.

#define NUM_AXIS 4 is used to set the number of axis (and also the number of variables in the axis related arrays). Since I have only four axis (X, Y, Z, and E) I can leave this as default. If say you had another extruder, and you wanted this extruder to have different properties to your first extruder, you could set NUM_AXIS 5.

#define HOMING_FEEDRATE sets the default speed at which the axis will move when it perform an axis homing. The unit is mm/min which is very confusing as all the other settings are in mm/sec - so it is common to set the homing rate as (something*60) where something is in mm/sec. The homing feedrate should be lower than your max feedrate (which will be covered next) as you want it to accurately find the endstops and not overshoot. As a starting point I set mine to (50*60, 50*60, 1*60, 0).

#define DEFAULT_AXIS_STEPS_PER_UNIT sets the number of stepper motor steps required to move the axis. This is where Prusa's RepRap Calculator comes in. For the X and Y axis, simply input your configuration, and the output will be the correct steps per unit. Mine was 80. Similarly for the Z-axis, mine was 4000. The extruder will require calibration, this will be handled at the calibration stage. The extruder can be left as default for now.

#define DEFAULT_MAX_FEEDRATE sets the maximum movement speed for each axis in mm/sec. These values are rarely used in practice, as the print speed is usually set by the slicing software, not the firmware. They can be left as default for now. They will be updated in the calibration stage.

#define DEFAULT_MAX_ACCELERATION controls the maximum acceleration of each axis. The Prusa's RepRap Calculator can be used again here. The aim is to set the acceleration so that the axis moves in a smooth manner, and also so that the axis actually hits it's desired speed.
Good acceleration profile - gradual beginning and end, max speed hit.
The acceleration depends on the axis speed - the faster the speed, the higher the acceleration can be. For now the acceleration can be left as default, but will be tweaked at the calibration stage.

#define DEFAULT_ACCELERATION sets the acceleration for normal axis moves during a print. This can be left as default for now. The DEFAULT_RETRACT_ACCELERATION set the acceleration for extruder retracts, and is usually set to the same as DEFAULT_ACCELERATION.

The next setting is commented out by default, and sets the distance between hot ends if you have more than one. As I only have one hot end, I left this as is.

#define DEFAULT_XYJERK sets the minimum speed change that doesn't require acceleration. If this value is set too high, the printer will try to instantaneously change the axis speed, and might wobble. I left this as default.

The final movement settings code looks like this -

#define NUM_AXIS 4 // The axis order in all axis related arrays is X, Y, Z, E
#define HOMING_FEEDRATE {50*60, 50*60, 1*60, 0}  // set the homing speeds (mm/min)

// default settings

#define DEFAULT_AXIS_STEPS_PER_UNIT   {80,80,4000,760*1.1}  // default steps per unit for Ultimaker
#define DEFAULT_MAX_FEEDRATE          {500, 500, 5, 25}    // (mm/sec)
#define DEFAULT_MAX_ACCELERATION      {9000,9000,100,10000}    // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for Skeinforge 40+, for older versions raise them a lot.

#define DEFAULT_ACCELERATION          3000    // X, Y, Z and E max acceleration in mm/s^2 for printing moves
#define DEFAULT_RETRACT_ACCELERATION  3000   // X, Y, Z and E max acceleration in mm/s^2 for retracts

// Offset of the extruders (uncomment if using more than one and relying on firmware to position when changing).
// The offset has to be X=0, Y=0 for the extruder 0 hotend (default extruder).
// For the other hotends it is their distance from the extruder 0 hotend.
// #define EXTRUDER_OFFSET_X {0.0, 20.00} // (in mm) for each extruder, offset of the hotend on the X axis
// #define EXTRUDER_OFFSET_Y {0.0, 5.00}  // (in mm) for each extruder, offset of the hotend on the Y axis

// The speed change that does not require acceleration (i.e. the software might assume it can be done instantaneously)
#define DEFAULT_XYJERK                20.0    // (mm/sec)
#define DEFAULT_ZJERK                 0.4     // (mm/sec)
#define DEFAULT_EJERK                 5.0    // (mm/sec)

The final setting that needs attention is the setting to activate the LCD screen. My LCD screen is a RepRapDiscount Smart Controller, and to activate it, simply remove the comments from the line #define REPRAP_DISCOUNT_SMART_CONTROLLER.

// The RepRapDiscount Smart Controller (white PCB)

Now the firmware can be compiled and uploaded to the Arduino on the printer. To do this, plug the printer into your PC via a USB cable. The board should automatically install drivers and appear in device manager as a COM Port. Now in the Arduino IDE go to Tools>Board and set to Arduino Mega. Also go to Tools>Port and set to the port to the same one as in the Device Manager. Then Upload.

Printer Control and Slicing software

The Printer Control software acts as the interface to the printer on your PC. Although it might be possible to not use printer control software, if you have a SD card reader on your printer (as I do), it is still very handy to be able to control the printer from a PC. There are several options for Printer Control software including Pronterface, Repetier-Host, and Cura. I chose to use Pronterface as it is open source, and widely used. Repetier-Host recently became closed source. Cura is written for Ultimaker printers, but can be used for RepRaps too.

There is only one real option for slicing - Slic3r. It comes bundled with Pronterface, is open source and widely used. Cura, uses its own slicing engine, and therefore doesn't need separate slicing software. It this respect Cura has an advantage.

Download the latest Pronterface with Slic3r here.  You can also compile it from source code, if you're that way inclined.

Now we can run Pronterface. First set the Port to the same Port as the Arduino appears as in your Device Manager. Set the BaudRate to the same as you set it in the Firmware (usually 250000, which is the default). Then hit Connect.

You should now be able to move the axis using the control pad inside Pronterface, but be careful not to accidentally drive an axis into the ends, or you may break something.

Next Calibration.