Control a servo with Arduino and Python (VPython)


In a previous post, I show how to control the servo position using a potentiometer and an Arduino bard. In this post, the idea is to make a step forward and control the servo position using the computer. In summary, we are going to connect the servo motor to the Arduino and establish a connection between Arduino and Python through the serial port. Finally, we use visual Python to show a little screen with an arrow pointing the current angle of the servo.

1. Arduino with an USB connection and the Arduino IDE installed.
2. A servo motor, for this example I used a TowerPro SG90, but any similar servo should work.
3. Python 2.7 installed with VPython and the following library: pyserial.
4. Some wires may be required to connect your servo to the Arduino board.

Circuit diagram:
We only require an Arduino board and a micro servo for this project, so the connection between components is very easy. If you are using a small micro servo as the one I use, you can power it with the Arduino board. In my case I use an Arduino Uno board and the micro servo is connected to 5V and GND pins to power it up and to the pin number 9 to control the position:

Schematics of a micro servo connected to the arduino
Schematics of a micro servo connected to the Arduino Uno

Arduino code:
The Arduino code is also quite straightforward if you have followed the previous post. In this case we only require to set the servo and read from the serial port:

The first part of the code (lines 1 to 7) declare all that we will require in the program: first we include the library to control the servo and then we declare three variables: ‘pos’ is the position of the servo and it is set to 90, we can set this initial value to anyone we want, I just put 90 to set the servo on the middle of its range of movement. To be safe, do not set this to 0 or 180 or you might force the servo if it’s range is shorter than 180 degrees. servoPin is the pin where we have connected the servo and servoDelay is a small delay to let the servo reach the position and settle down. With the last instruction of this part we declare a servo object called myServo.

The void setup (lines 9 to 12) is also very easy: we first start the serial port to be able to communicate with the computer later and we then declare where the servo is connected.

The void loop (lines 14 to 19) starts with an empty while loop who keeps the system waiting for information from the serial port. Once there is information on the serial port, that is, information is sent through the computer, it reads the message and stores it in the variable ‘pos’. Finally, we write the position ‘pos’ into the servo and we put a small delay to let the servo finish its movement.

Once you finish with your code, you can upload it to the Arduino and make a quick test: once it is upload, you can open the serial monitor (the magnifying glass icon on the top right of the Arduino IDE, check next picture if you don’t know where it is). Once it is open and running, you won’t see anything but you can write a number between 0 and 180 (and different from 90) and the servo should go to that direction.

The serial monitor icon on top right hand of the Arduino IDE
The serial monitor icon on top right hand of the Arduino IDE

Python code:
Now we have to deal with Python and write a code to do the following:
– Connect and ‘speak’ with the Arduino (in a language both know).
– Ask and get the angle from the user input.
– Show a small representation of the position of the servo.

The code below addresses all the needs: lines 2 to 5 import all the required libraries we need for this program: ‘serial’ is the pyserial library to allow Python use the serial port, struct is a library included with the main python installation and is used here to pack the data we enter and send it to the Arduino in a language that it can understand. ‘visual’ is the way to import the VPython visual environment and finally numpy is imported to perform a small mathematical calculation required in the visualization part.

On line 8 we open the serial port using serial.Serial(port,baud_rate,timeout), it is important to be sure that you enter the correct serial port where you have your Arduino board connected and with the same baud rate (9600 by default). I also included a small timeout to give time to windows to open the serial port, this is not necessary but recommended.

Now let’s create the virtual environment (lines 10 to 17): the idea is to get a screen that shows the angle of the servo as in the next picture:

VPython window showing the current angle of the servo

To create this, we first create an arrow and we set the position using x,y and z coordinates.The position is referred to the end of the arrow. The axis dimensions can be tricky: we can think about these dimensions as a triangle where x and y are the legs and the arrow we are showing is the hypotenuse. What we know at every step is the angle so we need to use trigonometry relations to get the correct values.

With the sine and cosine rules, we have that: cos(theta)=adjacent/hypotenuse and sin(theta)=opposite/hypotenuse. Applying this to our triangle we can see that x is our adjacent leg which can be calculated as adjacent = hypotenuse*cos(theta) and y is the opposite leg, which can be calculated by opposite = hypotenuse*sin(theta). If you remember, inside the Arduino code on line 3 we set the first position to be 90 degrees. If we use this angle to calculate the x and y for our arrow we get x = 0 and y = 10, which is what we write on the code in this line.

The next chunk of code writes all the labels in the window: we have to set the text we want to show, the position on the window, the height and by default, this labels are inside a box, writing ‘box=false’ removes the box from the labels.

Now we go to the main loop (lines 21 to 28): First we need to make an infinite loop and we do that on line 22, so the program will run forever. On line 23 we set the refresh rate, which is required for VPython to refresh our virtual environment. We ask our user to write a number on line 24 asking for an input, be aware that our final user may write anything: a number, a word, random characters… so it is a good idea to add code to check if the input is really a number and if it is between 0 and 180 before trying to send it to the Arduino. In this case I’m assuming that the user knows what it has to do and I won’t care about checking the input. In line 26 I send the value entered by the user to the Arduino, note that I have to use the command struct.pack(‘>B’,pos) to send the information in a ‘language’ that the Arduino will understand. Lines 25 and 27 update our main label with the angle the user entered and finally, on line 28 the arrow axis is updated using the sine an cosine rules described before. We use the functions numpy.cos(angle) and numpy.sin(angle) included on numpy to calculate the cosine and the sine respectively. Remember that for this equations the angle has to be on radians.

Now you can run the program and start sending angles to move the servo. As usual, you can copy the code above or find it on my github account.


  • Thank you so much for writing this, i wasn’t sure how to get the position from for a servo from python to arduino. I had been trying to send the data as a string and then in arduino use the -‘0’ to convert it into a integer. Then after looking for days on the all the posts there are on servo control i found you post here and it taught me to use the struct module 🙂 You sir are most certainly a legend!!!!!!!!!

    • Glad I could help! It took me a while to find that as well!

  • can we write the code of python on Arduino IDE

    • No, you cannot. You can write some code to see the current value, but you will not be able to visualize it as with python.

  • Dear Silvino, your program has helped me a lot to control the servo with Python.I have adapted your code, so that I can control the servo now with my iPhone.
    Here is the link to the code:

    Waiting for the robot arm to arrived, and then I will use your robot code, to see if I also can get it working with the phone.

    Thank you very much for providing the code on your website.
    By the way: Ik ben in het mooie Utrecht geboren, but live now in Brasil.

    Sincerely, -=b=-
    Bert Temminck

    • Glad I could help!

  • Hi,

    I try to use this sample code on python 3.6 and getting error all the time. It doesn’t detect visual library even I downloaded vpython too ( for arrow(pos part ) and I was getting error on label too so I added ” from tkinter import* ” to fix it.

    Shortly, if it is possible, would you modify your sample code according to python 3.6 version too please ? thank you very much. I look forward your reply to me,thank you.

    • The problem is that vPython does not work with python 3.6 (or any python 3, in any case). I still have to find a way to make the code run in python 3, if I make it, I will update the code here for sure.

  • Hey,
    This was really amazing! It has really helped me. I had a question. If I want to stop the servo motor at a certain angle, can I do that by using time delay and then running it again till it reaches a certain angle again?

  • I am not sure of what you are trying to do: this code allows you to move the servo motor at any angle you enter, so it will go there and will not do anything else. If you want the code to not wait for an additional angle. just remove the while loop and it will go to exactly 1 angle. Then you need to run the code again to reach another one. If what you want is the servo to go through several angles slowly, then you may want to use a for loop to send multiple positions until it reaches the value you want.

  • how to python code if there are two servos

    • If you want to write the same position for two servos, the write the received position to each servo on the Arduino code. If you want to write a different position, you need to add an identifier in the python code that is sent by the serial port. Then the Arduino should check the identifier to know where to sent each position you add.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.