English • العربية • български • català • česky • Deutsch • Ελληνικά • español • français • hrvatski • magyar • italiano • română • 日本語 • 한국어 • lietuvių • Nederlands • norsk bokmål • polski • português • русский • 中文（中国大陆） • 中文（台灣） • עברית •
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 Teensy, Generation 6 Electronics, Sanguinololu, Generation 3 Electronics 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 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 microcontroller existing, including the USB-equipped ones, and can still be tailored to run on controllers as small as the ATmega168.
Installation of Teacup Firmware is pretty much the same as with other firmwares, except there are more options for developers. It can be installed using the Arduino IDE or using the Makefile.
- Especially on Windows, make sure you can see file extensions. Vista is known to do funny things when they're hidden.
- Download the Arduino IDE for your platform if you haven't already. Teacup is independent from Arduinos libraries and uses the IDE as a compile and upload tool only, so the version doesn't matter much.
- In case you have an ATmega644, -644P or -1284P based electronics, also install Gen7 Arduino IDE Support.
- Go to the Teacup Git repository and download a Zip archive.
- Unpack that archive somewhere convenient.
- Before opening it with the Arduino IDE, rename the unpacked folder to
Teacup_Firmware. E.g. from
config.hfile. (duplicate config file which best suits your board and rename to 'config.h'). For example, if you were using a RAMPS board, you would do the following:
cp config.ramps.h config.h
- Do the same for
- Fire up the Arduino IDE and select your serial connection and board type from the Tools menu.
- Open the file
Teacup_Firmware.pdewith the IDE. In case the IDE asks, do not allow to make a copy of something. Instead, cancel and re-check 6. again. The name of the folder and the name of the .pde file inside it has to match.
- Hit the UPLOAD button in the IDE's toolbar.
- Done. Your controller is now ready to receive G-code from Pronterface, Skeinforge's send.py, a serial terminal or whatever sending tool you prefer. Not to forget, Teacup Firmware expects commands at a baudrate of 115200 baud.
Per default, Teacup allows only rarther slow movements. Be assured it's a fast firmware; these slow defaults shall protect your printer during the first steps of configuration. You likely want to at least adjust STEPS_PER_M_.. and increase MAXIMUM_FEEDRATE_.. as well as ACCELERATION in config.h. Hints are given in config.h and in the tuning section.
These instructions are for those who don't fear the command line.
- Install git, avr-libc, gcc-avr and avrdude. On Debian/Ubuntu, this would be:
sudo apt-get install git avr-libc gcc-avr avrdude
- Get a copy of the sources from the Teacup Git repository.
git clone https://github.com/Traumflug/Teacup_Firmware.git && cd Teacup_Firmware
- Choose the config template (config.<electronics>.h) best suiting your hardware and COPY it to config.h. Like:
cp config.ramps.h config.h
- Make a copy of the Makefile best suiting your electronics' architecture:
cp Makefile-AVR Makefile
- Check settings in the upper half of this Makefile. Everything is (hopefully) described inside the file.
- Build and upload it:
make && make program
- Enjoy your new firmware and play with config.h to match your expectations!
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. Hints on how to get the binary even smaller:
- Compile using the makefile instead of Arduino IDE. Saves more than 2000 bytes and likely enhances performance of the executable.
- Comment out EECONFIG in config.h. 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 in config.h. 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 60 bytes RAM.
- Uncomment BANG_BANG, BANG_BANG_ON and BANG_BANG_OFF in config.h. Using BANG_BANG significantly reduces the size of the temperature control code.
- Comment out DEBUG. That's the default already.
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.
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
Teacup Firmware supports using direct USB on ATmegas equipped with this, like the ATmega32U4 on the Teensy board. Enable this by uncommenting
in config.h. The config.teensy.h template has this uncommented by default, so things should work out of the box.
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.
How to set up devices is described pretty well in the comments in config.h. Also, all the electronics-specific config.h templates have bed and extruder already readily configured. Heaters and temperature sensors are connected together by giving them the same name.
Here's an example on how to define an extruder:
#define TEMP_THERMISTOR ... // name type pin additional DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO0, THERMISTOR_EXTRUDER) ... // name port pwm DEFINE_HEATER(extruder, PB3, 1) ... #define HEATER_EXTRUDER HEATER_extruder
And here's an example on how to define a fan:
// name port pwm DEFINE_HEATER(fan, PB4, 1) ... #define HEATER_FAN HEATER_fan
Make sure the definitions are right after the existing defintions, so compilation of the firmware continues to work. Also, the order of the definitions matters, as it gives the index for the P parameter in M104/M106.
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
High levels of microstepping increases the resolution and reduces the noise, but requires more processing per unit of motion. At defaults of 320000 steps/m and 200mm/min, a motion would require generating 200/60*320000/1000 = 1066 steps per second; due to the Bresenham algorithm it (almost) doesn't matter how many axes take part in this movement. In December 2011 Teacup was tested to generate a maximum of 15'570 steps per second on a 20 MHz CPU or about 12'000 steps/s on a 16 MHz CPU [].
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 /// used when searching endstops and as default feedrate #define SEARCH_FEEDRATE_X 50 #define SEARCH_FEEDRATE_Y 50 #define SEARCH_FEEDRATE_Z 50 // no SEARCH_FEEDRATE_E, as E can't be searched
These values should be calibrated for each axis seperately. Here's a procedure to find the maximum possible values on a typical Mendel-like design, starting with the Z axis:
- Set acceleration very low, like 100 mm/s2.
- Set MAXIMUM_FEEDRATE very high, like 50000 mm/min.
- Send movement commands, starting at a feedrate of 100 mm/min (G1 Z50 F100). Move some 50 mm each time.
- Raise the feedrate some amount each time, like F100, F120, F150, F180, ...
- At one feedrate, the axis will suddenly slow down to a crawl (1 or 2 steps per second) due to ATmega's processing power exhaustion.
- Reduce the feedrate a bit until stuff works again. Check repeatedly with 200 mm moves.
- Reduce this by some 20% to have a safety margin. This is the MAXIMUM_FEEDRATE you want.
For the belt driven axes (X, Y), the basic procedure is the same. Other limitations than ATmega exhaustion might kick in, like step losses. Step losses can be recognized by a suddenly stopping and beeping stepper motor. Also, achievable feedrates are typically some 10 to 20 times higher.
The official FiveD firmware changes speed by a fixed amount each step, however this also alters the step time. Acceleration = dv/dt, so with a fixed dv but a changing dt, acceleration changes during a move. This makes high speeds quite unattainable as the acceleration quickly outstrips the motor's ability to keep up.
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. The steepness of acceleration and deceleration is configurable.
A drawback of the current implementation is, it stops after each move. This means slow average speeds in case of many short moves. It'll be possible to improve this situation by taking the next move into account when ramping down. A not-so-trivial task though, predestined for a hacking week sometimes in the future.
Please note real-world machines in fact can't gain full speed instantly. Trying to do so results in bending and early wear out of the mechanical parts. Also, if acceleration is reliable, you can approximately double the speed of stepper motors without risking loss of steps. In tests with my setup I got over 1000 rpm with an ordinary NEMA-23 stepper!
As most current GCode generators expect RepRap-style acceleration and you can use only one style of acceleration, this feature is turned off by default. To use it, turn off ACCELERATION_REPRAP in config.h and turn on:
This is enough for first tests (always recompile and re-upload the firmware, btw.), but to get best results, you should adjust ramping steppness as well:
#define ACCELERATION_STEEPNESS 500000
Smaller values give quicker acceleration. Tune it to what you think your machine can achieve. Sturdier machines with powerful motors can accelerate quick, weak machines with light motors need a bigger value.
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. An untouched endstop should give a 0, a triggered one a 1. 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 in your config.h, it's described there.
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 LOOKAHEAD #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.
If you feel comfortable with hacking the source code, read on ...
- 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 experimental branches and forks with additional features:
- experimental is the development branch: https://github.com/Traumflug/Teacup_Firmware/tree/experimental
- Multi-extruder development: https://github.com/Traumflug/Teacup_Firmware/tree/multi-extruder
- Lookahead: see LOOKAHEAD in config.h and https://github.com/Traumflug/Teacup_Firmware/issues/23on
- ARM porting:
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 ./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:
make -f Makefile-AVR
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 ...
... 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 60 simulation seconds (which typically takes some 5 minutes wall clock time) and results in a file <your g-code>.processed.vcd.
- 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.).
Rationale and History
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.
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.
Is git new to you?
Git can be heavy going. You probably want to go to the github.com site, make an account and get the recommended git tools for your system. Once you've done that, try:
It wants Traumflug's username, not yours. The github instructions miss that little nugget of information. The PC should create a new directory and respond with something like:
remote: Counting objects: 1680, done. remote: Compressing objects: 100% (639/639), done. remote: Total 1680 (delta 1189), reused 1465 (delta 1021) Receiving objects: 100% (1680/1680), 428.26 KiB | 118 KiB/s, done. Resolving deltas: 100% (1189/1189), done.
cd Teacup_Firmware git pull origin master
If it says you're up-to-date, awesome. If not, it should grab a few files.
This should get you started. You'll pick git up as you go along.
|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.|
|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.|
|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.|