Controlling a stepper motor while reading I2C values

Hey,

I am currently building a line-following robot and having problems on the software-side of the project.

I have connected two stepper motors via a4988s on my board, they both have a STEP pin that needs to be pulled high and low at a certain frequency.
I get sensor readings from a sensorboard I built my own.

So what I need to do is: Read the I2C register (just one byte), approximately every 50ms, do some calculations (about a dozen additions/multiplications, really not much) and then set the output frequencies according to the speed the steppers should move.

My idea was to user Timer1 with a function attached and setPeriod to change the pulse speed, but I need at least two timers since both motors should move at different speeds and the attached function must not disturb my I2C communication.

Another idea would be using micros (the time between two pulses is always 500us or higher), but I thought that may lead to the motor not moving smoothly.

What would be the most elegant solution to this problem?

Bets regards,
Arduinojoey

EDIT: code examples

Using Timer1:

// defines pins numbers
const int stepPin1 = 5; 
const int dirPin1 = 2; 

int state = LOW;
 
void setup() {
  // Sets the two pins as Outputs
  pinMode(stepPin1,OUTPUT); 
  pinMode(dirPin1,OUTPUT);
  digitalWrite(dirPin1,LOW); 
  Timer1.initialize(500);
  Timer1.attachInterrupt(motorStep);
}

void loop() {
}

void motorStep() {
    digitalWrite(stepPin1, state); 
    state = 1-state;
}

This works great for one single motor, but I cannot drive two motors like this.

Using micros():

#define FACTOR 5.05  // conversion from mm to motor steps

// defines pins numbers
const int stepPin1 = 5; 
const int dirPin1 = 2; 
const int stepPin2 = 6; 
const int dirPin2 = 3; 
 
void setup() {
  // Sets the two pins as Outputs
  pinMode(stepPin1, OUTPUT); 
  pinMode(dirPin1, OUTPUT);
  pinMode(stepPin2, OUTPUT); 
  pinMode(dirPin2, OUTPUT);
  delay(2000);
  drive(200);
}

void loop() {
}

void drive(int mm) {
  // set direction
  if (mm > 0) {
    digitalWrite(dirPin1, LOW); 
    digitalWrite(dirPin2, HIGH);     
  }
  else {
    digitalWrite(dirPin1, LOW); 
    digitalWrite(dirPin2, HIGH); 
  }
  // spin motor
  int steps = mm * FACTOR;
  spin(steps, 500);
}

void spin(int steps, int interval) {
  steps *= 2;
  int i = 0;
  int state = HIGH;
  long last = micros();
  while (i < steps) {
    if ((micros() - last) >= interval) {
      digitalWrite(stepPin1, state); 
      digitalWrite(stepPin2, state);
      state = HIGH - state;
      last = micros();
      i++; 
    }
  }
}

This works like a charm too, as long as both motors have the same speed and I do not need to check I2C. While getting it to work with two different speeds and I2C could be possible, I'd like to know if there is a more elegant solution.

You need to post your program so we can see what you are talking about.

I can't imagine that there should be any difficulty interleaving the reading of your sensor with the steps.

...R
Stepper Motor Basics
Simple Stepper Code
also look up the AccelStepper library

Robin2:
You need to post your program so we can see what you are talking about.

Sure. I edited the main post for clarity.

Arduinojoey:
Sure. I edited the main post for clarity.

For the future please don't make changes to earlier posts other than to correct typos. Keep things in chronological order.

...R

I have only looked at your micros() example - you say it works fine and using micros() is much easier than using timers.

Just create two separate functions - say void spinMotor1(int steps, int interval) { and void spinMotor2(int steps, int interval) {

And do NOT use WHILE because that blocks. Instead use IF - which probably needs some reorganization of your code. I think it would be useful to have a global variable that keeps track of whether a move is in progress or is completed. That way you would not call spin(steps, 500); if a move is in progress.

Have you studied the second example in my Simple Stepper Code

...R