Arduino - Keypad-Controlled Servo

Introduction: Arduino - Keypad-Controlled Servo

For this project, we will take input from a keypad, process that input as an angle position, and move a servo motor based on the 3-digit angle acquired. The parts required are:

1 x Arduino Uno or similar

1 x Keypad (4x4)

1 x Servo Motor

I used a 4 x 4 keypad, but if you have a 3x4 keypad, it has a very similar hookup, so it could be easily adapted. Similarly, some Arduino kits come with a 4x4 push-button matrix which work in exactly the same way.

Step 1: Set Up the Hardware

The setup is straightforward. We will be using:

Pins 4 - 11: Keypad input

Pin 3: Servo motor output

VCC (5V)

GND (Ground)

The keypad input hooks up straight down the line. Hook up each wire on the keypad to pins 4 - 11 from right to left. The servo motor hooks directly up to 5V, ground, and pin 3. On my servo (Tower Pro SG90), the red wire was for 5V, brown was ground, and yellow was the output pin. Check the documentation for your servo motor.

Step 2: The Code

Let's look at the code:

#include <Keypad.h>
#include <Servo.h>

// Set up keypad variables:
int numKeyPresses = 0;            // Track number of key presses
int maxKeyPresses = 3;            // Only allow 3 digits to be entered
int keyPresses[3] = { 0, 0, 0 };  // Initialize an empty array to hold input
const byte numRows= 4;            // # of rows on the keypad
const byte numCols= 4;            // # of columns on the keypad

// Set up servo variables:
int angle = 0;                    // Angle in degrees to position servo [0-180]
int angleMultiplier = 1;          // Multiply by each digit, divide by 10 on each input
Servo servo;                      // Create the servo object
int servoPin = 3;                 // Set the servo pin

char keymap[numRows][numCols]=    // Setup the keypad layout
{
  {'1', '2', '3', 'A'}, 
  {'4', '5', '6', 'B'}, 
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'},
};

Here, we import Keypad and Servo libraries to help with some of the input and output processing. The numkeyPresses variable tracks how many entries have been input. When maxKeyPresses is reached, this will be reset to 0. The keymap is set up in a 4x4 matrix, mimicking the physical keypad.

// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[numRows] = { 11, 10, 9, 8 }; 

// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[numCols] = { 7, 6, 5, 4 };

// Create the Keypad
Keypad kpd = Keypad( makeKeymap( keymap ), rowPins, colPins, numRows, numCols );

void setup()
{
  Serial.begin(9600);           // Start up serial comms
  resetAngleMultiplier();       // Start accepting numeric input
  servo.attach( servoPin );     // Attaches the servo to the servo object
  
} // setup

Here we use a method called resetAngleMultiplier, which I will discuss in a moment. The idea is that we want to mathematically convert the input to a usable number, rather than characters. This sets us up to do that.

void loop()
{
  char key = kpd.getKey();
  if( key )                     // Check for a valid key
  {
    if( key >= 0x41 && key <= 0x44 || key == 0x23 || key == 0x2A )
    {
      resetInput();
      Serial.println( "ERROR: Numeric input only!" );
    }                           // ^ if invalid entry
    else                        // Else, entry is valid:
    {
      angle += angleMultiplier * ( key - 0x30 );
      angleMultiplier /= 10;
      
      if( numKeyPresses == maxKeyPresses - 1 )
      {
        setServo( angle );            // Use the input to turn servo
        resetInput();
      }
      else
      {
        numKeyPresses++;
      }
      Serial.println( (String) angle );
    }
        
  } // if( key )

} // loop

Here we take the input and process it. We will talk about how it is processed in a moment.

void setServo( int angle )
{
  if( angle > 180 )
    angle = 180;
  Serial.println( "Setting servo to " + (String) angle + " degrees." );
  servo.write( angle );          // Set the servo position
  
} // setServo


void resetAngleMultiplier()
{
  angleMultiplier = 1;
  /* We started out with a multiplier of 10^0 (or 1). For each 
  number we want to accept, we want to have a multiplier one 
  order of magnitude greater. So, for example, for 5 digits, the 
  multiplier starts out as 10 000. */
  for( int i = 0; i < maxKeyPresses - 1; i++ )
    angleMultiplier *= 10;
    
} // resetAngleMultiplier


void resetInput()
{
  resetAngleMultiplier();       // Reset the numeric input
  angle = 0;                    // Reset the angle
  numKeyPresses = 0;            // Reset number of key presses
}

The Math

In the loop, we check for non-numeric input and reset the numKeyPresses variable if detected. The part that does the conversion is: angle += angleMultiplier * ( key - 0x30 ). When we got the keypress, it is returned as a character from the keymap 2-diminsional array. key - 0x30 subtracts 30 in hexadecimal in order to get its numeric equivalent.

Then, we have to multiply it by angleMultiplier. The angle multiplier starts out at 100. So, for instance, if the first digit input is 3, the number added to the angle will be 300. Then divide the angle multiplier by 10 so that the next iteration, the angle multiplier will be 10. If 2 is input, it is multiplied by 10 and added, giving us 320. This continues until the end of the input.

I wrote this to be scaleable, allowing maxKeyPresses to be expanded. C++'s maximum integer value is 2147483647, so using this program, you could theoretically take up to 10 digits of input, as long as the actual key code did not add up to more than this. You could always use a long to store the input, but for our purposes, there is no need.

Applying the Math

So, now that we have our (probably overly-complicated) calculation, it is simply passed to the servo to position it to that particular angle. Here it is limited to 180 degrees of movement. If the numeric input is over 180, it is reset to 180 degrees and passed to the servo. This could have multiple applications, whether you wanted to implement a locking system in your home, a security camera positioner, or whatever you need it for.

Although this is not the only way to convert keypad input, this is one way that works well, and it turned out to be a fun experiment with computing and mathematical relationships as well.

Be the First to Share

    Recommendations

    • Game Design: Student Design Challenge

      Game Design: Student Design Challenge
    • Make It Bridge

      Make It Bridge
    • For the Home Contest

      For the Home Contest

    5 Comments

    0
    colinselina
    colinselina

    Question 1 year ago

    i dont know how to set the password??

    0
    technessgt
    technessgt

    Question 3 years ago

    Hey! Here's my code,somehow its not working... My servo motor moves for the 1st two times then stops,please help!

    #include <Keypad.h>
    #include <Servo.h>
    // Set up keypad variables:
    int numKeyPresses = 0; // Track number of key presses
    int maxKeyPresses = 3; // Only allow 3 digits to be entered
    int keyPresses[3] = { 0, 0, 0 }; // Initialize an empty array to hold input
    const byte numRows = 4; // # of rows on the keypad
    const byte numCols = 4; // # of columns on the keypad
    // Set up servo variables:
    int angle = 180; // Angle in degrees to position servo [0-180]
    int angleMultiplier = 1; // Multiply by each digit, divide by 10 on each input
    Servo servo; // Create the servo object
    int servoPin = 3; // Set the servo pin
    char keymap[numRows][numCols] = // Setup the keypad layout
    {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'},
    };
    // Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
    byte rowPins[numRows] = { 11, 10, 9, 8 };
    // Connect keypad COL0, COL1 and COL2 to these Arduino pins.
    byte colPins[numCols] = { 7, 6, 5, 4 };
    // Create the Keypad
    Keypad kpd = Keypad( makeKeymap( keymap ), rowPins, colPins, numRows, numCols );
    void setup()
    {
    Serial.begin(9600); // Start up serial comms
    resetAngleMultiplier(); // Start accepting numeric input
    servo.attach( servoPin ); // Attaches the servo to the servo object
    } // setup
    void loop()
    {
    char key = kpd.getKey();
    if ( key ) // Check for a valid key
    {
    if ( key >= 0x41 && key <= 0x44 || key == 0x23 || key == 0x2A )
    {
    resetInput();
    Serial.println( "ERROR: Numeric input only!" );
    } // ^ if invalid entry
    else // Else, entry is valid:
    {
    angle += angleMultiplier * ( key - 0x30 );
    angleMultiplier /= 10;
    if ( numKeyPresses == maxKeyPresses - 1 )
    {
    setServo( angle ); // Use the input to turn servo
    resetInput();
    }
    else
    {
    numKeyPresses++;
    }
    Serial.println( (String) angle );
    }
    } // if( key )
    } // loop
    void setServo( int angle )
    {
    if ( angle > 180 )
    angle = 180;
    Serial.println( "Setting servo to " + (String) angle + " degrees." );
    servo.write( angle ); // Set the servo position
    } // setServo
    void resetAngleMultiplier()
    {
    angleMultiplier = 1;
    /* We started out with a multiplier of 10^0 (or 1). For each
    number we want to accept, we want to have a multiplier one
    order of magnitude greater. So, for example, for 5 digits, the
    multiplier starts out as 10 000. */
    for ( int i = 0; i < maxKeyPresses - 1; i++ )
    angleMultiplier *= 10;
    } // resetAngleMultiplier
    void resetInput()
    {
    resetAngleMultiplier(); // Reset the numeric input
    angle = 0; // Reset the angle
    numKeyPresses = 0; // Reset number of key presses
    }

    0
    technessgt
    technessgt

    3 years ago

    Hi,I really need help. My servo motor is moving only the first two times,then it stops


    #include <Keypad.h>
    #include <Servo.h>
    // Set up keypad variables:
    int numKeyPresses = 0; // Track number of key presses
    int maxKeyPresses = 3; // Only allow 3 digits to be entered
    int keyPresses[3] = { 0, 0, 0 }; // Initialize an empty array to hold input
    const byte numRows = 4; // # of rows on the keypad
    const byte numCols = 4; // # of columns on the keypad
    // Set up servo variables:
    int angle = 180; // Angle in degrees to position servo [0-180]
    int angleMultiplier = 1; // Multiply by each digit, divide by 10 on each input
    Servo servo; // Create the servo object
    int servoPin = 3; // Set the servo pin
    char keymap[numRows][numCols] = // Setup the keypad layout
    {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'},
    };
    // Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
    byte rowPins[numRows] = { 11, 10, 9, 8 };
    // Connect keypad COL0, COL1 and COL2 to these Arduino pins.
    byte colPins[numCols] = { 7, 6, 5, 4 };
    // Create the Keypad
    Keypad kpd = Keypad( makeKeymap( keymap ), rowPins, colPins, numRows, numCols );
    void setup()
    {
    Serial.begin(9600); // Start up serial comms
    resetAngleMultiplier(); // Start accepting numeric input
    servo.attach( servoPin ); // Attaches the servo to the servo object
    } // setup
    void loop()
    {
    char key = kpd.getKey();
    if ( key ) // Check for a valid key
    {
    if ( key >= 0x41 && key <= 0x44 || key == 0x23 || key == 0x2A )
    {
    resetInput();
    Serial.println( "ERROR: Numeric input only!" );
    } // ^ if invalid entry
    else // Else, entry is valid:
    {
    angle += angleMultiplier * ( key - 0x30 );
    angleMultiplier /= 10;
    if ( numKeyPresses == maxKeyPresses - 1 )
    {
    setServo( angle ); // Use the input to turn servo
    resetInput();
    }
    else
    {
    numKeyPresses++;
    }
    Serial.println( (String) angle );
    }
    } // if( key )
    } // loop
    void setServo( int angle )
    {
    if ( angle > 180 )
    angle = 180;
    Serial.println( "Setting servo to " + (String) angle + " degrees." );
    servo.write( angle ); // Set the servo position
    } // setServo
    void resetAngleMultiplier()
    {
    angleMultiplier = 1;
    /* We started out with a multiplier of 10^0 (or 1). For each
    number we want to accept, we want to have a multiplier one
    order of magnitude greater. So, for example, for 5 digits, the
    multiplier starts out as 10 000. */
    for ( int i = 0; i < maxKeyPresses - 1; i++ )
    angleMultiplier *= 10;
    } // resetAngleMultiplier
    void resetInput()
    {
    resetAngleMultiplier(); // Reset the numeric input
    angle = 0; // Reset the angle
    numKeyPresses = 0; // Reset number of key presses
    }

    Here's the code. Please help asap

    0
    Ayah N.B
    Ayah N.B

    Question 4 years ago

    Can you give me the link of the keypad library used in this code?