TMCstepper - Arduino - TMC2209

Hi All,

This weekend I've been busy with my new stepper motor, an Arduino , a fysetc TMC2209 and the TCMstepper library, it worked out just fine :slight_smile:

This topic was a great help: Using a TMC2209 silent stepper motor driver with an arduino

Special thanks to adouglas88, your code was an awesome help. And AnshumanFauzdar, I'm really looking forward to the documentation you've been talking about.

This is not so much a post with a question but a post with a working result. Let's say a summary of what I've learned. Not perfect but it's a start.

My circuit:

My result:

My code:


#include <TMCStepper.h>         // TMCstepper - https://github.com/teemuatlut/TMCStepper
#include <SoftwareSerial.h>     // Software serial for the UART to TMC2209 - https://www.arduino.cc/en/Reference/softwareSerial
#include <Streaming.h>          // For serial debugging output - https://www.arduino.cc/reference/en/libraries/streaming/

#define EN_PIN           2      // Enable - PURPLE
#define DIR_PIN          3      // Direction - WHITE
#define STEP_PIN         4      // Step - ORANGE
#define SW_SCK           5      // Software Slave Clock (SCK) - BLUE
#define SW_TX            6      // SoftwareSerial receive pin - BROWN
#define SW_RX            7      // SoftwareSerial transmit pin - YELLOW
#define DRIVER_ADDRESS   0b00   // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f           // SilentStepStick series use 0.11 ...and so does my fysetc TMC2209 (?)

SoftwareSerial SoftSerial(SW_RX, SW_TX);                          // Be sure to connect RX to TX and TX to RX between both devices

TMC2209Stepper TMCdriver(&SoftSerial, R_SENSE, DRIVER_ADDRESS);   // Create TMC driver

int accel;
long maxSpeed;
int speedChangeDelay;
bool dir = false;


//== Setup ===============================================================================

void setup() {

  Serial.begin(11520);               // initialize hardware serial for debugging
  SoftSerial.begin(11520);           // initialize software serial for UART motor control
  TMCdriver.beginSerial(11520);      // Initialize UART
  
  pinMode(EN_PIN, OUTPUT);           // Set pinmodes
  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  digitalWrite(EN_PIN, LOW);         // Enable TMC2209 board  

  TMCdriver.begin();                                                                                                                                                                                                                                                                                                                            // UART: Init SW UART (if selected) with default 115200 baudrate
  TMCdriver.toff(5);                 // Enables driver in software
  TMCdriver.rms_current(500);        // Set motor RMS current
  TMCdriver.microsteps(256);         // Set microsteps

  TMCdriver.en_spreadCycle(false);
  TMCdriver.pwm_autoscale(true);     // Needed for stealthChop
  
}

//== Loop =================================================================================

void loop() {
  
  accel = 10000;                                         // Speed increase/decrease amount
  maxSpeed = 50000;                                      // Maximum speed to be reached
  speedChangeDelay = 100;                                // Delay between speed changes
  
  for (long i = 0; i <= maxSpeed; i = i + accel){             // Speed up to maxSpeed
    TMCdriver.VACTUAL(i);                                     // Set motor speed
    Serial << TMCdriver.VACTUAL() << endl;
    delay(100);
  }
  
  for (long i = maxSpeed; i >=0; i = i - accel){              // Decrease speed to zero
    TMCdriver.VACTUAL(i);
    Serial << TMCdriver.VACTUAL() << endl;
    delay(100);
  }  
  
  dir = !dir; // REVERSE DIRECTION
  TMCdriver.shaft(dir); // SET DIRECTION

}
4 Likes

Thanks for letting us know you did a good job. While you are in the learning stage try to learn schematics, There are several programs that are free such as Eagle and KiCad. The first two schematics you do will be real hard unless you are familiar with the terms, libraries, etc. After that they get easier each time you do one. It took me about two weeks to do my first PCB in KiCad but I had capture experience in another program so that part was similar, the PCB part was all new but I got great results.

Thank you for this. I was just about to give up on using a standalone 2209 until I tried your example and it worked with no modifications. One question, are you actually using an external slave clock?

SW_SCK 5 // Software Slave Clock (SCK) - BLUE

Thanks again and great job!!

1 Like

Have you used similar steppers with different stepper drivers? The reason that I ask is I usually use the A4988 or DRV8825 drivers and I have heard that steppers with the TCM2209 drivers are much quieter. Do you find it to be so?

@gilshultz: Yeah, I know about my "circuit design image issue", learning how to do that properly is on my to do list.

@mitchelg: I guess you should thank the guy(s) in the topic I mentioned, it was a great help.
And that SCK, I actually don't really know what it does or if does anything at all in this code. It's being used in all the examples at GitHub - teemuatlut/TMCStepper so I kept it. Just comment it out and try

@groundFungus: This is the first stepper driver I tried, so I have no comparison. It Being mentioned as silent was one of the considerations in my choice. And yes, it's really silent. You would have to put your ear to the motor and listen closely to hear anything. It depends on the motor though I guess, I'm using a nema23, 12V, 1.5A

Thanks for your answer. I will try some TCM2209 drivers. I have a 2D plotter (Zen garden) project that needs quieter steppers.

Another piece of code and a question. Below is the code to use a potentiometer to make the motor turn in both directions at variable speed. Nice to try and could come in useful some day.

And a question. Is it possible to count the number of steps that the motor has turned? I would like to use that for absolute/relative positioning and also change the speed while going to a position.

I have found the FlexyStepper library but the speed range is limited and the motor does not run as quietly.

Thanks
Ā­
Ā­
Ā­

Using potmeter to set speed and direction:

#include <TMCStepper.h>
#include <SoftwareSerial.h>         // Use software serial for the UART to TMC2209 
# include <Streaming.h>             // For debugging

#define EN_PIN           2          // Enable - PURPLE
#define DIR_PIN          3          // Direction - WHITE
#define STEP_PIN         4          // Step - ORANGE
#define SW_SCK           5          // Software Slave Clock (SCK) - BLUE
#define SW_TX            6          // SoftwareSerial receive pin - BROWN
#define SW_RX            7          // SoftwareSerial transmit pin - YELLOW
#define DRIVER_ADDRESS   0b00       // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f               // SilentStepStick series use 0.11 ...and so does my fysetc TMC2209 (?)
#define POT1             A0         // Potentiometers to adjust speed and travel
#define POT2             A1

SoftwareSerial SoftSerial(SW_RX, SW_TX);

TMC2209Stepper TMC_Driver(&SoftSerial, R_SENSE, DRIVER_ADDRESS);


//== Setup ======================================================================================

void setup() {

  Serial.begin(115200);               // initialize hardware serial for debugging
  SoftSerial.begin(115200);            // initialize software serial for UART motor control
  
  pinMode(EN_PIN, OUTPUT);
  digitalWrite(EN_PIN, LOW);          // Enable driver in hardware
  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);

  TMC_Driver.beginSerial(115200);     // Initialize UART

  TMC_Driver.begin();                                                                                                                                                                                                                                                                                                                            // UART: Init SW UART (if selected) with default 115200 baudrate
  TMC_Driver.toff(5);                 // Enables driver in software
  TMC_Driver.rms_current(1770);       // Set motor RMS current
  TMC_Driver.microsteps(256);         // Set microsteps

  TMC_Driver.en_spreadCycle(false);   // Toggle spreadCycle
  TMC_Driver.pwm_autoscale(true);     // Needed for stealthChop
  
}

//== Loop ========================================================================================

void loop() {

  int potVal = analogRead(A0);                              // Read potentiometer (0-1023)
  long stepperSpeed;

  if (potVal <= 500)                                        // In lower half of range turn counter clockwise
  {                                                         // (direction depends on motor wiring?)
    stepperSpeed =  map(potVal, 0, 500, -200000, 0);
  }
  else if (potVal >= 520)                                   // In high half of range turn clockwise
  {
    stepperSpeed =  map(potVal, 520, 1023, 0, 200000);
  }else                                                     // Create a "dead zone" between CW and CCW
  {                                                         // if 500 < potVal <520
    stepperSpeed = 0;                                           
  }

  Serial << potVal << "       " << stepperSpeed << endl;
  
  TMC_Driver.VACTUAL(stepperSpeed);
  TMC_Driver.step();

} // end loop

Its not, that's why the VACTUAL example is not particularly good. Its only useful if you have a closed loop stepper with an encoder.

This would also take the load off generating steps out from the microcontroller, so it would be optimal. But more complex.

You want to use some library like the flexystepper, or if you need more speed use speedystepper.

You could also use less microsteps, i don't think you really benefit from having 256 microsteps. This will just drain your MC unnecessarily as it needs to generate so many steps. This is the reason you can't get high enough speeds. You will also lose torque by using unnecessary microsteps. You can get perfectly smooth movement with much fewer microsteps.

Maybe also try using spreadCycle, in most cases it will make the stepper more smooth & silent.

I think toff(3) might be required for spreadCycle, not sure.

driver.en_spreadCycle(true);     // spreadCycle

Also, you need 1k resistor between the TX / RX. TMC2209 uses one wire UART?

With your setup, i think you have only one way UART connection. And if that's enough, you can just remove the other wire completely. It just means you can send commands but cant receive data from the driver.

You can test the two sided connection by setting your microsteps to anything other than 256 and then trying to read them. As it reads 256 when the connection is not working.

driver.microsteps(2);         // Set microsteps  to 2
Serial.print(F("Read microsteps via UART to test UART receive : "));
Serial.println(driver.microsteps());  //check if reads 2
1 Like

Thanks for the info. I would like to change the speed when sending the motor to a position, SpeedyStepper can't do that. The problem with FlexiStepper is that I cannot get it to move at very slow speeds close to zero. I'm looking into AccelStepper now, it seems to possible to do what I want with that library.

About the serial connection, I tried setting and reading the microsteps and indeed I get back 256 even though I set it to 2. I tested this in the setup I was using and the following setup shown in the Fysect datasheet shown below

It says:

  • R11 0R, I assume a direct connection ? I tested both direct connection and a 1K resistor
  • R12 1K
  • A connection to "UART", But what is that on the Arduino, TX, RX or both ?

I'm a bit confused.

For now I'll be focussing on AccelStepper but it would be nice to use TMCStepper with a functional UART. When I get that working I'll get a motor with encoder.


Just check the diagram i posted. The 1k between TX & RX. The driver only has one UART input/output, even if it has two pins for it. They are both the same pin.

The pins depend on your board, but you are using software serial? So you can just define what pins are you using.

You might want to check AS5600, those are cheap 12bit magnetic encoders you can easily add to your motor or at some other point in your device.

In most cases, you really do not need an encoder. Its only really useful if you have the possibility of losing steps. As you could also move to ESP32, as it has two cores you can just run the stepper on one core while running stuff on the other core. It also has 3 UARTS ports so no need for software uarts.

Im not sure are there any good libraries for stepper motors with encoders, i would need to find one soon.

But i think you can get enough speed from even Flexystepper, i have used it upto 35k steps. Though im using ESP32 and its muchs faster than arduino boards. Just drop your microsteps, as the more steps you need the slower it moves. There is really no benefit from having excessive microsteps.

Ok, you were talking about really slow speeds. Im not sure about that, i have had no issues with slow speeds. Not sure what your issues are with that.

I recommended the wrong library, this is what i was looking for:

Thanks for your reply, I will look into it next week - Have a nice weekend :wink:

Just got around switching to the FastAccelStepper library.
Its able to use the ESP32:s integrated mcpwm and provides extremely smooth stepper operation with far less CPU time. Highly recommend switching to ESP32 and this library if you are looking for really smooth non blocking stepper operation.

Thanks for the tip, I see FastAccelStepper also runs on an Uno (ATmega 328). I'll remember that for future projects. I solved my need for keeping track of the position by using a sensor that sets a zero position. I don't need absolute positioning, a zero reference works for me.

However, I'm still a bot confused about the serial connection. But I'm working and testing on that.

Hullo, thank you for providing this code! It looks similar to the Simple example on the TMCStepper github. I notice you have additionally used the SoftwareSerial.h library. i.e.

#include <SoftwareSerial.h>
...
SoftwareSerial SoftSerial(SW_RX, SW_TX);
TMC2209Stepper TMCdriver(&SoftSerial, R_SENSE, DRIVER_ADDRESS);

instead of

TMC2209Stepper driver(SW_RX, SW_TX, R_SENSE, DRIVER_ADDRESS);

like in the TMCStepper example. Any particular reason you made this choice?

Hi @monkeyfist ,
been using your advice from this thread and similar to MrExplore the serial is not returning 2 microsteps like it should be. I have 1 cable from the uart on the actual driver connected to a 1k resistor and then the tx and rx that I have assigned to 18 and 19 of the mega. You sorta diverted from this issue and I was wondering if you would be able to help?
Thanks in advance!

@littlesilvr
Maybe check my original example, as its for HW UART. As mega seems to have 3 UART ports. The example uses UART 2, you are using UART 1.

So change

#define SERIAL_PORT Serial2

To

#define SERIAL_PORT Serial1

Also, make sure you give the driver motor power before powering on the arduino board. This is important, as the UART won't work if there is no motor power. Easy way to waste a lot of time.

I'm not really sure but I did this on an Uno or Nano and I needed pins 0 and 1 (RX/TX) for debugging? That would be my best guess. Or I just found some example on Software Serial and it worked. Sorry, just not sure.

As to the serial connection, I never figured it out really. But setting microsteps did work eventually so I didn't bother diving into it further.

Ah yes, the turn on and turn of sequence. It seems to be advisable to:

  • Turn on the motor supply before turning on the logic supply on the TMC2209
  • Turn off the logic supply before turning off the motor supply

Hi @monkeyfist ,
I have the UART and Diag pin working great but sending the ennable pin to high isnt resetting it. any Ideas?

@littlesilvr
Good to hear you got it working.
I haven't ever needed the diag pin, so i have not tested this. But i have read about issues in resetting it.

Thanks for posting this @mrExplore it was a great help in getting set up with a Lolin D1 Mini and TMC2209. Posting here for future reference

I was interested in using UART configuration and control only (no step/pin) so my minimal setup excludes some options.

The later goal for this project is to simply turn on the motor until an endstop is hit (via HomeAssistant / ESPHome) for utilities like opening curtains and blinds. I might also look into using StallGuard to remove the need for endstops at all.

// Microcontroller https://www.wemos.cc/en/latest/d1/d1_mini.html
// TMC driver https://www.aliexpress.com/item/1005002965406330.html
// Stepper Motor 42SHDC3025-24B

#include <TMCStepper.h>           // TMCstepper - https://github.com/teemuatlut/TMCStepper

#define LED_PIN           2       // Visual feedback; onboard LED 
#define EN_PIN            0       // Enable pin
#define DRIVER_ADDRESS    0b00    // TMC2209 Driver address
#define R_SENSE           0.11f   // SilentStepStick series use 0.11
#define SERIAL_PORT       Serial  // TMC2208/TMC2224 HardwareSerial port

#define RMS_CURRENT       600     // Motor RMS current in mA
#define MICROSTEPS        8       // Microsteps; note that MicroPlyer will interpolate to 256
#define SPREADCYCLE       false   // Spreadcycle can have higher RPM but is louder

TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS);   // Create TMC driver

void setup() {
  pinMode(EN_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  SERIAL_PORT.begin(115200);

  digitalWrite(EN_PIN, LOW);            // Enable TMC2209 board

  driver.begin();
  driver.toff(5);                       // Enables driver in software
  driver.rms_current(RMS_CURRENT);      // Set motor RMS current (mA)
  driver.microsteps(MICROSTEPS);        // Set microsteps

  driver.en_spreadCycle(SPREADCYCLE);   // Toggle spreadCycle on TMC2208/2209/2224
  driver.pwm_autoscale(true);           // Needed for stealthChop
}

int32_t curr_speed = 0;
int32_t dest_speed = 1000 * MICROSTEPS;
int32_t acceleration = 10 * MICROSTEPS;

void ramp() {
  digitalWrite(LED_PIN, dest_speed > 0);
  while ((dest_speed > 0 && curr_speed < dest_speed) || (dest_speed < 0 && curr_speed > dest_speed)) {
    curr_speed += acceleration;
    driver.VACTUAL(curr_speed);
    delayMicroseconds(100);
  }
}

void loop() {
  ramp();
  delay(1000);
  dest_speed *= -1;
  acceleration *= -1;
}

dest_speed = 1000 * MICROSTEPS seems about the maximum with StealthChop.
With spreadcycle I can increase this 5-10x but torque suffers at the top end (and it gets louder!).