In the last course entry, we look at how to use the serial monitor to control the colour of an RGB LED. Today we will be looking at how to control servos (or motor servos) using a potentiometer. With this, we will be able to learn how to point a sensor in one direction to obtain information on a specific area. To do this, we will take a look at some new concepts, analogue reading and the map function, which will be explained later.
List of materials
- ZUM BT-328 controller board or one compatible with Arduino
- Servo
- Potentiometer
Electrical connections
Connecting the modules for this project is very easy, all we need to do is connect the servo to a pin with PWM output and the potentiometer to an analogue input. You can see a connection diagram below:
The code
We will start off with a super simple example which we can make more complicated later on.
Moving the servo to the position indicated by the potentiometer
First we will write the entire code, so that we can then look closely at it bit by bit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include int pinPot = A1; // The analogue pin connected to the potentiometer Servo miServo; // The variable for controlling the servo void setup() { miServo.attach(10); } void loop() { int potRead = analogRead(pinPot); // Reads the value of the analogue sensor int servoValue = map(potRead, 0, 1023, 0, 180); // Transforms the value between 0 and 1023 to 0 and 180 miServo.write(servoValue); // Sends the servo to the corresponding angle delay(500); // Wait 500 ms } |
Let´s start by including the Servo library and declaring the variables of the pins:
1 2 3 4 5 6 |
// We include the Servo library #include // We create a servo object Servo miServo; int pinPot = A1; // The analogue pin connected to the potentiometer |
The Servo library is specifically for this type of motor. Most servos allow movements between 0 and 180º. If you want to find out more about the Servo library, you can check the Arduino reference page.
Setup() function
1 2 3 4 |
void setup() { miServo.attach(10); } |
In this function, we indicate that the servo is connected to digital pin 10.
Loop() function
This is the function in which the task is carried out. The following steps must be followed.
- We read the value of the potentiometer. If it is an analogue input, the value will be between 0 and 1023. We use the analogRead() function.
- We transform the value between 0 and 1023 into a value between 0 and 180, using the map() function. The map function converts a variable from the original range to the target range, meaning a variable in an A-B interval will move to a C-D interval, maintaining the relative position of the original range to the target range. You can find out more about map on the reference page.
- We instruct the servo to move to the position obtained between 0 and 180.
- We stop executing the program for 500 ms. We do this to prevent the servo from constantly changing position, which means that our program will be less reactive, but more stable.
Here is the code:
1 2 3 4 5 6 7 8 9 |
void loop() void loop() { int potRead = analogRead(pinPot); // Reads the value of the analogue sensor int servoValue = map(potRead, 0, 1023, 0, 180); // Transforms the value between 0 and 1023 to 0 and 180 miServo.write(servoValue); // Sends the servo to the corresponding angle delay(500); // Wait 500 ms } |
Moving the servo to the position indicated by the potentiometer, without sudden movements
In the next example we will carry out the same task, but we will force the servo to move forward in small steps of 1 degree every 10 milliseconds. This way we can avoid any sudden jumps caused by abrupt movement of the potentiometer. After the initial declaration:
1 2 3 4 5 6 |
// We include the Servo library #include // We create a servo object Servo miServo; int pinPot = A1; // The analogue pin connected to the potentiometer |
Then we will declare the variables needed to correctly execute the program:
1 |
int aktValue, nextValue; // The variable that stores the values of the potentiometer |
Now we will use the setup function to start the program:
1 2 3 4 5 6 7 8 |
void setup() { // We start the Serial library so that we can communicate with the board Serial.begin(9600); pinMode(pinPot, INPUT); miServo.attach(10); //Indicates which pin is connected to the servo Serial.println("¡Listo!"); } |
In the setup function, we start the Serial library, we mark the potentiometer as input and we tell the program which pin is connected to the servo. Then we send a message to the user saying that the program is ready to run. Here we can see the loop function in full:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
void loop() { nextValue = analogRead(pinPot); // We read the value of the potentiometer (0-1023) nextValue = map(nextValue, 0, 1023, 0, 180); // We scale to value that the servo can read (0-180) // We print value of the position of the servo in degrees Serial.print("Ángulo = "); Serial.print(nextValue); Serial.println("º"); /* We can distinguish between two cases: In the first case, the next position is higher than the current one, so we increase the value In the second case, the next position is lower than the current one, so we decrease the value */ if (nextValue > aktValue) { for (int i = aktValue; i <= nextValue; i++) { miServo.write(i); // We use the specific function from the Servo library to write the valueor delay(10); // and we wait for the value to arrive. } } else if (nextValue < aktValue) { for (int i = aktValue; i >= nextValue; i--) { miServo.write(i); delay(10); } } aktValue = miServo.read(); delay(1000); } |
Within the loop function, first we will take readings from the potentiometer, then we use the map function to “translate” the reading of the potentiometer to one angle. Then we print the information of the angle on the serial monitor:
1 2 3 4 5 6 7 |
nextValue = analogRead(pinPot); // We read the value of the potentiometer (0-1023) nextValue = map(nextValue, 0, 1023, 0, 180); // We scale to a value that the servo can read (0-180) // We print value of the position of the servo in degrees Serial.print("Ángulo = "); Serial.print(nextValue); Serial.println("º"); |
In contrast to digital reading, analogue reading allows us to distinguish between values from 0 to 5 volts. ZUM and Arduino boards generally include a 10-bit analogue-digital converter, which means the range we can obtain is 0-1023. You can find out more about analogRead here. Then we move on to modifying the position of the servo, selecting from one of two scenarios:
- The value that the servo will move to is higher than the current one, so we have to increase the position:
1 2 3 4 5 6 |
if (nextValue > aktValue) { for (int i = aktValue; i <= nextValue; i++) { miServo.write(i); // We use the specific function from the Servo library to write the value delay(10); // and we wait for the servo to reach the value. } } |
- The value that the servo will move to is lower than the current one, so we have to decrease the position:
1 2 3 4 5 |
else if (nextValue < aktValue) { for (int i = aktValue; i > nextValue; i--) { miServo.write(i); delay(10); } } |
Finally, we save the value of the position of the servo and we pause the execution of the program for a second:
1 2 |
aktValue = miServo.read(); delay(1000); |