New interrupt-friendly versions of run() and runSpeed()

1,045 views
Skip to first unread message

Jeff Siddall

unread,
Mar 8, 2020, 10:42:00 PM3/8/20
to accelstepper
I needed smooth high speed stepper operation, while also doing other things in the background, so had to switch to using interrupts.  To accomadate that I created a new runSpeed() function called runSpeedNow(), and also a new function called runNow() to call it.  The only difference between the new runSpeedNow function and the original is that the new one does not contain any "time" checks.  As soon as it is called it performs a step.

I tested this in my project, using Timer1 interrupts, to over 2000 steps/s (600 RPM), running perfectly smooth on a Nano.  Since nothing from the existing code is modified these should be 100% compatible.

Patches against 1.59 are attached. Let me know if anyone has questions.
AccelStepper.h.patch
AccelStepper.cpp.patch

Justin Plante

unread,
Apr 7, 2020, 2:15:40 PM4/7/20
to accelstepper
Hi Jeff. Thank you for the patch you proposed here. Could you explain how to activate this patch?

Jeff Siddall

unread,
Apr 7, 2020, 2:28:50 PM4/7/20
to accelstepper


On Tuesday, April 7, 2020 at 2:15:40 PM UTC-4, Justin Plante wrote:
Hi Jeff. Thank you for the patch you proposed here. Could you explain how to activate this patch?

Sure, just download them to the same place as the library, then run these:

patch -p0 <AccelStepper.h.patch


and:


patch -p0 <AccelStepper.cpp.patch

Shane Horrocks

unread,
Apr 13, 2020, 7:07:10 AM4/13/20
to accelstepper
Hi Jeff,

I am using Interrupts to measure an RPM with a Hall sensor, and I want to drive the Stepper at a speed relative to the RPM measured.  I have passed the RPM value to setSpeed(), then called runSpeed() to turn the stepper at the RPM.  The desired RPM is printed correctly to the Serial, but the Stepper is not spinning at the printed speed - it's really slow.

To questions:
1. Do you think the interrupts could be the issue? (happy to post my code if that helps)
2. How do I run the patches that you wrote?  

Thanks for your help.

Shane.

Brian Engel

unread,
Apr 13, 2020, 9:03:21 AM4/13/20
to accels...@googlegroups.com
If your using timer interrupts,  using Serial.println within them will slow things down dramatically.  Try commenting out your serial.print calls and see if it runs normally. 

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/accelstepper/5df4fbc8-4483-456d-8c23-5f17a2695e6a%40googlegroups.com.

Shane Horrocks

unread,
Apr 13, 2020, 9:29:01 AM4/13/20
to accelstepper
G'day Brian,

Thanks so much for that - I commented out the Serial.prints() and that fixed it!! I immediately got the results I have been chasing for a couple of days.  Thanks for taking the time to post - I really appreciate it.

Shane

Jeff Siddall

unread,
Apr 13, 2020, 9:30:16 AM4/13/20
to accelstepper

On Monday, April 13, 2020 at 7:07:10 AM UTC-4, Shane Horrocks wrote:
To questions:
1. Do you think the interrupts could be the issue? (happy to post my code if that helps)
2. How do I run the patches that you wrote? 

Shane,

1. Yes, could be.  Depends on how you use them.  Keep in mind that ISRs interrupt everything else -- including other interrupts.  So if a step is supposed to happen while you are measuring RPM triggered by an interrupt then the step will be late.  Late steps = slower RPM, unless you make other steps early by the same amount to compensate.  That's where my patches _might_ be useful, because you can use your own algorithm to decide when you want to make a step and then use runSpeedNow() to step as soon as you call it.

As Brian pointed out, other code, like print calls, are also relatively slow, and because they use their own delays based on interrupts, can't be used in an ISR.

2. See my post to Justin.  You need to use the patch command.  Have a look at the documentation for that if you want to know what the commands I posted do.

Good luck!

Shane Horrocks

unread,
Apr 13, 2020, 9:46:10 AM4/13/20
to accels...@googlegroups.com
Hi Jeff,

Thanks very much for taking the time to Create the patch, and to reply. 

I don’t need a super responsive output, and removing the Serial.print calls has improved its operation. 

I will certainly look further into the patch commands. Appreciate your help. 

Shane. 



--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.

Brian Engel

unread,
Apr 13, 2020, 9:52:50 AM4/13/20
to accels...@googlegroups.com
No problem Shane... I'm in the middle of my own project and have learned much thru pain and suffering :-).  Remember to keep those ISR's as quick as possible.  Also make sure your variables are marked as "volatile" if they are shared outside an ISR. 

Depending on how you use it, you may find the accelstepper library too slow due to the floating point math it is using.  In my particular application where rapid accelerations and very precise step intervals are required, I couldn't get it to work well... So I took a different approach of using a precomputed acceleration profile that I stored in an array. It's completely "flat" and is calculated at 10,000 steps/second2. 

Basically I just "move" up and down that array pulling out a step interval as my motor is speeding up or slowing down until I reach my target. Each time I shove that time interval into the timer interrupt register value and perform a "step" (pulse on a pin). 

 With this approach, all that real time floating point math is performed off of my poor arduino :-) 

Here are a couple of links you may find useful. 






 



--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.

Ralph Hulslander

unread,
Apr 13, 2020, 11:02:30 AM4/13/20
to accels...@googlegroups.com
Shane, I'd like to see your code. You could email me instead of posting.

Ralph

Shane Horrocks

unread,
Apr 13, 2020, 6:29:37 PM4/13/20
to accelstepper
Hi Ralph,

No worrries.  I'm very new to coding & arduino, so it's pretty much just cobbled together from stuff I've found on the internet.  Critique is welcome.

/*
Tachometer using micros


On this sketch we are going to measure the period between pulses using the micros() function to get the RPM
(Revolutions Per Minute) from a sensor on pin 2.
This way of measuring RPM makes it accurate, responsive and versatile. No matter how fast or slow the loop is
running, the reading accuracy is not going to be affected. Although, the faster you run the loop, the more amount
of readings you are going to be able to display every second.


It's coded in a way that the micros rollover doesn't create glitches every 71 minutes, so it can run forever
without problems.


We use an interrupt for the input so you have to choose pin 2 or 3 (for Arduino Uno/nano). In this example we
use pin 2.


This sketch was made for my video tutorial shown here: https://www.youtube.com/watch?v=u2uJMJWsfsg


Made by InterlinkKnight
Last update: 05/23/2019
*/







///////////////
// Calibration:
///////////////


const byte PulsesPerRevolution = 1;  // Set how many pulses there are on each revolution. Default: 2.




// If the period between pulses is too high, or even if the pulses stopped, then we would get stuck showing the
// last value instead of a 0. Because of this we are going to set a limit for the maximum period allowed.
// If the period is above this value, the RPM will show as 0.
// The higher the set value, the longer lag/delay will have to sense that pulses stopped, but it will allow readings
// at very low RPM.
// Setting a low value is going to allow the detection of stop situations faster, but it will prevent having low RPM readings.
// The unit is in microseconds.


const unsigned long ZeroTimeout = 800000;                                 // For high response time, a good value would be 100000.
                                                                         
// For reading very low RPM, a good value would be 300000.




// Calibration for smoothing RPM:
const byte numReadings = 2;                                               // Number of samples for smoothing. The higher, the more smoothing, but it's going to
                                                                         
// react slower to changes. 1 = no smoothing. Default: 2.


/////////////
// Variables:
/////////////


volatile unsigned long LastTimeWeMeasured;                                // Stores the last time we measured a pulse so we can calculate the period.


volatile unsigned long PeriodBetweenPulses = ZeroTimeout+1000;            // Stores the period between pulses in microseconds.
                                                                         
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
volatile unsigned long PeriodAverage = ZeroTimeout+1000;                  // Stores the period between pulses in microseconds in total, if we are taking multiple pulses.
                                                                         
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
unsigned long FrequencyRaw;                                               // Calculated frequency, based on the period. This has a lot of extra decimals without the decimal point.


unsigned long FrequencyReal;                                              // Frequency without decimals.


unsigned long RPM;                                                        // Raw RPM without any processing.


unsigned int PulseCounter = 1;                                            // Counts the amount of pulse readings we took so we can average multiple pulses before calculating the period.


unsigned long PeriodSum;                                                  // Stores the summation of all the periods to do the average.


unsigned long LastTimeCycleMeasure = LastTimeWeMeasured;                  // Stores the last time we measure a pulse in that cycle.
                                                                         
// We need a variable with a value that is not going to be affected by the interrupt
                                                                         
// because we are going to do math and functions that are going to mess up if the values
                                                                         
// changes in the middle of the cycle.
                                   
unsigned long CurrentMicros = micros();                                   // Stores the micros in that cycle.
                                                                         
// We need a variable with a value that is not going to be affected by the interrupt
                                                                         
// because we are going to do math and functions that are going to mess up if the values
                                                                         
// changes in the middle of the cycle.


unsigned int AmountOfReadings = 1;                                        // We get the RPM by measuring the time between 2 or more pulses so the following will set how many pulses to
                                                                         
// take before calculating the RPM. 1 would be the minimum giving a result every pulse, which would feel very responsive
                                                                         
// even at very low speeds but also is going to be less accurate at higher speeds.
                                                                         
// With a value around 10 you will get a very accurate result at high speeds, but readings at lower speeds are going to be
                                                                         
// farther from eachother making it less "real time" at those speeds.
                                                                         
// There's a function that will set the value depending on the speed so this is done automatically.


unsigned int ZeroDebouncingExtra;                                         // Stores the extra value added to the ZeroTimeout to debounce it.
                                                                         
// The ZeroTimeout needs debouncing so when the value is close to the threshold it
                                                                         
// doesn't jump from 0 to the value. This extra value changes the threshold a little
                                                                         
// when we show a 0.
 


// Variables for smoothing tachometer:
unsigned long readings[numReadings];                                     // The input.
unsigned long readIndex;                                                 // The index of the current reading.
unsigned long total;                                                     // The running total.
unsigned long average;                                                   // The RPM value after applying the smoothing.










/////////////
// Added Code for Stepper:
/////////////


#include <AccelStepper.h>                                                 //Importing the Arduino Stepper motor library






#define motorPin1  8      // IN1 on the ULN2003 driver
#define motorPin2  9      // IN2 on the ULN2003 driver
#define motorPin3  10     // IN3 on the ULN2003 driver
#define motorPin4  11     // IN4 on the ULN2003 driver


#define MotorInterfaceType 8


//AccelStepper stepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper = AccelStepper(MotorInterfaceType, motorPin1, motorPin3, motorPin2, motorPin4);




//#define joystick  A0                                                   //Unused at the moment; commented out




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void setup()  // Start of setup:
{


 
Serial.begin(9600);  // Begin serial communication.
  attachInterrupt
(digitalPinToInterrupt(2), Pulse_Event, RISING);  // Enable interruption pin 2 when going from LOW to HIGH.


  stepper
.setMaxSpeed(1500);
 
//stepper.setAcceleration(1500);
 
  delay
(1000);  // We sometimes take several readings of the period to average. Since we don't have any readings
               
// stored we need a high enough value in micros() so if divided is not going to give negative values.
               
// The delay allows the micros() to be high enough for the first few cycles.


 


}  // End of setup.


void loop()  // Start of loop:
{


 
// The following is going to store the two values that might change in the middle of the cycle.
 
// We are going to do math and functions with those values and they can create glitches if they change in the
 
// middle of the cycle.
 
LastTimeCycleMeasure = LastTimeWeMeasured;  // Store the LastTimeWeMeasured in a variable.
 
CurrentMicros = micros();  // Store the micros() in a variable.


 
// CurrentMicros should always be higher than LastTimeWeMeasured, but in rare occasions that's not true.
 
// I'm not sure why this happens, but my solution is to compare both and if CurrentMicros is lower than
 
// LastTimeCycleMeasure I set it as the CurrentMicros.
 
// The need of fixing this is that we later use this information to see if pulses stopped.
 
if(CurrentMicros < LastTimeCycleMeasure)
 
{
   
LastTimeCycleMeasure = CurrentMicros;
 
}


 
// Calculate the frequency:
 
FrequencyRaw = 10000000000 / PeriodAverage;  // Calculate the frequency using the period between pulses.


 
// Detect if pulses stopped or frequency is too low, so we can show 0 Frequency:
 
if(PeriodBetweenPulses > ZeroTimeout - ZeroDebouncingExtra || CurrentMicros - LastTimeCycleMeasure > ZeroTimeout - ZeroDebouncingExtra)
 
{  // If the pulses are too far apart that we reached the timeout for zero:
   
FrequencyRaw = 0;  // Set frequency as 0.
   
ZeroDebouncingExtra = 2000;  // Change the threshold a little so it doesn't bounce.
 
}
 
else
 
{
   
ZeroDebouncingExtra = 0;  // Reset the threshold to the normal value so it doesn't bounce.
 
}


 
FrequencyReal = FrequencyRaw / 10000;  // Get frequency without decimals.
                                         
// This is not used to calculate RPM but we remove the decimals just in case
                                         
// you want to print it.


 
// Calculate the RPM:
  RPM
= FrequencyRaw / PulsesPerRevolution * 60;  // Frequency divided by amount of pulses per revolution multiply by
                                                 
// 60 seconds to get minutes.
  RPM
= RPM / 10000;  // Remove the decimals.




 
// Smoothing RPM:
  total
= total - readings[readIndex];  // Advance to the next position in the array.
  readings
[readIndex] = RPM;  // Takes the value that we are going to smooth.
  total
= total + readings[readIndex];  // Add the reading to the total.
  readIndex
= readIndex + 1;  // Advance to the next position in the array.


 
if (readIndex >= numReadings)  // If we're at the end of the array:
 
{
    readIndex
= 0;  // Reset array index.
 
}
 
 
// Calculate the average:
  average
= total / numReadings;  // The average value it's the smoothed result.


 
// Print information on the serial monitor:
 
// Comment this section if you have a display and you don't need to monitor the values on the serial monitor. **Commented by SH to improve the speed of the program**
 
// This is because disabling this section would make the loop run faster.
 
//Serial.print("Period: ");
 
//Serial.print(PeriodBetweenPulses);
 
//Serial.print("\tReadings: ");
 
//Serial.print(AmountOfReadings);
 
//Serial.print("\tFrequency: ");
 
//Serial.print(FrequencyReal);
 
//Serial.print("\tRPM: ");
 
//Serial.print(RPM);




///////////////
// Stepper Motor Function:
///////////////
if(average > 10){
 
float stepperSpeed = map(average, 10, 1600, 0, 1500);
 
if(stepperSpeed > 1000){
    stepperSpeed
= 999;
 
}
  stepper
.setSpeed(stepperSpeed);
  stepper
.runSpeed();}
 
 
//Serial.print("\tTachometer: ");
 
//Serial.print(average);
 
//Serial.print("    StepperSpeed ");
 
//Serial.println(stepperSpeed);}


 
//int stepperspeed = map(average, 10, 1200, 5, 1000);
 
//if(average > 10){
   
//stepper.setSpeed(average);
   
//stepper.runSpeed();}
   
//Serial.print("Stepper Speed ");
   
//Serial.println(stepperspeed);


}  // End of loop.






 
void Pulse_Event()  // The interrupt runs this to calculate the period between pulses:
{


 
PeriodBetweenPulses = micros() - LastTimeWeMeasured;  // Current "micros" minus the old "micros" when the last pulse happens.
                                                       
// This will result with the period (microseconds) between both pulses.
                                                       
// The way is made, the overflow of the "micros" is not going to cause any issue.


 
LastTimeWeMeasured = micros();  // Stores the current micros so the next time we have a pulse we would have something to compare with.










 
if(PulseCounter >= AmountOfReadings)  // If counter for amount of readings reach the set limit:
 
{
   
PeriodAverage = PeriodSum / AmountOfReadings;  // Calculate the final period dividing the sum of all readings by the
                                                   
// amount of readings to get the average.
   
PulseCounter = 1;  // Reset the counter to start over. The reset value is 1 because its the minimum setting allowed (1 reading).
   
PeriodSum = PeriodBetweenPulses;  // Reset PeriodSum to start a new averaging operation.




   
// Change the amount of readings depending on the period between pulses.
   
// To be very responsive, ideally we should read every pulse. The problem is that at higher speeds the period gets
   
// too low decreasing the accuracy. To get more accurate readings at higher speeds we should get multiple pulses and
   
// average the period, but if we do that at lower speeds then we would have readings too far apart (laggy or sluggish).
   
// To have both advantages at different speeds, we will change the amount of readings depending on the period between pulses.
   
// Remap period to the amount of readings:
   
int RemapedAmountOfReadings = map(PeriodBetweenPulses, 40000, 5000, 1, 10);  // Remap the period range to the reading range.
   
// 1st value is what are we going to remap. In this case is the PeriodBetweenPulses.
   
// 2nd value is the period value when we are going to have only 1 reading. The higher it is, the lower RPM has to be to reach 1 reading.
   
// 3rd value is the period value when we are going to have 10 readings. The higher it is, the lower RPM has to be to reach 10 readings.
   
// 4th and 5th values are the amount of readings range.
   
RemapedAmountOfReadings = constrain(RemapedAmountOfReadings, 1, 10);  // Constrain the value so it doesn't go below or above the limits.
   
AmountOfReadings = RemapedAmountOfReadings;  // Set amount of readings as the remaped value.
 
}
 
else
 
{
   
PulseCounter++;  // Increase the counter for amount of readings by 1.
   
PeriodSum = PeriodSum + PeriodBetweenPulses;  // Add the periods so later we can average.
 
}


}  // End of Pulse_Event.


Hope it helps/

Shane





On Tuesday, April 14, 2020 at 1:02:30 AM UTC+10, Ralph Hulslander wrote:
Shane, I'd like to see your code. You could email me instead of posting.

Ralph

On Mon, Apr 13, 2020 at 9:52 AM Brian Engel <br...@engelmail.us> wrote:
No problem Shane... I'm in the middle of my own project and have learned much thru pain and suffering :-).  Remember to keep those ISR's as quick as possible.  Also make sure your variables are marked as "volatile" if they are shared outside an ISR. 

Depending on how you use it, you may find the accelstepper library too slow due to the floating point math it is using.  In my particular application where rapid accelerations and very precise step intervals are required, I couldn't get it to work well... So I took a different approach of using a precomputed acceleration profile that I stored in an array. It's completely "flat" and is calculated at 10,000 steps/second2. 

Basically I just "move" up and down that array pulling out a step interval as my motor is speeding up or slowing down until I reach my target. Each time I shove that time interval into the timer interrupt register value and perform a "step" (pulse on a pin). 

 With this approach, all that real time floating point math is performed off of my poor arduino :-) 

Here are a couple of links you may find useful. 






 



On Mon, Apr 13, 2020 at 9:29 AM Shane Horrocks <ratt...@gmail.com> wrote:
G'day Brian,

Thanks so much for that - I commented out the Serial.prints() and that fixed it!! I immediately got the results I have been chasing for a couple of days.  Thanks for taking the time to post - I really appreciate it.

Shane

On Monday, March 9, 2020 at 12:42:00 PM UTC+10, Jeff Siddall wrote:
I needed smooth high speed stepper operation, while also doing other things in the background, so had to switch to using interrupts.  To accomadate that I created a new runSpeed() function called runSpeedNow(), and also a new function called runNow() to call it.  The only difference between the new runSpeedNow function and the original is that the new one does not contain any "time" checks.  As soon as it is called it performs a step.

I tested this in my project, using Timer1 interrupts, to over 2000 steps/s (600 RPM), running perfectly smooth on a Nano.  Since nothing from the existing code is modified these should be 100% compatible.

Patches against 1.59 are attached. Let me know if anyone has questions.

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accels...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accels...@googlegroups.com.

Brian Engel

unread,
Apr 13, 2020, 6:46:10 PM4/13/20
to accels...@googlegroups.com
Shane,

I right off the bat noticed your comment about the he micros() function will overflow after about 70 minutes.

If you cast it to an unsigned long  like this when you do the math, (unsigned long)(micros() - prevStepTimeMicros), it avoids the problem. There are articles that go into the gorey details you can google search for. 





To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/accelstepper/31dccb56-cc4e-45b1-bdd5-fe69dc4a8d22%40googlegroups.com.

Ralph Hulslander

unread,
Apr 14, 2020, 10:17:00 AM4/14/20
to accels...@googlegroups.com
Thanks Shane, Brian I'd like more info on using a Array instead processor math, it seems to make sense, but I am not a programmer so I need more info.

Ralph

Harrzack

unread,
May 14, 2020, 7:39:40 AM5/14/20
to accelstepper
Nice addition!  Could you be a tad more clear on how to do the patch?  To what program am I passing the line "patch -p0..."  "run these..." is a bit vague and not to helpful for a noob!   :-)

Jeff Siddall

unread,
May 15, 2020, 5:24:16 PM5/15/20
to accelstepper
Patch is the name of the command.  But you don't need to use patch if you don't want to.  Just edit the file and insert the lines from the patch file at the row indicated.

PW

unread,
May 15, 2020, 6:22:27 PM5/15/20
to accelstepper
You say in your first reply to download them to the library and run the two lines you posted
On your last reply you said to edit the file and insert the lines from the patch file at the line indicated.?

Sorry but I’m still confused as to how you actually use these patch files.

Jeff Siddall

unread,
May 19, 2020, 6:23:52 PM5/19/20
to accelstepper
A patch file is just a list of which lines need to be added/removed from an existing file.  The patch command in my original post will do that for you.  If you don't have the patch command you can install it.  If you prefer you can manually insert the lines in the file at the lines indicated in the patch file.  In that case everything with a single "+" in front of it needs to be added to the original file.

Peter Wright

unread,
May 20, 2020, 7:54:06 AM5/20/20
to accels...@googlegroups.com
Thanks. 

Sent from my iPad
--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.

Andrei CNCman

unread,
Dec 30, 2021, 5:09:24 AM12/30/21
to accelstepper
Hi, Jeff,

Many thanks for so useful work. Are patches posted on May 9, 2020 latest versions? Do you have patches for AccellStepper 1.61?
Thanks.

je...@siddall.name

unread,
Dec 30, 2021, 9:55:09 AM12/30/21
to accelstepper
Yes, those are the latest.  The changes are relatively minor so they probably work with 1.61, though I have not tested.  Give it a try and if it doesn't work I will see if I can come up with some new ones.

A. Craig West

unread,
Feb 27, 2022, 8:30:12 PM2/27/22
to accelstepper
I recently started working on my own version of something similar to this, as I noticed that it is very difficult to synchronise the movement of two separate motors precisely. I am still working on the code, but even without interrupts I have something working fairly well using a fairly simple schedule based system where one run method controls the stepping of all of the motors. My initial implementation uses a subclass of AccelStepper, but I essentially have to duplicate a lot of code because _currentPos and _stepInterval are private. If those were both protected, it would require a lot less work. Making computeNewSpeed virtual and protected would also be very helpful. In my case, I want to use the original computeNewSpeed, but my scheduler needs to know when it has been called, which is very easy to do if it is virtual. If computeNewSpeed returned the _stepInterval I wouldn't even need _stepinterval to be protected. Somebody else was looking at using non-linear acceleration profiles, which would benefit much more directly from computeNewSpeed being made virtual protected.
I will work up a patch for what I have in mind and post it, as well as my ScheduledStepper code

Mike McCauley

unread,
Feb 27, 2022, 8:48:56 PM2/27/22
to accels...@googlegroups.com
On Monday, 28 February 2022 11:30:12 AEST A. Craig West wrote:
> I recently started working on my own version of something similar to this,
> as I noticed that it is very difficult to synchronise the movement of two
> separate motors precisely. I am still working on the code, but even without
> interrupts I have something working fairly well using a fairly simple
> schedule based system where one run method controls the stepping of all of
> the motors. My initial implementation uses a subclass of AccelStepper, but
> I essentially have to duplicate a lot of code because _currentPos and
> _stepInterval are private. If those were both protected, it would require a
> lot less work. Making computeNewSpeed virtual and protected would also be
> very helpful. In my case, I want to use the original computeNewSpeed, but
> my scheduler needs to know when it has been called, which is very easy to
> do if it is virtual. If computeNewSpeed returned the _stepInterval I
> wouldn't even need _stepinterval to be protected. Somebody else was looking
> at using non-linear acceleration profiles, which would benefit much more
> directly from computeNewSpeed being made virtual protected.
> I will work up a patch for what I have in mind and post it, as well as my
> ScheduledStepper code

Maybe a new protected function that returns _stepInterval would suit, rather
than change the signatures of existing members?
--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com 5R3MRFM2+X6
Phone +61 7 5598-7474



A. Craig West

unread,
Feb 27, 2022, 10:36:17 PM2/27/22
to accelstepper
On Sunday, 27 February 2022 at 20:48:56 UTC-5 mikem wrote:
> Maybe a new protected function that returns _stepInterval would suit, rather
> than change the signatures of existing members?

That might work, I will try it and see. Changing the method signature (other than making it protected and virtual, that is) shouldn't change existing code, however, as the return value can be ignored. One of the benefits of being able to override the function is to allow the subclass to be notified of changes in the value. ie:
unsigned long SubclassStepper::computeNewSpeed() {
  unsigned long stepInterval = AccelStepper::computeNewSpeed();
  // Do something with the stepInterval here
  return stepInterval;
}

Alternatively, you could just implement a new acceleration algorithm there, if that's how you roll... I happen to like the one we have, though :-)
As to making _currentPos protected, having a long incrementCurrentPos() and long decrementCurrentPos(), this is helpful for implementing replacements for runSpeed and run, which is necessary for both my scheduled stepper, and interrupt based steppers. or, alternatively, have methods stepForward() and stepBackward() which increment or decrement _currentPos and then call step(), probably returning _currentPos as well. That would probably be the best solution.

Message has been deleted

A. Craig West

unread,
Feb 28, 2022, 9:20:14 PM2/28/22
to accelstepper
Well, I have an initial version of my changes. So far, I am using it in a ScheduledStepper class which works much like MultiStepper, except it ALWAYS uses accelerations. Next step is using interrupts instead of loop, after which I will do full arbitrary microstepping using an internal PWM implementation. I could do that without interrupts, but it would be limited in it's accuracy
make-computeNewSpeed-virtual.patch
Reply all
Reply to author
Forward
0 new messages