DC motor with quadrature encoder: how to position exactly and turn back to original place

I'm currently working for a school project and need a DC motor with built in encoder (this type) to turn x amount of rotations clockwise and then return to its original position.
With this code it turns and goes back but it keeps returning a bit more and as a result rotates slowly over time instead of going back to its original position.

Here is my current code

#define encoderPin 3    // 
#define PWM_PIN 2       //
#define M1 0            //
#define M2 1            //
#define rotations 10
#define rotationSpeed 150

int revCount = 0;

void setup() {
  pinMode(encoderPin, INPUT);
  pinMode(PWM_PIN, OUTPUT);
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  Serial.begin(9600);
  analogWrite(PWM_PIN, 0);
  attachInterrupt(digitalPinToInterrupt(encoderPin), revCounter, RISING);
}

void loop() {
  Serial.println("motor program start");

  // Rotate clockwise
  rotateClockwise();

  // Rotate counterclockwise
  rotateCounterclockwise();
}

void rotateClockwise() {
  revCount = 0;

  Serial.println("Rotating clockwise...");

  while (revCount < rotations) {
    digitalWrite(M1, HIGH);
    digitalWrite(M2, LOW);
    analogWrite(PWM_PIN, rotationSpeed);
    delay(100); // Introduce a small delay to allow the revCounter to increment
  }

  Serial.println("Stopped rotating clockwise.");
  analogWrite(PWM_PIN, 0);
  delay(1000); // Optional delay to ensure the motor stops before the next rotation
}

void rotateCounterclockwise() {
  revCount = 0;

  Serial.println("Rotating counterclockwise...");

  while (revCount < rotations) {
    digitalWrite(M1, LOW);
    digitalWrite(M2, HIGH);
    analogWrite(PWM_PIN, rotationSpeed);
    delay(100); // Introduce a small delay to allow the revCounter to increment
  }

  Serial.println("Stopped rotating counterclockwise.");
  analogWrite(PWM_PIN, 0);
  delay(1000); // Optional delay to ensure the motor stops before the next rotation
}

void revCounter() {
  revCount++;
  Serial.print("Revolution count: ");
  Serial.println(revCount);
}

The serial print states that my revolution count resets at around 900


Calculate PWM value that is the difference between the desired position and current position. That way the motor will slow as it approaches the end position.

Serial.print inside an ISR is double plus ungood.

1 Like

There are a couple of problems in your code. Using interrupts needs additional knowledge about hardware and compiler internals - especially the optimizer.

  • Within an interrupt don't use functionality that itself needs interrupts - like Serial.print. That's what @dougp already stated.
  • If you change a variable within the interrupt und use it outside, this variable must be declared volatile, so that the optimizer knows it can change at any time.
  • Which board are you using? If it is a 8-bit board then you must read your 16-bit (int) variable in loop() with interrupts disabled. Otherwise the interrupt may change it while it is read - which leads to wrong values.

Unfortunately your link doesn't provide any useful information about the used motor and encoder. How many pulses does it provide per revolution? And how fast does your motor turn - that means how many pulses per second?

I'm using a raspberry pico with the arduino IDE, so 32 bits.

I will change the volatile and remove the serial prints

Also for the motor specs, this is the type Im using. I dont know any more info about it then what is stated on the page. On the motor itself it says DC: 6V 265RPM
I'm also using a 7.5V LiPo battery

Unfortunately I never did anything with this Arduino - so can only give general advice.
From your last link you can see, that the encoder creates 11 pulses per motor revolution. The table shows for 6V/256RPM a reduction ratio of 18.8 what means 18.8 x 11 = 206.8 pulses per RPM revolution at the output shaft.
With 256 RPM this means about 882 pulses per second. May be a little less, if you don't provide the full 6V to the motor.
With your

    delay(100); // Introduce a small delay to allow the revCounter to increment

you will not be able to stop immediately when this number of pulses is reached. A lot of pulses will be counted during that delay. So you may stop too late. The advice af @groundFungus is also important, because you cannot really stop a motor immediately because of its inertia.
What was the purpose of that delay? The revCounter will increment without the delay too.

N.B. Your code seems to assume that there is only one pulse per revolution ??

#define encoderPin 3    // 
#define PWM_PIN 2       //
#define M1 0            //
#define M2 1            //
#define MaxPulseCount 120   //Amount of pulses needed to stop motor, experimentally defined
#define rotationSpeed 120 // Higher value is slower speed

volatile int pulseCount = 0;

void setup() {
  pinMode(encoderPin, INPUT);
  pinMode(PWM_PIN, OUTPUT);
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  Serial.begin(9600);
  analogWrite(PWM_PIN, 0);
  attachInterrupt(digitalPinToInterrupt(encoderPin), pulseCounter, RISING);
}

void loop() {
  Serial.println("motor program start");

  // Rotate clockwise
  rotateCounterclockwise();

  // Rotate counterclockwise
  rotateClockwise();
}


void pulseCounter() {pulseCount++;}


void rotateClockwise() {
  pulseCount = 0;
  while (pulseCount < MaxPulseCount) {
    digitalWrite(M1, LOW);
    digitalWrite(M2, HIGH);
    analogWrite(PWM_PIN, rotationSpeed);
  }
  analogWrite(PWM_PIN, 255); //Turn off motor (255 closes MOSFET)
  delay(2000); // Optional delay to ensure the motor stops before the next rotation
}


void rotateCounterclockwise() {
  pulseCount = 0;

  while (pulseCount < MaxPulseCount) {
    digitalWrite(M1, HIGH);
    digitalWrite(M2, LOW);
    analogWrite(PWM_PIN, rotationSpeed);
  }
  analogWrite(PWM_PIN, 255);
  delay(2000); // Optional delay to ensure the motor stops before the next rotation
}

This is how my code looks right now.
This is the program in action right now

The added weight (which is less then what it should have to handle) causes it to rotate to far back when returning to its position so I have to slow the PWM down like you and @groundFungus said. Right now it keeps going back further until it hits its own base and breaks itself.

I tried slowing it down in general and not progressively but then the motor wont have enough torque to start the rotation.

Is there a specific way you recommend to gradually decrease the PWM value so it efficiently does so? I tried this so far and a for loop but they didnt seem to work

void rotateClockwise() {
  pulseCount = 0;
  int i = 0;

  while (pulseCount < MaxPulseCount) {
    digitalWrite(M1, LOW);
    digitalWrite(M2, HIGH);
    analogWrite(PWM_PIN, rotationSpeed+i);
    i++;
  }
  analogWrite(PWM_PIN, 255); //Turn off motor (255 closes MOSFET)
  delay(2000); // Optional delay to ensure the motor stops before the next rotation
}

With this code it just doesnt rotate at all.

I would recommend not clearing the pulseCount when going back. Leave it as it is, and count back to zero. As it is a quadrature encoder you should be able to detect the rotation direction and count up or down accordingly.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.