Control 3 Servo Motors (SG90, MG995, MG996R) with 16-Channel PWM Servo Shield on Arduino

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

  • 16-Channel 12-bit PWM/Servo Shield Servo Drive Module

  • SG90 Micro-Servo Motor
  • 
    
  • MG995 High Torque Setvo Motor Metal Gear (360 rotation)
  • 
    
  • MG996R High-Torque Servo Metal Gear
  • 
    
  • Potentiometer
  • 
    
  • Joystick Module
  • 
    
  • Arduino Uno w/ cable
  • 
    
  • 5V 2A Power Supply AC-DC Wall Adapter
  • Large Tactile Push Button
  • 
    
  • Wires
  • 
    
    

     

    Software Components

    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

    16 channel 12-bit pwm servo shieldAdafruitAnalogAnalog joystickArduinoArduino unoArduino uno r3ButtonControlMg995Mg996rMotorPotentiometerPush buttonPushbuttonsPwmServoServo motorSg90

    Leave a comment

    All comments are moderated before being published