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.
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 ??
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
}
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.