Arduino - Motor PID speed control

Saturday, December 31, 2016

With PID control, speed of motor can be archived exactly. This article mainly introduces about making program in Arduino Pro mini, program in Computer (Visual Studio) to control motor speed by PID algorithm.

Fig1. General connection

Arduino Pro mini is used to store motor control, PID algorithm, communicating with PC (through COM Port)
Computer will have HMI made by Visual Studio to communicate with Arduino. HMI will show motor speed graph, change motor speed setting
Fig2. HMI on Computer (made by Visual Studio)

Let's go detail:
Hardware needed:
1. Motor with Encoder
2. H-bridge PCB
3. Arduino Pro mini
5. Computer (with Visual Studio)

Step 1. Hardware connection
This article doesn't mention about detail connection, it is based on previous article which can be found at this link

Step 2. Code works at Arduino
void loop() {
  if (stringComplete) {
    // clear the string when COM receiving is completed
    mySt = "";  //note: in code below, mySt will not become blank, mySt is blank until '\n' is received
    stringComplete = false;

  //receive command from Visual Studio
  if (mySt.substring(0,8) == "vs_start"){
    digitalWrite(pin_fwd,1);      //run motor run forward
    motor_start = true;
  if (mySt.substring(0,7) == "vs_stop"){
    digitalWrite(pin_bwd,0);      //stop motor
    motor_start = false;
  if (mySt.substring(0,12) == "vs_set_speed"){
    set_speed = mySt.substring(12,mySt.length()).toFloat();  //get string after set_speed
  if (mySt.substring(0,5) == "vs_kp"){
    kp = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_kp
  if (mySt.substring(0,5) == "vs_ki"){
    ki = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_ki
  if (mySt.substring(0,5) == "vs_kd"){
    kd = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_kd

void detect_a() {
  encoder+=1; //increasing encoder at new pulse
  m_direction = digitalRead(pin_b); //read direction of motor
ISR(TIMER1_OVF_vect)        // interrupt service routine - tick every 0.1sec
  TCNT1 = timer1_counter;   // set timer
  pv_speed = 60.0*(encoder/200.0)/0.1;  //calculate motor speed, unit is rpm
  //print out speed
  if (Serial.available() <= 0) {
    Serial.println(pv_speed);         //Print speed (rpm) value to Visual Studio

  //PID program
  if (motor_start){
    e_speed = set_speed - pv_speed;
    pwm_pulse = e_speed*kp + e_speed_sum*ki + (e_speed - e_speed_pre)*kd;
    e_speed_pre = e_speed;  //save last (previous) error
    e_speed_sum += e_speed; //sum of error
    if (e_speed_sum >4000) e_speed_sum = 4000;
    if (e_speed_sum <-4000) e_speed_sum = -4000;
    e_speed = 0;
    e_speed_pre = 0;
    e_speed_sum = 0;
    pwm_pulse = 0;

  //update new speed
  if (pwm_pulse <255 & pwm_pulse >0){
    analogWrite(pin_pwm,pwm_pulse);  //set motor speed
    if (pwm_pulse>255){


At the beginning of program, it will receive command from Computer (start/stop motor, motor speed setting, kP, kI, kD gain of PID)
Next is void detect_a(): encoder sum calculation -> used for speed calcluation in Timer interrupt routine.
Timer interrupt routine ISR(TIMER1_OVF_vect): every 0.1 this program is called, content includes:
   (1) Calculate motor speed
   (2) Send motor speed to Computer
   (3) Calculate PWM pulse (base on PID algorithm)
   (4) Push result of PWM to H-brigde
The entire of code for Arduino Pro mini can be download at this link

Step 3. Code works at Computer
Visual Studio 2012 is used to make HMI program, in which:
   (1) Send speed setting to Arduino
   (2) Send PID gain (kP, kI, kD) to Arduino
   (3) Receive motor speed -> show on graph
Fig3. Visual studio program

The whole code of Visual Studio program can be downloaded at this link
To make a Visual Studio program -> see detail step at this article
In general, the code will have:
#pragma endregion
private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
mStr = "0";
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_set_speed"+textBox1->Text); //send set_speed to Arduino
serialPort1->WriteLine("vs_kp"+textBox2->Text); //send kP to Arduino
serialPort1->WriteLine("vs_ki"+textBox3->Text); //send kI to Arduino
serialPort1->WriteLine("vs_kd"+textBox4->Text); //send kD to Arduino
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
String^ length;
//print motor speed into Chart
this->chart1->ChartAreas["ChartArea1"]->AxisX->Minimum=i-300; //shift x-axis
private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e) {
private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_start"); //start motor
private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_stop"); //stop motor



  1. Thanks, You wrote awesome, I have learn lots of things from your article. It's really helpful for any readers.
    Battery Operated Flow Meter

  2. Will it work on arduino uno

    1. so we just didn't need the UART PCB and we don't need to edit arduino code even when using arduino uno?

      sorry to bother you but i really need a fast answer

    2. Right, Arduino UNO already had UART PCB inside. When Arduino UNO connect to Computer via USB Port, an COM Port will be shown -> use this COM Port No. to communicate between Arduino UNO and Computer.
      About the code, i try to compile for UNO, it run perfectly. Let's try it!

  3. How can i try to input the value of speed, kp, ki, kd by using serial monitor in arduino program?

    thank you

    1. Hi. Unfortunately i already removed this model (motor, circuit,...) -> can't make project again as your mention.
      You should find an example to read data input from serial port -> then, import parameters (kP, kI, kD) to formular. It's not difficult, let's do it

  4. Bro Can you kindly Provide the whole circuit diagram. its quite confusing which connection goes where. thnx in advance

  5. Please come to thí link to see connection

  6. Bro I followed all the steps you mentioned and made the project with Arduino Uno. The problem I get is that the PID is not working, the motor keeps on rotating with a constant speed of 530 RPM and when I apply some load to the motor the speed reduces and does not go back to 538 RPM. Also I can't send commands to arduino. like i want the motor to rotate with a speed for 700 RPM and 300 RPM for example, it does not work when i press the send button on HMI.

    1. Hi,
      (1) First step is make sure you can send command (and convert it to integer) into Arduino. You can search on web this kind of project, or see here
      (2) Tuning PID parameter is not easy. Each motor will have each parameter. First tuning, you should make kP, kI some small value, kD=0, then see motor reaction. Then gradually increase or decrease kP, kI. Normally, no need kD.

  7. Is there an equivalent motor that can be used with this system, it is very difficult to find the one you used.

    1. You can use any DC motor, then find kP kI kD for it

  8. Hi this is one s brilliant!! I'm want to design kind of this but slightly different..
    There is LCD or 7 segment using the rotary encoder rpm 0-8000 per 1 rpm changes, first we need to a rpm then once after pressing start arduino need to rpm of the motor
    Can you suggest me any enocder motor up to 8000rpm n how to control the motor?
    Help me pls

    1. Hi, There's rearly motor with encoder 8000 ppr (pulse per round). If you need 8000ppr encoder, you have to use external encoder, connect to motor shaft by coupling. I was wondering if what application you need 8000ppr encoder? Could you share? We will make it together (because i'm free now haha)

  9. I want to draw Set Speed in chart with series 2 is color Red, Can you help me

    1. Please try yourself in Visual Studio. I think it's not difficult.

    2. Set Speed in Textbox I run Series2 but i don't know convert Text

    3. /this->chart1->Series["Series2"]->Points->AddXY(i,System::Convert::ToDouble(textBox1)); i++;
      // this->chart1->ChartAreas["ChartArea1"]->AxisX->Minimum=i;

    4. Thank you: i did convert textbox1->text ok

  10. Can you please guide me about the specifications of motor with encoder? And also where to buy it from in INDIA?

    1. Hi, you can buy any DC motor which has encoder inside.

  11. Hey , can you explain to me how to convert speed RPM to PWM ?

    1. Hi, i don't know calculation to convert speed rpm to PWM. This calculation is difficult, depends on characteristic of motor, load.
      Instead of calculation speed to PWM, we use PID algorithm. How it works?
      Let's think, PWM=0% -> speed is 0rpm; PWM=100% -> speed is max, let's say 2000rpm
      PID will calculate PWM depend on error of speed feedback and speed setting -> output to PWM pulse to reach setting speed.
      Hope i can help you.

  12. Hi
    thanks for sharing this brilliant works . I have couples of question , first what version of visual studio you use and if I have motor with 2 wires only and it's gear motor , do u think that i still need encoder cuz I want to control the speed by using ur way in where if I need 450 rpm and the motor should rotate the desired speed , Do u think my motor able to do , here is my motor characters
    thank you so much

    1. Hi,
      I use Arduino 1.6.11, but you can use latest version -> it's no problem
      Your suggested motor has no encoder -> impossible to control speed. Without encoder, how could we know speed? You should choose another motor with encoder.
      Thanks for visit my blog.

  13. This comment has been removed by the author.

  14. how i can calculate the parameter of dc motor (j.B.L.)?
    2-Are you can send me code of pid controller without using parameters(j.B.L)?

    1. Hi, i don't calculate parameter of DC Motor
      The code of PID controller (without using parameter DC Motor) is in my blog. Please read it all.
      Thanks for visit.

  15. ok..
    but are you have any away to calculate parameter of dc by matlab ?

    1. Hi, i know your mean: using calculated parameter to make PID parameter. Unfortunately, this project is not aimed to do that, this just tuned PID by experience and make it run.



Search This Blog

Most Reading

Google+ Followers