New Stepper Motor Library

I am moving up to a 4.1 from a 3.6 with an existing program that uses the RotateControl in TeensyStep library (which works mavelously using the 3.6, BTW.)
Of course, the compiler chokes on RotateControl as the structure of the TeensyStep4 library is quite different than the old TeensyStep.

The existing program uses these lines:

#define Y_STEP_PIN 23 // Spool on Y-axis stepper
#define Y_DIR_PIN 18 // Spool direction
float SpoolPPR = (360.0 / 1.8) * 16.0; // (360° / angle per steps°) * microsteps
float SpoolPPS = SpoolPPR / 60.0; // 1.0 RPM base speed for Spool

Stepper motor_1(Y_STEP_PIN,Y_DIR_PIN );
RotateControl Spool;
motor_1
.setMaxSpeed(SpoolPPS) // steps/s
.setAcceleration(20000); // steps/s^2
Spool.rotateAsync(motor_1);
Spool.overrideSpeed(SpoolRPM); // Sets the spool speed as the program is running and is called as the spool RPM needs to change

This asynchronously runs the the stepper quite nicely with a 3.6 under the old TeensyStep library. :)

A simple example snippet, translated into the the new format would be enormously helpful. Enormously.

Bill D.
 
overrideSpeed is not yet implemented in TeensyStep4. I'll see if I can do a test version. Give me a day or two.
 
Hi Luni,

Is there some clever way to get TeensyStep4 (as written) to asynchronously run motors and alter the speed on the fly?

Can I update the value in setMaxSpeed() in TeensyStep4 while the motor is operating?

Bill D.
 
Hi Luni,
Is there some clever way to get TeensyStep4 (as written) to asynchronously run motors and alter the speed on the fly?
Can I update the value in setMaxSpeed() in TeensyStep4 while the motor is operating?
Bill D.

@killacycle: Pushed a quick implementation of overrideSpeed. Can you give it a try?
 
TeensyStep4 library

@killacycle: Pushed a quick implementation of overrideSpeed. Can you give it a try?

Hmm... Won't compile. :-(

I think I might be missing some portion of the library perhaps? Is there an additional library that needs to be included in my code?

If you were to post very simple example code that uses TeensyStep4 and its new subroutine(s) might be helpful.

Compiler says:
>>>>>>>>>>>>>>>>>>>>>>>>

In file included from C:\Users\NEW\Documents\Arduino\libraries\TeensyStep4-main\src\teensystep4.cpp:7:0:
C:\Users\NEW\Documents\Arduino\libraries\TeensyStep4-main\src\timers/Teensy4/TMR/TMR.h:176:51: error: 'IMXRT_TMR1_ADDRESS' was not declared in this scope
static constexpr uintptr_t tmrAddresses[]{IMXRT_TMR1_ADDRESS, IMXRT_TMR2_ADDRESS, IMXRT_TMR3_ADDRESS, IMXRT_TMR4_ADDRESS};
^
C:\Users\NEW\Documents\Arduino\libraries\TeensyStep4-main\src\timers/Teensy4/TMR/TMR.h:176:71: error: 'IMXRT_TMR2_ADDRESS' was not declared in this scope
static constexpr uintptr_t tmrAddresses[]{IMXRT_TMR1_ADDRESS, IMXRT_TMR2_ADDRESS, IMXRT_TMR3_ADDRESS, IMXRT_TMR4_ADDRESS};
^
C:\Users\NEW\Documents\Arduino\libraries\TeensyStep4-main\src\timers/Teensy4/TMR/TMR.h:176:91: error: 'IMXRT_TMR3_ADDRESS' was not declared in this scope
static constexpr uintptr_t tmrAddresses[]{IMXRT_TMR1_ADDRESS, IMXRT_TMR2_ADDRESS, IMXRT_TMR3_ADDRESS, IMXRT_TMR4_ADDRESS};
^
C:\Users\NEW\Documents\Arduino\libraries\TeensyStep4-main\src\timers/Teensy4/TMR/TMR.h:176:111: error: 'IMXRT_TMR4_ADDRESS' was not declared in this scope
static constexpr uintptr_t tmrAddresses[]{IMXRT_TMR1_ADDRESS, IMXRT_TMR2_ADDRESS, IMXRT_TMR3_ADDRESS, IMXRT_TMR4_ADDRESS};
>>>>>>>
 
C:\Users\NEW\Documents\Arduino\libraries\TeensySte p4-main\src\teensystep4.cpp

Looks like something went wrong installing the library? There is a space in your installation path: "..\libraries\TeensySte p4-main\src...".
 
I managed to get the code to compile by moving the individual library components into tabs directly associated with my main program code so they would become local code.

I then renamed the include calls in my local copy to refer to the files in the program tabs. (Basically, I carefully removed the subfile structure from the library, in my local copy.)

Tomorrow, I will connect the hardware and see how it runs! :)

Many thanks for writing this very powerful stepper driver library for the Teensy platform!

Bill D.

I am located in New Zealand, so I am shifted by about half of a day from the rest of the world. :)
 
I managed to get the code to compile by moving the individual library components into tabs directly associated with my main program code so they would become local code.

Good that it works now. However, while manually copying files to your source folder certainly works it should not be necessary. It should be enough to download the *.zip from Github and do a "zip install" using the library manager. As you know, TeensyStep4 is in a very experimental stage. Thus, a lot of changes can be expected. Manually copying files and adjusting the include paths can get quite tedius quickly.
 
Luni wrote: "However, while manually copying files to your source folder certainly works it should not be necessary. It should be enough to download the *.zip from Github and do a "zip install" using the library manager."

The TeensyStep4 library is not visible to the library manager (under "tools"), which is understandable for such a new library. I am not sure that "zip install" is an option under the library manager.
Zip install _is_ an option under "sketch" then "add library" however. Adding a zip library by that method somehow does not work correctly when the zip directory has libraries in sub-directories, it seems. :-( Not sure why, but I suspect it doesn't see the sub-folder branches in the library. That is exactly what I tried initially.

Since the compiler wasn't managing to find the libraries in the sub-folders when I did a normal zip install, I figured I would pull the library files into tabs in the code using "sketch" "add file" and then edit the paths in the local files to remove the sub-folder structure.
 
Luni wrote: "However, while manually copying files to your source folder certainly works it should not be necessary. It should be enough to download the *.zip from Github and do a "zip install" using the library manager."

The TeensyStep4 library is not visible to the library manager (under "tools"), which is understandable for such a new library. I am not sure that "zip install" is an option under the library manager.
Zip install _is_ an option under "sketch" then "add library" however. Adding a zip library by that method somehow does not work correctly when the zip directory has libraries in sub-directories, it seems. :-( Not sure why, but I suspect it doesn't see the sub-folder branches in the library. That is exactly what I tried initially.

Since the compiler wasn't managing to find the libraries in the sub-folders when I did a normal zip install, I figured I would pull the library files into tabs in the code using "sketch" "add file" and then edit the paths in the local files to remove the sub-folder structure.
I just tried it using Sketch.Include Library.Add .ZIP Library... and it worked just fine, creating all the sub libraries as they exist in the GitHub source.
When you post in the future, instead of writing
Luni wrote: "However, while manually copying files to your source folder certainly works it should not be necessary. It should be enough to download the *.zip from Github and do a "zip install" using the library manager."
could you include it between quote tags as I have done above.
 
Hi,
I'm using TeensyStep for the first time in a project. Everything is going well so far. I just have a first basic question that I can't solve on my own:
To drive the motor, I use controller.stop();(motor), because the function blocks and I can automatically transfer new parameters only at the reversal points.
Now I've created an interrupt routine to be able to stop the motor at any time (via an interrupt pin).
With controller.stop(); unfortunately it doesn't work. Also controller.stopAsync(); doesn't really work.

How can I stop a by controller.move() activated motion (e.g. with interrupt) without loosing steps???

Thanks in advance

As required here, I post my code for it:

Code:
#include "Arduino.h"
#include "TeensyStep.h"

Stepper motor(22,23);
StepControl controller; 

const byte interruptPin1 = 33;

int32_t targetposition = 0; // Vordere, errechnete Zielpositon
int32_t backposition = 0; // Hintere, errechnete Zieposition
int32_t initialtarget = 0; // Initialisierte (während Setup festgelegte) max. Zielposition
int32_t initialback = 0; // Initialisierte (während Setup festgelegte) max. Backposition
// int reps = 0; //Anzahl Wiederholungen FOR
String buttons_read = ""; // Zwischenspreeicher Auslesen Button
int32_t ownmmaxspeed = 0; // Eigener Wert für MaxSpeed aus dem der aktuelle Speed errechnet wird
int32_t ownminspeed = 0; // Eigener Wert für minimale Laufgeschwindigkeit ca. 1/2 Hub/Sec
int32_t safetyspeed = 0; // Für Rückzug nach STOP
int32_t motorspeed_prozent = 50; // Geschwindigkeit 0-100 in 10-er Schritten zum Berechnen der Frequnz f. Motor
int32_t progressSpeed = 0; // Wert zur Übertragzng an SpeedGauge
int32_t actualspeed = 0;
int32_t accelfactor = 0; //Multiplikator für Bems-/Beschleunigungsrampen
int32_t stepwide = 0; // 5-10-20... % Schritte für Geschwindigkeit +/-
int32_t risedeep = 0; // Wert um die Tiefe pro Hub zu erhöhen

int32_t sondercounter = 0; // Anzahl Schleifen Sonderfunktion
int32_t sonderprogramm = 0; // Sonderfunktion ein/aus
int32_t sonderteiler = 0; // Zur Berechnung aus sondercounter abgeleiteter Wert zur Berechnung der Tiefenschritte

void isrStop()
{
  Serial.println("Interrupt");
  Serial.println(motor.getPosition());

  controller.stop();
  
  Serial2.print("progressSpeed.val=");
  Serial2.print(0);
  Serial2.write(0xFF);
  Serial2.write(0xFF);
  Serial2.write(0xFF);
  delay(5);

  actualspeed = 0;
  
  // Rouine zur Rückfahrt auf den Homepoint oder neue Referenzfahrt folgt hier

}

void setup() 
{ 
    pinMode(interruptPin1, INPUT);
    attachInterrupt(digitalPinToInterrupt(interruptPin1), isrStop, FALLING);
    
    // Setup some motor properties
    ownmmaxspeed = 15300; 
    ownminspeed = 2550; 
    motorspeed_prozent = 30; // Startwert nach Initialisierung
    initialtarget = 4000;
    initialback = 0;
    targetposition = initialtarget;
    backposition = initialback;
    accelfactor = 3;
    stepwide = 10;
    risedeep = (targetposition-backposition);
    safetyspeed = 1500;

    sonderprogramm = 1; // Sonderfunktion ein/aus
    sondercounter = 5; // Anzahl Schleifen Sonderfunktion
    sonderteiler = sondercounter; // Initial aus Anzahl Schleifen abgeleiteter Wert zur Berechnung der Tiefenschritte

    actualspeed = map(motorspeed_prozent, stepwide, 100, ownminspeed, ownmmaxspeed);
    motor.setMaxSpeed(actualspeed);
    motor.setAcceleration(actualspeed*accelfactor);    // stp/s^2
    motor.setPosition(0);
    motor.setTargetAbs(targetposition);
    progressSpeed = motorspeed_prozent * 1.8;

    Serial.begin(9800);
    Serial2.begin(115200);
    delay(20);
    
    Serial2.print("progressSpeed.val=");
    Serial2.print(progressSpeed);
    Serial2.write(0xFF);
    Serial2.write(0xFF);
    Serial2.write(0xFF);
    delay(5);

}

void serialRunWatch()
{
    buttons_read += char (Serial2.read());
    if ((buttons_read == "D") && (motorspeed_prozent > 0))
    {
       motorspeed_prozent = motorspeed_prozent - stepwide;
       progressSpeed = motorspeed_prozent * 1.8;
       Serial2.clear();
    }

    if ((buttons_read == "U") && (motorspeed_prozent < 100))
    {
      motorspeed_prozent = motorspeed_prozent + stepwide;
      progressSpeed = motorspeed_prozent * 1.8;
      Serial2.clear();
    }

    if ((buttons_read == "8") && (motorspeed_prozent <= 100))
    {
      motorspeed_prozent = 80;
      progressSpeed = motorspeed_prozent * 1.8;
      Serial2.clear();
    }

    if ((buttons_read == "S") && (motorspeed_prozent != 0))
    {
      motorspeed_prozent = 0;
      progressSpeed = motorspeed_prozent * 1.8;
      Serial2.clear();
    }

    buttons_read = "";    
    
    Serial2.print("progressSpeed.val=");
    Serial2.print(progressSpeed);
    Serial2.write(0xFF);
    Serial2.write(0xFF);
    Serial2.write(0xFF);
    delay(5);

    actualspeed = map(motorspeed_prozent, stepwide, 100, ownminspeed, ownmmaxspeed);
    if (actualspeed < ownminspeed)
    {
      actualspeed = 0;
    }

    accelfactor = map(actualspeed, ownminspeed, ownmmaxspeed, 3, 10);

    Serial.println(actualspeed);
    Serial.println(accelfactor);

}

void riseDeep5x()
{
  
  targetposition = risedeep - (risedeep/sonderteiler*sondercounter) + (risedeep/sonderteiler);
  // targetposition = ((1/sondercounter) * risedeep); 
  sondercounter = sondercounter-1;
  if (sondercounter == 0)
  {
    sondercounter = sonderteiler;
  }

  Serial.println(targetposition);
  Serial.println(sondercounter);

}

void loop()

{

  if (Serial2.available() > 0)
  {
    serialRunWatch();
  }

  if (motor.getPosition() == targetposition)
  { 
    if (actualspeed != 0)
    {
    motor.setMaxSpeed(actualspeed); 
    motor.setAcceleration(actualspeed*accelfactor);
    }
    motor.setTargetAbs(backposition);

  if (sonderprogramm == 1)
  {
    riseDeep5x();
  }

  }

  if (motor.getPosition() == backposition)
  {
    motor.setMaxSpeed(actualspeed); 
    motor.setAcceleration(actualspeed*accelfactor);
    motor.setTargetAbs(targetposition);
  }

  if ((actualspeed != 0) || (motor.getPosition() != 0))
  {
  controller.move(motor);
  }
  
  delay(5);

}
 
Does this also work for teensy 4.1? I'm thinking I could just make some slight modification such as

TeensyStep.h
Code:
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include "timer/teensy4/TimerField.h"
Would this be sufficient?
 
No reason to include " defined(__IMXRT1052__) " - the 1052 was dismissed long ago after 1062 was made the chosen MCU for T_4.x and is no longer supported or maintained.
 
Hi luni, thanks for your work on TeensyStep4.

You may remember a few years ago I assisted in getting the multi-stepper move to use the fastest speed instead of the slowest.

I've just started transitioning my camera pan / tilt / slide mount from Teensy3.2 to Teensy4.0 that used TeensyStep. I've been using the Teensy3.2 version for a number of years, so the code should be solid. I'm now adapting it for Teensy4.0.

For the "controlled by joystick" part I used to use rotateAsync with overrideAcceleration and overrideSpeed. I've removed overrideAcceleration for now, and I'm able to control a single axis (pan for example) by starting rotateAsync and the joystick adjusts overrideSpeed with stopAsync when the joystick is centralised. However, when I try a different axis (e.g. tilt) nothing happens and I'm unable to move the original axis / the program freezes. If I power off and on again, I can move the 2nd axis (tilt), but when I try the 1st (pan) the program freezes. It's as if once you've started 1 motor, you can never move a different one.

TL:DR, am I able to use multiple rotateAsync and overrideSpeed (up to 3) at the same time?

Thanks again for your time with TeensyStep.
 
I'll let you know my findings whether it helps someone else or someone can help me.

So far I've found you can have more than one rotateAsync and overrideSpeed, it appears to be the stopAsync which stops not only the assigned rotateAsync but stops ALL rotateAsync motors, and stops you from creating any further rotateAsync.

I hope and expect it's my code which is at fault. I'll report on any further findings here.

Here's part of the code that matters.
(speedFactorP / T / S are values from the joysticks that range from -1.0, 1.0 with zero the centre point.)

Code:
Stepper stepper_pan(19, 18);
Stepper stepper_tilt(17, 16);
Stepper stepper_slider(15, 14);

void setup(){
    TS4::begin();

    stepper_pan.setMaxSpeed(10'000);
    stepper_pan.setAcceleration(50'000);

    stepper_tilt.setMaxSpeed(10'000);
    stepper_tilt.setAcceleration(50'000);

    stepper_slider.setMaxSpeed(10'000);
    stepper_slider.setAcceleration(50'000);

}

...

      if (speedFactorP == 0) { stepper_pan.stopAsync(); }
      else {
        stepper_pan.rotateAsync(pan_set_speed * 100);
        stepper_pan.overrideSpeed(speedFactorP);
      }

      if (speedFactorT == 0) { stepper_tilt.stopAsync(); }
      else {
        stepper_tilt.rotateAsync(tilt_set_speed * 100);
        stepper_tilt.overrideSpeed(speedFactorT);
      }

      if (speedFactorS == 0) { stepper_slider.stopAsync(); }
      else {
        stepper_slider.rotateAsync(slider_set_speed * 100);
        stepper_slider.overrideSpeed(speedFactorS);
      }
 
This lib is still very experimental. But I'll have a look. However it will l take a couple of days I'm afraid.
 
Hi luni,
No worries, I appreciate that. No hurry. I'm enjoying figuring it out.
Like I say, I've been using this with TeensyStep for that past few years with much success, I just need to modify my code to use your new version.

What I've been using is rotateAsync with overrideSpeed to use a joystick to set a location (pan, tilt, slider),
then store that position with ".getPosition()" for each of the 3 motors,
then recall positions and set targets with".setTargetAbs(xxx)",
then do a synchronised move with what used to be "StepControl multi_stepper; multi_stepper.move(stepper_pan, stepper_tilt, stepper_slider);"
Hopefully "StepperGroup ({stepper_pan, stepper_tilt, stepper_slider}).move();" would be the TS4 equivalent.

Are any of these features missing? like .getPosition()?
 
then do a synchronised move with what used to be "StepControl multi_stepper; multi_stepper.move(stepper_pan, stepper_tilt, stepper_slider); Hopefully "StepperGroup ({stepper_pan, stepper_tilt, stepper_slider}).move();" would be the TS4 equivalent."
Yes, this replaces the controller moves. You can either do it on the fly as you did or you can predefine the groups you need. The groups are cheap, you can have as many as you like.

Are any of these features missing? like .getPosition()?
GetPosition works, GetVelocity is still missing.

A quick experiment showed that overridespeed seems to work only once and is ignored after that, I need to dig into that
Code:
#include "Arduino.h"
#include "TeensyStep4.h"

using namespace TS4;

Stepper s1(19, 18);
Stepper s2(17, 16);
Stepper s3(15, 14);

elapsedMillis stopwatch;

IntervalTimer t1;
void onTimer()
{
  Serial.printf("%d\t%6d\t%6d\t%6d\n",(unsigned)stopwatch, s1.getPosition(), s2.getPosition(), s3.getPosition());
}


void setup()
{
  TS4::begin();

  while (!Serial);

  t1.begin(onTimer, 50'000);  // print out motor positions

  s1.setMaxSpeed(1000);
  s1.setAcceleration(1000);

  s2.setMaxSpeed(1000);
  s2.setAcceleration(1000);

  stopwatch = 0;

  s1.rotateAsync(1000);
  s2.rotateAsync(2000);
  delay(1000);

  s1.overrideSpeed(0.1);
  delay(2000);
  s1.overrideSpeed(0.9);  // this does not work...
}

void loop()
{
}

Capture.PNG

first overrideSpeed at t=1s works, second one at t=3s doesn't work
 
ok good, glad it's not me :)

In my tests so far if the time between changing overrideSpeed is short enough, it'll keep working and I can keep the motors moving at different speeds and in different directions. I haven't tested very long, but I'd go so far as to say, it works indefinitely.
It's just if you stop, or as you've experienced don't change it for a few seconds, then it stops working altogether.

Another issue which you may discover, is that at very small negative numbers with overrideSpeed, the direction of the motor will be wrong.
I'll try to do some specific tests and find at what number it fails.

Thanks again for any time spent on this.
 
https://youtu.be/jVCTMr_b2xM

Here's a video of it working but only if I don't stop moving all axis for about a second. If I do stop for a full second, all rotateAsync stop working, which you can see from 0:52 in the video. You can also see that the Teensy4 hasn't hung as I've set the built in LED to light when the joystick is > 0 and off when = 0.

You can also see that I'm moving the joystick very slightly left at 0:25 but the axis turns the wrong way (clockwise). At 0:33 you can see when I move the joystick a little more, it starts moving the correct way.
In testing the breaking point seems to be an overrideSpeed of -0.2. -0.20001 moves the correct direction, but between -0.2 and 0 it the motor moves the wrong way.

The code I'm using for this bit is:
Code:
      if (speedFactorT == 0.0) { 
        stepper_tilt.overrideSpeed(0);
      }
      else {
        stepper_tilt.setAcceleration(tilt_accel * 100);
        stepper_tilt.rotateAsync(tilt_set_speed * 100);
        stepper_tilt.overrideSpeed(speedFactorT);
      }

Obviously this is calling rotateAsync a lot. I've tried replacing overrideSpeed(0) with stopAsync(), but as soon as that axis = 0 it stops responding to further moves, while the other axis continue to work.

I hope this assists you in finding any bugs.
 
I've been running more tests. Using rotateAsync and overrideSpeed, speeds between 0 and 100 are not linear. Specifically speed = 71 is significantly faster than speed = 100.
Code:
s2.rotateAsync(100);
s2.overrideSpeed(0.71);

I've plotted a graph using Arduino IDE serial plotter with the following code.
As you can see, "Value 3" (green) stepper s2, is much faster than "Value 1" (red) stepper s1.

I hope this helps in bug finding.

Screenshot 2023-08-08 at 17.31.33.png

Code:
#include "Arduino.h"
#include "teensystep4.h"

using namespace TS4;

Stepper s1(19, 18);
Stepper s2(17, 16);

elapsedMillis stopwatch;

IntervalTimer t1;
void onTimer()
{
  Serial.printf("%d\t%6d\t%6d\n",(unsigned)stopwatch, s1.getPosition(), s2.getPosition());
}

void setup()
{
  TS4::begin();
  while (!Serial);
  t1.begin(onTimer, 100'000);  // print out motor positions

  s1.setMaxSpeed(100);
  s1.setAcceleration(10000);
  s2.setMaxSpeed(100);
  s2.setAcceleration(10000);

  stopwatch = 0;

  s1.rotateAsync(100);
  s1.overrideSpeed(1);
  s2.rotateAsync(100);
  s2.overrideSpeed(0.71);  // Speeds = 0 - 100 not linear. Specifically speed = 71 is VERY fast.
}

void loop()
{
}
 
Thanks that will help.
I already started debugging and fixed a few things in the overrideSpeed code. But, there is still something wrong. Hope I'll find some time to work on it the next days...
 
Great!
I've been able to keep the overrideSpeed working by uncommenting this line in startStopping:

Code:
        if (!isMoving) return;

Overall I've found the rotateAsync with overrideSpeed on TS4 to me much slower than TS. I've had to *100 the rotateAsync speed for it to be similar. But the "group move" max speeds are fine. Odd how they're not similar.

I tried inserting lots of "Serial.print...." lines to see what was happening, and there seems to be an infinite loop happening around the "void Stepper::stopAsync()" function. But that could be my poor testing methods.
 
Back
Top