New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limit on number of controllers #53
Comments
The number of controllers running at the same time is limited by the number of hardware timers, i.e, to 4. There are however ways to work around this limitation:
void goHome(Stepper& motor_A, Stepper& motor_B)
{
StepControl localSC; // create the controller locally. Will be released when leaving the function
motor_A.setTargetAbs(0);
motor_B.setTargetAbs(0);
localSC.move(motor_A, motor_B);
} And a similar function for the rotation part of your sketch.
#include "TeensyStep.h"
//-----------------------------------------------------------------------
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.stop();
}
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.stop();
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() {} Please note: You need to use the library from the devTimer branch. (I didn't find time yet to merge it into the main branch) |
Thanks for the detailed explanation. So theoretically I can do what I need with a limit of 4 motors. Initially I have some basic questions. It seems you are saying I can have more than 4 controllers declared globally or within a function, so long as only use 4 at a time? In your sample code setup() I see 5 controllers declared with the fifth sc5 only called after sc3 has been stopped. I presume that using sc3.stop() ensured that controller was released? Also the ".isrunning" query can confirm if it is free? You also mention this is only possible if I use the "devTimer" branch?) I hope you don't mind the innocent question : How do I do that? (I have the V2 Teensystepper code installed. ) Finally - any chance in future to add one or two more timers - or because they are "hardware timers" that is limited by the Teensy hardware? |
Yes, by "use" I mean the controller is actually driving a motor. I.e. if isRunning returns true.
This code is a couple of month old, I don't recall why I placed that stop() here. Probably without any reason.
Yes. Personally I prefer declaring the controllers locally in the functions. (Encapsulation is always a good idea)
Just switch to the other branch using the shown dropdown and download as you are used to. (Consider to use GitHub Desktop which makes those things much easier, you can switch on the fly, get notice of new versions etc)
No chance, and yes, the timers are built into the processor chip. So unless I redesign the chip I can't change that :-) |
Fabulous thanks. I'm a bit of a virgin with Github... Re the encapsulation, I agree that seems ideal. I may use a combination, such as the rotation controllers global and others local - then I can check if one of the rotation controllers is still busy from a separate function. Does that sound fair? Or do you think another way of doing that is preferable? To put it in context, the steppercontrollers will only be used occasionally for isolated events such as moving axes to a position, which can't be done with the rotation controller. |
Ok. I had a quick experiment but the devTimer library is not working properly for me. Maybe I am overlooking something, but I couldn't get it respond to a locally declared controller. Then I noticed some other problems, even when using only global controllers. Basically I am using an external Windows program which allows me to jog an axis back and forth.. I am sending information via serial to the Teensy, and when I press a jog button on my application it sends a code to identify the axis accompanied by a positive or negative value , which is attended to in the Teensy by this snippet... speedFactor = receivedint0 / 20000.0; Then when I release the button it sends a stop signal which provokes this: Rctl_0.overrideSpeed(0); The Teensy main code loop also sends serial data back to my application to show the motor position. I can jog a motor back and forth all day with the previous library, but using the new devTimer lib it ceases to respond after a half dozen or so times pressing the button. The Teensy is not entirely crashed at this stage since I can still zero the position of a motor, but I can no longer jog it. |
Since this branch is still under development it definitely can be that there are bugs in it. Would it be possible to generate / post a minimal sketch showing the behaviour so that I can try to fix it? |
2 chnl Teensy Serial debug.zip Simplified Windows app and ino file which shows a bug using devTimer version. You can jog ok initially and zero axes. However as soon as you make a goto position move, the jog fails to respond. Goto position continues to work however. This is not exactly the same bug as I saw earlier but will have to do, and it is fairly clear when changing between devKit and previous version where it worked fine. I hope this is not the tip of the iceberg... I intend still to play more with creating multiple controllers locally, and will move on to that once I have more time. |
I downloaded your files and everything seems to work (Teensy 3.2). How long do I need to play with the jog until the problem usually appears? |
Tried now for about 5min. I'm not able to crash it. Looking at your code (nicely written by the way) I only see a few possible problems:
if (letter0 == "G0")
{
//Rctl_0.stop();
moveto = receivedint0;
Mtr_0.setTargetAbs(moveto);
Sctl_0.moveAsync(Mtr_0);
counting = true;
letter0 = "qq";
} The new code is more sensitive to that problem because it will allocate a new timer when you start the movement and only release it after stopping. 0-> If the old controller is still running you might run out of timers. Best would be to make sure that the motor stands still
class usb_serial_class : public Stream
{
public:
constexpr usb_serial_class() {}
void begin(long) {
//uint32_t millis_begin = systick_millis_count;
//disabled for now - causes more trouble than it solves?
//while (!(*this)) {
// wait up to 2.5 seconds for Arduino Serial Monitor
// Yes, this is a long time, but some Windows systems open
// the port very slowly. This wait allows programs for
// Arduino Uno to "just work" (without forcing a reboot when
// the port is opened), and when no PC is connected the user's
// sketch still gets to run normally after this wait time.
//if ((uint32_t)(systick_millis_count - millis_begin) > 2500) break;
//}
} |
Hi thanks I think you might still be thinking of the bug I mentioned earlier. The code sample has a slightly different bug which I mentioned in the comment. I said the bug happens after issuing a goto command. ie. with cursor in the goto position input press the Enter key. Motor will move to the commanded position, but after that the jogging will no longer respond. :( The existing code is really just for R&D so it is a bit quick and dirty variety. I will likely not use strings, and code structure will be developed to prevent cross purpose problems. But also if I use local declarations it is going to have to be quite different. I already found for example that using local declaration inside for the goto if() statement has a problem. ie. when the statement is exited, the motor will stop moving even if it has not completed its move to position... :( So I will have to make sure it remains inside the statement - which currently means the counting (feedback) is blocked and no other goto or jogging can be performed during this time. So I can see it getting a lot more complicated... |
When I wrote the answer I did think of the other bug indeed, but after rereading I tested the Go command as well. Works without problems. I can even jog while it moves and I can have both motors moving. Nothing crashes. BTW: I didn't bother to connect motors but looked at the display only. Hope that shows the error as well? Which board are you using? I used a T3.2 for the tests. Are you sure that you link in the correct branch? In case you use the Arduino IDE it often keeps cached object code around. To check: the devTimer branch implements a setPullInOutSpeed function which is not there in the master branch. If a call to this function compiles you should be good.
That's clear. If you leave a function all local variables will be deconstructed so the local controller does not exist anymore and can't move your motors. In your application where you don't finalize the movements within one function a local controller is not the right tool. |
I must not have the devTimer installed correctly as that function returns "no matching function" error. |
Since Arduino is caching quite agressively I'd delete all the object files. They are somewhere at %temp% if IIRC google for arduino build folder. |
I messed around using the Arduino library manager as well as deleting what looked like possible cache files in User\LoginName\AppData\Local\Arduino15\ *.json Searching for "Arduino Build Folder" lead me to the folder where the sketch's compiled hex file goes. Didn't seem to be what I'm looking for... so still no joy... no compile of setPullInSpeed... BTW I am using Teensy V3.6. Maybe that's the problem? Thanks |
Arduino is being very belligerent I'm afraid. I deleted those files but made no difference. Under Arduino library manager it lets you back track to older versions of installed libraries but there is no option to remove or replace - which seems what is really needed in this case... Short of completely uninstalling and re-installing Arduino I'm a bit stuck. Then maybe even that won't work! I am getting ready to travel now - so this will have go on the backburner... :( |
:-) This IDE is perfect for giving newbies a quick start but it definitely is not suited for serious work. Hate to advertise myself but since you are under Windows (?) you could give VisualTeensy a try. Removes a lot of headaches. You can download binaries here: https://github.com/luni64/VisualTeensy/releases Anyway: Did you check the sources as I wrote above? Maybe you simply copied the wrong ones? |
Thanks, I'll check out Visual Teensy when I get a chance. Yes the source was in the folder. I used this line to call it. Mtr_0.setPullInOutSpeed(1000); Ah, I just realised a mistake - that line needs two values. So I tried this : Mtr_0.setPullInSpeed(1000); And now it compiles. But does it still have the bug? Yes. Although it is only the first axis that is affected (will not jog). |
Sure, but this function is there in both branches so it doesn't help to check if you have the right branch installed. |
Tested everything with a T3.6. Works perfectly. Something seems to be wrong at your side... |
First got this... "get_version_sep07a:8: error: 'TeensyStep' has not been declared" So I assumed I also need to download a new version of devTimer. So now I get the response Ver2.01 devTimer. I compiled and ran my code again, and the bug is still there. Perhaps we have to assume Teensy 3.6 is the reason? Given there is not other variable I can think of - or that our compiler is slightly different as you aren't using the Arduino IDE. |
Good, at least one unknown less... Tested the T3.6 already (see above). Good idea with the IDE. Here my Setup:
Testing:
This works perfectly. Repeated with different targets, first or second motor etc. Edit: |
I have the same. |
I'd really like to nail this down but I do understand that you don't want to spend more time on it.
The main new feature in the devTimer version is the on the fly assignment of the timers. This allows you to have as much controllers constructed as you want as long as you don't have more than 4 controllers actually moving motors at the same time. This would be a perfect fit for your application since you could have the 4 rotational controllers as well as 4 step controllers living in global space. Current plan is to merge the devTimer branch into the master branch when I did the adaptions to the T4. |
Just in case I can talk you into a last test: I thought maybe the command interface plays tricks (EMC noise etc) Can you try the version below? I added code to toggle the LED on received commands and to go into panic mode (fast blinking LED) if you start a movement while one is running. This should not happen as long as you don't Jog while the motor is in a Goto movement. #include "TeensyStep.h"
int currentPos;
float period = 100; //interval between counts in milliseconds
long counter = 0; //increment counter
unsigned long startMillis;
unsigned long currentMillis;
boolean counting = true;
float speedFactor;
float accelFactor;
float moveto;
// stuff needed for SERIAL to work
String sendstr, pref;
String inData; //everything from Serial comes to this variable to then be processed.
String receptacle0, receptacle1;// this variable is used to store data that has been stripped from any letters, keeping numbers (in String form).
String letter0, letter1;//this variable holds the first letter of the incoming data.This first letter determines where to send the data.
float receivedint0, receivedint1; // Set value
//-------------------------------
RotateControl Rctl_0;
RotateControl Rctl_1;
Stepper Mtr_0(0, 1); // STEP pin: 0, DIR pin: 1
Stepper Mtr_1(2, 3);
Stepper Mtr_2(4, 5);
Stepper Mtr_3(6, 7);
StepControl Sctl_0;
StepControl Sctl_1;
bool isSomethingRunning()
{
return Sctl_0.isRunning() || Sctl_1.isRunning() || Rctl_0.isRunning() || Rctl_1.isRunning();
}
void panic()
{
while(1)
{
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
delay(50);
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(57600);
Serial.println("");
Mtr_0.setMaxSpeed(20000);
Mtr_0.setAcceleration(20000);
Mtr_1.setMaxSpeed(20000);
Mtr_1.setAcceleration(20000);
Mtr_2.setMaxSpeed(20000);
Mtr_2.setAcceleration(20000);
Mtr_3.setMaxSpeed(20000);
Mtr_3.setAcceleration(20000);
Rctl_0.rotateAsync(Mtr_0);
Rctl_0.overrideSpeed(0); // start with stopped axis
Rctl_1.rotateAsync(Mtr_1);
Rctl_1.overrideSpeed(0); // start with stopped axis
}
//---------------------------------function to split incoming data
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = { 0, -1 };
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
void loop() {
while (Serial.available() > 0) {
char received = Serial.read();
inData += received;//the variable that holds the "raw" incoming string
if (received == '\n')
{
receptacle0 = getValue(inData, ' ', 0);// hold the first axis data so that it can be stripped and used accordingly
receptacle1 = getValue(inData, ' ', 1);//hold the second axis data so that it can be stripped and used accordingly
String copyrcp0 = receptacle0;
String copyrcp1 = receptacle1;
letter0 = copyrcp0.remove(2, 16);//removes the numbers and keeps the first two letters
letter1 = copyrcp1.remove(2, 16);//removes the numbers and keeps the first two letters
receptacle0.remove(0, 2);//removes the first digit (a letter) and keeps the numbers which are still in tring form
receptacle1.remove(0, 2);//removes the first digit (a letter) and keeps the numbers which are still in tring form
receivedint0 = receptacle0.toInt();//this converts string of "numbers" to integers, needed to set steps, speed, etc.
receivedint1 = receptacle1.toInt();//this converts string of "numbers" to integers, needed to set steps, speed, etc.
inData = ""; // Clear received buffer
}
}//end while
currentMillis = millis();
//---------------------------------command to go to position
if (letter0 == "G0") {
if(isSomethingRunning()) panic();
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // toggle LED to see comming in commands
//Rctl_0.stop();
moveto = receivedint0;
Mtr_0.setTargetAbs(moveto);
Sctl_0.moveAsync(Mtr_0);
counting = true;
letter0 = "qq";
}
if (letter1 == "G1") {
if(isSomethingRunning()) panic();
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // toggle LED to see comming in commands
//Rctl_1.stop();
moveto = receivedint1;
Mtr_1.setTargetAbs(moveto);
Sctl_1.moveAsync(Mtr_1);
letter1 = "qq";
}
//---------------------------------command to reset currentPos to zero
if (letter0 == "Z0") {
Mtr_0.setPosition(0);
letter0 = "qq";
}
if (letter1 == "Z1") {
Mtr_1.setPosition(0);
letter1 = "qq";
}
//---------------------------------command to set acceleration
if (letter0 == "A0") {
accelFactor = receivedint0 / 20000.0;
Rctl_0.overrideAcceleration(accelFactor);
letter0 = "qq";
}
if (letter1 == "A1") {
accelFactor = receivedint1 / 20000.0;
Rctl_1.overrideAcceleration(accelFactor);
letter1 = "qq";
}
//---------------------------------command to STOP running move or jogging
if (letter0 == "S0") { //STOP ALL
Rctl_0.overrideSpeed(0);
letter0 = "qq";}
if (letter1 =="S1") {
Rctl_1.overrideSpeed(0);
letter1 = "qq";}
//---------------------------------command to run at specified speed
if (letter0 == "J0") { // move/jog
if(isSomethingRunning()) panic();
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // toggle LED to see comming in commands
speedFactor = receivedint0 / 20000.0;
Rctl_0.overrideSpeed(speedFactor);
counting = true;
letter0 = "qq";
}
if (letter1 == "J1") { // move/jog
if(isSomethingRunning()) panic();
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // toggle LED to see comming in commands
speedFactor = receivedint1 / 20000.0;
Rctl_1.overrideSpeed(speedFactor);
counting = true;
letter1 = "qq";
}
//-------------------------------------------------------------
if (counting) {
if (currentMillis - startMillis >= period) {
counter++;
int p0 = Mtr_0.getPosition();
int p1 = Mtr_1.getPosition();
int p2 = Mtr_2.getPosition();
int p3 = Mtr_3.getPosition();
Serial.printf("P0%i,\P1%i,\P2%i,\P3%i\n", p0, p1, p2, p3);
startMillis = currentMillis;
}//end current millis
}//end counting
}//end loop |
Thanks for your patience. I am on vacation for the next few weeks, in Belgium of all places and later in the Balkans.. Despite that I hope I can still find some time to do a little test. I don't have the same computer with me, but I have the Teensy, so we will see... |
Hi Lutz. Thank you for the extra effort. I installed Arduino on my travel computer and retried the code. Surprise it now works! Very strange. Both are Win7 but maybe the other computer has more "history" whereas this one is a fresh installation. Also possible the process I went through of changing from the previous teensystepper library to the devTimer messed things up? Anyway, now I can explore using local controllers... So some time for a quick experiment. Because the local controller has to remain in it's own happy space I tried this if (letter0 == "G0") { I added a while loop to keep the process alive until complete. Note that the LocalSc.moveAsync line is commented. I tried that first and it didn't respond, so I changed to LocalSc.move which did work. Hmm well maybe that's a beta thing? Then I added the serial printF command, but that doesn't return anything. Any idea there? Thanks G |
Hi. I've been writing an app to control 4 or more steppers using random independent graph curves. This is a bit like a cnc application but quite the same. I have struck a problem with the apparent limit on 4 controllers. I need to use a separate rotatecontroller for each motor because of the independent but coordinated movement of these axes. I also need to run atleast one steppercontroller which is used for positioning of the individual axes when not running them via the rotatecontroller. This effectively limits me to 3 axes. Without going into too much detail and alternatives, my question is if it is possible to increase the number of timers so that more than 4 controllers can be used simultaneously? Thanks
The text was updated successfully, but these errors were encountered: