Low-level library and timer assistance -- TeensyStep library ???

The Teensy is controlled by a Nextion touchscreen. It will be rather difficult to reproduce the crashes without the touchscreen. Hopefully, I can narrow down where the crashes are happening.
 
Joe -- To elaborate a bit on what Ed just replied, you can see some of the screens that the users of our system interact with, to change settings with respect to the multiple steppers, coordinating the motions, and then making the cuts on the lathe(s).

Here's a link to the controller's screen descriptions...

Nextion Multiple Stepper Control Screen Descriptions

The Nextion touchscreen is a serial device, talking bi-directionally with the Teensy, which then, via TeensyStep library, manages the coordinated motion of the motors.

If you're familiar with them, think of a CNC system, but instead of CAM software generating g-codes, we manage a bunch of "canned functions" via the touchscreen interface, and the user can adjust parameters prior to running a cut / process. Our setup would be somewhat like a "conversational controller" on a CNC machine these days.

Hope that helps a bit,

--Jon
 
Joe -- To elaborate a bit on what Ed just replied, you can see some of the screens that the users of our system interact with, to change settings with respect to the multiple steppers, coordinating the motions, and then making the cuts on the lathe(s).

Here's a link to the controller's screen descriptions...

Nextion Multiple Stepper Control Screen Descriptions

The Nextion touchscreen is a serial device, talking bi-directionally with the Teensy, which then, via TeensyStep library, manages the coordinated motion of the motors.

If you're familiar with them, think of a CNC system, but instead of CAM software generating g-codes, we manage a bunch of "canned functions" via the touchscreen interface, and the user can adjust parameters prior to running a cut / process. Our setup would be somewhat like a "conversational controller" on a CNC machine these days.

Hope that helps a bit,

--Jon
Hi Jon,
because I find your application very interesting I am still following this thread. May I ask some questions?

Do I understand correctly, that there is already a prototype version of the control software for the rose lathe, that uses the TeensyStep4 library?
(((It is interesting to see, that the TeensyStep4 library works in a way similar to the one I suggested. I have the impression, that some difficulties might arise from the fact, that the original control program and also the original library seem to be centred around SPEEDS and their relations. (Edit: I have now seen, that absolute position is even reset before starting a movement. And that speeds are indeed changed dynamically.) The T4 library is using Bresenham and is therefore fixed to linear movements between POSITIONS. And the leading axis is not allowed to change during one movement. I would assume, that problems can arise from dynamically switching dependency of axis and/or speeds. I would split all movements into very short linear movements. This will mean, that the influence of acceleration will diminish. During one of these movements all axis can be handled as dependant.
Also it seems that float or double variables are used. I would recommend to use integers for all position data to be sure, that errors do not sum up. )))

The software seems to be capable to do the radial movements, so you actually don't need the Swinging Headstock with the Rosettes any more?
And one curious question: How do you handle backlash of the axis? (((In the software for my little lathe, where I also do not have ball screws, I have constants for backlash travel, which works quite well, as long as friction is high in relation to working force.)))
@joepasquariello , I am well aware, that it is you, who has given real help here, while my comments are not much of practical use. Great!
Cheers and good luck! Christof

P.S. Perhaps I should repeat my suggestion to use Teensy threads library here, because with this, the handling of the user interface can be somewhat separated from movements. Or some layer of software could be inserted to go from SPEEDS to POSITIONS.
 
Last edited:
The T4 library is using Bresenham and is therefore fixed to linear movements between POSITIONS.
P.S. Perhaps I should repeat my suggestion to use Teensy threads library here, because with this, the handling of the user interface can be somewhat separated from movements. Or some layer of software could be inserted to go from SPEEDS to POSITIONS.
TeensyStep4, like TeensyStep, provides methods for reaching and holding a given speed, or moving a specific number of steps. In both cases, Bresenham is used to rotate or move multiple motors in fixed ratios. In terms of computing resources, Bresenham allows generation of steps to multiple motors from a single timer.
 
Hi Christof -- Of course you can ask questions. Isn't that why we're all here? ;)

Ed / @Elf has taken down his develop branch with the T4.1 work in progress, but he is working on the T4.1 version privately. I think because it is in such a state of experimentation, and not really close enough to call a beta, it is easier for changes and testing to not have the experimental version out there.

What I think I tried to say before was that your suggestions are good ones, but at least for the time being we're trying to preserve the huge amount of complicated code, rather than re-writing from scratch. Our conundrum is really because we got caught between a broken library, and an incomplete replacement (and a chip shortage, and architectural timer changes, etc.).

The speeds, as you now see, are dynamic. Most of the functions are designed around coordinating the motion of two or more motors, and the original library is very good at that. Hard to say with the replacement right now. Joe's fixes seem like they are helping that along... ongoing testing.

The whole concept for the lathes is based on the historical "rocking headstock and rosettes" design. Most of the lathes using the controller are electro-mechanical, and generate patterns from the rosettes (cams). While the lathes have multiple motors, there is no requirement for their sliderests / cross slides to be parallel to the spindle axis, or orthogonal to one another (X and Z). Some are, some aren't. So in use, a lot of the setup is a manual process to position the cutters and workpiece, then, when ready, begin a coordinated movement with the various motors. Quite a few of the lathes use MagSwitches on a steel top, to allow positioning the sliderests arbitrarily on the lathe bed.

For this project, the goal was to retain the manual interaction, and the physical rosettes for their geometric patterns. But there are other designs out there, where the rosettes have been "virtualized", typically therefore requiring the sliderest stages to be orthogonal. You can look at COrnLathe and Embellished Turnings or the new-ish LatheEngraver for examples of those kids of configurations. Typically g-code based. Each of those has related GitHub repositories. Let me know if you can't find them. (One of the early pioneers, Dewey Garrett's site seems to not respond at the moment.)

This project was envisioned as a way to make complicated patterns seem approachable, especially to artistically-oriented users, as opposed to mathematically- or tech-oriented users. Hence the easy to understand touchscreen, no computer per se, and relatively little technical knowledge required to use the system. But, as with any project like this, where you're tasked with "hiding the complexity under the hood", it is often harder to do than most people realize, e.g. it can be very difficult to make things look easy.

Because this is a "hands-on" system (as opposed to CNC), most backlash is dealt with in the same way an experienced machinist would approach it on a manual machine... when you are preparing to make a cut, you back out far enough, so that you can turn in the desired direction, taking up the backlash before you reach the workpiece / start of your cut, and then lead into your cut, with the leadscrew already having removed all the backlash before you entered the cut. Reverse by retracting the cutter, backing out further than where you need to start, and repeat, leading into the cut again while taking up the backlash on your way to starting the actual cut again.

I am sure the Teensy Threads library would work perfectly, if we were starting over from scratch. Other options might have worked as well, like the very good AccelStepper library, which we experimented with early on, before @luni introduced TeensyStep.

Hopefully that answers most your questions,

--Jon
 
Hi Christof -- Of course you can ask questions. Isn't that why we're all here? ;)

Ed / @Elf has taken down his develop branch with the T4.1 work in progress, but he is working on the T4.1 version privately. I think because it is in such a state of experimentation, and not really close enough to call a beta, it is easier for changes and testing to not have the experimental version out there.

What I think I tried to say before was that your suggestions are good ones, but at least for the time being we're trying to preserve the huge amount of complicated code, rather than re-writing from scratch. Our conundrum is really because we got caught between a broken library, and an incomplete replacement (and a chip shortage, and architectural timer changes, etc.).

The speeds, as you now see, are dynamic. Most of the functions are designed around coordinating the motion of two or more motors, and the original library is very good at that. Hard to say with the replacement right now. Joe's fixes seem like they are helping that along... ongoing testing.

The whole concept for the lathes is based on the historical "rocking headstock and rosettes" design. Most of the lathes using the controller are electro-mechanical, and generate patterns from the rosettes (cams). While the lathes have multiple motors, there is no requirement for their sliderests / cross slides to be parallel to the spindle axis, or orthogonal to one another (X and Z). Some are, some aren't. So in use, a lot of the setup is a manual process to position the cutters and workpiece, then, when ready, begin a coordinated movement with the various motors. Quite a few of the lathes use MagSwitches on a steel top, to allow positioning the sliderests arbitrarily on the lathe bed.

For this project, the goal was to retain the manual interaction, and the physical rosettes for their geometric patterns. But there are other designs out there, where the rosettes have been "virtualized", typically therefore requiring the sliderest stages to be orthogonal. You can look at COrnLathe and Embellished Turnings or the new-ish LatheEngraver for examples of those kids of configurations. Typically g-code based. Each of those has related GitHub repositories. Let me know if you can't find them. (One of the early pioneers, Dewey Garrett's site seems to not respond at the moment.)

This project was envisioned as a way to make complicated patterns seem approachable, especially to artistically-oriented users, as opposed to mathematically- or tech-oriented users. Hence the easy to understand touchscreen, no computer per se, and relatively little technical knowledge required to use the system. But, as with any project like this, where you're tasked with "hiding the complexity under the hood", it is often harder to do than most people realize, e.g. it can be very difficult to make things look easy.

Because this is a "hands-on" system (as opposed to CNC), most backlash is dealt with in the same way an experienced machinist would approach it on a manual machine... when you are preparing to make a cut, you back out far enough, so that you can turn in the desired direction, taking up the backlash before you reach the workpiece / start of your cut, and then lead into your cut, with the leadscrew already having removed all the backlash before you entered the cut. Reverse by retracting the cutter, backing out further than where you need to start, and repeat, leading into the cut again while taking up the backlash on your way to starting the actual cut again.

I am sure the Teensy Threads library would work perfectly, if we were starting over from scratch. Other options might have worked as well, like the very good AccelStepper library, which we experimented with early on, before @luni introduced TeensyStep.

Hopefully that answers most your questions,

--Jon
Thank you for your explanations and for the interesting links! Christof
 
Unfortunately, I've found another stepper issue: rotateAsync fails after multiple stops. One of the main functions of the program is to run two (or three) steppers simultaneously but not synchronized. Stopping and restarting one of the steppers while the others keep running is common. In the attached program send capital S to stop a stepper. Repeat until the stepper(s) don't start.
 

Attachments

  • Rotate_StopAsync.ino
    2.1 KB · Views: 20
Unfortunately, I've found another stepper issue: rotateAsync fails after multiple stops. One of the main functions of the program is to run two (or three) steppers simultaneously but not synchronized. Stopping and restarting one of the steppers while the others keep running is common. In the attached program send capital S to stop a stepper. Repeat until the stepper(s) don't start.
Elf, this program starts 4 steppers (s1-s4) in setup(). In loop(), it starts s1 and s3, then waits for serial input. It stops s3 on 'S' and stops s1 on 'c'. In either case, it disables all 4 steppers and then starts over. Can you tell me what you are entering, what you expect steppers s1 and s3 to do, and what you observe that's wrong?
 
It initializes the steppers in setup(), then only runs two of them. (I wasn't sure if I needed more steppers running to show the problem.) Sending an 'S' or a 'c' stops the steppers and exits the while loop which allows loop() to continue. The steppers start running again. Repeat sending the 'S' or 'c' until both steppers fail to move. It usually takes between 3 and 15 times. Increasing the MaxSpeeds of the steppers seem to make the failure happen faster.
 
It initializes the steppers in setup(), then only runs two of them. (I wasn't sure if I needed more steppers running to show the problem.) Sending an 'S' or a 'c' stops the steppers and exits the while loop which allows loop() to continue. The steppers start running again. Repeat sending the 'S' or 'c' until both steppers fail to move. It usually takes between 3 and 15 times. Increasing the MaxSpeeds of the steppers seem to make the failure happen faster.

I think the problem is not in TeensyStep4, but in your test program. 'S' causes s3.stopAsync() and 'c' causes s1.stopAsync(), but then loop() calls startAsync() for BOTH steppers. If I change the program so that 'S' or 'c' calls stopAsync() for BOTH motors, then BOTH motors always restart okay. In other words, I think you should always be careful to have matching calls to startAsync() and stopAsync(). I haven't tried to change the program to allow stop/start of EITHER s1 or s3. Is that what you want the program to do?

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

using namespace TS4;

Stepper s1(3, 2);
Stepper s2(6, 5);
Stepper s3(21, 20);
Stepper s4(16, 23);

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  TS4::begin();

  Serial.begin(115200);
    
  pinMode(4, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(17, OUTPUT);

// When this block is in loop, it takes longer to fail
  s1.setMaxSpeed(30'000).setAcceleration(50'000);
  s2.setMaxSpeed(31'000).setAcceleration(25'000);
  s3.setMaxSpeed(30'000).setAcceleration(50'000);
  s4.setMaxSpeed(31'000).setAcceleration(25'000);

  s1.setPosition(0) ;     
  s2.setPosition(0) ;   
  s3.setPosition(0) ;   
  s4.setPosition(0) ;   
// End block
}

void loop()
{
  // Enable steppers
  digitalWrite(4,LOW);
  digitalWrite(14,LOW);
  digitalWrite(22,LOW);
  digitalWrite(17,LOW);

  s1.rotateAsync(0);
  s3.rotateAsync(0);
 
  int i=0;
  bool stop=false;
  while (s1.isMoving && !stop) {
    Serial.printf( "%4d %10lu %10lu\n", i++, s1.getPosition(), s3.getPosition() );
    delay(1000);
    if (Serial.available() > 0) {
      int incomingData = Serial.read();
      switch (incomingData) {
        case 'S': // 83
        case 'c': // 99
          Serial.printf( "%c\n", incomingData );
          s3.stopAsync();
          s1.stopAsync();
          stop = true;
          break;
      }
      //digitalToggleFast(LED_BUILTIN);
      delay(200);
    }
  }

  // Disable Steppers
  digitalWrite(4,HIGH);
  digitalWrite(14,HIGH);
  digitalWrite(22,HIGH);
  digitalWrite(17,HIGH);
  delay(1000);
}
 
Here is another version that lets you stop/start s1 by entering '1' or stop/start s3 by entering '3'. This seems to work okay.

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

using namespace TS4;

Stepper s1(3, 2);
Stepper s2(6, 5);
Stepper s3(21, 20);
Stepper s4(16, 23);

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  TS4::begin();

  Serial.begin(115200);
    
  pinMode(4, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(17, OUTPUT);

  s1.setMaxSpeed(30'000).setAcceleration(50'000);
  s2.setMaxSpeed(31'000).setAcceleration(25'000);
  s3.setMaxSpeed(30'000).setAcceleration(50'000);
  s4.setMaxSpeed(31'000).setAcceleration(25'000);

  s1.setPosition(0) ;     
  s2.setPosition(0) ;   
  s3.setPosition(0) ;   
  s4.setPosition(0) ;   

  // Enable steppers
  digitalWrite(4,LOW);
  digitalWrite(14,LOW);
  digitalWrite(22,LOW);
  digitalWrite(17,LOW);

  s1.rotateAsync(0);
  s3.rotateAsync(0);
}

int i=0;
void loop()
{
  delay(1000);
  Serial.printf( "%4d %10lu %10lu\n", i++, s1.getPosition(), s3.getPosition() );
  if (Serial.available() > 0) {
    int data = Serial.read();
    if (data == '1') {
      if (s1.isMoving) {
        Serial.println("stop 1");
        s1.stopAsync();
      }
      else {
        Serial.println("start 1");
        s1.rotateAsync();
      }
    }
    else if (data == '3') {
      if (s3.isMoving) {
        Serial.println("stop 3");
        s3.stopAsync();
      }
      else {
        Serial.println("start 3");
        s3.rotateAsync();
      }
    }
  }
}
 
I'm still getting failures but it isn't consistent. Sometimes it fails between 3 and 15 cycles and sometimes it will work for more than a 100. When it does fail, the other stepper does not respond to the stop command. I'll keep testing to see if I can get a consistent repro.

Thanks again for looking at this.
 
I'm still getting failures but it isn't consistent. Sometimes it fails between 3 and 15 cycles and sometimes it will work for more than a 100. When it does fail, the other stepper does not respond to the stop command. I'll keep testing to see if I can get a consistent repro.

Thanks again for looking at this.
Okay, when you get a chance, please tell me which program you are using now, exactly how you are testing, and whether you have made the modifications to TeensyStep4 from the previous messages.
 
Yes, I have made the modifications. I'm using a slightly modified version from your post #62 (Just added a counter and an option to use a steppergroup). Testing is just repeatedly sending either a 1 or a 3 over the serial connection.

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

using namespace TS4;

Stepper s1(3, 2);
Stepper s2(6, 5);
Stepper s3(21, 20);
Stepper s4(16, 23);
StepperGroup g23{s2,s3};

int counter1=0;
int counter2=0;

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  TS4::begin();

  Serial.begin(115200);
    
  pinMode(4, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(17, OUTPUT);

  s1.setMaxSpeed(40'000).setAcceleration(50'000);
  s2.setMaxSpeed(31'000).setAcceleration(25'000);
  s3.setMaxSpeed(30'000).setAcceleration(50'000);
  s4.setMaxSpeed(31'000).setAcceleration(25'000);

  s1.setPosition(0) ;     
  s2.setPosition(0) ;   
  s3.setPosition(0) ;   
  s4.setPosition(0) ;   

  // Enable steppers
  digitalWrite(4,LOW);
  digitalWrite(14,LOW);
  digitalWrite(22,LOW);
  digitalWrite(17,LOW);

  s1.rotateAsync(0);
  s3.rotateAsync(0);
  //g23.startRotate();
}

int i=0;
void loop()
{
  delay(1000);
  Serial.printf( "%4d %10lu %10lu\n", i++, s1.getPosition(), s3.getPosition() );
  if (Serial.available() > 0) {
    int data = Serial.read();
    if (data == '1') {
      if (s1.isMoving) {
        Serial.println("stop 1");
        s1.stopAsync();
        counter1 ++;
        Serial.print("counter1: ");
        Serial.println(counter1);
      }
      else {
        Serial.println("start 1");
        s1.rotateAsync();
      }
    }
    else if (data == '3') {
      if (s2.isMoving||s3.isMoving) {
        Serial.println("stop 3");
        s3.stopAsync();
        //g23.stopAsync();

        counter2 ++;
       Serial.print("counter2: ");
        Serial.println(counter2);
      }
      else {
        Serial.println("start 3");
        s3.rotateAsync();
        //g23.startRotate();
      }
    }
  }


}
 
Okay, with your latest update, it hangs pretty quickly. For example, I can send "1111111", and it will hang, sometimes on the very first "1". In all cases I have seen so far, it hangs after stopping s1.

However, with trivial changes to the program, it seems to run forever without hanging. I'm trying to understand what is the difference. I thought it might simply be due to writing to USB, which does sometimes disable interrupts.
 
Back
Top