Stepper and HC-SR04 at the same time

Hey there,

I've just started out with my arduino and am planning to build a robot with two steppers and a HC-SR04.

The basic behavior I am aiming for:

  1. Drive forward with constant speed
  2. Call function "avoidObstacle()" or something similar that literally just plays a pattern that should make the robot go back and turn to the right to continue on a new path

The problem I'm facing:
The motor stepping always gets interrupted, resulting in non-uniform motion of the wheels.

What I've tried:

  • Interrupts => can make it work with a simple button, but I don't know how to send an interrupt when the SR04 detects something within X cm - which, as I understand interrupts, is not possible.
  • Multiple libraries for the steppers as well as the HC-SR04 => They all block each other and the program still runs purely linear
  • Timers => I've read a couple of suggestions (including the SeveralThingsAtTheSameTime.ino) and I still can't wrap my brain around how timers will somehow magically make functions async, but I probably just don't understand it.

(maybe) stupid questions:

  • Is there any way of proper multi-tasking/async functions on an Arduino? I'm used to this from other programming languages and this is my first time with C++ and a microcontroller.
  • If not: How can I achieve my planned behavior for the robot?

Here is my most recent code. Tried timers this time. Didn't know why I expected this to work though:

#include <AccelStepper.h>
#include <NewPing.h>

#define dirPin1 51
#define stepPin1 53
#define dirPin2 47
#define stepPin2 49
#define motorInterfaceType 1
AccelStepper stepper1(AccelStepper::DRIVER, stepPin1, dirPin1);
AccelStepper stepper2(AccelStepper::DRIVER, stepPin2, dirPin2);

#define TRIG_PIN 45
#define ECHO_PIN 43
#define MAX_DISTANCE 200
#define PING_INTERVAL 33
NewPing sonar(TRIG_PIN, ECHO_PIN , MAX_DISTANCE);

unsigned long pingTimer;
unsigned int cm;

unsigned long currentMillis = 0; 
unsigned long previousSrMillis = 0;
unsigned long previousStepperMillis = 0; 

void setup()
{ 
  Serial.begin(9600);
  stepper1.setMaxSpeed(1300);
  stepper2.setMaxSpeed(1300);
  stepper1.setSpeed(1300);
  stepper2.setSpeed(1300);
  pingTimer = millis() + 75; // First ping start in ms.
}

void loop()
{
  currentMillis = millis();
  checkDistance();
  runSteppers();
}

void checkDistance()
{
    if (millis() >= pingTimer) {
      pingTimer += PING_INTERVAL;
      oneSensorCycle(); // Do something with results.
      sonar.timer_stop();
      cm = 0;
      sonar.ping_timer(echoCheck);
    }
}

void echoCheck() { // If ping echo, set distance to array.
  if (sonar.check_timer())
    cm = sonar.ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // Do something with the results.
  Serial.println(cm);
}

void runSteppers()
{
  if (currentMillis - previousStepperMillis >= 1){

    if (cm == 0) {
      //Serial.println("move forward");
      stepper1.setSpeed(1300);
      stepper2.setSpeed(1300);
      stepper1.runSpeed();
      stepper2.runSpeed();
    }

    previousStepperMillis = currentMillis;
  }
}

You don't want to put your runSpeed() calls inside an if() statement that only gets called once per millisecond. You want to call this as often as possible (every time through loop). If it is not time for another step, it won't do anything.

Thanks!

Actually, I would prefer all of this to run without timers as I want the SR04 to poll the distance as often as possible. Sadly, even without the timers, the SR04 will still interrupt the Steppers. Is there any way to let this run async.

If you compare your code to the NewPingEventTimer code, you will notice that your should be "doing things" in the echoCheck() function after the timer has expired, not every cycle in the checkDistance() function. You are also pinging every 33msec but your baud rate is only 9600. You can easily fill up your Tx buffer which will cause your Serial.print() calls to block until there is room to use. Make it 115200.

If you get an ESP32, they are dual core so you can have each doing things, but it will take some work to get there.

You could give the MobaTools library a try. The MoToStepper class of his library creates the stepper pulses in timer interrupts. If your SR04 doesn't block interrupts it should run in parallel without interfering each other.

Check out my tutorial on Multi-tasking in Arduino which has a complete non-blocking stepper motor example that also takes a sensor input and user commands.

Thanks for the suggestions. I will have a look into MobaTools, will also check out your multi-tasking tutorial drmpf and also check what's going on with ESP32's.

While I'm working on those things, a more general question for the direction I want to go in:
Essentially, the long-term goal is to build a floor swiping robot that works with a couple of steppers, SR04's and a 360° Lidar.

Looking at that long-term vision: Is that something that is viable to be done with Arduino's alone or will it make sense for me to move quickly to a Raspberry Pi or a combination of the two?

Before you get too far down the stepper rabbit hole, why stepper motors? Steppers are less efficient than some other choices like brushed motors with encoders especially when battery powered.

Since I was quite new to the topic and I only knew DC motors as unprecise, I went with steppers. The reason I was looking for precision was the fact that at some point I want to add a 360° Lidar to accurately map my place for efficient cleaning and customizability.

Looking into brushed motors with encoders it looks like I will be able to get the same thing out of them, am I right?

If yes, which one will be easier to implement? Thinking about the big plan behind the small start I will eventually have to move to a Raspberry Pi anyways, so multitasking will probably not be a problem anymore if I go down that path and since I already got the steppers...

My suggestion to use geared DC motors with encoders versus stepper motors has more to do with energy efficiency than considerations of code for them. The DC motors can be pretty precise. They can have the encoder on the motor shaft, before the gearbox. So the encoder count is multiplied by the gear ratio. They can then have a pulse per rev count comparable to a stepper. For instance, the Roomba robots have geared DC motors with encoders.

Do you have any recommendations for a geared DC motor with encoders for my project? I only seem to find ones that have fixed RPM's.

No way to make any recommendation without knowing the torque requirements, size constraints, available power and so on.

Pololu has a selection of gear motors with, optional, encoders.

For all DC motors, the speed depends on the applied voltage. This is usually controlled via PWM. The data sheet only shows the speed at nominal voltage.