TeensyStep - How to switch from 1 speed to another

Status
Not open for further replies.
That's really cool! If you need somebody to outsource series production to, let me know :)

I'll add that to the application page if you don't mind.
 
I want to share another observation: if I issue a stop command during deceleration the motor would speed up and begin a new deceleration ramp:

Code:
#include "TeensyStep.h"

Stepper motor(22, 41);
StepControl controller;

int accel = 500;
int speed = 1000;
int target = 2000;

void setup() {
  motor
  .setAcceleration(accel)
  .setMaxSpeed(speed)
  .setTargetRel(target);

  controller.moveAsync(motor);
  delay(3500);
  controller.stopAsync();
}

Since a sudden speed up could break my thin winding wires I work around as follows:

Code:
#include "TeensyStep.h"

Stepper motor(22, 41);
StepControl controller;

int accel = 500;
int speed = 1000;
int target = 2000;

void setup() {
  motor
  .setAcceleration(accel)
  .setMaxSpeed(speed)
  .setTargetRel(target);

  float decel = speed / accel;       // calc accel duration
  decel = decel * decel * accel / 2; // calc travel
  decel = target - decel;            // calc start of decel

  controller.moveAsync(motor);
  delay(3500);
  if (motor.getPosition() < decel) {controller.stopAsync();}
}

But I wonder if that is elegant since the library must already know the position where deceleration starts...?
 
The bug is in the development branch right? I did a lot of work on the new RotateController recently, looks like the new StepController needs some attention as well. I'll have a look tomorrow.
 
The bug is in the development branch right?

Yes. I checked the master branch and it did only happen with the develop branch...

That should be fixed now, can you give it a try?

I just tried: it's fixed! Many thanks!! It helps a lot in my multi layer winding applications when spooling fibers from one spool to another. Following your earlier suggestion I use the StepController for this, moving both motors to a target and change the direction of the feeder periodically. That works well!
 
Thanks a lot for testing, I think it finally is time to merge the development branch into master.
 
Thanks a lot for testing

Thansk for fixing! But with the new version it seems I can't get callbacks working:

Code:
#include "TeensyStep.h"

Stepper motor(22, 41);
StepControl controller;

bool flag = 0;

void setup() {
  motor.setTargetRel(2000);
  controller.setCallback(finished);
  controller.moveAsync(motor);
}

void finished() {flag = 1;}

void loop() {if (flag == 1) {Serial.println("finished"); flag = 0;}}
 
Thanks again! I fixed this and uploaded. Looks like it got lost during one of the merges. Should work now.
 
A new observation: moveAsync works only if stp.controller is defined in global space:

Code:
#include "TeensyStep.h"

Stepper motor1(22, 41);
Stepper motor2(43, 42);

void setup() {
  StepControl controller1;
  motor1.setTargetRel(1000);
  controller1.move(motor1);      // works

  StepControl controller2;
  motor2.setTargetRel(1000);
  controller2.moveAsync(motor2); // does not work
}

void loop() {}
 
Sorry, that can't work. You generated the controller objects on the stack of Setup(). Thus, they automatically will be (and need to be) destructed before Setup is left and the allocated stack on which they lived is released. Destruction happens immediately after your (non blocking) call to moveAsync so the controller has no chance to do its work before it is destructed.

If you replace the moveAsync() by a blocking move() it should work as you expect.

Edit: BTW: the controllers are prepared to handle a destruction while they are still running. -> The first thing they do in their destructors is calling emergencyStop(), then the allocated timers will be stopped and released, so nothing bad will happen in your code.
 
Sorry, that can't work. You generated the controller objects on the stack of Setup(). Thus, they automatically will be (and need to be) destructed before Setup is left

Thanks for clarifying!

BTW: the controllers are prepared to handle a destruction while they are still running. -> The first thing they do in their destructors is calling emergencyStop()

I see! Does this mean I also could delete a controller manually and define a new one within the scope, re-using the resources?
 
Thanks for the links, great project!!! I will try to learn from these examples. I have to say: a big big thank to luni for his help and his library!!! He saves my project.



Nice!!!! Yes, teensyfied and lunified indeed :cool: But as further I get, the farer the destination seems... I will keep you informed and will provide videos as soon as something is showable! For the moment I can show my recently completed PCBs, basically a board for the periphery of the well known teensy breakout-board:

View attachment 16352

The ICs used are: ADS1231, MAX3490, LM335, MCP100-475D one each, and multiple 74HCT125, 74LVC245, ULN2803A.



Sorry, I didn't make this clear: I don't need changes of acceleration for the speed changes of a single motor, but as soon as both motors change speed I want them to be in sync. I updated my diagram accordingly:

View attachment 16354

jpk - is this board for sale ?
 
I see! Does this mean I also could delete a controller manually and define a new one within the scope, re-using the resources?

Yes, but you should never directly call a destructor. (https://isocpp.org/wiki/faq/dtors#dont-call-dtor-on-local).
Basically, you have two options.

  1. Dynamically create the object on the heap with new and delete it if you don't need it anymore. (this is the usual way how to do this in c++)
  2. Use braces to generate as much scopes as you need and declare your objects within those scopes. They will be destroyed when the code leaves the scope. This will generate the objects on the stack. So, if you are paranoid about not generating objects on the heap, you will like this method.


Here a few examples and the generated output:

Code:
#include "Arduino.h"
#include "TeensyStep.h"
 
Stepper a(0,1), b(2,3), c(4,5), d(6,7), e(8,9);
  
void setup() 
{
  // heap version ---------------------------------------------
  RotateControl *rctrl_heap_1 = new RotateControl();
  RotateControl *rctrl_heap_2 = new RotateControl();
  RotateControl *rctrl_heap_3 = new RotateControl();
  RotateControl *rctrl_heap_4 = new RotateControl(); 

  a.setMaxSpeed(1500);
  b.setMaxSpeed(3000);
  c.setMaxSpeed(2000);
  d.setMaxSpeed(4000);
  e.setMaxSpeed(6000);

  // do something with the controllers
  rctrl_heap_1->rotateAsync(a, b);
  rctrl_heap_2->rotateAsync(c);
  rctrl_heap_3->rotateAsync(d);
  rctrl_heap_4->rotateAsync(e);

  // stop rctrl_heap2 and rctrl_heap3 after 2s
  delay(2000);
  rctrl_heap_3->stop();
  rctrl_heap_2->stop();

  // get rid of two controllers, implicitley emergency stops them and releases timers
  delete rctrl_heap_1;
  delete rctrl_heap_4;
  
  // construct new controllers
  StepControl *sctrl_heap_1 = new StepControl(5, 1000);
  StepControl *sctrl_heap_2 = new StepControl(3, 10000);

  a.setTargetRel(1000);
  b.setTargetRel(5000);

  sctrl_heap_1->moveAsync(a);
  sctrl_heap_2->moveAsync(b);
  rctrl_heap_2->rotateAsync(c,d);
  rctrl_heap_3->rotateAsync(e);

  while(sctrl_heap_1->isRunning() || sctrl_heap_2->isRunning());
  
  delete sctrl_heap_1;
  delete sctrl_heap_2;
  delete rctrl_heap_2;
  delete rctrl_heap_3;

  // stack - version -------------------------------------------

  delay(500);

   // this generates a new scope, the controllers will be 
   // deleted automatically when the program leaves the scope
  {  
    RotateControl r_stack_1;
    RotateControl r_stack_2;
    RotateControl r_stack_3;
    RotateControl r_stack_4;

    r_stack_1.rotateAsync(a);
    r_stack_2.rotateAsync(b, c);
    r_stack_3.rotateAsync(d);
    r_stack_4.rotateAsync(e);

    delay(1000);   // leave scope after 1s, this will emergency stop the motors
  }

  // new scope
  {
    RotateControl r_stack_5;
    RotateControl r_stack_6;

    r_stack_5.rotateAsync(a,b);
    r_stack_6.rotateAsync(c,d);

    delay(1000);

    r_stack_5.stopAsync();
    r_stack_6.stopAsync();
    
    while(r_stack_5.isRunning() || r_stack_6.isRunning()); // wait until motors stopped
  } 
}

void loop()
{
  
}


scopes.jpg
 
Dynamically create the object on the heap with new and delete it if you don't need it anymore.

Many thanks for your example! I played with it but couldn't do the following:
Code:
#include "TeensyStep.h"

Stepper a(22, 41);

void setup() {
  RotateControl *rctrl_heap_1 = new RotateControl();
  rctrl_heap_1->rotateAsync(a);
  delay(2000);
}

void loop() {
  rctrl_heap_1->stop();
  delete rctrl_heap_1;
  
  while (1);
}
Is there any way to create a controller and remove it at another place?

Also I noticed that negative override factors do decelerate and accelerate again, but without actually toggling the dirPin:
Code:
#include "TeensyStep.h"

Stepper motor(22, 41); RotateControl ctrl(5, 1000);

void setup() {
  ctrl.rotateAsync(motor); delay(2000);
  ctrl.overrideSpeed(-1);  delay(2000);
  ctrl.overrideSpeed(1);   delay(2000);
  ctrl.stopAsync();
}

void loop() {}
I could write the dirPin manually, but I hoped that you intended the library to do so... BTW your earlier suggestion about the HCT125s works great: maybe add these to your "Hooking up a Driver / Logic Level Conversion" page...?
SN74HCT125 connections.jpg
jpk - is this board for sale ?
I have only one left and that has bugs, but I can send you my (corrected) KiCad files if you PM me your email address!
 
Last edited:
....
Is there any way to create a controller and remove it at another place?

Technically this is trivial, just declare the pointer to the controller in global space. But beware, this can very quickly lead to a complete mess. Usually you try not to pass responsibility for deleting objects around.

Code:
#include "TeensyStep.h"

Stepper a(22, 41);
RotateControl* rctrl_heap_1;

void setup() 
{
  rctrl_heap_1 = new RotateControl();
  rctrl_heap_1->rotateAsync(a);
  delay(2000);
}

void loop() 
{
  rctrl_heap_1->stop();
  delete rctrl_heap_1;
  
  while (1);
}


BTW: What do you want to achieve? I'm sure there are better ways...


Also I noticed that negative override factors do decelerate and accelerate again, but without actually toggling the dirPin:
Code:
#include "TeensyStep.h"
[/QUOTE]

That used to work perfectly, I'll have a look later today or tomorrow.


[QUOTE="jpk, post: 204879, member: 49821"]
 BTW your earlier suggestion about the HCT125s works great: maybe add these to your "Hooking up a Driver / Logic Level Conversion" page...?
[ATTACH=CONFIG]16540[/ATTACH]
[/QUOTE]

That's a good idea, I'll add it on the weekend.
 
BTW: What do you want to achieve? I'm sure there are better ways...
At the moment I am using 2 rotate and 2 step controllers for my 5 motors and get along with them if used carefully - but if I add a new motor (say for a future grinding device) I will get in troubles, so I thought I could for each move create the controller on the fly and destroy it afterwards. As I try to run the motor movements non-blocking I do most stuff by activating a flag so that loop() knows when to execute for example a stop command. So I need to be careful not to mess up the use of controllers as I only have 2 of each sort. But at the moment I am fine with that! Just wondered if it were better to generate and destroy the controllers only when needed:
Code:
void setup() {
  create_controller();
  start_motor();
}

void process_serial() {
  if (something_is_true) {flag = 1;}
}

void loop() {
  process_serial();
  if (flag) {
    stop_motor();
    delete_controller();
    flag = 0;
  }
}
Another approach could be to merge the 2 types of controllers into one. Deceleration for a pre specified target I do with a rotate controller as shown in the 2nd code block of #56: so if I run out of controllers I could code everything for rotate controllers, but I am not sure if I should prepare for this now.

(BTW the bug I described in #32 is still there, and also setPullInSpeed is not enabled yet...)
 
Also I noticed that negative override factors do decelerate and accelerate again, but without actually toggling the dirPin

This is fixed now! The problem only occurred when using default speed settings and not calling setMaxSpeed. Now it should work correctly for default values as well. Thanks for spotting that one.

At the moment I am using 2 rotate and 2 step controllers for my 5 motors and get along with them if used carefully - but if I add a new motor (say for a future grinding device) I will get in troubles, so I thought I could for each move create the controller on the fly and destroy it afterwards. As I try to run the motor movements non-blocking I do most stuff by activating a flag so that loop() knows when to execute for example a stop command. So I need to be careful not to mess up the use of controllers as I only have 2 of each sort.

I see your problem. Doing that in a clean way is interesting. From the first trials I'd say something like a ControllerFactory for generating and disposing the controllers and a state machine based approach for your asynchronous commands might be a good solution. This might be interesting for other users as well. I'll work something out and add an example to the webpage.

(BTW the bug I described in #32 is still there, and also setPullInSpeed is not enabled yet...)
Yes, I know, I need to dig into this timing issue sometime. As already mentioned, as a workaround you can insert a short delay after the estop then should work. Regarding the pullInSpeed. I'm not sure if this feature is really useful? Do you have a use case for it?
 
This is fixed now! The problem only occurred when using default speed settings and not calling setMaxSpeed. Now it should work correctly for default values as well. Thanks for spotting that one.
Thanks for fixing! And sorry for so many requests...
Regarding the pullInSpeed. I'm not sure if this feature is really useful? Do you have a use case for it?
Yes, I think it's good to have. Some of my winding processes move really slow, for example when threading in the wires. With thin wires I could end up with feeder speeds below pullin-speed. So I tried to take this into account: I set it to the lowest possible value and used that value for calculations. I don't know yet how often I will need such slow speeds though...
 
At the moment I am using 2 rotate and 2 step controllers for my 5 motors and get along with them if used carefully - but if I add a new motor (say for a future grinding device) I will get in troubles, so I thought I could for each move create the controller on the fly and destroy it afterwards. As I try to run the motor movements non-blocking I do most stuff by activating a flag so that loop() knows when to execute for example a stop command. So I need to be careful not to mess up the use of controllers as I only have 2 of each sort.

I did some experiments on your problem and came up with a quite simple and obvious solution. The only reason why the number of controllers was limited to a total number of 4 was that each controller reserves one interval-timer and two FTM channels. The controllers grabbed those at construction time and only released them when it (the controller) was destructed. In the new scheme the controller only grabs the timers when it really needs them, i.e. during the movement. After the motor reaches the target or is stopped the timers are released.

While the change seems to be marginal it has a huge impact on the usage of the controllers: You can now generate as much controllers as you like as long as you make sure that you don't operate more than 4 at the same time. This makes handling much easier. E.g. you can define 4 RotateControllers and 4 StepControllers and switch between Rotational and Target modes without the need to dispose the objects in between.

The lib can be found in the DevTimer branch.
Here a quick example (BTW: the emergencyStop bug from #33 is also fixed)
Code:
#include "TeensyStep.h"

Stepper m1(0, 2), m2(4, 6);

RotateControl rc1, rc2, rc3, rc4, rc5;   
StepControl sc1, sc2, sc3, sc4, sc5;

void setup()
{
  m1
      .setMaxSpeed(2000)
      .setAcceleration(2000);

  m2
      .setMaxSpeed(2000)
      .setAcceleration(2000);

  m1.setTargetRel(2000);
  sc1.moveAsync(m1);

  m2.setTargetRel(100);
  sc2.move(m2);

  m2.setTargetRel(500);
  sc3.moveAsync(m2);
  delay(300);
  sc3.emergencyStop();

  m2.setTargetRel(100);
  sc4.move(m2);

  m2.setTargetRel(100);
  sc5.move(m2);

  while (sc1.isRunning());

  goHome(m1, m2);

  //------------
  delay(100);
  startMotor(m1, rc5);
  startMotor(m2, rc1);

  stopMotor(m1, rc5);
  stopMotor(m2, rc1);
}

void loop() {}

//-----------------------------------------------------------------------
void goHome(Stepper &motor_A, Stepper &motor_B)
{
  StepControl localSC;

  motor_A
      .setAcceleration(25000)
      .setMaxSpeed(10000)
      .setTargetAbs(0);

  motor_B
      .setAcceleration(25000)
      .setMaxSpeed(10000)
      .setTargetAbs(0);

  localSC.move(motor_A, motor_B);
}

void startMotor(Stepper &m, RotateControl &rc)
{
  m.setMaxSpeed(5000);
  rc.rotateAsync(m);
  delay(500);
}

void stopMotor(Stepper &m, RotateControl &rc)
{
  rc.stopAsync();
}
devTimer1.jpg

Would be great if you could test it...
 
Last edited:
Here a quick example (BTW: the emergencyStop bug from #33 is also fixed)
Would be great if you could test it...

Thanks a lot!! I think it's a good solution. I did some tests on my machine, everything works fine. Also the e-stop I can now execute faster without the delay!

EDIT: just noticed that slow movements don't work well. I will figure it out and try to give some code asap...

EDIT2: I did some more tests: the 3rd code block of #21 does not work.
 
Last edited:
Status
Not open for further replies.
Back
Top