Yes, it does, thanks. I know he already said he hasn't found a repeatable set of actions that causes a crash, but perhaps he will.Hope that helps a bit,
Hi 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
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.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.
Thank you for your explanations and for the interesting links! ChristofHi 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
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?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.
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.
#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);
}
#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();
}
}
}
}
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.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.
#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();
}
}
}
}