Steppers interrupted by serial or serial loses data due to stepper interrupt

I want to drive up to 10 stepper motors at the same. All with a constant but individual speed. Meanwhile other calculations and data transfer is to be done.

All Steppers have their own driver with dir/step pin. I use AccelStepper for non blocking functions to run the steppers. It is very important that the steppers run as smooth as possible. First I simply put the runSpeed function for every stepper in the main loop, resulting in stuttering when data was written to the serial by the arduino. I now use Timer5 of the Mega to run a function that does nothing but call all runSpeed functions every 200 µs or so. This works very good with the steppers and the rest of the code except for the serial communication. I want to send commands to the arduino but with the interrupt being active some bytes never reach the arduino. Right now I am sending a notification byte that data will be transfered, the arduino answers and stops the interrupt, receives the command and starts the interrupt again. Works like a charm except for the 0.5 s stepper pause inbetween. This is to be solved.

Now I thought about the following: 1. Making the interrupt adoptable. Means: Track alls steps on every motor and calculate when a motor has to do the next step. Make the timer interrupt at exactly that time. Of course the interrupt has to be adjusted after every step of any motor. My fear is that the time demand for the calculations would be quite high, if not too high, so that the arduino gets stuck in interrupts and does not execute other code anymore. 2. Using a second arduino which only runs the steppers in the loop so the timing is no problem and serial data can be received. However, if I want that arduino to confirm the new settings to the main arduino, then I will get short pauses again... Solution would be to switch to interrupts when sending data, else use the main loop (sending data with interrupt works, receiving has the issue).

This is my idea of how to do it. But before I put all the effort in it, I wanted to discuss this with you and ask you, if you have another, better solution that for me.

I attached an exemplary code which with which the arduino cannot receive longer texts without missing bytes.

Multiple Steppers.ino (652 Bytes)

There is a limit to what a 16MHz Arduino can do. If it is fully occupied generating step pulses then it can't also deal with Serial data.

How many steps per second do you need with all of the motors combined?

The AccelStepper library is convenient but it consumes a lot of CPU cycles as it uses floating point maths.

If you are using stepper drivers that take step and direction signals and if you do not need acceleration then you will probably get much better performance with your own stepper code. Have a look at the second (non-blocking) example in this Simple Stepper Code

If the motors are running in an uncoordinated way at different speeds there will be unpredictable occasions when several of them need a step at very close intervals and that is the time when the system will be most vulnerable to interference if some other code gets in the way. For a short period of time a very high pulse rate may be required from the Arduino.

...R

An ESP32 (dual core) running freeRTOS might be of interest to you.

Idahowalker:
An ESP32 (dual core) running freeRTOS might be of interest to you.

Has it enough I/O pins for 10 stepper motor drivers and other things?

What percentage of the extra CPU speed do you lose with freeRTOS? Why not just program it without the RTOS?

...R

Robin2:
Has it enough I/O pins for 10 stepper motor drivers and other things?

What percentage of the extra CPU speed do you lose with freeRTOS? Why not just program it without the RTOS?

...R

Now that's a good question about number of available pins. Me, not sure.

The freeRTOS API is built into the ESP32 and I have not noticed a processing hit with freeRTOS on an ESP32. I, using a LIDAR do 12 scans per degree, 45 degrees per sweep, process those scans to make an image (lots of floating points), and process MMU data with MahonyQuaternionUpdate, with error correction enabled (lots a floating point thingies), Quaternion to angle conversions, and then onto servo torque angles (more floating point thingies), and even send the data to another ESP32 (CAN buss), all under freeRTOS.

Also, as a note, the freeRTOS, which is built-in, on an ESP32, allows a task to be assigned to a core for multiprocessing. The loop function runs on core 1 with a priority of 1, the lowest priority assigned on an ESP32.

Still the number of pins. If needed, now this is just me, I'd be inclined to use STM32 Bluepills to run the motors, if need be, and communicate with the Blupills via the CAN Buss.

Just tossing out ideas.

Another platform is not really what I have in mind, mostly due to lack of time.

I try to keep the steps/s as low as possible, but high enough so that the movement seems to be smooth.
Means, depending on what the steppers have to do, I adjust the microstepping so that I get between 50-100 steps/s right now.

What is of utmost importancy is the accuracy of the whole system. Thats why I did not think of an own stepper library that is not based on floating point. The steps interval for every stepper is calculated before so I do not have rounded steps/s and I ndo not know how big of an impact it is when I round them and therefore lose minimal precision.
However, I already changed the AccelStepper library so that it would fit better to my purposes, deleting lots of code and optimizing it. So it already is faster than the original.

What do you think of my approaches from the first post? Especially the second one.

S3cret:
I try to keep the steps/s as low as possible, but high enough so that the movement seems to be smooth.

You have not answered my question "How many steps per second do you need with all of the motors combined?" And I want to know how many motor steps are needed, not the frequency of your 200µsec call to runSpeed().

Facts are always a great starting point for problem solving.

...R

Sorry, I sometimes think my answers are clear when they are not.

Every stepper that runs, runs at 50-100 steps/s. So in total with all steppers being active it can be up to 1000 steps/s, typically I would say it is around 500.

For now I assumed that a minimum of 50 steps/s is necessary, but I think even less (maybe 20) would still work fine. This is something that I will definitely try later.

A 16MHz Arduino won't have any trouble generating a step every millisec. But a problem may arise if all the motors require their next step within the same millisec when, from time to time, the step timing for the motors coincides. That could happen even at low motor speeds if the speeds of the motors are independent. Look at how these three sequences coincide from time to time

0...3...6...9..12..15..18..21..24
0....4.....8...12...16...20....24
0.....5.....10.....15....20......25

If this was my project I think I would control the steps with my own code without the AccelStepper library - mainly using the style in the second example in my link. And use the digitalWriteFast library for a significant increase in performance.

...R

Yeah, this is a problem that will occur, but it is not noticeable. The steppers themselves run great, not complaining about that. It only gets problematic when Serial reading is to be done or other code is executed. Thus, the only way for me is to work with interrupts for the steppers to run the way they are meant to, but then serial reading will fail for some bytes. And shorter execution times for the iterrupt will unfortunately not really change it, just increase the chance that every byte comes through.

That is why I am thinking about the 2 options from my first post.

S3cret:
It only gets problematic when Serial reading is to be done or other code is executed.

The reason for that problem is that the control of the stepper motors is using too many of the Arduinos CPU cycles and is not leaving enough available to do other work.

Which is why I have been suggesting that you need to find the most efficient way to produce the steps.

...R

Yup, I made a new stepper library that I will test later. However, as I said, this will not fix the problem. For the smoothest execution of the steppers there are only 2 ways for me: Interrupt (as I am doing now, but then serial won't work, no matter how efficient the code within the interrupt is, only less bytes will be lost on the way, but it needs to be zero) or doing nothing big besides running the stepperfunctions in the main loop, but the rest of my code is too heavy, this will not work.

This is why I asked if working with an adjustable interrupt or with 2 arduinos might be a solution.
Or if there are any other possibilities to get this solved.

I missed your question about using 2 Arduinos. If you are having trouble communicating with one Arduino you may still have trouble communicating between the 2 Arduinos. How are you thinking of sharing the load between them?

I still don't have a good mental image of the project you are trying to implement. If I had it would make it a lot easier to make useful suggestions (or avoid silly ones).

And don't forget that responding to an interrupt uses extra CPU cycles.

Post the latest version of the program together with your new stepper library.

...R

Unfortunately, the project itself is confidential, but I will try give you an idea of how I planned it.

As said, steppers should run as smooth as possible with an individual constant speed.

MainArduino does the communication with the PC via Serial, receives commands, does all necessary calculations I talked about. StepperArduino (second arduino) runs the steppers, nothing else.

When a new command from the PC comes in, the MainArduino does all the preparations e.g. calculating the motor speeds. It then sends the speeds to the StepperArduino.

StepperArduino:
See Code. Switch between running the steppers in the loop vs interrupt whenever the other is better. Use the loop when new speeds can come in to prevent data loss, switch to interrupt while setting everything up. (see Code).

The code is all untested, just a proof of concept for now.

I found another solution: One can use the TMC 5130 via SPI and tell them the speed they have to drive the motor. They have an integrated ramp generator and can handle the running of the motors themselves. However, they are of course more expensive than the 2130 I am using now...

StepperArduino.ino (1.93 KB)

Stepper.h (436 Bytes)

Stepper.cpp (993 Bytes)

S3cret:
Unfortunately, the project itself is confidential,

That makes me uneasy.

Why is is confidential? Two thoughts come to mind {A} it is a commercial for-profit project and you are hoping to increase your profit by getting free advice here rather than paying for professional advice or {B} it is something anti-social or illegal.

...R

Robin2:
That makes me uneasy.

Why is is confidential? Two thoughts come to mind {A} it is a commercial for-profit project and you are hoping to increase your profit by getting free advice here rather than paying for professional advice or {B} it is something anti-social or illegal.

...R

I'm not as suspicious as @Robin2, but it seems to me you have 3 choices:

  • Post enough information here so that someone could possibly help you for free. But, you get what you pay for.

  • Solve the problem yourself.

  • Pay someone to solve the problem for you after signing an NDA.