Overview
Manipulating servo motors is a fun activity to work with especially with its potential use for different array of projects such as robotics and automation. But we take this activity a step further by using the the 16-Channel PWM Servo Shield for easier control of 3 different servo types.
In this tutorial, we will control multiple servos using the 16-Channel PWM Servo Shield with the help of some potentiometers and joysticks. We will also switch between 2 control modes by using a push button switch to select between potentiometer or joystick.
Hardware Components
Software Components
- Arduino IDE
- Pushbutton.h
- Adafruit_PWMServoDriver.h
- Wire.h (Built-in Arduino Library)
Application Description
- 16-Channel 12-bit PWM/Servo Shield Servo Drive Module
With just 2 pins, this16-Channel 12-bit PWM/Servo Driver Shield can drive up to 16 servos over I2C. All 16 channels will be driven simultaneously by the on-board PWM controller without the need for further Arduino processing.
This I2C device (which makes use of the Wire library) can typically be shared or stacked with other I2C shields and devices (including numerous PWM/Servo Shields), so long as they all have distinct addresses. This shield's solder pads allow you to choose among 62 addresses (0x40-0x7E). Without solder jumpers, the default address is 0x40.
- SG90 Micro-Servo Motor
An actuator that delivers a rotating motion roughly between 0° and 180° is the SG90 Servo Motor. The output shaft can only rotate 180 degrees because it is fastened to the potentiometer.
A small DC motor, plastic gears to boost torque, a potentiometer to indicate the output shaft's present position, and a control circuit make up the SG90 Servo Motor.
- MG995 High Torque Servo Motor Metal Gear (360 rotation)
A robust, dependable servo motor is the MG995 servo motor. It is a reasonably priced, low-power motor. The twin shock-proof ball-bearing MG995 servo design has a metal gear, making industrial production quite doable. The motor responds and turns quickly. It has a stable constant torque range and excellent holding power.
- MG996R High-Torque Servo Metal Gear
A metal gear servo motor with a maximum stall torque of 11 kg/cm is the MG996R. The motor rotates from 0 to 180 degrees depending on the duty cycle of the PWM signal given to its signal pin, just like other RC servos.
- Potentiometer
A potentiometer, also known as a variable resistor or rheostat, is a three-terminal resistor with a sliding or revolving contact that creates a changeable voltage divider. If just one terminal, the wiper, is utilized, the potentiometer functions as a variable resistor.
- Joystick Module
This module has two 5K potentiometers that are spring-loaded and have X and Y analog outputs that may be connected to analog inputs on the Arduino. If the cap is held down long enough to produce an audible click, a push button switch output is also available. Any digital input pin on the Arduino can be used to connect the pushbutton output.
Hardware Setup
Note that the PWM Servo Shield goes on top of the Robotdyn UNO. The servo shield requires an external power source as indicated in the schematic to power the servo motors (with 5V 2A DC power, we can connect 1-6 motors without experiencing power issues). The Robodyn UNO also requires power for the chip to function.
Highlighted with the red arrow and circle above, Robotdyn UNO has two extra analog pins that we utilized on top of the standard 6 analog pins. A4 and A5 pins can't be utilized for analog inputs since the SDA and SCL for I2C share the same pins. Therefore, Wire.h library when used, utilizes A4 and A5 for the SDA and SCL pins, respectively.
Software Setup
NOTE: Before we can use the code, we must first download the libraries
that we are going to use. Links for the libraries are provided in the
SOFTWARE COMPONENTS section above.
//Library Used
#include <Pushbutton.h>
#include <Adafruit_PWMServoDriver.h>
#include <Wire.h>
#define buttonPin 2 //Pin for the Button
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); //Servo Driver library initialization
Pushbutton pushButton(buttonPin); //Push Button library Initialization
#define SERVOMIN 150 //Minimum angle Value from the library
#define SERVOMAX 600 //Maximum angle Value from the library
uint8_t servoNum1 = 0, servoNum2 = 1, servoNum3 = 2; //Pins for the Servo in the Servo Shield
//Potentiometer pins
int potPin1 = A0, potPin2 = A1, potPin3 = A2;
//Potentiometer Value Storage
int potVal1, potVal2, potVal3;
//Joystick Pins
int joystickXPin = A6;
int joystickYPin = A7;
int joystick2XPin = A3;
//Joystick Value Storage
int joystickXVal, joystickYVal, joystick2XVal;
int counter = 0; //push button counter
void setup() {
Serial.begin(9600); //Standard Serial interface initialization
pwm.begin(); //Start the Servo Driver Library
pwm.setPWMFreq(60); //Operating frequency of the Servo Shield & Servos
Serial.println("Press the button to begin");
}
void loop() {
//Code from the pushbutton library to read if the button is pressed and how many times it is pressed
if (pushButton.isPressed()) {
counter = counter + 1; //increase the counter once the button is released
delay(350);
//-------------------------------------Serial codes----------------------//
if (counter == 1 || counter == 2) {
Serial.print("Counter Value: ");
Serial.println(counter);
if (counter == 1) {
Serial.println("Potentiometer Servo Controll");
Serial.println(" ");
}
if (counter == 2) {
Serial.println("Joystick Servo Controll");
Serial.println(" ");
}
}
if (counter == 3) {
Serial.println("Counter Value: 1");
Serial.println("Potentiometer Servo Controll");
Serial.println(" ");
}
//-------------------------------------Serial codes----------------------//
}
//Reaction if the counter value is 1
if (counter == 1) {
potVal1 = analogRead(potPin1); //Reading the pin of the 1st potentiometer
potVal1 = map(potVal1, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
pwm.setPWM(servoNum1, 0, potVal1); //Setting the Servo in pin 0 (servoNum1) to move according to the Scaled Value
delay(15);
potVal2 = analogRead(potPin2); //Reading the pin of the 2nd potentiometer
potVal2 = map(potVal2, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
pwm.setPWM(servoNum2, 0, potVal2); //Setting the Servo in pin 1 (servoNum2) to move according to the Scaled Value
delay(15);
potVal3 = analogRead(potPin3); //Reading the pin of the 3rd potentiometer
potVal3 = map(potVal3, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
pwm.setPWM(servoNum3, 0, potVal3); //Setting the Servo in pin 2 (servoNum3) to move according to the Scaled Value
delay(15);
}
//Reaction if the counter value is 2
if (counter == 2) {
joystickXVal = analogRead(joystickXPin) ; //Reading the X axis pin of the joystick
joystickXVal = map (joystickXVal, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
pwm.setPWM(servoNum1, 0, joystickXVal); //Setting the Servo in pin 0 (servoNum1) to move according to the Scaled Value
delay(15);
joystickYVal = analogRead(joystickYPin) ; //Reading the Y axis pin of the joystick
joystickYVal = map (joystickYVal, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
pwm.setPWM(servoNum2, 0, joystickYVal); //Setting the Servo in pin 1 (servoNum2) to move according to the Scaled Value
delay(15);
forservoNum3(); //Calling the class for the servo number 3
}
//Reaction if counter is 3
if (counter == 3) {
counter = 1; //Go back to reaction #1
}
}
void forservoNum3 () {
joystick2XVal = analogRead(joystick2XPin) ; //Reading the X axis pin of the 2nd joystick
joystick2XVal = map (joystick2XVal, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
//Code for the 360 continous servo to stop at a given scaled value
if (joystick2XVal >= 360 && joystick2XVal <= 380) {
joystick2XVal = 398;
pwm.setPWM(servoNum3, 0, joystick2XVal); //Setting the Servo in pin 2 (servoNum3) to move according to the Scaled Value
delay(15);
joystick2XVal = analogRead(joystick2XPin) ; //Reading the X axis pin again to exit the IF function
joystick2XVal = map (joystick2XVal, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
}
else
pwm.setPWM(servoNum3, 0, joystick2XVal); //Setting the Servo in pin 2 (servoNum3) to move according to the Scaled Value
delay(15);
}
Code Breakdown:
- Signals the compiler to use the Pushbutton library, PWM Servo Driver library, and Wire library.
-
#include <Pushbutton.h>
-
#include <Adafruit_PWMServoDriver.h>
-
#include <Wire.h>
- Define the pin for push button
-
#define buttonPin 2
- Creating object instances for the libraries
-
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
-
Pushbutton pushButton(buttonPin); //or
-
Pushbutton pushButton(2);
- Initialize the value for angles which the PWM Servo Driver Library can understand.
-
#define SERVOMIN 150 //For 0°
-
#define SERVOMAX 600 //For 180° or Depending on the max angle of your servo
- We then initialize the servo pins according to which pin you attached the servo on the servo shield. Note that we used uint8_t here instead of the traditional int or unsigned int since we know the size of our integer.
-
uint8_t servoNum1 = 0, servoNum2 = 1, servoNum3 = 2;
- Counter for the number of times that the button is pressed.
-
if (pushButton.isPressed()) {
}
counter = counter + 1;
delay(350); - Create condition for counter == 1:
-
if (counter == 1) {
- Inside the If statement, we create the code where it will read the nth number of the potentiometer and store it in potVal.
-
potVal1 = analogRead(potPin1);
- Since the reading of a potentiometer is typically 1 - 1023, we have to rescale this value for our PWM Servo Driver library to read.
-
potVal1 = map(potVal1, 0, 1023, SERVOMIN, SERVOMAX);
- Move the desired servo according to the potVal value.
-
pwm.setPWM(servoNum1, 0, potVal1);
- The same will happen on the 2nd and 3rd potentiometers and servos. The only difference is what number of potentiometer corresponds to what servo number.
- Now, we go to the Joystick codes. Since we know that a joystick is basically two potentiometers. One to control the X axis, and one to control the Y axis. I decided to put servos 1 and 2 in one joystick and servo 3 on the 2nd joystick but only utilizing one axis.
- Same process applies on both servo 1 and 2
-
joystickXVal = analogRead(joystickXPin) ; //Reading the X axis pin of the joystick
-
joystickXVal = map (joystickXVal, 0, 1023, SERVOMIN, SERVOMAX); //Scaling the analog value into a readable value for the library
-
pwm.setPWM(servoNum1, 0, joystickXVal); //Setting the Servo in pin 0 (servoNum1) to move according to the Scaled Value
- But here comes the tricky part. A problem was encountered with servo 3. The values read from the potentiometer by nature when left with no regulation is erratic at times which makes the servo 3 (MG995 Continuous 360°) move randomly.
- The solution was to create a function which when it reads a specific range of values for the joystick, it won't move the servo.
-
void forservoNum3 () {
- Start by reading the values on the joystick and scale it to readable values for the Adafruit library.
-
joystick2XVal = analogRead(joystick2XPin) ;
-
joystick2XVal = map (joystick2XVal, 0, 1023, SERVOMIN, SERVOMAX);
- And then create the range of values that the Arduino is reading from the joystick.
-
if (joystick2XVal >= 360 && joystick2XVal <= 380) {
- If the if condition above is true, then set the joystick2XVal value to a number (pre-determined) where the servo is in a complete still
-
joystick2XVal = 398; // The Value which the servo won`t move
-
pwm.setPWM(servoNum3, 0, joystick2XVal);
-
delay(15);
- Then read the joystick value again to get out of the if statement when the condition is no longer true.
-
joystick2XVal = analogRead(joystick2XPin) ;
-
joystick2XVal = map (joystick2XVal, 0, 1023, SERVOMIN, SERVOMAX);
- If false, set the value of the servo to either more than 380 (clockwise) or less than 360(counter clockwise).
-
pwm.setPWM(servoNum3, 0, joystick2XVal);
-
delay(15);
- Then call the created function on the void loop() function
-
forservoNum3();
Video Demo:
Conclusion:
Building this project is without a doubt, a fun and a very good exercise for the brain in terms of problem solving. By learning how modules work such as the joystick module, we can use it to control things like different types of servo motors. We also learned that depending on the gear configurations in the servo makes it function differently from one another. One can turn 360° continuous and others are limited to 180°.
Also, controlling these servos with map function, servo shield, and its corresponding library made our work so much easier and convenient.
This project can be improved by actually making use of this controls in a robotics projects such as a Crane or a robotic arm for picking up simple things such as cans, glass, etc.
References:
Ampere Electronics MG996r Servo
https://microcontrollerslab.com/mg995-servo-motor-pinout-interfacing-with-arduino-features-examples/
https://en.wikipedia.org/wiki/Potentiometer