Ivan Nestorovski
Published © GPL3+

DC Motor Position Control with Potentiometer and Arduino

Low-cost alternative to using servo motors, using cheap and simple code that can be modified according to your needs.

BeginnerProtip1 hour663
DC Motor Position Control with Potentiometer and Arduino

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
L293D Motor Driver Shield
×1
N20 Geared DC Motor
×1
B10K Potentiometer
×1
3D Printed Gears and Case
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Premium Female/Male Extension Jumper Wires, 40 x 6" (150mm)
Premium Female/Male Extension Jumper Wires, 40 x 6" (150mm)

Story

Read more

Custom parts and enclosures

DC MOTOR POSITION CONTROL WITH POTENTIOMETER AND ARDUINO

Proof of concept design that works in my case.

Code

Bang-Bang Position Control

Arduino
The Arduino reads the analog value from the potentiometer (mapped to a range of 0 to 1023) and the desired position sent through the computer's serial input. It then calculates the error between these two values and uses a proportional control algorithm (with a constant factor called Kp) to adjust the motor's speed and direction. This algorithm basically converts the error into an output signal (between 0 and 255) sent to the L293D shield. The shield then translates this signal into a PWM signal that controls the motor accordingly.

To keep track of what's happening, the Arduino displays the current and desired positions along with the error on the serial monitor. This lets you see how the motor behaves and even allows you to enter new desired positions to test the system's response in real-time.
//Made by Ivan Nestorovski
//You can change this according to your needs of use and precission needed, i had a big problem with the position control of the N20 DC Motors so i think it might help others too. Enjoy your projects.

#include <AFMotor.h> // For all similar shields, you may need to install it first

AF_DCMotor motor(1); // Change "1" to the pin connected to your motor driver
unsigned long previousMillis = 0;
const long interval = 20; // Adjust the update interval in milliseconds
int target;
int newTarget;
bool newCommandAvailable = false;
int lastError = 0;
int tolerance = 10; // Tolerance for the error

void setup() {
  Serial.begin(57600);
  motor.setSpeed(255); // You can set your speed here (0-255)
  int initialPotValue = analogRead(A0); // Get the initial potentiometer reading
  Serial.print("Initial potentiometer value: ");
  Serial.println(initialPotValue);
}

void loop() {
  unsigned long currentMillis = millis();

  if (Serial.available()) {
    while (Serial.available()) {
      char c = Serial.read();
      if (c == '\n') {
        target = newTarget; // Update the target when a newline character is received
        newCommandAvailable = true;
        break;
      }
      else if (c >= '0' && c <= '9') {
        newTarget = newTarget * 10 + c - '0'; // Update newTarget instead of target
        newTarget = constrain(newTarget, 0, 1023); // Ensure newTarget is within the range of the potentiometer
      }
    }
    if (newCommandAvailable) {
      Serial.print("New target value: ");
      Serial.println(target);
    }
  }

  if (currentMillis - previousMillis >= interval && newCommandAvailable) {
    previousMillis = currentMillis;

    int potValue = analogRead(A0);
    int error = target - potValue;

    Serial.print("Current value: ");
    Serial.print(potValue);
    Serial.print(", Desired value: ");
    Serial.print(target);
    Serial.print(", Error: ");
    Serial.println(error);

    if (abs(error) > tolerance) { // Change this condition to use the tolerance
      if (error < 0 && error < lastError) {
        motor.run(FORWARD);
      } else if (error > 0 && error > lastError) {
        motor.run(BACKWARD);
      }
    } else {
      motor.run(RELEASE); // Set motor to idle when error is small
      newCommandAvailable = false; // Reset the flag after each command
      newTarget = 0; // Reset the newTarget value after each command
    }
    lastError = error; // Store the last error
  }
}

Credits

Ivan Nestorovski

Ivan Nestorovski

2 projects • 3 followers

Comments