PID-controlled DC motor driver with encoder feedback. Speed control, direction, and smooth acceleration.
// DC Motor PID Controller — FreeRobotStore
// MIT License | freerobotstore.online/robots/motor-controller
const int ENA = 9;
const int IN1 = 8;
const int IN2 = 7;
const int ENCA = 2;
const int ENCB = 3;
// PID gains — tune for your motor
float Kp = 2.0, Ki = 0.5, Kd = 0.1;
float targetRPM = 120.0;
volatile long encoderCount = 0;
float integral = 0, prevError = 0;
unsigned long lastTime = 0;
long lastCount = 0;
void setup() {
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENCA, INPUT_PULLUP);
pinMode(ENCB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCA), readEncoder, RISING);
Serial.begin(115200);
setDirection(1); // forward
}
void loop() {
unsigned long now = millis();
if (now - lastTime < 50) return; // 20Hz control loop
float dt = (now - lastTime) / 1000.0;
long count = encoderCount;
float rpm = ((count - lastCount) / 600.0) * (60.0 / dt);
// PID
float error = targetRPM - rpm;
integral += error * dt;
integral = constrain(integral, -100, 100);
float derivative = (error - prevError) / dt;
float output = Kp * error + Ki * integral + Kd * derivative;
prevError = error;
int pwm = constrain((int)output, 0, 255);
analogWrite(ENA, pwm);
Serial.print("RPM: "); Serial.print(rpm, 1);
Serial.print(" | PWM: "); Serial.print(pwm);
Serial.print(" | Err: "); Serial.println(error, 2);
lastTime = now;
lastCount = count;
}
void readEncoder() {
int b = digitalRead(ENCB);
encoderCount += (b > 0) ? 1 : -1;
}
void setDirection(int dir) {
digitalWrite(IN1, dir > 0 ? HIGH : LOW);
digitalWrite(IN2, dir > 0 ? LOW : HIGH);
}
1. Copy the code into Arduino IDE. 2. Adjust Kp, Ki, Kd for your motor. 3. Set targetRPM. 4. Open Serial Monitor at 115200 baud to see RPM, PWM output, and error.
MIT License | FreeRobotStore