Teacup Firmware

From RepRapWiki
Jump to: navigation, search
Firmware Firmware development

Teacup Firmware is a RepRap firmware with emphasis on performance, stability, code cleanliness and simple usage. It was RepRap's first firmware to feature acceleration, the first being ported to an ARM-based controller and continues to be excellent at what it does. Teacup is known to run on RAMPS, Generation 7 Electronics, the Teensy2, Teensy3, Generation 6 Electronics, Sanguinololu, Generation 3 Electronics, NanoHeart and for all other RepRap controllers it should be a matter of adjusting the pin mapping to get it running.

Perfection (in design) is achieved not when there is nothing more to add, but rather when there is nothing more to take away.
Antoine de Saint-Exupéry

Teacup is fast and runs steppers smoothly. It avoids C++ in favour of plain C, uses 100% integer math and works very hard to minimize/eliminate long math operations in interrupt context. As a result, it offers optimum precision over a wider range (integer = 31 bit, float = 23 bit), it's only half the binary size of similarly featured firmwares, runs on about any ATmega and several ARM microcontrollers, and can still be tailored to run on controllers as small as the ATmega168.

Recent Discussion



  • All the usual stuff, like 4 axes, extruder, jerk-based look-ahead, thermistors.
  • It's said it runs stepper smoother than average firmwares. It's sure Teacup developers take great care of code quality.
  • Decent performance: can run up to 48'000 evenly spaced steps/second on 20 MHz ATmegas, much more on ARMs.
  • Arbitrary number of heaters.
  • Arbitrary number of temperature sensors.
  • Support for the MAX6675 temperature sensor.
  • Teacup Configtool for GUI based calibration and uploading.
  • Tool to calibrate thermistors for accurate readings.
  • Independent from Arduino IDE, runs on AVR ATmega as well as ARMs and even a normal PC.
  • Many topic branches with developments like support for wire cutters, support for 9 axes, support for even smoother stepping, etc, etc..


Teacup Firmware comes with Teacup Configtool, a nice GUI which makes configuration, calibration, building and uploading very easy. It also compiles with Arduino IDE and/or using a Makefile.


You need the same as when running Pronterface:

  • Python 2.7.x.
  • Matching wxPython.
  • avr-gcc and avrdude OR Arduino IDE (installed or just unpacked).

That's it, chances are good you have this installed already. How to best get these packages largely depends on your PC operating system. Linux typically has all these in it's package manager. Recent versions of Mac OS X have a sufficiently recent version of Python as part of the standard distribution. For others, consult the download pages of Python, wxPython and Arduino.

Simple Installation

  • Get the Teacup Firmware master branch package and unpack it. If you know what "Git" means, prefer that, see #Developer Installation below.
  • Open the just unpackaged directory and double-click configtool.py to start it.
  • Choose Menu -> File -> Load printer and select the one closest to the printer you want to operate.
  • Choose Menu -> File -> Load board and again select the one closest to (or matching) your actual hardware.
  • Choose Menu -> File -> Save config.h.

Now Teacups basic setup is done already, you should see something like this:

Teacup Configtool.png

At this point you have to help Configtool to find the compiler and the USB/serial port your controller is connected to. This has to be done only once.

  • Choose Menu -> Edit -> Settings. A dialog like this should come up:
Teacup Configtool Settings.png

Here you likely have to adjust a few things. In case you're unsure, simply try.

Arduino Directory
Enter here the full path to where you unpackaged Arduino IDE. Teacup Configtool will figure the details on where to find the neccessary tools (avr-gcc, avrdude) inside this package.
If you don't want to use Arduino's tools, you can leave this field empty. In this case it's up to the operating system to find the tools, which will work fine if you installed them with the package mananger or made them available in your $PATH environment variable.
This is the full path of the serial port your controller is connected to.

These checked, hit Save in the settings dialog (or Exit, if there was no change). You're now ready to build and upload:

  • For building, choose Menu -> Build -> Build.
  • For uploading, choose Menu -> Build -> Upload.

After each change to the firmware configuration you have to repeat just these last two steps.

Configuration and calibration in general

With the first upload done, you can start testing and using Configtool for adjusting and calibrating the printer. Just change the values, build, upload. Much easier than wrestling with obscure M-codes and also makes sure your controller is always working at maximum performance.

  • About all the fields have large and explaining tooltips. Simply hover with the mouse over the fields to get hints.
  • Teacup developers also took great care of giving meaningful error messages. Read them, please :-)
  • Teacups default configuration is very conservative, to avoid damaging your printer on your first steps. For example, it's very slow ... until you raise Maximum Feedrate.
  • You might have noticed that Teacup Configtool won't allow you to overwrite configuration files coming with the distribution. Uhm ... experience ... Use another name.
  • When seeing unexpected trouble, connect with a serial terminal to the controller or use Pronterface with Menu -> Settings -> Debug G-code turned on. Especially more complex hosts do not always send what you think they send.
  • If you still have trouble, choose Menu -> Help -> Report Problem... This will open your emailer with a few simple questions, ready to be sent to the Github issue tracker.

Configtool troubleshooting

When trying to upload, it just says "timed out"
Timeout means, there is no communications happening at all. Possible causes:
  • Wrong baud rate for the bootloader. Bootloader and firmware its self can have different baur rates. While 115200 baud is the most commonly used speed, you can simply try all of them: 19200, 38400, 57600, 115200. It's set in Menu -> Edit -> Settings -> Upload Speed.
  • Different type of bootloader. Depends entirely on who made the board. Known values are stk500v2 and arduino. It's set in Menu -> Edit -> Settings -> AVR Programmer.
  • Auto-Reset not working. Some boards simply don't feature it. Manual reset works always: Press and release the Reset button a second before or after starting the upload. It's a two-hand operation, one finger on the Reset button, the other hand at the computer mouse.

Installation using Arduino IDE

While Teacup Configtool should do all you need, there might be some situations where you want to give Arduino IDE a try. Here's how to do this:

  1. Especially on Windows, make sure you can see file extensions. Vista is known to do funny things when they're hidden.
  2. In case you have an ATmega644, -644P or -1284P based electronics, also install Gen7 Arduino IDE Support.
  3. Before opening it with the Arduino IDE, rename the unpacked folder to Teacup_Firmware. E.g. from Traumflug-Teacup_Firmware-448df5d to Teacup_Firmware.
  4. Do basic setup like above.
  5. Menu -> File -> Save board as..., then save not, as offered by default, into config/, but directly into the main Teacup folder.[1]
  6. Same for the printer configuration.
  7. Menu -> File -> Save config.h.

Then you can open Teacup_Firmware.pde directly in Arduino IDE. If it asks to rename this file to *.ino, accept this. It should build and upload.

After each configuration change, do a Menu -> File -> Save config.h in Configtool before uploading with Arduino IDE again.

  1. The reason for this is, Arduino IDE copies all files into another directory and builds there. Unfortunately it copies only files directly in the main folder, not those in subfolders, so board and printer configuration have to be in the main folder, too.

Developer Installation

If you intend to change more than just the configuration, there are a few things you should consider. Especially the use of Git, which makes it very easy to jump between branches and also to fetch changes provided by others.

Before you report a bug, please try the experimental branch first. This is where new stuff goes. Development happens in topic branches, so if your bug got fixed, it may well happen you're asked to checkout such a topic branch for testing. Once such a topic reaches satisfactory results, these branches are cherry-picked to experimental. Unless regressions appear, they're further forwared to master after a month or two. master is the stable branch.

The concept of topic branches means, we do not merge. Merging is error-prone. Instead we follow more recent development by rebasing these topic branches to experimental. Then we cherry-pick good commits from the topic branch to experimental and refine the topic branch further. This goes on until the topic branch becomes empty and gets deleted. For more details on best practices to do so, see Github issue #81.

Getting a copy of the Repository
git clone https://github.com/Traumflug/Teacup_Firmware.git && cd Teacup_Firmware
Building using the Makefile

Before building with make, it's a good idea to do a build using Configtool, first. This does all the required setups, especially creating config.h and thermistortable.h. After that you can change these files with a text editor, too, of course.

This done, copy Makefile-AVR (or Makefile-ARM) to Makefile:

cp Makefile-AVR Makefile

Most likely, Makefile needs some adjustments, like your type of ATmega and your serial port. These are collected at the top of the makefile, together with commented examples and some instructions. Edit them to your needs, then build:

make           # compile and build teacup.hex
make program   # upload it to the controller

That's it, given you've dealt with all the upcoming errors, your controller is ready to go now.

Changing to another branch
git checkout <branch name>
Updating the repository

This is a procedure which always works. Drawback of working with topic branches is, they don't always update smoothly, sometimes asking for manual conflict resolutions for things as small as a whitespace change. Branch master does always update cleanly, however (we keep it this way). This example shows how to update the branch experimental (and master as well):

git checkout .   # <=== CAUTION: this deletes all your uncommited changes (but not your configuration).
git checkout master
git branch -D experimental
git fetch --prune
git rebase master origin/master
git checkout experimental

The last command will create a new local branch on top of the remote one, exactly what you need.

Keeping local changes

To keep local changes, you simply create a local branch. Other developers won't even notice this local branch unless you push it to the central repository. Let's name this branch mywork:

git checkout experimental
git checkout -b mywork

That's it, now you can commit whatever you want without fearing to mess things up! If you do mess up, simply delete this branch and start over.

To update with such a local branch, the above procedure changes slightly:

git checkout .
git checkout master
git fetch --prune
git rebase master origin/master
git checkout mywork
git rebase experimental

If you get conflicts, Git prints instructions on how to solve them.

Tuning for small binaries

Teacup is the currently smallest RepRap firmware, so it's the natural choice for small controllers like the ATmega168 based Arduino Diecimilia, or Uno. Hints on how to get the binary even smaller:

  • Don't use Arduino IDE directly. Using Arduino IDE adds more than 2000 bytes and likely reduces performance of the executable. No such penalty with Configtool using Arduino's compiler.
  • Uncheck Enable EEPROM Storage in Configtool -> Printer -> Miscellaneous. Saving parameters in EEPROM is convenient, but not mandatory. You can use the hardcoded defaults instead (that's what you get initially, anyways).
  • For less RAM usage, reduce Movebuffer Size on the same tab. Unless you have a very slow serial connection, you'll barely notice a difference between 4, 8 or 16. But each slot in the buffer currently costs some 100 bytes RAM.
  • Enable BANG BANG Bed Control, also on the Miscellaneous tab. Using BANG_BANG significantly reduces the size of the temperature control code.
  • Remove Travel Limits (software endstops) in Configtool -> Printer -> Mechanical. This removes the code handling it.
  • Uncheck Look Ahead in Configtool -> Printer -> Acceleration. While very convenient, it can be commented out for very small CPUs. Saves over 3000 bytes.

Using the firmware after install

Before diving into printing, you have to calibrate at least the basics: Triffid Hunter's Calibration Guide.

Very similar to all other firmwares, just default values are conservative to protect your mechanics. For example, higher speeds require to set MAXIMUM_FEEDRATE, first.

Typically you connect with a printing host, like Pronterface. GtkTerm, PuTTY, CoolTerm work as well. Commands are a subset of the ones for LinuxCNC and those for RepRap: G-code. For example, to move something you send G1 and the target coordinates in millimeter, like G1 X10 Y20 F500. F is speed in mm/minute.

Design Details and Tuning

While chances are good things start to work right after installation, a lot of details can be adjusted for more comfort, less machine wear or faster operation.

In general, everything is configured in the file config.h. After changing config.h, re-upload the firmware and the new settings should take effect. Config.h contains a lot of hints, see also the following paragraphs.

Communications to the Host

Teacup Firmware has the capability to receive additional commands while working on the first one. This avoids short stops between commands and will allow for even more smooth transitions from one movement to the next in the future.

Baud Rate

For serial communications using the RS232 protocol, the baud rate can be set in config.h. The default is

#define BAUD 115200

which has been shown to be a good compromise between speed, compatibility and reliability. Lower baud rates, like 57600, 38400, 19200 might be more successful in electrically noisy environments. Higher baud rates should work, too, and have yet to be tested.

XON/XOFF Flow Control

Teacup Firmware has XON/XOFF flow control implemented. This is not needed when sending G-code files with RepRap Hosts, but quite helpful when sending files with a serial terminal emulator (GtkTerm, CoolTerm, HyperTerminal, etc.).

This feature is off by default, turn it on in config.h by uncommenting

#define XONXOFF
Direct USB

Teacup Firmware supports using direct USB on ATmegas equipped with this, like the ATmega32U4 on the Teensy board. Enable this by uncommenting

#define USB_SERIAL

in config.h. The config.teensy.h template has this uncommented by default, so things should work out of the box.

SD card support

The red oval marks the board pin for enabling SD card support.

On AVR based controllers, printing from SD card is supported, commands M20 to M25 are implemented. To enable (or disable for a smaller binary size) it, simply choose the Chip Select pin of the SD card adapter. All other pins are given by the board design, so Teachup chooses them automatically.

Chip Select is not to be confused with Card Detect. Card Detect is useless, so it's ignored.

To check SD card support, send a M21, then a M20 to the controller. It should list all files in the top level directory of the card.

Supported filesystems on the SD card are FAT16 (typical for cards up to 2 GB) and FAT32 (typical for bigger cards) in a partition-less volume.

For those who care about binary size of the firmware: SD card support counts in at just 4.5 kB on AVR. It uses Petit FatFs by ELM ChaN. Excellent library if you care about size.

SD card Troubleshooting
Sending M21 reports something like Bad M-code M21..
SD card support isn't compiled in, SD_CARD_SELECT_PIN isn't defined at all. Check that.
Sending M21 reports E: SD init failed..
SD card support is compiled in, but communications with the card doesn't work at all. Either due to the wrong Chip Select pin choosen, a poorly seated card or ... is there actually a card in the slot?

Pin configuration

In the file config.h, functions should be assigned to ths mcu's pins. The pins names should be AIOx or DIOx, with x a number.

  • AIO stands for Analog Input Output
  • DIO stands for Digital Input Output.

These names are references to the Arduino boards.

If you use your microcontroller documentation to assign pin to functions, you will get a port name and a pin number. You will have to convert your pin into a AIOx or a DIOx. You have two way to do it :

  • you take an electric shematic of an Arduino board which use the same microcontroller and look at the corresponding name.
  • you open the file arduino_.h and look at the corresponding name of your pin. In this file your pin will be named PINxy, with x the name of the port and y the pin number

Here is what you can find in the file arduino_168_328p.h :

#define AIO3_PIN      PINC3
#define AIO3_RPORT    PINC
#define AIO3_WPORT    PORTC

We can see that the pin 3 of the port C is called AIO3 by Teacup

Heater & Fan Setup

Extruders, heated beds, fans, milling spindles etc. have at least one pin to turn the device on and off and optionally a temperature sensor to control its temperature. Devices with a temperature sensor are controlled by M104, such without one by M106. Additionally, there's M140, which is a special case of M104 for the heated bed.

Adding or removing heaters ( = general devices) and temperature sensors in Configtool is pretty straightforward and to be found on the board tab. In case you want to define a pair, define the heater first. A heater and a sensor of the same name make up a pair. There can be as many heaters as you have pins to spare and as many sensors as you have heaters, plus one heaterless sensor.

As you might know already, thermistors require a translation table. Configtool will create this table on the fly, just select your type from the menu of presets or enter your custom values. As always, there are tooltips all over the place to give detailed help.

Note: there's no obligation to have a heater or sensor at all. If your machine has no such stuff, remove all of them. Stepper motors will continue to work just fine, performance might be a bit better, binary size will be smaller.


The KINEMATICS keyword in config.h defines which type of kinematics your printer uses. If you have a CoreXY printer, change this




As always, there's a description in the config.h templates. Default is KINEMATICS_STRAIGHT (for "ordinary" printers like Mendel, Mendel90, Prusa i3). Scara support (RepRap Morgan) is already in the works, Delta support shouldn't be too difficult to add as well.

Distances and Velocities

Default Feedrates are set quite slow, to encourage you to adjust them to match your hardware.

Distances as expressed in steps
/** \def STEPS_PER_M
       steps per meter ( = steps per mm * 1000 )
       calculate these values appropriate for your machine
       for threaded rods, this is
               (steps motor per turn) / (pitch of the thread) * 1000
       for belts, this is
               (steps per motor turn) / (number of gear teeth) / (belt module) * 1000
       half-stepping doubles the number, quarter stepping requires * 4, etc.
       valid range = 20 to 4'0960'000 (0.02 to 40960 steps/mm)
#define STEPS_PER_M_X                                   320000
#define STEPS_PER_M_Y                                   320000
#define STEPS_PER_M_Z                                   320000
/// http://blog.arcol.hu/?p=157 may help with this one
#define STEPS_PER_M_E                                   320000

These values have to match your electronics, there is only one correct value for each axis. Movements actually moving the commanded distance indicate correct STEPS_PER_M settings.

Most electronics allow to change microstepping. Doing so requires to change STEPS_PER_M, too. A high level of microstepping increases resolution and reduces noise, but requires more processing per unit of motion. In December 2013 Teacup was tested to generate a maximum of about 48'000 steps per second on a 20 MHz CPU, which translates to 600 mm/s on a typical belt driven axis with 1/16 microstepping, so there's not much reason to not use such a setting.

Velocities in mm/minute
/// used for G0 rapid moves and as a cap for all other feedrates
#define MAXIMUM_FEEDRATE_X              200
#define MAXIMUM_FEEDRATE_Y              200
#define MAXIMUM_FEEDRATE_Z              100
#define MAXIMUM_FEEDRATE_E              200

These values should be calibrated for each axis seperately. Here's a procedure to find the maximum possible values:

  1. Set acceleration very low, like 100 mm/s2 on a threaded rod driven axis or 500 mm/s2 on a belt driven axis.
  2. Set MAXIMUM_FEEDRATE very high, like 30000 mm/min.
  3. Send movement commands, starting at a feedrate of 100 mm/min (G1 Z50 F100). Move some 50 mm each time, longer distances as speeds increase.
  4. Raise the feedrate some amount each time, like F100, F120, F150, F180, ...
  5. Find out how far you can go. A sudden slowdown to a crawl typically means the ATmegas' processing power is exhausted. A motor suddenly stalling and beeping means your motor doesn't have enough torque for such a speed. There are other mechanical limitations, of course.
  6. Reduce the feedrate a bit until stuff works again. Check repeatedly with 200 mm moves.
  7. Reduce this by some 20% to have a safety margin. This is the MAXIMUM_FEEDRATE you want.

Adaptive homing feedrates

When doing homing movements (G28, G28 X, G28 Y and similar), there's another thing to consider. Other than most firmwares, Teacup doesn't halt apruptly when hitting the endstop, but decelerates smoothly to save your mechanics. This ineviteably means the carriage moves somewhat over the endstop trigger point. The farther the higher your speed is.

To allow highest homing feedrates while keeping compatibility with your hardware, Teacup asks for an endstop clearance distance. Set this to the distance your carriage can move over the endstop trigger point without crashing. On mechanical endstops this can be short, like 1 mm, on optical endstops you typically have more clearance, like 10 mm. Put there whatever matches your hardware, a bit less if you're not certain.

Units: micrometers
Sane values: 0 to 20000   (0 to 20 mm)
Valid range: 0 to 1000000


Teacup firmware will make sure your carriage never exceeds your endstops more than this distance, still getting there as fast as possible. And, perhaps you already guessed it, increasing ACCELERATION means shorter breaking distances, so higher ACCELERATION values also mean higher homing feedrates.

You can set this distance to zero. In this case, SEARCH_FEEDRATE is used. Expect minutes for homing movements.

A value of minor importance is SEARCH_FEEDRATE. It's used as default feedrate (before receiving the first F command) and for this short movement after hitting an endstop, searching the endstop more precisely.

Units: mm/min
#define SEARCH_FEEDRATE_X                       50
#define SEARCH_FEEDRATE_Y                       50
#define SEARCH_FEEDRATE_Z                       50

With high endstop clearances it can make sense to increase these values a bit.

Constant Acceleration

Real-world machines/printers can't gain full speed instantly, so a defined acceleration is actually a requirement. Trying to do without results in printer frame bending, early wearing of mechanical parts and/or plain stalling when trying to move fast.

Teacup Firmware contains a constant acceleration implementation.

Acceleration, RepRap Style

This matches the strategy of the official firmware around the Darwin / early Mendel days. Each movement starts at the speed of the previous command and accelerates or decelerates to reach target speed at the end of the movement.

Goal of this strategy is to allow calculation of smooth movements in the host software. If communication between host and controller gets delayed, hard stops and starts are to be expected. This strategy isn't useful for commands issued by hand, either.

You can get this behaviour in config.h by uncommenting

Acceleration, Start/Stop Ramping

This variation guarantees always smooth starts and stops, as all acceleration and deceleration is calculated in firmware (and actually uses less code than the above). Each movement starts at (almost) no speed, linearly accelerates to target speed and decelerates just in time to smoothly stop at the target. Implementation is expected to be accurate enough to avoid any step losses at all. Some users run their printer for weeks without a single miss.

An often wanted feature of start/stop ramping is to join distinct, but similar moves without a full stop in between, keeping movement speed. This is called "lookahead", see below.

Most current G-code generators expect RepRap-style acceleration. You can use only one style of acceleration. To use start/stop ramping, turn off ACCELERATION_REPRAP in config.h, then turn on:

#define ACCELERATION 50.

The first #define turns the feature on, the second one defines acceleration steepness in mm/s2. Smaller values give slower, higher numbers quicker acceleration, useful range is 1. to 10'000. A good starting point is 10. for milling (high precision) or 1000. for printing.

Tune acceleration to what you think your machine can achieve and how much bending of your printer you want to allow. Sturdier machines bend less at high acceleration, weak machines need a smaller value to keep reasonable printing accuracy.

Also, it's pointless to try to accelerate faster than your steppers torque / axis weight can deal with, of course. If your motors just stall after raising acceleration, you wanted too much.

Endstop Handling

For efficiency reasons (and to avoid ruined prints when endstops get false triggers) the endstops are only checked during homing operations.

To test an endstop, send G-code M119. This shows you the status of all configured endstops. Trigger the endstop and send M119 again to see it working. If an endstop runs inverted, uncomment X_INVERT_MIN, X_INVERT_MAX, Y_INVERT_MIN, etc. in your config.h (or comment it out if it was uncommented already).

When connecting mechanical endstops, NC (normally closed) as well as NO (normally open) types are supported. For using ATmega's internal pullup resistor, watch out for USE_INTERNAL_PULLUP. It is normally configured through Configtool under the Printer tab -> Miscellaneous -> second from top in the left column.

Lookahead tuning

Lookahead is a mechanism which avoids halting between two adjectant moves if the direction of movement is similar enough. This not only increases overall printing speed, it also avoids blobs from the extruder at each corner.

For lookahead, you'll find these entities in config.h:

#define MAX_JERK_X 20
#define MAX_JERK_Y 20
#define MAX_JERK_Z 0
#define MAX_JERK_E 20

The first one has to be uncommented (like shown) to get lookahead at all.

The tuning part is, how much can you change speed of an axis instantly without suffering step losses. This is what the other four lines describe, one for each axis. To change direction without halting and without moving a curve, axis have to change speed instantly and the numbers tell how big this speed change can be. A value of 20 means, speed (of an axis) can change by 20 mm/min instantly, e.g. from 2000&mm/min to 2020 mm/min.

If the numbers are too low, Teacup will slow down unneccessarily between adjectant moves. Accordingly, if the printer runs your G-code constant speed, there's no point in raising the numbers.

If the numbers are too high, you'll suffer step losses or (too) severe bumps in the axis driving mechanism. If the printer suddenly halts an axis at a corner, steppers are just beeping, lowering the numbers are a good idea.

As always, default values are on the safe side. 20 is fine even for threaded rod driven axes. Belt driven axes can typically go much higher, like 200 or 500. For the Z axis you likely want to keep zero, this ensures a full stop before and after each Z move.

Canned G-code

Teacup has an option to run G-code compiled right into the binary. Comment out the string below CANNED_CYCLE in config.h and adjust it to your needs. This G-code will be executed in a loop, without any user interaction or even a serial connection. The only ways to stop this are to cut off power, hold down the reset button permanently or to upload another firmware.

Developer Section

If you feel comfortable with hacking the source code, read on ...

Design Goals

  • No dependence on Arduino libraries
  • Easy to read and modify the source code
  • 100% integer computations
  • Serial transmit buffer
  • Can fit (with a sparse config) onto an ATmega168
  • Works on ATmega328P and larger ( = 644, 644P, 1280, 1284P, 2560)
  • Works on ATmega32U4 used by Teensy (http://www.pjrc.com/teensy)
  • C89 compatibility, no C++


If you have a change or a patch you want to discuss, or if you don't have github commit access, please upload the patch here.

To apply patches, type git apply in your terminal, inside your local copy of git repo, then copy the text given here into the terminal and hit ctrl-d.

If you work with git on Teacup Firmware, please do commits in small pieces, one topic/bug at a time. This helps a lot in reviewing patches. That done, you can create a set of patches easily:

git format-patch --keep-subject -o out origin

You'll find each of your patches in the out/ directory, nicely equipped with a mail header and a proper file name.


Teacup has some topic branches and forks with additional features:

experimental is the development branch. All new developments go here, so if you want all the latest fixes and features, get this one.

multi-extruder: as the name says, support for multiple extruders. A bit behind with other features, though.

External ARM ports, which were done before a generic ARM port appeared in August 2015:

Running Teacup in a simulator

Teacups' high technical quality has a reason: you can run it in a simulator and precisely view every single step.

Teacup on i386 hosts

That's right, Teacup compiles and runs on an i386 just fine. You can't (yet) run a stepper this way, but it's excellent to check accuracy of new algorithms. To do so:

make -f Makefile-SIM

That's it, now you can run it and feed G-code on the command line. Stuff normally going to the serial line is written to the console. Just fine for printf-type debugging. And very fast, much faster than realtime.

Teacup in SimulAVR

Teacup is prepared for running in SimulAVR, a cycle-accurate simulator. This is quite a bit slower than realtime, but it allows you to record pin changes (motor steps!) accurate to the nanosecond. It also allows to connect a debugger to the running binary. And the best of all: the binary is exactly the same as the one sent to real hardware, so you can be very sure simulation results match what would happen on this hardware.

To work with SimulAVR, put it's source code directory side by side with Teacup ones'. Makefile tests for ../simulavr/ and if it finds it, it'll do everything neccessary.

Building for SimulAVR is then simple, it's exactly the same as building for the real controller (and yes, you can run the binary for the controller in SimulAVR and vice versa):

make -f Makefile-AVR
Infobox info icon.svg.png Note
Build for an ATmega644 (without 'P'), as this is the ATmega variant apparently supported best by SimulAVR. For simulation purposes, ATmega644, ATMega644P, ATmega1284P, etc. are all the same anyways.

Running Teacup isn't too complex, too:

../simulavr/src/simulavr -f build/teacup.elf

More details and on how to attach GDB are described on the SimulAVR page.

Getting these graphs ...

Teacup in SimulAVR.png

... is a bit more complex. That's why a script is prepared to do the hard work:

cd testcases
./run-in-simulavr.sh <your g-code>.gcode
  • This will run the simulation until it hits a M2 in the G-code, until the G-code ends, or until you hit Ctrl-C on the command line and results in a file <your g-code>.processed.vcd. Length of the G-code file is arbitrary, but note that simulation in SimulAVR on a commodity PC is about 5 times slower than running on the real controller.
  • Open the .processed.vcd in GTKWave or another viewer for VCD files.
  • For convenient viewing of speed ramps, choose Menu -> File -> Read Save File and then save-file-velocities.gtkw.

Then you can view every single step, even measure how long or how short a single step pulse is (signal X_step, Y_step, etc.).

Doing precision profiling

The principle of timing code sequences is shown in SimulAVR#Doing precision time measurements.

Here's an example timing step interrupt when running testcases/short-moves.gcode with code from June 2014:


The blue curve shows X axis speed for orientation. The second row shows the Debug LED signal, which you can measure as shown on the SimulAVR page.

The bottom two rows show a derivative signal which you get when running the simulation with ./run-in-simulavr.sh short-moves.gcode. It's both times the same signal, once shown analog, once shown as decimal number. The derivative is the time between the last raising and the last falling flange, so you get a pretty good idea of where the step interrupt is fast and in which situations it falls behind average (these spikes upwards). As you can see, the interrupt is usually pretty fast with 15300 ns = 328 clock cycles, but also has spikes up to over 700 clock cycles high where one G-code command ends and the next one begins. This added time is likely the time taken in dda_start().

For even more convenience, run-in-simulavr.sh also prints a statistics to the terminal it runs in:

$ ./run-in-simulavr.sh short-moves.gcode
Assuming pin configuration for a Gen7-v1.4 + debug LED on DIO21.
Statistics (assuming a 20 MHz clock): 
LED on occurences: 838.
Sum of all LED on time: 262055 clock cycles.
LED on time minimum: 306 clock cycles.
LED on time maximum: 717 clock cycles.
LED on time average: 312.715 clock cycles.


Toolchain for ARM

Teacup uses GNU Tools for ARM Embedded Processors, a toolchain provided and maintained by ARM, the chip design company, its self. This toolchain is built around arm-none-gcc-eabi. Additionally you need make. Third neccessary part is an uploading tool. Gen7-ARM's LPC1114 needs lpc21isp. Long story short, this should install all you need on Ubuntu:

sudo apt-get purge gcc-arm-none-eabi # ignore errors
sudo apt-get purge binutils-arm-none-eabi # ignore errors
sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded
sudo apt-get update
sudo apt-get install build-essential gcc-arm-none-eabi lpc21isp

Arduino IDE uses the same toolchain, so it should be possible to make an add-on package for this. This should include lpc21isp binaries for all common operating systems (Linux, Mac OS, Windows), because Arduino natively uses Bossa, which supports Atmel SAM devices only.

Porting to other (ARM- or non-ARM-)platforms

The generic ARM port makes a nice tutorial for porting even more platforms. The unfortunate thing with all the ARM flavours is, each of them requires pretty much a unique port. The good thing is, the tutorial leads you through all the pieces step by step, so it's much less overwhelming.


  • arm-none-eabi-gcc
  • make
  • A clone of MBED.
  • A tool to upload the built firmware to the controller.

Initial steps

Get a copy of the Teacup repository:

git clone https://github.com/Traumflug/Teacup_Firmware/tree/experimental
cd Teacup_Firmware
git checkout experimental

Find out how many commits happened since the generic ARM port. Write down the number this command outputs, we'll later refer to it as NUMBER:

git log --oneline --ancestry-path arm-ports-start-here..experimental | wc -l

Last initial step, get to the starting point:

git checkout arm-ports-start-here
gitk --all&

The latter gives you a graphical GUI, which helps tremendously for orientation inside the repo.

Porting steps

Being at the starting point of a new tutorial stage, read what the commit message says. Implement the stuff what the generic ARM port did, just for your chip. Not more, but also no less. Copy files as neccessary from MBED. Create a Makefile for your platform. Test it. That's the actual porting work. If you run into trouble or abiguities, Teacup developers can help at Github's issue tracker.

If you think you're done, run regression tests:

make regressiontests

Note that no commit not passing regression tests will be accepted to mainstream, not even intermittent ones. If regression tests succeeded, commit your work (you can commit earlier, of course) ...

git add <new files>  # <== add new files here, if any; else they'll be forgotten
git commit -a        # Type a short description of what works now, perhaps test results, binary sizes, etc.

... and enter the next stage. To find the next stage, we need the NUMBER. Decrement it by one, then do this:

git rebase experimental~<NUMBER>

There may be conflicts, Git command line will write instructions on how to solve them. Usually by opening the file with the conflict and deciding which version shall remain, then doing git add and git rebase --continue. After having done so, select Menu -> File -> Reload in Gitk and look at the result. You should see your work branching off of the series of commits leading to experimental. As the port goes on, you'll climb this ladder up, step by step.

Having this done, you resolved one stage and can continue with the next one by reading its commit message. The ladder of commits for the full port has 87 steps, so you should go this round 87 times. No sweat, many of them require no work of yours at all, so you can climb them in a minute or two. Just run regression tests and rebase again.

Final porting steps

Final and most important step is to bring your work into the official Github repo. If you don't, your work is likely forgotten soon. Personal forks happen to get not much public attention.

Getting contributions into the official repo is easy. Open a Github issue for it, if you din't do so earlier already. Developers providing code always get write access. You can also email a patch series. A Pull Request is another mean to pick up discussion.

Teacup developers very happily accept code, they even fix whitespace issues and other minor stuff them selfs. Still there might be some remaining open questions after review. Or a few fixes which need testing. So please don't run away immediately, you're the only one with the hardware required for testing, Teacup developers count on you.

Once the port is in mainstream, you'll get all updates, bug fixes and new features for free. A regression test or two will make sure others don't break your code. Your platform is a full citizen of Teacup, then.

Rationale and History

Triffid Hunter's Original Blog Post

I started building my electronics with only a regular arduino to test with. This was perfectly sufficient for playing with the pololu stepper controllers and the max6675 I bought after reading about all the issues with thermistors that people were having. After a while I decided to check out the official firmware but it required an atmega644. I wondered why. So, I decided to skim through the code to see what took up so much space. From what I could see, it was written by someone who was familiar with programming desktop systems and larger embedded devices, but didn't have much experience with small devices such as the atmega168 and atmega644. This showed in the use of C++ which served only to make the code harder to read, and the prolific use of floating-point math, with some appearing even in interrupt context! I came to the conclusion that there was no reason that the main body of code couldn't fit onto an atmega168 except for the burdensome and unnecessary overheads from object-oriented code and floating point math. A quick count assured me that the atmega168 had enough pins, but only barely, and I started reading the official firmware properly, with an eye to rewriting as much as possible in a fashion suitable for small microcontrollers.

Starting with an arduino skeleton library I had assembled over time, some of my test code and the official firmware, I hacked up a passable integer-only, straight C implementation of the dda, and wrote my own gcode parser from scratch which processed each character as it arrived (with some buffering of course) instead of waiting for a whole line and then trying to process it all at once.

As soon as my new firmware was able to run a few consecutive moves, I released it for peer review.

The forum thread http://forums.reprap.org/read.php?147,33082 has much of the history from this point on.

Traumflug was the first to send patches, and has done a significant amount of work on a number of different parts of this firmware. Jakepoz ported it to gen3 branch electronics. Cefiar posted me some thermistors to sponsor addition of thermistor-reading code

Many others have given encouragement and suggestions without which this firmware may never be what it is today.

Architectural Overview

Teacup Firmware is quite similar to the official firmware in some ways, and markedly different in others. Teacup Firmware has as much modularity as I could get away with without sacrificing efficiency.

At startup, the code in mendel.c is run first. This initialises all the modules that need it, then starts polling the clock flags and feeding incoming serial characters to the gcode parser. The gcode parser processes each character individually, keeping track via internal state rather than buffering a line and skipping back and forth. The gcode parser converts floating values to integer or fixed-point representations as soon as it encounters a non-numeric character. It calls many module functions directly, but the most interesting part is move creation, where it passes a target position and speed to enqueue()[dda_queue.c] which adds it to the queue, and fires up dda_start()[dda.c] if the queue was empty. dda_start initialises the dda, figures out the stepper directions and first step timeout and a few other bits of housekeeping, then sets the timer for the appropriate timeout. When the timer fires, it calls dda_step()[dda.c] which sends all the step signals then figures out the next step timeout based on acceleration and speed settings. When the last step has been made, the dda "dies" (sets 'live' property to 0) after which queue_step[dda_queue.c] advances the queue read pointer and starts the next dda.

It is necessary to keep interrupts very short on small microcontrollers, and I have endeavoured to keep them all as short as possible. Unfortunately, dda_step[dda.c] is fairly large. I simply hope that it doesn't take so much time that it interferes with the other interrupts too much.

Interesting code sections

The serial ringbuffers are critical for good communication, but for some reason the official arduino libraries don't implement a tx queue, all but preventing sending stuff from interrupt context. As long as the queues have a length of 2^n, we can use bitwise operations rather than numerical comparison to trim the read and write pointers. The serial send function (serial_writechar[serial.c]) is necessarily careful about checking if it's in an interrupt and only waiting for space in the queue if it's not. The dda queue is also a ringbuffer, although its implementation is harder to see as it's embedded in lots of other stuff.

The gcode parser shows how to parse each character as it comes in, so 99% of a command can be processed before the EOL is even received. It started off as a simple state machine, which then grew and shrank and morphed until it was both smaller and more functional.

The fixed-point stuff is fun, although we have to manually ensure that the decimal point stays in the right spot. decfloat_to_int[gcode.h] is used to convert incoming floats to integer implementations by starting off with a (very!) crude floating point implementation, then choosing appropriate scaling factors within the gcode parser itself. This allows us to do a little stuff that looks like floating-point math without the burdensome overhead of a full fp implementation.

The PID code in heater.c is probably quite generalisable, and seems to work well when tuned. Google knows of plenty of PID tuning guides.

File descriptions

Teacup File descriptions
File Description
analog.[ch] This is the analog subsystem. Only used if you have a Thermistor or AD595.
arduino.h Pin mappings and helper functions for various arduinos ('168/'328-based, '644-based, '1280-based.
clock.[ch] A system clock for periodic tasks. Supports a long-running clock, but this is disabled by default as nothing uses it (yet!).
copier.[ch] A totally untested and currently unused chunk of code for copying firmware to another identical chip.
dda.[ch] A rather complex block of math that figures out when to step each axis according to speed and acceleration profiles and received moves.
dda_queue.[ch] The queue of moves received from the host.
debug.[ch] Debugging aids.
Teacup.pde Allows firmware to be built in Arduino IDE
func.sh Lots of host-side shell scripts for talking to firmware.
gcode.[ch] Gcode interpreter. Scaling of factors to internally used integer or fixed point happens here too.
heater.[ch] Heater management, including PID and PWM algorithms, and some configuration parameters.
machine.h Configuration variables to match firmware to your hardware.
Makefile instructions for make on how to build firmware. has a list of modules to build which may need to be updated every so often.
mendel.c Firmware startup and main loop code.
pinout.h This file associates various functions with particular pins on your avr.
README This file.
sender.sh A simple talker. Needs "waitfor"
serial.[ch] Serial management and buffers.
sermsg.[ch] Functions for sending messages and values to host.
sersendf.[ch] A small, crude printf implementation.
temp.[ch] Temperature sensor management, includes some configuration parameters.
timer.[ch] Timer management, used primarily by dda.c for timing steps.
watchdog.[ch] Watchdog management. Resets chip if firmware locks up or does something strange.