DC Motor doesn't work as expected

Hi there,
I am stuck once again with my lack of skill, tried many different variations based on this idea and I can either get it working a bit or not at all, please help pros!

My code does very simple stuff;
Reads measurement equipment from RS232, converts char to string to float (for example 25.2225) this is working good!

Drives a motor to do the following;
Drive motor to measurement 25.2260, then drives it backwards to 25.2250 (to remove any backlash, mechanical error in position), at this point I want it to wait here for 30 seconds.

Then drive motor to measurement 24.8840, then drive opposite way to 24.8850 (to remove any backlash, mechanical error in position), then wait here for 30 seconds.

This should be looping continuously;

The problem I am having is I can’t seem to make the motor loop work properly, it seems to get stuck somewhere, periodically turning the motor on and off.

If I simplify the loop part of the code to just an if statement for example if(FloatRad < Hthreshold) {MotorDown();}
it will complete fine, its once I start adding the following commands to move down afterwards, then it seems to get stuck somewhere, please help, I’m learning but I don’t pick up coding very fast!
Thanks for your time!

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad;
const float Hthreshold = 25.2245;
const float HthresholdOS = 25.2255;
const float Lthreshold = 24.8853;
const float LthresholdOS = 24.8845;


void setup() {
  Serial.begin(9600);
  Serial.setTimeout(100);
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
}

void loop() {
  MoveUp();
  TopDownBacklashWAIT();
  MoveDown();
  BottomUpBacklashWAIT();
}

void ReadSerial() {
  sylvacRad = Serial.readString();
  delay(500);
  FloatRad = sylvacRad.toFloat();
  Serial.println(FloatRad, 4);
}

void MoveUp() {
  if (FloatRad < Hthreshold) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);     //MoveUp
    ReadSerial();
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
  }
}

void TopDownBacklashWAIT() {
  if (FloatRad > HthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 100);
    ReadSerial();
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
    delay(5000);
  }
}

void MoveDown() {
  if (FloatRad > Lthreshold) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 255);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
    delay(5000);
  }
}

void BottomUpBacklashWAIT() {
  if (FloatRad < LthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 100);
    ReadSerial();
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
    delay(5000);
  }
}


not sure i understand the code

the first thing loop() does is call MoveUp(), which compares a motor position, “FloatRad”, received from the serial interface to a threshold, Hthreshold", to decide whether to turn the motor on on off.

the comparison to the threshold suggests the motor should be kept running until it reaches the threshold.

but MoveUp() returns after reading the serial interface and loop then invoked TopDownBacklashWait() regardless of whether “FloatRad” has reached the threshold

after MoveUp() turns the motor on, shouldn’t it repeatedly check the serial interface, stop the motor and return when it reaches the threshold?

in general, there needs to be only one function that controls the motor, driving the motor in one direction or the other or stopping the motor depending on it’s position relative to the endpoint thresholds

1 Like

As mentioned from gcjr, you are not checking the position of the motor in a way that suits your application.

Let’s assume you start your program at position 25. Your loop starts with MoveUp():
Your motor turns on and starts moving. Then you read in the position almost immediately, so I assume , that the value is close to 25.
Then you wait 500ms, but the motor is still moving and I dont know at which speed.

Then comes the TopDownBacklashWAIT():
You check if the read in value ( that was stored 500 ms ago) is above your upper thresshold. But the real position has changed in the meantime. Since the stored value is close to 25, the motor turns off. Then you wait 5s.

Then comes MoveDown():
Here you start to move down, but you do not read in a new position.

Then comes BottomUpBacklashWAIT():
Here you wait for 5s.

Then the loop starts over.

Depending on the speed at which your motor is moving, you repeat the following cycle:
move up for 500ms
wait 5s
move down for 500 ms
wait 5s

This explains your observation, that the motor is turned on and off periodically.

Generally speaking if you want a motor to go to a specific position, you should not wait too long after reading the signal to turn off the motor.

If you want to solve it in a “good” way, you should attach an Encoder to your drive and control the position with that. If that is not possible, you should somehow figure out how fast your signal is changing and also get rid of the various delays in your code.

1 Like

Thank you guys, this has shed alot of light on the subject. I will work at it some more and report back with my progress, thank you!

Hi all,
Found a solution in the end, unfortunately @Kerbolosh , it requires to be totally hands free (automated) so using an encoder is not possible. It seems ‘If else’ control structures are not as good at solving my problem. Instead I used ‘do while’ loops until the correct measurement was reached and ‘if else’ statements to check the measurement is within the required range.

Another addition was a change to ReadSerial(); void loop, it now reads the serial much much faster and is responding the measurement very fast.

I have another topic to search into, but is a little off topic for the title so I will post in another forum regarding that and create a link here later if anyone wants to follow a similar project to mine.

Until next post, here is the working code;

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad;
const float Hthreshold = 25.2245;
const float HthresholdOS = 25.2255;
const float Lthreshold = 24.8853;
const float LthresholdOS = 24.8845;


void setup() {
  Serial.begin(9600);
  Serial.setTimeout(100);
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  ReadSerial();
}

void loop() {
  do {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
    ReadSerial();
  } while (FloatRad < Hthreshold);       //MoveUp

  if (FloatRad > HthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 100);
    ReadSerial();
  } else if (FloatRad < HthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
  }

  delay(50000);                          //delay for autoIT

  do {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 255);
    ReadSerial();
  } while (FloatRad > Lthreshold);       //MoveDown

  if (FloatRad < LthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 100);
    ReadSerial();
  } else if (FloatRad > LthresholdOS) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
  }

  delay(50000);                          //delay for autoIT
}

void ReadSerial() {
  if (Serial.available() > 0) {
    sylvacRad = Serial.readString();
  }
  delayMicroseconds(2);
  FloatRad = sylvacRad.toFloat();
  Serial.println(FloatRad, 4);
}

thanks everyone for your help!

Good that it works now. Just out of curiosity: Why does an encoder not work?
With encoder I mean a device that is attached to the motor and measures its rotation. If you attach this to the arduino using interrupt pins, you can control the position pretty precisely and also your code gets much simpler. See 3rd picture in wikipedia article: Rotary encoder - Wikipedia

I dont mean a rotary knob, that you rotate with your finger :wink:

1 Like

I did think you meant a rotary knob haha, encoder would be an ideal option but the DC motor is concealed. There is also a hall effect sensor which I can measure voltage from and use ADC values, I am due to do more testing but it really does have to land within a precise tolerance.

Once I have investigated more I will post again, but the idea of an encoder sparks inspiration for something new! thanks again! :wink:

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