Using a TMC2209 silent stepper motor driver with an arduino

Maybe post your setup code? That you set up the TMCStepper instances correctly?

If it works with a single driver with different addresses and the UART works i think your stepper board is ok, i think you problem is the way the library is set up.

Though not sure what pin that MS3 switch connects to is, is it the UART pin on TMC drivers? But if the UART is working, its probably ok.

On drv8825, the VIO pin is "fault" pin.. an output pin. Does that board let you hook up the 3.3/5v VIO input?

1 Like

I don't have any documentation for the board. I try to check what the MS3 switch does.

I try to arrange my code to put it here, this is only a simple test code for one motor.

What i doesn't understand why it breaks when i connect the UART wire to more than one TMC...
I check it again, but if i remember well, the STEP/DIR signals reach the drivers, only the UART doesn't work.
Later if everything works, i want to set up the microsteps, the motor current and another changeable parameters via UART in my original program and control three TMC/steppers.

#include "FastAccelStepper.h"
#include <TMCStepper.h>

#define STEP_PIN 22
#define DIR_PIN 23

#define RXD2 16
#define TXD2 17

#define SHAFT true

#define SERIAL_PORT_2 Serial2  // TMC2208/TMC2224 HardwareSerial port - pins 16 & 17
#define DRIVER_ADDRESS_1 0b00    // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f          // E_SENSE for current calc, SilentStepStick series use 0.11

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;

TMC2209Stepper driver(&SERIAL_PORT_2, R_SENSE, DRIVER_ADDRESS_1);  // Create TMC driver

int current = 1000;
int stall = 10;
int accel = 1000;
int max_speed = 3000;
int tcools = (3089838.00 * pow(float(max_speed), -1.00161534)) * 1.5;  //*64 after max speed

int motor_microsteps = 16;
int motor_steps_per_rev = 200;

void setup() {
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);

  // INTITIALIZE SERIAL0 ITERFACE
  Serial.begin(115200);
  delay(500);
  Serial.println(F("---------------------------------------------------"));
  Serial.println(F("UART0 serial output interface intitialized @ 115200"));

  // INITIALIZE SERIAL2 UART FOR TMC2209
  SERIAL_PORT_2.begin(115200);  // INITIALIZE UART TMC2209, SERIAL_8N1, 16, 17
  Serial.println(F("UART2 interface for TMC2209 intitialized   @ 115200"));
  Serial.println(F("---------------------------------------------------"));

  //TMCSTEPPER SETTINGS
  driver.begin();
  driver.pdn_disable(true);      // Use PDN/UART pin for communication
  driver.toff(5);                // [1..15] enable driver in software
  driver.blank_time(24);         // [16, 24, 36, 54]
  driver.I_scale_analog(false);  // Use internal voltage reference
  driver.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."

  driver.mstep_reg_select(true);
  read_ms();
  driver.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  read_ms();

  driver.TCOOLTHRS(tcools);
  driver.seimin(1);  // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver.semin(0);
  driver.iholddelay(3);  // 0 - 15 smooth current drop
  driver.shaft(SHAFT);
  driver.intpol(true);  // interpolate to 256 microsteps
  driver.SGTHRS(stall);

  driver.pwm_autoscale(true);    // Needed for stealthChop
  driver.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver.internal_Rsense(false);
  driver.TPWMTHRS(0);

  engine.init();
  stepper = engine.stepperConnectToPin(STEP_PIN);
  stepper->setDirectionPin(DIR_PIN);
  stepper->setAutoEnable(true);

  stepper->setSpeedInHz(max_speed);
  stepper->setAcceleration(accel);
}

//***********************************************************************
void read_ms() {
  uint16_t msread = driver.microsteps();
  Serial.print("Microsteps: ");
  Serial.println(msread);
}
//***********************************************************************

void loop() {

  driver.microsteps(64);
  read_ms();
  driver.VACTUAL(16000);  //SET SPEED OF MOTOR
  delay(2000);            // MOTOR MOVES 2 SEC THEN STOPS


  driver.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  delay(3000);

  driver.microsteps(32);
  read_ms();
  driver.VACTUAL(-16000);  //SET SPEED OF MOTOR
  delay(2000);             // MOTOR MOVES 2 SEC THEN STOPS

  driver.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  delay(3000);
}

So it stops working when you connect the UART wire to another driver, even if you are not trying to run it? And these drivers have different addresses set up?

"I check it again, but if i remember well, the STEP/DIR signals reach the drivers, only the UART doesn't work."
This code only shows one driver? Im slightly confused about your setup and when the problem happens.

It might be simply if you are not giving power to the drivers, the drivers needs motor power for the UART to work. Connect motor power, before powering the MCU, for both drivers.

1 Like

Sorry for the confusion. So step by step:

  • 3 TMC2209 connected to the ESP32 with STEP/DIR wire
  • 3 enable wire connected to ESP32 GND
  • 3 TMC2209 connected to motor power and motors connected too
  • while this above linked board need it, all three boards connected to 5V/GND (RED/BLACK pins on the board image)
  • 3 TMC2209 TX pin linked and connected to ESP32 GPIO16/17 with the 1K resistor between TX-RX

If i switch on the power button the motor power and the 5V to the black board comes on, and after a few seconds i connect the ESP32 to the computer. It's powered from the USB port.

The address is set up with the microswitches differently, and yes, i don't do anything rather than connect two or three boards and not one. If one board is connected UART works, if more it doesn't.
This code is only a test to test the UART working or not, but i think this is not a software error, it's more of a hardware operating problem that I want to solve, but I can't find a solution anywhere, so I tried to search here to see if anyone has encountered something similar.

(If i have time i write a program to test the three drivers at the same time)

Yeah, the demo code is my original example.. so i know it works. I was just curious about how you set the 3 drivers up in it.

The wiring is so simple, that if one driver works you should have everything set up correctly.

This is why i think it might be a software issue.

Unless there are some extra components on those boards, there are some resistors there.. don't know why. Maybe you should measure if there is some extra resistors on that UART pin.

"TMC2209 TX pin" There is no TX pin on those drivers, the two UART pins are the same.

1 Like

I check the board and resistance tomorrow. I tried to the simplest solution to use three TMC with an ESP32, but it ended up a long building process.

I know that TX RX are the same on this TMC2209 but the TX characters printed near to the pin that i connected to the ESP this is why wrote TX pin.

This is what i have:

That's how it always is, there is always some small mistake somewhere.

Maybe its exactly because these driver boards have the 1k resistor onboard? So you have two resistors... that's why it has TX and RX outputs. That struck to me as odd, why would it have TX & RX markings when on most driver boards its just single PDN output.

So you should use the RX pin that does not have the resistor for single wire UART.

1 Like

This is really confusing but i check it out tomorrow if i have time! Thank you very very much! i hope this is the solution.

If i connect the second and third TMC2209 the resistance is not 1K but much more and the UART breaks?

So i need the wire i have with 1K resistor but to RX on all three TMC2209 and not the TX pin, if i good understand. Or can i use a simple wire without resistor if i use the TX pin, what do you think?

Not easy to understand these motor drivers, every new version is different a little or more, so you have to pay close attention what wire goes where.

Yes, just connect all drivers using the RX pin, single wire.

And you have the 1k resistor on the ESP32, just like you said:
"ESP32 GPIO16/17 with the 1K resistor between TX-RX"

image
This is the single wire UART.

The issue was just that you had a bit uncommon driver board with an extra feature on it.

1 Like

Okay, my test today with variable results:

If i connect only 2 TMC2209 RX to the ESP32 it works, the motors move, i can read the microsteps in serial monitor, but some changes missed, motor not stopping or microsteps doesn't change. But this is better than yesterday.

Hovewer if i connect all the 3 TMC2209 RX pins to ESP32 nothing happens, nothing moves, the UART is dead.

Serial monitor with the two drivers:
Microstep_test

This is my test program today:

#include "FastAccelStepper.h"
#include <TMCStepper.h>

#define STEP_PIN 22
#define DIR_PIN 23

#define RXD2 16
#define TXD2 17

#define SHAFT true

#define SERIAL_PORT_2 Serial2  // TMC2208/TMC2224 HardwareSerial port - pins 16 & 17
#define DRIVER_ADDRESS_1 0b00  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_ADDRESS_2 0b01  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_ADDRESS_3 0b10  // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f          // E_SENSE for current calc, SilentStepStick series use 0.11

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper_s = NULL;
FastAccelStepper *stepper_p = NULL;
FastAccelStepper *stepper_t = NULL;

TMC2209Stepper driver_s(&SERIAL_PORT_2, R_SENSE, DRIVER_ADDRESS_1);  // Create TMC driver
TMC2209Stepper driver_p(&SERIAL_PORT_2, R_SENSE, DRIVER_ADDRESS_2);  // Create TMC driver
TMC2209Stepper driver_t(&SERIAL_PORT_2, R_SENSE, DRIVER_ADDRESS_3);  // Create TMC driver

int current = 1000;
int stall = 10;
int accel = 1000;
int max_speed = 3000;
int tcools = (3089838.00 * pow(float(max_speed), -1.00161534)) * 1.5;  //*64 after max speed

int motor_microsteps = 16;
int motor_steps_per_rev = 200;

void setup() {
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);

  // INTITIALIZE SERIAL0 ITERFACE
  Serial.begin(115200);
  delay(500);
  Serial.println(F("---------------------------------------------------"));
  Serial.println(F("UART0 serial output interface intitialized @ 115200"));

  // INITIALIZE SERIAL2 UART FOR TMC2209
  SERIAL_PORT_2.begin(115200);  // INITIALIZE UART TMC2209, SERIAL_8N1, 16, 17
  Serial.println(F("UART2 interface for TMC2209 intitialized   @ 115200"));
  Serial.println(F("---------------------------------------------------"));

  //TMCSTEPPER SETTINGS - 01
  driver_s.begin();
  driver_s.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_s.toff(5);                // [1..15] enable driver_s in software
  driver_s.blank_time(24);         // [16, 24, 36, 54]
  driver_s.I_scale_analog(false);  // Use internal voltage reference
  driver_s.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."

  driver_s.mstep_reg_select(true);
  read_ms();
  driver_s.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  read_ms();

  driver_s.TCOOLTHRS(tcools);
  driver_s.seimin(1);  // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_s.semin(0);
  driver_s.iholddelay(3);  // 0 - 15 smooth current drop
  driver_s.shaft(SHAFT);
  driver_s.intpol(true);  // interpolate to 256 microsteps
  driver_s.SGTHRS(stall);

  driver_s.pwm_autoscale(true);    // Needed for stealthChop
  driver_s.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_s.internal_Rsense(false);
  driver_s.TPWMTHRS(0);

  //TMCSTEPPER SETTINGS - 02
  driver_p.begin();
  driver_p.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_p.toff(5);                // [1..15] enable driver_p in software
  driver_p.blank_time(24);         // [16, 24, 36, 54]
  driver_p.I_scale_analog(false);  // Use internal voltage reference
  driver_p.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."

  driver_p.mstep_reg_select(true);
  read_ms();
  driver_p.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  read_ms();

  driver_p.TCOOLTHRS(tcools);
  driver_p.seimin(1);  // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_p.semin(0);
  driver_p.iholddelay(3);  // 0 - 15 smooth current drop
  driver_p.shaft(SHAFT);
  driver_p.intpol(true);  // interpolate to 256 microsteps
  driver_p.SGTHRS(stall);

  driver_p.pwm_autoscale(true);    // Needed for stealthChop
  driver_p.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_p.internal_Rsense(false);
  driver_p.TPWMTHRS(0);

  //TMCSTEPPER SETTINGS - 03
  driver_t.begin();
  driver_t.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_t.toff(5);                // [1..15] enable driver_t in software
  driver_t.blank_time(24);         // [16, 24, 36, 54]
  driver_t.I_scale_analog(false);  // Use internal voltage reference
  driver_t.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."

  driver_t.mstep_reg_select(true);
  read_ms();
  driver_t.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  read_ms();

  driver_t.TCOOLTHRS(tcools);
  driver_t.seimin(1);  // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_t.semin(0);
  driver_t.iholddelay(3);  // 0 - 15 smooth current drop
  driver_t.shaft(SHAFT);
  driver_t.intpol(true);  // interpolate to 256 microsteps
  driver_t.SGTHRS(stall);

  driver_t.pwm_autoscale(true);    // Needed for stealthChop
  driver_t.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_t.internal_Rsense(false);
  driver_t.TPWMTHRS(0);
}

//***********************************************************************
void read_ms() {
  uint16_t msread_s = driver_s.microsteps();
  Serial.print("S Microsteps: ");
  Serial.print(msread_s);
  uint16_t msread_p = driver_p.microsteps();
  Serial.print("  P Microsteps: ");
  Serial.print(msread_p);
  uint16_t msread_t = driver_t.microsteps();
  Serial.print("  T Microsteps: ");
  Serial.println(msread_t);
}
//***********************************************************************

void loop() {

  driver_s.microsteps(64);
  driver_p.microsteps(64);
  driver_t.microsteps(64);
  read_ms();
  driver_s.VACTUAL(16000);  //SET SPEED OF MOTOR
  driver_p.VACTUAL(16000);  //SET SPEED OF MOTOR
  driver_t.VACTUAL(16000);  //SET SPEED OF MOTOR
  delay(2000);              // MOTOR MOVES 2 SEC THEN STOPS

  driver_s.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  driver_p.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  driver_t.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  delay(3000);

  driver_s.microsteps(32);
  driver_p.microsteps(32);
  driver_t.microsteps(32);
  read_ms();
  driver_s.VACTUAL(-16000);  //SET SPEED OF MOTOR
  driver_p.VACTUAL(-16000);  //SET SPEED OF MOTOR
  driver_t.VACTUAL(-16000);  //SET SPEED OF MOTOR
  delay(2000);               // MOTOR MOVES 2 SEC THEN STOPS

  driver_s.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  driver_p.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  driver_t.VACTUAL(0);  //STOP MOTOR BY SETTING SPEED TO 0
  delay(3000);
}
1 Like

One thing i was wondering is why did you mention you are using 5v on the VIO?

If you are using 5v logic voltage, it tries to communicate using 5v UART signal. You need to use 3.3v logic voltage. ESP32 is 3.3v logic, never mix 5v logic with 3.3v logic. It might actually fry the UART inputs in the worst case. But the main problem is that the ESP32 speaks at 3.3v and the TMC2209 speaks in 5v. So its a mismatch.

"* while this above linked board need it, all three boards connected to 5V/GND (RED/BLACK pins on the board image)"

This might cause your issues in UART acting weird. I missed this yesterday, was a bit tired.

If switching to 3.3v does not help, you should try using pull up resistors on the UART, i think 10k-100k resistors from 3.3.v to UART TX & RX.

EDIT : Also, take the 3.3v power from the ESP32 dev board. As the UART also needs the ground connection. Im just saying this because you mentioned that you first power the motor & driver logic and then the ESP32. So it seemed that you are not taking the logic power from the ESP32 board. There needs to be a ground connection between the ESP32 & the driver boards.

EDIT: It works!! Changed the logic voltage to 3.3V (from ESP32) and all the three TMC2209 moves with the above test code flawlessly! Thank you very much for your help, I would never have figured this out without you! Now I can work on the program because the communication between the devices is already working.

Thank you again for the detailed help and infos!!

This is the only site where i find this stepper panel i use but with an another driver module.
The infos from here i use 5V for the panel. It's a Raspberry Pi but i checked the pins and it uses 5V.

I try it with 3.3V and i hope i not damaged anything with 5V.
The three panels are working if i only use one of them, so fingers crossed.

https://makersportal.com/blog/raspberry-pi-stepper-motor-control-with-nema-17

1 Like
#include <TMCStepper.h>

// Yes ESP32cam with second serial port, using 3.1 driver and one wire via 1k resister. No dir / step pins connected.

#define SERIAL_PORT Serial2 // HardwareSerial port 
#define DRIVER_ADDRESS 0b00
#define ENABLE_PIN 15

#define STALL_VALUE  15 // [-64..63]
#define RXD2 12
#define TXD2 13

#define R_SENSE 0.11f // Match to your driver
                      // SilentStepStick series use 0.11
                      // UltiMachine Einsy and Archim2 boards use 0.2
                      // Panucatt BSD2660 uses 0.1
                      // Watterott TMC5160 uses 0.075

bool shaft = false;  // ONLY NEEDED FOR CHANGING DIRECTION VIA UART, NO NEED FOR DIR PIN FOR THIS

TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS);
int accel = 0;


void setup() {
  pinMode(ENABLE_PIN ,OUTPUT);
  digitalWrite(ENABLE_PIN ,LOW);

  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);     // INITIALIZE UART to TMC2208
  Serial.begin(115200);
  delay(500);
  Serial.println(F("Serial Initialized"));

  driver.begin();                // Initialize driver
                           
  driver.toff(5);                 // Enables driver in software

  driver.rms_current(600);       // Set motor RMS current
  Serial.print("current set to:");
  Serial.println(driver.rms_current());
 
  driver.microsteps(2);           // Set microsteps to 1/2
  driver.TCOOLTHRS(0xFFFFF); // 20bit max
  driver.TPWMTHRS(0); 
  driver.semin(5);
  driver.semax(2);
  driver.SGTHRS(STALL_VALUE);
  driver.pwm_autoscale(true);   // Needed for stealthChop
  driver.en_spreadCycle(false);   // Toggle spreadCycle for smooth & silent operation
  driver.blank_time(24);
  driver.iholddelay(3); 
}

void loop() {

uint16_t msread=driver.microsteps();
Serial.print(F("Read microsteps via UART to test UART receive : "));   
Serial.println(msread); 
delay(1000);

driver.rms_current(600); 


//Serial.println(F("Wait 3sec and turn current low so you can turn the motor shaft"));
//driver.rms_current(10); 
//delay(3000);
Serial.println("move under tmc2208 uart interface control");
//driver.TPWMTHRS(47);//transition threshold from one pwm mode to the other one, stealthchop to spreadcyccle I think, in clock periods per 1/256 microstep, the internal clock is 12 mhz 

accel=5;
Serial.print("accel:");
Serial.println(accel);
long int spd = 1700;
Serial.print("spd:");
Serial.println(spd);
for (long int i = 100; i <=spd; i = i + accel){
driver.VACTUAL(i); //SET SPEED OF MOTOR
Serial.print("vactual set to:");
Serial.println(driver.VACTUAL());
delay(10);

}

for (long int i = spd; i >=100; i = i - accel){
driver.VACTUAL(i); //SET SPEED OF MOTOR
Serial.print("deccelerating, vactual set to:");
Serial.println(driver.VACTUAL());
delay(40);

}

shaft = !shaft; // REVERSE DIRECTION
driver.shaft(shaft); // SET DIRECTION
driver.VACTUAL(0);
}

Many thanks to monkeyfist (Post #65) which got me over the last hurdle in getting three TMC2209's running nicely on Esp32 with RTOS, reporting positions on the serial monitor with the motors running smoothly!! I decided to add a small display (I2C, 128 x 64 OLED) to the project thinking I could report positions to that just as quickly. I'm new to RTOS and have not been able to get the little display to refresh more than about once or twice per second while the motors are running.

The sketch below is an attempt to isolate the issue without the complexity of the motors, just a few lines of code to keep the motor core (Core0) busy while trying to drive the display with Core1. During the "Init_Oled()" part of this sketch the display appears to refresh in less than 10 ms, but then slows down to ~500-1000 ms while the "TestMotors()" function is running. The Serial monitor refreshes in less than 1ms while the same "TestMotors()" function is running.

The only thing connected to the Esp32 (node32s) is the display with: GND, VCC, SCL (22) and SDA (21)

#include <Arduino.h>
#include <SSD1306Wire.h>
SSD1306Wire oled(0x3C, 21, 22);

long currentPos[] = { 0, 0, 0 };
int myTask = 0;
long startShowTime = 0;
long previousShowTime = 0;
long startFuncTime = 0;
long funcTime = 0;

TaskHandle_t Motor;
TaskHandle_t Print;
TaskHandle_t Input;

// Function declarations for PlatformIO IDE (not needed in Ardduino IDE)
void PrintTask(void *);
void InputTask(void *);
void MotorTask(void *);
void Init_Oled();
void TestMotors();

void setup()
{
	Serial.begin(115200);
	Init_Oled();
	Serial.println(F("Init_Oled completed"));

	xTaskCreatePinnedToCore(PrintTask, "Print", 2000, NULL, tskIDLE_PRIORITY, &Print, 1);
	xTaskCreatePinnedToCore(MotorTask, "Motor", 5000, NULL, 5, &Motor, 0);
	xTaskCreatePinnedToCore(InputTask, "Input", 5000, NULL, 2, &Input, 1);

	Serial.println(F("Setup completed"));
}

void loop()
{
	vTaskDelete(NULL);
}

void Init_Oled()
{
	startFuncTime = millis();
	oled.init();
	oled.flipScreenVertically();
	oled.setFont(ArialMT_Plain_16);

	for (int i = 0; i < 10; i++)
	{
		startShowTime = millis();
		oled.clear();
		funcTime = millis() - startFuncTime;
		oled.drawString(0, 1, "totalTime: " + String(funcTime));
		oled.drawString(0, 16, "Motor 0: " + String(currentPos[0]));
		oled.drawString(0, 31, "Motor 1: " + String(currentPos[1]));
		oled.drawString(0, 46, "showTime: " + String(previousShowTime));
		oled.display();
		previousShowTime = millis() - startShowTime;
		delay(100);
	}
	delay(1000);
	for (int i = 0; i < 10; i++)
	{
		startShowTime = millis();
		char s[64];
		funcTime = millis() - startFuncTime;
		snprintf_P(s, sizeof(s), PSTR("fTime: %ld ms | 0: %ld | 1: %ld | showTime: %ld"), funcTime, currentPos[0], currentPos[1], previousShowTime);
		Serial.println(s);
		previousShowTime = millis() - startShowTime;
		delay(100);
	}
	delay(1000);
}

void PrintTask(void *)
{
	float dTime;
	for (;;)
	{
		// Read values of currentPos[] with this core (1) and show them with either:
		//   1. Serial.print or 
		//   2. 0.96" I2C monochrome OLED display 
		if (myTask == 1)
		{
			startShowTime = millis();
			char s[64];
			funcTime = millis() - startFuncTime;
			snprintf_P(s, sizeof(s), PSTR("fTime: %ld ms | 0: %ld | 1: %ld | showTime: %ld"), funcTime, currentPos[0], currentPos[1], previousShowTime);
			Serial.println(s);
			previousShowTime = millis() - startShowTime;
		}

		if (myTask == 2)
		{
			startShowTime = millis();
			oled.clear();
			funcTime = millis() - startFuncTime;
			oled.drawString(0, 1, "totalTime: " + String(funcTime));
			oled.drawString(0, 16, "Motor 0: " + String(currentPos[0]));
			oled.drawString(0, 31, "Motor 1: " + String(currentPos[1]));
			oled.drawString(0, 46, "showTime: " + String(previousShowTime));
			oled.display();
			previousShowTime = millis() - startShowTime;
		}

		vTaskDelay(100);
	}
}

void InputTask(void *)
{
	for (;;)
	{
		while (Serial.available() > 0)
		{
			char incomingCharacter = Serial.read();
			switch (incomingCharacter)
			{
				case '0':
					myTask = 0;
					break;
				case '1':
					myTask = 1;
					break;
				case '2':
					myTask = 2;
					break;
				default:
					//Nothing
					break;
			}
		}
		vTaskDelay(5);
	}
}

void MotorTask(void *)
{
	for (;;)
	{
		if (myTask > 0) TestMotors();
		vTaskDelay(2000);
	}
}

void TestMotors()
{
	int i;
	float fTemp;
	long idleNeededStart = millis();
	long idleNeededElapsed;
	// Keep this core (0) busy and write values to currentPos[]
	for (i = 1; i < 1000000; i++)
	{
		idleNeededElapsed = millis() - idleNeededStart;
		fTemp = (float)i;
		fTemp = sqrt(fTemp);
		currentPos[0] = i;
		currentPos[1] = (long)(fTemp);
		fTemp = pow(fTemp, 0.163);
		currentPos[2] = (long)(fTemp);
		if (idleNeededElapsed > 1000)
		{
			vTaskDelay(1);
			idleNeededStart = millis();
		}
	}
	myTask = 0;
}

When run, the last few entries of the Serial monitor are:

fTime: 65425 ms | 0: 990653 | 1: 995 | showTime: 0
fTime: 65526 ms | 0: 993339 | 1: 996 | showTime: 0
fTime: 65627 ms | 0: 996017 | 1: 998 | showTime: 0
fTime: 65728 ms | 0: 998696 | 1: 999 | showTime: 0

and the last thing on the OLED monitor is:

totalTime: 114653
Motor 0: 986952
Motor 1: 997
showTime: 1074

Any thoughts would be greatly appreciated, and I apologize if this is too far off subject for this topic.

The print task is set as IDLE_PRIORITY, so it only is engaged when nothing else is running.
You can elevate its priority by setting some number on it. For example the MOTOR task is set as priority 5, the highest number is the highest priority. So maybe set it as 4 for example, so its just under the motors in priority. I usually set these debug print task as idle_priority so they dont take any resources away from the actual task.

The other thing is the memory reserved for the print task, it might be too low for running anything display related.
Its the "2000", you can test setting a larger memory reservarvation for it.

You might want to create a separate task for the OLED, and leave the PRINT task just a a debug terminal printout. Its also good practise in understanding how the RTOS works. Its quite simple when you get the hang of it.

That worked! Thank you for your time and expertise.

Good to hear, those priorities and choosing cores are one of the best aspects of RTOS. The last number is the core, 0 or 1. And the fact that you can kill & restart any of those task at any point.

Ones you get used to RTOS, you really dont want to go back. Its a real.. you know, a game changer :slight_smile:

Hi @monkeyfist!

Update:
(When this happens the controller loses actual positions i think, because the start and end point of the steppers that set up at the start changes. For example the tilt motor starts centered, moves back and forth max 200 steps, from center. But if this uncontrolled movements occur the center point is moving away from the starting center point. Updated the code!

You already solved one of my problems here with the uart communication, now there is another error of a different nature.

I have a strange situation and after a day of debugging doesn't find the cause of it.
I',m using three TMC2209 with fastaccelstepper library on ESP32. Now everything works, except i have a strange uncontrolled movement on the axes. I control the steppers with an RC controller, and if i have stop moving the input joysticks the steppers stopping and after a while the motors make movements (few random steps) back and forth. So not stopping the last position.
My uart is working because if i set the current or microsteps i see the result, but this error ruins everything for me.

I commented out the wifi and most of the code, but doesnt find the root of this.

Can you please help me with this situation?
Any help would be great.

Thank you very very much if you have some time to help me.

#include "SumdRx.h"
#include "FastAccelStepper.h"
#include <TMCStepper.h>
#include <HardwareSerial.h>

#include <WiFi.h>
#include <WebServer.h>
#include <WebSocketsServer.h>
#include <ArduinoSort.h>

#define SLIDE_MOTOR_STEP_PIN 26
#define SLIDE_MOTOR_DIRECTION_PIN 27
#define PAN_MOTOR_STEP_PIN 22
#define PAN_MOTOR_DIRECTION_PIN 23
#define TILT_MOTOR_STEP_PIN 19
#define TILT_MOTOR_DIRECTION_PIN 21

#define SLIDE_MAX_SPEED 700
#define SLIDE_MAX_ACCELERATION 8000
#define PAN_MAX_SPEED 500
#define PAN_MAX_ACCELERATION 8000
#define TILT_MAX_SPEED 250
#define TILT_MAX_ACCELERATION 8000

#define RXD2 16
#define TXD2 17

#define SERIAL_PORT_2 Serial2  // TMC2208/TMC2224 HardwareSerial port - pins 16 & 17
#define DRIVER_S_ADDRESS 0b00  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_P_ADDRESS 0b01  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_T_ADDRESS 0b10  // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f          // E_SENSE for current calc, SilentStepStick series use 0.11

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *slide_stepper = NULL;
FastAccelStepper *pan_stepper = NULL;
FastAccelStepper *tilt_stepper = NULL;

TMC2209Stepper driver_s(&SERIAL_PORT_2, R_SENSE, DRIVER_S_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_p(&SERIAL_PORT_2, R_SENSE, DRIVER_P_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_t(&SERIAL_PORT_2, R_SENSE, DRIVER_T_ADDRESS);  // Create TMC driver

// //*Web things***********************************//
//-----------------------------------------------
const char *ssid = "ESP32Slider";       // Your WiFi AP SSID
const char *password = "testpassword";  // Your WiFi Password
//-----------------------------------------------
WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
String JSONtxt;
//-----------------------------------------------
#include "webpage.h"
#include "functions.h"
// //**********************************************//

const int ReedSensor = 35;  //REED switch port
int ReedState;              // 0 close - 1 open switch

// RC Channel order and functions //
// CH1 Stepper 1 Axle S Slide (Only negative positions enabled, the ZERO is the limitswitch)
// CH2 Stepper 2 Axle P Pan
// CH3 Stepper 3 Axle T Tilt
// CH4 Speed Control for AutoMOve
// CH5 Set IN/OUT position if CH8 ENABLED, the In/Out block is selected in CH7 (1,2,3)
// CH6 Start / Stop / ReSet AUTO MOve according to CH7 setting (1,2,3), 3 sec long push resets all axles to ZERO
// CH7 Set IN/OUT SET (1,2,3)
// CH8 UP In/Out setting ENABLED, center nothing, LOW Move to In or OUT, selected by CH5 - CH8
// CH9 Reset board

//TMC2209 variables
int current = 1000;
int stall = 255;
int motor_microsteps = 4;
int motor_steps_per_rev = 200;
int msread;
int mpread;
int mtread;

//Position variables
int ActualInOut = 1;
int InandOut = 0;  //IN-OUT points active or not
boolean SetInOut = false;
long SInPoint[4];          //Slide IN
long PInPoint[4];          //Pan IN
long TInPoint[4];          //Tilt IN
long SOutPoint[4];         //Slide OUT
long POutPoint[4];         //Pan OUT
long TOutPoint[4];         //Tilt OUT
boolean InSet[4];          //IN point is stored
boolean OutSet[4];         //OUT point is stored
boolean automove = false;  //automove is ACTIVE or not
long gotoposition[3];      // An array to store the In or Out position for each stepper motor
int SMS;                   //Slide Stepper speed from pwm[0]
int PMS;                   //Pan Stepper speed from pwm[1]
int TMS;                   //Tilt Stepper speed from pwm[2]

//*** Speed calculation variables ***
long pan_steps_to_move = 0;
long tilt_steps_to_move = 0;
long slide_steps_to_move = 0;
long steps_to_move[3] = { 0, 0, 0 };
double pan_time_motion_complete = 0;
double tilt_time_motion_complete = 0;
double slide_time_motion_complete = 0;
int pan_speed_motion_complete = 0;
int tilt_speed_motion_complete = 0;
int slide_speed_motion_complete = 0;
//*** Speed calculation end

//Variables for Homing
long initial_homing = 1;  // Used to Home Stepper at startup

//Speed variables
int inOutSpeed = 0;

//PWM values array
int pwm[9];

//RC read timing variables
unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 1000;  //the value is a number of milliseconds

//HardwareSerial SerialPort(1);  // use UART1

//remote control status
bool failSafe = true;
SumdRx *sumdDecoder;

void setup() {

  pinMode(SLIDE_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(SLIDE_MOTOR_STEP_PIN, OUTPUT);
  pinMode(PAN_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(PAN_MOTOR_STEP_PIN, OUTPUT);
  pinMode(TILT_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(TILT_MOTOR_STEP_PIN, OUTPUT);

  // INTITIALIZE SERIAL0 ITERFACE
  Serial.begin(115200);
  delay(500);
  Serial.println(F("---------------------------------------------------"));
  Serial.println(F("UART0 serial output interface intitialized @ 115200"));

  //*********************************************************************//
  //Server is accessible at 192.168.4.1/webserial URL.
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);
  server.on("/", handleRoot);
  server.begin();
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
  //*********************************************************************//

  Serial1.begin(115200, SERIAL_8N1, 33, -1);

  // INITIALIZE SERIAL2 UART FOR TMC2209
  SERIAL_PORT_2.begin(115200);  // INITIALIZE UART TMC2209, SERIAL_8N1, 16, 17
  Serial.println(F("UART2 interface for TMC2209 intitialized   @ 115200"));
  Serial.println(F("---------------------------------------------------"));

  //Attach SUMD decoder to the serial port
  sumdDecoder = new SumdRx();
  delay(1000);

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_s.begin();
  driver_s.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_s.toff(3);                // [1..15] enable driver_s in software
  driver_s.blank_time(24);         // [16, 24, 36, 54]
  driver_s.I_scale_analog(false);  // Use internal voltage reference
  driver_s.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_s.mstep_reg_select(true);
  driver_s.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_s.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_s.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_s.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_s.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_s.intpol(true);                  // interpolate to 256 microsteps
  driver_s.SGTHRS(stall);
  driver_s.pwm_autoscale(true);    // Needed for stealthChop
  driver_s.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_s.internal_Rsense(false);
  driver_s.TCOOLTHRS(0);
  driver_s.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_p.begin();
  driver_p.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_p.toff(3);                // [1..15] enable driver_p in software
  driver_p.blank_time(24);         // [16, 24, 36, 54]
  driver_p.I_scale_analog(false);  // Use internal voltage reference
  driver_p.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_p.mstep_reg_select(true);
  driver_p.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_p.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_p.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_p.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_p.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_p.intpol(true);                  // interpolate to 256 microsteps
  driver_p.SGTHRS(stall);
  driver_p.pwm_autoscale(true);    // Needed for stealthChop
  driver_p.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_p.internal_Rsense(false);
  driver_p.TCOOLTHRS(0);
  driver_p.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_t.begin();
  driver_t.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_t.toff(3);                // [1..15] enable driver_t in software
  driver_t.blank_time(24);         // [16, 24, 36, 54]
  driver_t.I_scale_analog(false);  // Use internal voltage reference
  driver_t.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_t.mstep_reg_select(true);
  driver_t.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_t.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_t.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_t.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_t.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_t.intpol(true);                  // interpolate to 256 microsteps
  driver_t.SGTHRS(stall);
  driver_t.pwm_autoscale(true);    // Needed for stealthChop
  driver_t.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_t.internal_Rsense(false);
  driver_t.TCOOLTHRS(0);
  driver_t.TPWMTHRS(0);
  //************************************************************************************************************************//

  engine.init();
  pan_stepper = engine.stepperConnectToPin(PAN_MOTOR_STEP_PIN);
  tilt_stepper = engine.stepperConnectToPin(TILT_MOTOR_STEP_PIN);
  slide_stepper = engine.stepperConnectToPin(SLIDE_MOTOR_STEP_PIN);
  if (pan_stepper) {
    pan_stepper->setDirectionPin(PAN_MOTOR_DIRECTION_PIN);
    pan_stepper->setAutoEnable(true);

    pan_stepper->setSpeedInHz(0);            // steps/s
    pan_stepper->setAcceleration(PAN_MAX_ACCELERATION);  // steps/s²
  }

  if (tilt_stepper) {
    tilt_stepper->setDirectionPin(TILT_MOTOR_DIRECTION_PIN);
    tilt_stepper->setAutoEnable(true);

    tilt_stepper->setSpeedInHz(0);            // steps/s
    tilt_stepper->setAcceleration(TILT_MAX_ACCELERATION);  // steps/s²
  }
  if (slide_stepper) {
    slide_stepper->setDirectionPin(SLIDE_MOTOR_DIRECTION_PIN);
    slide_stepper->setAutoEnable(true);

    slide_stepper->setSpeedInHz(0);            // steps/s
    slide_stepper->setAcceleration(SLIDE_MAX_ACCELERATION);  // steps/s²
  }

  for (int i = 0; i < 5; i++) {  //SET all IN and OUT points to zero
    InSet[i] = false;
    OutSet[i] = false;
  }

  //pinMode(ReedSensor, INPUT_PULLUP);
  /*

      // Move the slider to the initial position - homing
      // Start Homing procedure of Stepper Motor at startup
      println("Stepper is Homing . . .");
      ReedState = digitalRead(ReedSensor);
      print("LiMiT SW: ");
      println(ReedState);

      delay(5000);

      while (digitalRead(ReedSensor)) {  // Make the Stepper move CCW until the switch is activated
        stepper1.moveTo(initial_homing); // Set the position to move to
        initial_homing++;                // Decrease by 1 for next move if needed
        stepper1.run();                  // Start moving the stepper
        delay(5);
      }

      stepper1.setCurrentPosition(0);    // Set the current position as zero for now
      stepper1.setMaxSpeed(100.0);       // Set Max Speed of Stepper (Slower to get better accuracy)
      initial_homing = -1;

      ReedState = digitalRead(ReedSensor);
      print("LiMiT SW: ");
      println(ReedState);

      while (!digitalRead(ReedSensor)) { // Make the Stepper move CW until the switch is deactivated
        stepper1.moveTo(-2000);
        stepper1.run();
        initial_homing--;
        delay(5);
      }
    stepper1.setCurrentPosition(0);
    println("Homing Completed");
    println("");
    stepper1.setMaxSpeed(max_speed); // Set Max Speed of Stepper (Slower to get better accuracy)*/
  startMillis = millis();  //initial start time
  pan_stepper->setCurrentPosition(0);
  tilt_stepper->setCurrentPosition(0);
  slide_stepper->setCurrentPosition(0);
}

//***********************************************************************
void read_ms() {
  msread = driver_s.microsteps();
  mpread = driver_p.microsteps();
  mtread = driver_t.microsteps();
}
//***********************************************************************

void loop() {

  webSocket.loop();
  server.handleClient();
  //-----------------------------------------------
  static unsigned long l = 0;
  unsigned long t = millis();
  if ((t - l) > 1000) {
    //-----------------------------------------------
    String pwm0valString = String(pwm[0]);
    String pwm1valString = String(pwm[1]);
    String pwm2valString = String(pwm[2]);
    String pwm3valString = String(pwm[3]);
    String pwm4valString = String(pwm[4]);
    String pwm5valString = String(pwm[5]);
    String pwm6valString = String(pwm[6]);
    String pwm7valString = String(pwm[7]);
    String pwm8valString = String(pwm[8]);
    String slidespdvalString = String(SMS);
    String panspdvalString = String(PMS);
    String tiltspdvalString = String(TMS);
    //-----------------------------------------------
    JSONtxt = "{\"pwm0\":\"" + pwm0valString + "\",";
    JSONtxt += "\"pwm1\":\"" + pwm1valString + "\",";
    JSONtxt += "\"pwm2\":\"" + pwm2valString + "\",";
    JSONtxt += "\"pwm3\":\"" + pwm3valString + "\",";
    JSONtxt += "\"pwm4\":\"" + pwm4valString + "\",";
    JSONtxt += "\"pwm5\":\"" + pwm5valString + "\",";
    JSONtxt += "\"pwm6\":\"" + pwm6valString + "\",";
    JSONtxt += "\"pwm7\":\"" + pwm7valString + "\",";
    JSONtxt += "\"pwm8\":\"" + pwm8valString + "\",";
    JSONtxt += "\"slidespd\":\"" + slidespdvalString + "\",";
    JSONtxt += "\"panspd\":\"" + panspdvalString + "\",";
    JSONtxt += "\"tiltspd\":\"" + tiltspdvalString + "\"}";
    webSocket.broadcastTXT(JSONtxt);
  }

  GetData();  //read SUMD from Serial RX

  CheckCH1();
  CheckCH2();
  CheckCH3();
  pwm[3] = sumdDecoder->channel[3];
  pwm[4] = sumdDecoder->channel[4];
  pwm[5] = sumdDecoder->channel[5];
  pwm[6] = sumdDecoder->channel[6];
  pwm[7] = sumdDecoder->channel[7];
  pwm[8] = sumdDecoder->channel[8];
  /*CheckCH4();
  if (pwm[4] > 1550 || pwm[4] < 1450) {
    CheckCH5();
  } else if (pwm[5] > 1550 || pwm[5] < 1450) {
    CheckCH6();
  }
  CheckCH7();
  CheckCH8();
  CheckCH9();*/

  // currentMillis = millis();                   //get the current "time" (actually the number of milliseconds since the program started)
  // if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  // {
  //   startMillis = currentMillis;  //IMPORTANT to save the start time
  // }
}

void CheckCH1() {
  pwm[0] = sumdDecoder->channel[0];                                       // Get pwm[0]
  SMS = abs(map(pwm[0], 1000, 2000, -SLIDE_MAX_SPEED, SLIDE_MAX_SPEED));  // MAP speed from PWM
  slide_stepper->setSpeedInHz(SMS);
  if (pwm[0] > 1510) {
    if (slide_stepper->getCurrentPosition() >= -200) {
      slide_stepper->setSpeedInHz(0);
      slide_stepper->stopMove();
    } else {
      slide_stepper->moveTo(-200);
    }
  } else if (pwm[0] < 1490) {
    if (slide_stepper->getCurrentPosition() <= -5000) {
      slide_stepper->setSpeedInHz(0);
      slide_stepper->stopMove();
    } else {
      slide_stepper->moveTo(-5000);
    }
  } else {
    slide_stepper->setSpeedInHz(0);
    slide_stepper->stopMove();
  }
}

void CheckCH2() {
  pwm[1] = sumdDecoder->channel[1];
  PMS = abs(map(pwm[1], 1000, 2000, -PAN_MAX_SPEED, PAN_MAX_SPEED));  // Increase speed as moving
  pan_stepper->setSpeedInHz(PMS);
  if (pwm[1] > 1520) {
    if (pan_stepper->getCurrentPosition() >= 200) {
      pan_stepper->setSpeedInHz(0);
      pan_stepper->stopMove();
    } else {
      pan_stepper->moveTo(200);
    }
  } else if (pwm[1] < 1480) {
    if (pan_stepper->getCurrentPosition() <= -200) {
      pan_stepper->setSpeedInHz(0);
      pan_stepper->stopMove();
    } else {
      pan_stepper->moveTo(-200);
    }
  } else {
    pan_stepper->setSpeedInHz(0);
    pan_stepper->stopMove();
  }
}

void CheckCH3() {
  pwm[2] = sumdDecoder->channel[2];
  TMS = abs(map(pwm[2], 1000, 2000, -TILT_MAX_SPEED, TILT_MAX_SPEED));  // Increase speed as moving
  tilt_stepper->setSpeedInHz(TMS);
  if (pwm[2] > 1520) {
    if (tilt_stepper->getCurrentPosition() >= 200) {
      tilt_stepper->setSpeedInHz(0);
      tilt_stepper->stopMove();
    } else {
      tilt_stepper->moveTo(200);
    }
  } else if (pwm[2] < 1480) {
    if (tilt_stepper->getCurrentPosition() <= -200) {
      tilt_stepper->setSpeedInHz(0);
      tilt_stepper->stopMove();
    } else {
      tilt_stepper->moveTo(-200);
    }
  } else {
    tilt_stepper->setSpeedInHz(0);
    tilt_stepper->stopMove();
  }
}

void CheckCH4() {
  inOutSpeed = pwm[3];
  inOutSpeed = map(inOutSpeed, 1000, 2000, 0, SLIDE_MAX_SPEED);  // Increase speed as moving
}

void CheckCH5() {
  if (pwm[4] < 1490 && SetInOut == true) {                        // Set IN position
    SInPoint[ActualInOut] = slide_stepper->getCurrentPosition();  // Set the IN position for steppers
    PInPoint[ActualInOut] = pan_stepper->getCurrentPosition();
    TInPoint[ActualInOut] = tilt_stepper->getCurrentPosition();
    InSet[ActualInOut] = true;
  } else if (pwm[4] > 1510 && SetInOut == true) {                  // Set OUT position
    SOutPoint[ActualInOut] = slide_stepper->getCurrentPosition();  //  Set the OUT Points for steppers
    POutPoint[ActualInOut] = pan_stepper->getCurrentPosition();
    TOutPoint[ActualInOut] = tilt_stepper->getCurrentPosition();
    OutSet[ActualInOut] = true;
    InandOut = 0;
  }
}

void CheckCH6() {
  if (pwm[5] > 1510 && OutSet[ActualInOut] == true) {
    automove = true;
    switch (InandOut) {
      case 0:  // Move to IN position / go to case 1
        // Place the IN position into the Array
        gotoposition[0] = SInPoint[ActualInOut];
        gotoposition[1] = PInPoint[ActualInOut];
        gotoposition[2] = TInPoint[ActualInOut];
        CalcMoves();
        if (slide_stepper->getCurrentPosition() != gotoposition[0]) {
          slide_stepper->moveTo(gotoposition[0]);
        }
        if (pan_stepper->getCurrentPosition() != gotoposition[1]) {
          pan_stepper->moveTo(gotoposition[1]);
        }
        if (tilt_stepper->getCurrentPosition() != gotoposition[2]) {
          tilt_stepper->moveTo(gotoposition[2]);
        }
        if (!(pan_stepper->isRunning()) && !(tilt_stepper->isRunning()) && !(slide_stepper->isRunning())) {
          delay(500);
          InandOut = 1;
        }
        break;

      case 1:  // Move to OUT position / go back to case 0
        // Place the OUT position into the Array
        gotoposition[0] = SOutPoint[ActualInOut];
        gotoposition[1] = POutPoint[ActualInOut];
        gotoposition[2] = TOutPoint[ActualInOut];
        CalcMoves();
        if (slide_stepper->getCurrentPosition() != gotoposition[0]) {
          slide_stepper->moveTo(gotoposition[0]);
        }
        if (pan_stepper->getCurrentPosition() != gotoposition[1]) {
          pan_stepper->moveTo(gotoposition[1]);
        }
        if (tilt_stepper->getCurrentPosition() != gotoposition[2]) {
          tilt_stepper->moveTo(gotoposition[2]);
        }
        if (!(pan_stepper->isRunning()) && !(tilt_stepper->isRunning()) && !(slide_stepper->isRunning())) {
          InandOut = 0;
          delay(500);
        }
        break;
    }
  } else if (pwm[5] < 1490 && pwm[7] > 1510) {  // If we hold RC 6 longer then half second, reset the in and out positions
    delay(500);
    if (pwm[5] < 1490) {           //Exit from autoMOve / reset In-Out positions
      SInPoint[ActualInOut] = 0;   //Slide
      PInPoint[ActualInOut] = 0;   //Pan
      TInPoint[ActualInOut] = 0;   //Tilt
      SOutPoint[ActualInOut] = 0;  //Slide
      POutPoint[ActualInOut] = 0;  //Pan
      TOutPoint[ActualInOut] = 0;  //Tilt
      InSet[ActualInOut] = false;
      OutSet[ActualInOut] = false;
      automove = false;
      //SetSpeed();
    }
  }

  if (pwm[5] < 1490 && pwm[7] < 1510) {  // If we hold RC 6 longer then three second, reset to ZERO point
    delay(3000);
    if (pwm[5] < 1490) {  //Reset to ZERO all axles
      gotoposition[0] = 0;
      gotoposition[1] = 0;
      gotoposition[2] = 0;
      slide_stepper->setSpeedInHz(inOutSpeed);
      pan_stepper->setSpeedInHz(inOutSpeed);
      tilt_stepper->setSpeedInHz(inOutSpeed);
      automove = false;
      delay(200);
      //SetSpeed();
    }
  }
}

//***************************************************************************************
void set_Speed(char _pts) {
  switch (_pts) {
    case 'p':
      {
        pan_time_motion_complete = double(steps_to_move[2]) / double(PAN_MAX_SPEED);
        pan_speed_motion_complete = PAN_MAX_SPEED;
        tilt_speed_motion_complete = abs(tilt_steps_to_move) / pan_time_motion_complete;
        slide_speed_motion_complete = abs(slide_steps_to_move) / pan_time_motion_complete;
        break;
      }
    case 't':
      {
        tilt_time_motion_complete = double(steps_to_move[2]) / double(TILT_MAX_SPEED);
        tilt_speed_motion_complete = TILT_MAX_SPEED;
        pan_speed_motion_complete = abs(pan_steps_to_move) / tilt_time_motion_complete;
        slide_speed_motion_complete = abs(slide_steps_to_move) / tilt_time_motion_complete;
        break;
      }
    case 's':
      {
        slide_time_motion_complete = double(steps_to_move[2]) / double(SLIDE_MAX_SPEED);
        slide_speed_motion_complete = SLIDE_MAX_SPEED;
        pan_speed_motion_complete = abs(pan_steps_to_move) / slide_time_motion_complete;
        tilt_speed_motion_complete = abs(tilt_steps_to_move) / slide_time_motion_complete;
        break;
      }
  }
}

void CalcMoves() {
  pan_steps_to_move = PInPoint[ActualInOut] - POutPoint[ActualInOut];
  tilt_steps_to_move = TInPoint[ActualInOut] - TOutPoint[ActualInOut];
  slide_steps_to_move = SInPoint[ActualInOut] - SOutPoint[ActualInOut];

  steps_to_move[0] = abs(pan_steps_to_move);
  steps_to_move[1] = abs(tilt_steps_to_move);
  steps_to_move[2] = abs(slide_steps_to_move);

  sortArray(steps_to_move, 3);

  if (steps_to_move[2] == abs(pan_steps_to_move)) {
    //Serial.println("most steps 2 move on pan axis");
    set_Speed('p');
  }
  if (steps_to_move[2] == abs(tilt_steps_to_move)) {
    //Serial.println("most steps 2 move on tilt axis");
    set_Speed('t');
  }
  if (steps_to_move[2] == abs(slide_steps_to_move)) {
    //Serial.println("most steps 2 move on slide axis");
    set_Speed('s');
  }
  pan_stepper->setSpeedInHz(pan_speed_motion_complete);
  tilt_stepper->setSpeedInHz(tilt_speed_motion_complete);
  slide_stepper->setSpeedInHz(slide_speed_motion_complete);
}
//***************************************************************************************

void CheckCH7() {
  if (pwm[6] > 1510) {  // Set 1st InOut position
    ActualInOut = 1;
  } else if (pwm[6] > 1490 && pwm[6] < 1510) {  // Set 2rd InOut position
    ActualInOut = 2;
  } else if (pwm[6] < 1490) {  // Set 3nd InOut position
    ActualInOut = 3;
  }
}

void CheckCH8() {
  if (pwm[7] < 1490) {  // Activate the MoveToIn or MoveToOut sequence
    switch (ActualInOut) {
      case 1:
        if (pwm[4] < 1490) {
          inOutSpeed = pwm[3];  // Auto speed potentiometer
          // Place the 1st IN position into the Array
          gotoposition[0] = SInPoint[ActualInOut];
          gotoposition[1] = PInPoint[ActualInOut];
          gotoposition[2] = TInPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        } else if (pwm[4] > 1490) {
          inOutSpeed = pwm[3];
          // Place the 1st OUT position into the Array
          gotoposition[0] = SOutPoint[ActualInOut];
          gotoposition[1] = POutPoint[ActualInOut];
          gotoposition[2] = TOutPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        }
        break;
      case 2:
        if (pwm[4] < 1490) {
          inOutSpeed = pwm[3];  // Auto speed potentiometer
          // Place the 2nd IN position into the Array
          gotoposition[0] = SInPoint[ActualInOut];
          gotoposition[1] = PInPoint[ActualInOut];
          gotoposition[2] = TInPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        } else if (pwm[4] > 1490) {
          inOutSpeed = pwm[3];
          // Place the 2nd OUT position into the Array
          gotoposition[0] = SOutPoint[ActualInOut];
          gotoposition[1] = POutPoint[ActualInOut];
          gotoposition[2] = TOutPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        }
        break;
      case 3:
        if (pwm[4] < 1490) {
          inOutSpeed = pwm[3];  // Auto speed potentiometer
          // Place the 3rd IN position into the Array
          gotoposition[0] = SInPoint[ActualInOut];
          gotoposition[1] = PInPoint[ActualInOut];
          gotoposition[2] = TInPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        } else if (pwm[4] > 1490) {
          inOutSpeed = pwm[3];
          // Place the 3rd OUT position into the Array
          gotoposition[0] = SOutPoint[ActualInOut];
          gotoposition[1] = POutPoint[ActualInOut];
          gotoposition[2] = TOutPoint[ActualInOut];
          slide_stepper->setSpeedInHz(inOutSpeed);
          pan_stepper->setSpeedInHz(inOutSpeed);
          tilt_stepper->setSpeedInHz(inOutSpeed);
          delay(200);
        }
        break;
    }
  } else if (pwm[7] > 1510) {
    SetInOut = true;
  } else if (pwm[7] > 1490 && pwm[7] < 1510) {
    SetInOut = false;
  }
}

void CheckCH9() {
  if (pwm[8] > 1900) {
    pan_stepper->setCurrentPosition(0);
    tilt_stepper->setCurrentPosition(0);
    slide_stepper->setCurrentPosition(0);
  }
}

/*void SetSpeed() {
  // Set speed values for the steppers
  slide_stepper->setSpeedInHz(max_speed);  //SPEED = Steps / second
  pan_stepper->setSpeedInHz(max_speed);    //SPEED = Steps / second
  tilt_stepper->setSpeedInHz(max_speed);   //SPEED = Steps / second
}*/

void GetData() {
  uint8_t index;
  //process sumd input, due to lead time from previous decoding, buffer may overflow and being corrupted -> reset
  sumdDecoder->reset();
  while (Serial1.available() > 0) sumdDecoder->add(Serial1.read());

  //display failsafe status on console
  if (failSafe && !sumdDecoder->failSafe()) {
    failSafe = false;
    //println("Reception OK");
  } else if (!failSafe && sumdDecoder->failSafe()) {
    failSafe = true;
    //println("Reception Lost");
  }
  //  if (!sumdDecoder->failSafe()) {
  //    for (index = 0; index < sumdDecoder->channelRx; index++) { //display channel status
  //      print(sumdDecoder->channel[index]);
  //      print(" ");
  //    }
  //    println();
  //  }
  delay(25);
}

Ok, the problem is more interesting than i think before.

My late night test results:

  1. I have disconnected and grounded the step pins from the esp32 to TMC drivers one after another -> nothing happens, random moves with the linked test code.
  2. disconnected one TMCs motor power from the power supply, (the another two TMC drivers are connected!) and with all wires (STEP, DIR, UART, Logic Power (3,3V)) connected -> few very small moves for 1-2 secs at the start then stop and stable motors
  3. disconnected one TMCs logic power from the power supply (the another two TMC drivers are connected!) and with all wires (STEP, DIR, UART, Logic Power (3,3V)) connected -> no movement at all on any axis, the motors hold their position, and if I want to move the axes, the motors hold their position firmly.

For the last two tests, it doesn't matter which stepper motor I switch off the motor's power supply or the logic supply voltage. The situation is the same! They react exactly the same!

The ESP32 is powered from a 5V supply, if this matter, the 5V voltage comes to the ESP32 from the power supply, then all the power is output from the ESP32, the 3.3V is also to all the TMC2209.
If I understand correctly, the ESP32 sends out a 3.3V signal on the output pins and for UART communication the voltage must be the same as the logic supply voltage of the TMC2209, so I use 3.3V for this purpose. I read this somewhere when I was looking for the correct wiring and setup between ESP32 -> TMC2209.

In short, as I see the situation, two controllers work flawlessly, but three do not. It's incomprehensible to me for now. Of course, I should also do a test by using the radio remote control to see if it's perfect with the control or not, I'll try to arrange that tomorrow as well!

When you finish the movement ordered by the ESP32, are you disabling the 2209's or are you leaving them enabled? The easiest way to ensure that there is no random movement is to only pull the enable pins low when movement is going to be ordered from the ESP. The timing on the 2209 is such that only a few nanoseconds are required between pulling the enable pin low and pulling the step pin high to step the motor. If you are leaving the 2209's enabled to use the holding power of the motor to keep them in place, you might be getting errant signals on your step lines that eventually get high enough to cause an errant step. You could always switch to shielded cables to keep that from happening. but with a 2209, the smallest microstep you're going to get is 1/8th. So, with one errant step you are only talking 0.225 degrees per step (1600 steps per revolution). Your setup is such that 1/8th of a step is noticeably moving you off-target?

If you are only using the UART connection and using the UART to move the motor, are you pulling the step lines low or are you leaving them floating (unconnected). If you aren't using the step/dir pins, I would pull the step pins to ground to keep them from stepping the motor inappropriately.