ambrop72 / aprinter

3D printer firmware written in C++
Other
144 stars 42 forks source link

aprinter

APrinter is a portable firmware system for RepRap 3D printers and other desktop CNC devices. It supports many controller boards based on different microcontroller families: AVR, AT91SAM (e.g.. Arduino Due), STM32F4 and Freescale MK20 (Teensy 3). A web-based configuration system is used to configure the high-level features for a particular machine, but also to define the low-level configuration for supporting different controller boards.

Here is a list of the boards which are supported out of the box. This means that a predefined Board configuration is provided in the configuration editor. Note that for some of the supported microcontrollers (STM32F4, Teensy 3), there is no specific board supported. It is up to you to build a board and bring the code to life :)

The following machines are supported out of the box (meaning that a functional Configuration section is provided).

Major functionality

Other features and implementation details

Coding style

APrinter Web Service

Build and run the configuration web interface locally using the following commands. A prerequisite is the Nix package manager running on Linux.

nix-build -A aprinterService -o ~/aprinter-service
mkdir ~/aprinter-service-temp
~/aprinter-service/bin/aprinter-service

The web interface will then be available at http://127.0.0.1:4000/. It provides a graphical configuration editor, and can also build the firmware with the click of a button.

Instructions for using the web service:

WARNING: When you build firmware from the web interface, it will be built using the Aprinter code as it was when the service was built (the nix-build) command above. If you make changes to the code afterward, you will need to rebuild and restart the service for the changes to be reflected.

Note that this does not apply if you use the command-line method of building firmware. However if you change the code of the configuration editor, you will still need to rebuild/restart the service.

Building manually

It is possible to build without using the web service, given a JSON configuration file. This file would usually be produced by the web GUI, but you're free to manage it manually. Again, the prerequisite for building is the Nix package manager on Linux. If you're not familiar with Nix, please use the installer, not distribution packages.

./tools/build.py -c path_to_config.json -o ~/aprinter-build

Uploading

Before you can upload, you need to install the uploading program, which depends on the type of microcontroller:

There is a Python program included in the root of the source that will do the upload using the appropriate tool. It is generally used like this:

./tools/flash.py -t <board-type> -f <file-to-flash> [-p <port>]

Below, the specific command used to flash manually are also shown.

RAMPS

avrdude -p atmega2560 -P /dev/ttyACM0 -b 115200 -c stk500v2 -D -U "flash:w:$HOME/aprinter-build/aprinter.hex:i"

Melzi

You will have to set the Debug jumper and play with the reset button to get the upload going.

avrdude -p atmega1284p -P /dev/ttyUSB0 -b 57600 -c stk500v1 -D -U "flash:w:$HOME/aprinter-build/aprinter.hex:i"

Arduino Due

Make sure you connect via the programming port while uploading (but switch to the native USB port for actual printer communication).

First you need to set baud rate to 1200 to start the bootloader:

stty -F /dev/ttyACM0 1200

Then upload the firmware using BOSSA:

bossac -p ttyACM0 -U false -i -e -w -v -b ~/aprinter-build/aprinter.bin

Some Due clones have a problem resetting. If after uploading, the firmware does not start (LED doesn't blink), press the reset button.

For communication with host software (not programming), the software supports both the programming and native USB ports at the same time. The programming port is by default configured at baud rate 115200, because 250000 does not work on all Due boards due to a design defect. It is recommented to use the native USB port due to greater speed.

Duet

Before flashing, you need to bring the chip to boot mode by pressing the erase button (near the Ethernet jack). If the board does not reset after flashing (despite us telling it to reset, go figure), you will have to power cycle.

bossac -p ttyACM0 -i -e -w -v -b ~/aprinter-build/aprinter.bin

Teensy 3

You need to press the button on the board before trying to upload, to put the board into bootloader mode.

teensy-loader-cli -mmcu=mk20dx128 ~/aprinter-build/aprinter.hex

Feature documentation

Different features of the firmware are described in the following sections.

Note that the ability to define new devices (heaters, fans, extruders...) depends on the particular board/microcontroller. In the web GUI, if there is an available "port" (stepper port, PWM output), it is just a matter of adding a device to the Configuration section. Otherwise it may or may not be possible to add a new port, based on hardware availability (timer units, PWM channels).

Runtime configuration

When runtime configuration is available, the values of many parameters can be adjusted at runtime, and the values specified in the web GUI are used as defaults only. Notable things that cannot be configured at runtime are various structural aspects such as the presence of devices/features and hardware mapping of features (including pins).

Board-specific notes:

Runtime configuration commands:

After changing any configuration (e.g. with M926, M502 or M501), the configuration needs to be applied with M930. Only then will the changes take effect. However, when the firmware starts up, the stored configuration is automatically loaded and applied, as if M501 followed by M930 was done.

The M930 command does not alter the current set of configuration values in any way. Rather, it recomputes a set of values in RAM which are derived from the configuration values. This is a one-way operation, there is no way to see what the current applied configuration is.

If configuration is stored on the SD card, the file aprinter.cfg in the root of the filesystem needs to exist. The firmware is not capable of creating the file when saving the configuration! An empty file will suffice.

Error handling

The firmware understands the concept of failed commands. The conditions for failure are command-specific.

This error information is used by a special feature which prevents executing commands after one command fails:

This feature is intended to be used when printing via serial or TCP - you can put M932 at the top of the program, and M933 at the end. Please note if you do this and an error appears, your host software probably won't be able to recover, but will just send the rest of all the gcodes which will fail. If you want to use this feature and have the option to recover, you should implement this in your host software :)

However, some host software will itself stop sending commands when an error is returned in one of the commands. This works fine when the host waits for each "ok" before sending the next command. But if you want to stream commands (presumably over TCP), the use of M932/M933 is essential for stopping at the first error.

SD card

The firmware supports reading G-code from a file in a FAT32 partition on an SD card. When the SD card is being initialized, the first primary partition with a FAT32 filesystem signature will be used.

There is partial write support; existing files can be written but new files cannot be created. Write support can be utilized for uploading G-code (M28, M29) and for storing the configuration (see the Runtime Configuration section).

WARNING: Back up any important data on the SD cards you would be using with the device. Data loss is possible, e.g. due to bugs in the SD card driver and the FAT filesystem code.

SD card support is enabled by default on the following boards: Duet, RADDS, RAMPS-FD, 4pi, RAMPS 1.3, Melzi. Write support is enabled on Duet, RADDS, RAMPS-FD, 4pi, Melzi. Note, the ATMEGA2560 has too little RAM for enabling write support.

The following SD-card related commands are implemented:

Directory and file paths may be absolute (starting with /), otherwise they are treated as relative to the current directory.

When passing a file or directory name to a command, any spaces in the name have to be replaced with the escape sequence \20, because a space would be parsed as a delimiter between command parameters.

Note, currently Pronterface's SD button does not work with Aprinter, you need to use the SD commands directly.

When printing from SD card, the firmware will stop on the first failed command (the error will be printed to the console). You can resume at the next command using M24.

Example: start printing from the file gcodes/test.gcode.

M21
M23 Dgcodes
M32 Ftest.gcode

Example: interrupt and restart the same print.

M25
M26
M24

Example: repeat a successful print.

M26
M24

G-code can be uploaded using the commands M28 and M29. You should send M28, then send all the gcode to be written to the file (you can just tell Pronterface to "print"), then send M29. Alternatively, you can put M28/M29 into the start/end gcode in your slicer's settings. Please make sure that the file exists, the firmware currently cannot create new files, only overwrite existing ones.

Futher, to avoid accidentally executing the commands in case opening the file fails, you should wrap the whole thing in M932/M933.

M932
M28 Fprint.gcode
// Send your gcode.
M29
M933

Networking

On the Duet board, Ethernet networking is supported. Currently, the only network service provided is a Gcode console over a TCP connection.

By default, DHCP will be used to acquire an IP address automatically. You can run the command M940 to see the assigned IP address and other network status information. Alternatively, you can configure a static IP, as shown. Don't forget to run M930 and/or M500 to apply/save this configuration (see the Runtime Configuration section).

M926 INetworkDhcpEnabled V0
M926 INetworkIpAddress V192.168.1.234
M926 INetworkIpNetmask V255.255.255.0
M926 INetworkIpGateway V192.168.1.1
M930

The TCP console will be available on port 23. You tell Pronterface to connect to this TCP interface by entering <ip_address>:23 into the Port box. By default, two concurrent connections are permitted.

Axes

The standard gcodes for axis motion are implemented:

Heaters

The firmware supports three types of heaters: extruder heaters, bed heaters and chamber heaters. These types are functionally equivalent, the only difference is that different M-commands are used. There may be any number of heaters of each type, and each heater has a number (>= 0) which identifies it within its type. The heater types and numbers are specified in the configuration editor. Note that heater numbers do not need to be assigned sequentially. Heaters are typically identified as T<num> (extruder heaters), B<num> (bed heaters) and C<num> (chamber heaters), e.g. in the output of M105 (which prints the heater states) and in the parameters of M116 (which waits for temperatures to be reached).

To configure the setpoint for a heater and enable it, use M104/M140/M141 P<heater_number> S<temperature_degC>. The M-command depends on the heater type (extruder, bed and chamber respectively). For extuder heaters, T<heater_number> is also accepted as an alternative to P, for compatibility. If the heater number is not specified, 0 is assumed as the default. The heater will be turned off if the S parameter is missing, has the value nan, or the value is outside of the heater's safe range.

For example:

The above commands also support the parameter F, which causes the setpoint to be changed immediately instead of going through motion planning. This is useful when changing the temperature during a print running from SD card.

The command M116 can be used to wait for the set temperatures of heaters to be reached: M116 <heater_type><heater_number> .... For example, M116 T0 T1 B C waits for extruder heaters #0 and #1, bed heater #0 and chamber heater #0. Note that with only the heater type and no number (e.g. B), heater #0 is assumed. Without any (configured) heaters specified, the effect is as if all heaters with configured setpoints were specified. The command will fail immediately if a heater which is explicitcly specified does not have a setpoint configured.

The firmware detects thermal runaways, when the temperature falls outside the defined safe range. Upon runaway, the specific heater is automatically disabled, and an error message is generated. The command M922 can be used to re-enable (all) heaters which had experienced a thermal runaway. Note, a heater being disabled due to a thermal runaway does not change its setpoint - this is implemented such to provide predictable semantics of M116.

Fans

Fans are configured with a fan number (>=0). This is similar as for heaters (but there are no fan types).

A fan is controlled using M106 P<fan_number> S<speed_0_to_255>. If P is not specified, fan #0 is assumed. If S is not specified, full speed is assumed (S255). Setting the speed to zero (S0) turns off the fan. A fan can also be turned off using M107 P<fan_number> (the same applies about missing P).

Like for heaters, the F parameter can be used to perform the change immediately instead of going through motion planning.

Extruders

The firmware allows any number of axes (given sufficient hardware resources), but it does not, by design, implement tool change commands. Extruder axes need to be configured with the "is cartesian" option set to false. This distinction is required for the implementation of the feedrate parameter (G1 Fxxx ...).

The recommented naming for extruder axes is E, U, V in order.

The included tools/DeTool.py script can be used to convert tool-using g-code to a format which the firmware understands, but more about that will be explained later.

Multiple steppers per axis

More than one stepper can be assigned to an axis, and they will be driven synchronously.

In the web GUI, additional steppers can be added in the Stepper section for a particular stepper. If there is no existing suitable stepper port definition, you will need to add one in the Board configuration. When doing this, note that for the additional (non-first) steppers in an axis, the "Stepper timer" does not need to be defined (you may set it to "Not defined").

Coordinate transformation

APrinter has generic support for non-cartesian coordinate systems. Currently, linear delta, rotationa delta, SCARA and CoreXY/H-bot are supported out of the box.

NOTE: The nonlinear transforms will not work well on AVR based platforms due to lack of CPU speed and RAM. CoreXY may or may not, depending on how many axes (esp. extruders) you configure.

Generally, the transformation is configured as follows in the web GUI:

Stepper configuration

Steppers involved in the coordinate transform (typically A, B and maybe C) have their own configuration which is generally the same as for other steppers. In particular they need to be defined the position limits.

For CoreXY, the position limits should not constrain motion as defined by the limits of the cartesian axes. The following formulas give the minimum required stepper limits to support the cartesian limits (assuming no reordering of steppers): Arange = [Xmin + Ymin, Xmax + Ymax], Brange = [Xmin - Ymax, Xmax - Ymin].

For delta, the stepper limits should be configured appropriately for the machine. It helps to know that the A/B/C stepper positions are actually cartesian Z coordinates of assemblies on the towers, and that the maximum position limit corresponds to meeting the endstop at the top.

For rotational delta and SCARA, these steppers use units of degrees, not millimeters. You should not get confused when the configuration editor mentions millimeters. For example, the position limits are degrees, speeds (accelerations) are degrees per second (squared), and steps-per-unit are steps per degree.

Bed probing and correction

The firmware supports bed probing based on a digital input (e.g. some sort of switch). Bed probing is configured in the configuration editor in the section Configuration -> Bed probing configuration. Note that the values of many parameters can be changed at runtime (see the Runtime Configuration section), in such cases the parameter name is indicated in this description.

The following general settings must be defined:

The firmware is able to apply correction based on the results of probing using the least-squares method, based either on a linear or quadratic correction model. If you want correction (you probably do) then enable it in the configuration editor. Unless you are constrained by RAM or flash, also enable support for quadratic correction, as you can enable/disable its use at runtime (ProbeQuadrCorrEnabled). Note that due to the firmware design, enabling correction requires that the X, Y and Z axes are involved in the coordinate transformation. On delta machines, this is obviously satisfied, however, on Cartesian or CoreXY machines, special configuration is needed (see the sub-section below).

The list of probe points is also configured in the configuration editor. You can add, remove and reorder points. The parameters of each probe point are:

It is not possible to add additional probe points at runtime beyond the number defined in the configuration editor. If you think you might need more then you can define more but leave them disabled.

With linear correction, you need at least 3 probe points; with quadratic at least 6. However, be careful with the positioning of the points to avoid over-fitting the correction to these points while introducing errors at that are not close to any probe point. For example, if you have a number of points very close together, you should treat that as a single point for the purpose of the above requirement.

Probing is initiated with the command G32. The machine will probe the enabled probe points in the order defined in the configuration. For each point, the machine will:

For each point, the measured bed height is printed (e.g. //ProbeHeight@P6 -4.31501). The measured bed height is the Z position when the sensor was triggered plus the general Z offset plus the point-specific Z offset. Therefore, it directly represents the assumed height error (positive -> the bed and the nozle are too close, negative -> they are too far apart). If you perform probing again after having applied corrections (see below), these numbers should be close to zero.

If correction is enabled in the configuration editor, then G32 will calculate, print and apply the correction after probing all enabled points. You can use the D parameter to only calculate and print but not apply the correction (G32 D).

In any case, the computation of corrections is done using the linear least-squares method, via QR decomposition by Householder reflections. Even though this code was highly optimized for memory use, it still uses a substantial chunk of RAM on the stack, so you should watch out for RAM usage. The RAM needs are proportional to the number of points.

The purpose of Z offsets (ProbeGeneralZOffset and ProbeP<N>ZOffset) is to allow calibrating the offset between the point when the sensor triggers and the point where the nozzle touches the bed:

The firmware provides the capability to probe a specific point and retract for a specific distance, which can be used to manually calibrate the Z offsets. This is invoked using G32 P<point_number> R<retraction>. The machine will probe the specified point (which does not need to be enabled) as described above, with the exception that after probing slowly it will retract exactly for <retraction>. This allows you to try to insert an object of a uniform height (possibly a sheet of paper) between the bed and the nozzle; if you cannot insert it then retry with a greater retraction, and if you can insert it without any contact then retry with a smaller retraction (after removing the object). After a few iterations you should obtain a retraction value where you can insert the object such that it gently scratches the nozzle. Once this is performed for all probe points, set the point-specific Z offsets to the determined retraction distances, and set the general Z offset to minus the height of the object.

Configuring probing for Cartesian machines

First do the basic configuration of axes:

You also must make sure the limits and homing for the C (Z) stepper are set correctly. Generally, the C/Z position should be sufficiently precise after homing so that probing works correctly. If you have a min-endstop on Z, it is advised to configure as follows:

Maximum speed

There are different ways to specify the maximum speed of a move. The following rules apply, in order. Please note that the notion of some axis appearing in a move command is literal regardless of the amount of motion, e.g. E does appear in "G1 E0" while X does not.

Note that the desired speed as described above is only understood as a nominal value. The machine will not move faster than that, but it may move slower. For example the software will still respect the configured maximum accelerations and maximum speeds of the actuator axes.

The speed is also bound to not exceed the capability of the processor. The configuration parameter MaxStepsPerCycle controls this limit; it is available in the Board configuration section under Performance parameters, and also as a runtime setting. The firmware will ensure that the cumulative step frequency (across all actuator axes) does not exceed the frequency of the processor multiplied by MaxStepsPerCycle.

If you are aiming for high step rates , check that the firmware is being compiled without size optimization (under Board, Performance parameters) and with assertions disabled (under Board, Development features).

Lasers

There is currently experimental support for lasers, more precisely, for a PWM output whose duty cycle is proportional to the current speed. The laser configuration parameters are:

A prerequisite for configuring a laser is the availability of hardware PWM and its support by the firmware. See the section "Hardware PWM configuration" for more details.

A laser is configured in the web GUI as follows:

In the g-code interface, either of the following parameters can be used in a G0/G1 command to control the laser:

The energy density M is cached, so you can specify it in one command, and leave it out for any further commands where you want the same energy density. If L is specified, it takes precedence over M or its cached value, but it does not alter the M cached value.

Here's an example of how this works. Suppose that LaserPower=MaxPower=100. Consider a move "G1 X10 L200", to move X 10mm while emitting 200 units of laser energy. See that this move will require the laser to be on (summing all the individual PWM on-times) for L / MaxPower = 200 / 100 = 2 seconds. This is true regardless of accelerations and possible speed limits of motion axes.

Now let's also assume that the time for the machine to accelerate and decelerate is negligible and the speed of X motion is not limited here. In that imaginary case the move would take 2 seconds with the laser running at full power. Note that while generally imaginary this simple case does exist when there is no motion at all.

If we introduce and increase a speed limit for the X axis, then at some point the move may have to take longer than 2 seconds because the axis cannot move as fast as we can emit the specified energy over the segment. If that happens the power of the laser will be reduced so that while the move will take longer than 2 seconds, the total laser on-time will still be 2 seconds.

When the machine is accelerating and decelerating at the corners of segments, the laser power is controlled in real-time so that the distribution of laser energy over the segment is constant. For example this does mean that when the machine just starts from zero speed speed or stops at zero speed, at that very moment the laser power will also be zero.

Be aware that the precision of the laser control generally is not exact. You should simply consider that the laser is controller using hardware PWM, and that the duty cycle of the PWM is adjusted by software about every DutyAdjustmentInterval seconds. The lower DutyAdjustmentInterval you use, the better the precision will be. Note that the adjustment points are synchronized with the start and end of segments.

The DeTool g-code postprocessor

The tools/DeTool.py script can either be called from command line, or used as a plugin from Cura. In the latter case, you can install it by copying (or linking) it into Cura/plugins in the Cura installation folder.

To run the script, you will need to provide it with a list of physical extruders, which includes the names of their axes, as understood by your firmware, as well as the offset added to the coordinates. Futher, you will need to define a mapping from tool indices to physical extruders. The command line syntax of the script is as follows.

./tools/DeTool.py [-h] --input InputFile --output OutputFile
                 --tool-travel-speed Speedmm/s --physical AxisName OffsetX
                 OffsetY OffsetZ --tool ToolIndex PhysicalIndexFrom0
                 [--fan FanSpeedCmd PhysicalIndexFrom0 SpeedMultiplier]
                 [--sdcard]

For example, if you have two extruder axes, E and U, the U nozzle being offset 10mm to the right, and you want to map the T0 tool to U, and T1 to E, you can pass this to the script:

--physical E 0.0 0.0 0.0 --physical U -10.0 0.0 0.0 --tool 0 1 --tool 1 0

The argument --tool 0 1 means to associate T0 to the second physical extruder (with the letter E, in this case).\ Note that the offset in --physical is what the script adds to the position in the source file, that is, the negative of the nozzle offset.

The script also processes fan control commands (M106 and M107) to appropriate fans based on the selected tool. For example, if your first fan (M106) blows under the first extruder, and the second fan (M406) under the second extruder, you will want to pass:

--fan M106 0 1.0 --fan M406 1 1.0

If the fans are not equally powerful, you can adjust the SpeedMultiplier to scale the speed of specific fans.

Support

If you need help or want to ask me a question, you can find me on Freenode IRC in #reprap (nick ambro718), or you can email me to ambrop7 at gmail dot com. If you have found a bug or have a feature request, you can use the issue tracker.