Arduino servo position selection with five buttons, Arduino Uno, servo motor control, Proteus simulation

Control Servo Motor with Arduino in Proteus

2.1M Views
40
700
60
25
60
PCBWay

Hello friends! In this tutorial, we will control a servo motor with Arduino in Proteus. We will begin with a simple sweep, understand the signal that commands the position, and then add five push buttons so we can select individual positions.

Libraries

Libraries used in this tutorial

Arduino UNO Library for Proteus: V1.0 (2015, used in this tutorial) · latest version: V4.0 (October 2026)

Helping Tools

A positional hobby servo is convenient because its motor, gearbox and position controller are assembled in one unit. Arduino supplies a command signal while the servo's internal electronics move the shaft towards the requested position. This makes it useful for small mechanisms, camera mounts and steering experiments.

We will keep the original circuit's control connection on Arduino pin 4. Along the way, I will explain how the angle shown by a Proteus model relates to the value in our sketch, why a servo needs a suitable power supply, and how to avoid confusing a software command with an accurate mechanical measurement.

Arduino servo position selection with five buttons, Arduino Uno, servo motor control, Proteus simulation
Figure: Arduino servo position selection with five buttons.

What We Are Controlling

This project uses a positional servo. Its control input requests a position within a limited travel range. A continuous-rotation servo behaves differently: a similar input generally requests direction and speed, so it cannot replace the positional model in this exercise without changing the meaning of the program.

A servo is not the only way to obtain controlled motion. A stepper motor can move through commanded steps, and a DC motor with suitable feedback can form a positioning system. The advantage here is that a hobby servo already contains much of the required control hardware.

Do not assume that writing 47 degrees produces a measured shaft position of exactly 47 degrees. Gear backlash, load, supply voltage, deadband and calibration affect the result. The simulator is useful for checking connections and program behaviour, while mechanical accuracy must be assessed on the actual mechanism.

Components and Software

ItemPurpose
Arduino Uno modelRuns the compiled sketch
Positional servo modelDisplays movement in response to the control pulses
Five momentary push buttonsSelect positions in the second circuit
Power and ground terminalsProvide the electrical reference and model supply
Arduino IDE and Servo libraryCompile the control program
Proteus with compatible simulation modelsSimulate the assembled circuit

Confirm that the selected Arduino and servo components have simulation models, not only schematic symbols. An attractive component drawing does not guarantee that its firmware or movement can be simulated. If you need the board model, start with our Arduino Uno library for Proteus.

Understand the Servo Signal

The control signal is a repeated pulse whose high duration represents the position command. A commonly used illustrative range is 1000 to 2000 microseconds, with 1500 microseconds at its midpoint. The correct limits depend on the particular servo or simulation model.

For a linear mapping over a nominal 180-degree command range, pulse width p = pmin + (theta/180) x (pmax - pmin). With limits of 1000 and 2000 microseconds, a command of 45 degrees produces 1250 microseconds. A 135-degree command produces 1750 microseconds.

This equation describes our command mapping. It does not prove that the physical servo travels through exactly 180 degrees. Keep the distinction clear when you compare a displayed angle, a library argument and a measurement made with a protractor or encoder.

The Arduino Servo API allows explicit minimum and maximum pulse widths in attach(). Its default endpoints are 544 and 2400 microseconds, so attach(4) and attach(4, 1000, 2000) do not produce the same mapping. The examples below declare their endpoints rather than hiding that difference.

Servo.read() reports the command setpoint, not independent feedback from the shaft. Use writeMicroseconds() when you want to express a pulse width directly. Our examples use write() with angles between 0 and 180 and an explicitly selected mapping.

Build the Basic Arduino and Servo Circuit

Open your schematic and place the Arduino Uno and positional servo. Identify the servo terminals by their labels and the model documentation. Do not assume that the centre terminal is always the signal connection; connector layouts vary between symbols and real products.

Arduino Uno and servo wiring in Proteus, Arduino Uno, servo motor control, Proteus simulation
Figure: Arduino Uno and servo wiring in Proteus.
Servo connectionConnect toReason
Signal or controlArduino D4Carries the position command
GroundCommon circuit groundProvides the signal reference
SupplyThe voltage specified for the selected modelPowers the servo electronics and motor

On real hardware, use a regulated supply appropriate for the servo's rated voltage and current, including startup and stall demand. A GPIO pin cannot power a motor. When Arduino and the servo use separate supplies, their grounds must share a suitable reference for the control signal.

Keep motor current out of delicate signal return paths. Loose breadboard contacts or thin supply leads can cause voltage drops that reset the controller when the servo starts moving. A simulation with ideal supply rails does not demonstrate that a physical power arrangement will remain stable.

Arduino Code for a Simple Sweep

We will sweep through the model's nominal full command range. Before using this sketch on hardware, establish safe endpoints for that servo and mechanism. A horn attached to a linkage may hit a mechanical stop long before the servo reaches its own electrical limit.

#include <Servo.h>

Servo arm;
const byte servoPin = 4;
const int minimumPulseUs = 1000;
const int maximumPulseUs = 2000;

void setup() {
  arm.attach(servoPin, minimumPulseUs, maximumPulseUs);
  arm.write(90);
  delay(500);
}

void loop() {
  for (int angle = 0; angle <= 180; ++angle) {
    arm.write(angle);
    delay(15);
  }
  for (int angle = 179; angle >= 1; --angle) {
    arm.write(angle);
    delay(15);
  }
}

The startup command requests the midpoint. The first loop then requests increasing positions, and the second requests decreasing positions. The 15-millisecond delay controls how quickly our sketch changes the command; it is not a measurement of how long the shaft takes to reach each position.

There are 181 commands in the forward loop and 179 in the return loop, giving approximately 360 x 15 ms = 5.4 seconds per command cycle, plus execution overhead. The actual servo may lag behind these commands, particularly under load.

For a mechanism that cannot safely travel to the endpoints, change the bounds to an established working interval such as 30 through 150 degrees. Those values are only an example; the safe interval must come from your own servo and linkage.

Compile the Sketch and Run Proteus

  1. Select Arduino Uno as the target board in the Arduino IDE.
  2. Compile the sketch and resolve any errors before opening the simulation.
  3. Use the IDE's export-compiled-binary option to obtain the application HEX file.
  4. Open the Arduino model's properties in Proteus and select that HEX file as its program.
  5. Check the model's clock configuration against the Uno firmware target, normally 16 MHz.
  6. Run the simulation and observe the servo while the command sweeps.

Menu placement can differ between IDE versions. Our guide to obtaining an Arduino HEX file explains the purpose of the compiled file. Use the current build, not an older file with the same sketch name in another directory.

Servo sweep positions in the original Proteus simulation, Arduino Uno, servo motor control, Proteus simulation
Figure: Servo sweep positions in the original Proteus simulation.

The original screenshots show a servo display using negative and positive angles around a centre position. Our sketch uses a zero-to-180 command scale. These are different coordinate conventions, and the model's direction can also differ from the direction you expect when viewing a real shaft from the other side.

Start with the midpoint and then try two nearby commands to establish the direction. If the selected model expects different pulse endpoints, adjust the mapping to its documentation. Avoid copying unexplained angle offsets simply to force one screenshot to match.

Add Five Push Buttons

Now we will replace the automatic sweep with five selected positions. Connect the buttons to digital pins 8, 9, 10, 11 and 12. Each button connects its input to ground when pressed; the sketch enables the internal pull-up on every input.

Five push buttons connected to Arduino servo control inputs, Arduino Uno, servo motor control, Proteus simulation
Figure: Five push buttons connected to Arduino servo control inputs.

An unpressed button therefore reads HIGH, while a pressed button reads LOW. This active-low arrangement avoids a floating input. Check the internal connections of four-leg tactile switches: two legs may already be connected together, so choosing the wrong pair can make the input appear permanently pressed.

Input pinCommand anglePulse with our mapping
D80 degrees1000 microseconds
D945 degrees1250 microseconds
D1090 degrees1500 microseconds
D11135 degrees1750 microseconds
D12180 degrees2000 microseconds

This table is deliberately consistent with the declared endpoints. The historical sketch used model-specific values such as 117 and 68 to obtain particular displayed positions. Those numbers should not be treated as universal calibration values for every servo.

Button-Controlled Servo Code

A mechanical button can bounce between states when it closes. We will accept a new button pattern only after it remains unchanged for 25 milliseconds. We will also ignore simultaneous presses instead of allowing the order of several independent if statements to decide the result.

#include <Servo.h>

Servo arm;
const byte buttonPins[] = {8, 9, 10, 11, 12};
const byte positions[] = {0, 45, 90, 135, 180};
const byte buttonCount = 5;
const unsigned long debounceMs = 25;
byte candidateMask = 0;
byte acceptedMask = 0;
unsigned long candidateSince = 0;

byte readButtons() {
  byte mask = 0;
  for (byte i = 0; i < buttonCount; ++i) {
    if (digitalRead(buttonPins[i]) == LOW) {
      mask |= (1U << i);
    }
  }
  return mask;
}

void setup() {
  for (byte i = 0; i < buttonCount; ++i) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
  arm.attach(4, 1000, 2000);
  arm.write(90);
}

void loop() {
  const unsigned long now = millis();
  const byte sample = readButtons();
  if (sample != candidateMask) {
    candidateMask = sample;
    candidateSince = now;
  }
  if (now - candidateSince < debounceMs ||
      candidateMask == acceptedMask) {
    return;
  }
  acceptedMask = candidateMask;
  if (acceptedMask == 0 ||
      (acceptedMask & (acceptedMask - 1)) != 0) {
    return;
  }
  for (byte i = 0; i < buttonCount; ++i) {
    if (acceptedMask == (1U << i)) {
      arm.write(positions[i]);
      break;
    }
  }
}

Each bit in the mask represents one button. A nonzero mask containing exactly one set bit represents an unambiguous selection. The expression involving mask minus one rejects patterns containing two or more set bits.

Releasing every button does not write a new position. The servo therefore retains the last command. Holding a button also does not repeatedly change the setpoint, although the Servo library continues generating the pulses associated with the selected position.

The unsigned subtraction used for elapsed time follows the usual millis() timing pattern. It avoids a long blocking delay in the main loop and leaves room for later additions such as status indicators or serial messages.

Check the Button Simulation

Compile the second sketch and replace the model's program file with its new HEX output. It is easy to leave the sweep program selected and then wonder why the buttons have no effect.

Original button-selected servo positions in Proteus, Arduino Uno, servo motor control, Proteus simulation
Figure: Original button-selected servo positions in Proteus.

The retained figure shows the original experiment's selected positions. Use it to understand the intended interaction, while using the revised table and the selected model's pulse calibration to interpret your own result.

  1. Start with no buttons pressed and confirm that the midpoint is requested.
  2. Press D10's button and check that the centre command remains selected.
  3. Press D9 and D11 separately to compare movement on either side of centre.
  4. Try the endpoint buttons only when the model or mechanism permits that travel.
  5. Release all buttons and confirm that the last selected command remains.
  6. Press two buttons together and confirm that no new ambiguous command is accepted.

The final check does not mean a servo can mechanically hold any load forever. Holding torque has limits, and a stalled or heavily loaded servo can draw substantial current and heat up. Remove unnecessary load during initial hardware experiments.

Review the Design Before Building Hardware

Separate three questions: does the program choose the intended command, does the signal have the intended pulse width, and does the mechanism reach the intended position? A correct answer to the first does not automatically prove the other two.

An oscilloscope or logic analyser can check the signal relative to ground. A mechanical measurement checks travel and repeatability. Supply measurements during movement reveal drops that a static voltage reading may miss. Record these observations independently so a wiring fault is not mistaken for a software calibration problem.

For a heavier mechanism, calculate the approximate static torque before choosing a servo. A 0.2 kg mass acting at a perpendicular lever arm of 0.05 m produces torque of about 0.2 x 9.81 x 0.05 = 0.0981 N m. Acceleration, friction, linkage geometry and a suitable design margin add to that simple estimate.

Move gradually from unloaded motion to the intended load. If the shaft cannot reach a command, repeatedly increasing the command does not solve insufficient torque or a mechanical obstruction. Check the mechanism and power arrangement first.

Practical Improvements for a Real Mechanism

Plan Startup and Recovery Behaviour

Our demonstration requests the midpoint after every reset. That is convenient on an unloaded bench, but it may be unsuitable when the horn is attached to a mechanism. Imagine a lid that is already fully open: a restart that immediately requests the centre can move it without a new button press. Decide whether your application should centre, wait for an enable input, or establish position through an additional sensor.

The five-button sketch accepts a button held during startup after its debounce interval. If your machine should require a fresh press, add a startup state that waits until every button has been released. This is a behavioural requirement, so define it before changing the input logic.

A power interruption also removes the assumption that the last software command describes the present mechanical position. Someone may move the horn while power is disconnected. A stored command can help reconstruct the intended setpoint, but it cannot replace feedback about where the mechanism actually is.

Choose a Command Rate Deliberately

The button sketch jumps directly to its newly selected setpoint. For a gentler command trajectory, maintain separate target and commanded positions. At each scheduled update, move the commanded position a small step towards the target, stopping when the target is reached.

For example, a two-degree step every 20 milliseconds corresponds to a nominal command rate of 100 degrees per second. Moving the command from 30 to 150 degrees would then require approximately 1.2 seconds. This calculation describes the setpoint trajectory, not guaranteed physical travel time.

Keep button reading active while the trajectory changes so a new selection can replace the target promptly. A long for loop with delays makes that harder because the program may not inspect the inputs until the whole movement sequence finishes. Use elapsed-time scheduling when responsiveness matters.

Keep a Small Calibration Record

For each tested servo, record the model identifier, supply voltage, pulse limits and measured positions at several intermediate commands. Approach each position from both directions to reveal backlash or hysteresis. Repeat the measurements with the intended load if it changes the result.

Describe the reference direction used for positive rotation and the surface from which the shaft is viewed. This removes an easy source of disagreement between two otherwise correct measurements. Also record whether an angle refers to the servo shaft or the output of a linkage, since those angles may not be equal.

These records make a later replacement easier to assess. A new servo with the same connector may have different travel or centre behaviour, and an old calibration table should not automatically be applied to it.

Troubleshooting

SymptomCheck
No movementProgram file, model support, supply, ground and D4 signal connection
Continuous spinningWhether a continuous-rotation servo was selected
Unexpected displayed anglePulse endpoints and the model's angle convention
Buttons appear reversedINPUT_PULLUP means pressed is LOW
Arduino resets during movementSupply capacity, voltage drop and ground routing
Buzzing at an endpointUnsafe travel limit, obstruction or excessive load

Frequently Asked Questions

Can I move the signal from D4?

Yes, use a suitable pin supported by the library and board, then change both the wiring and attach() argument. This tutorial keeps D4 to match the original circuit.

Does the delay set the servo's exact speed?

No. It spaces software commands. Physical speed depends on the servo, supply, load and distance between positions.

Why does the servo stay in position after button release?

The program retains the previous setpoint and keeps the servo attached. No new position is requested when all inputs are released.

Can I use the same endpoints on every servo?

No. Match the pulse range and allowed travel to the selected device. Start around a known safe centre and establish limits before attaching a restrictive linkage.

What should I try next?

Add a controlled rate of change between selected positions, then compare the requested trajectory with the measured shaft movement. That extension connects this basic experiment to practical motion control.


Comments

18

Join the conversation

1 reply
1 reply
2 replies
1 reply
1 reply
1 reply
Reply5

could you please specify the servo motor specifications ( the servo motor details ) i.e the max, min angles we should set or leave it at default

Reply7

sir it is good description it work properly

but I need same help in voice Control Dc Motor with Arduino by android phone for my project can you help me
with code please sand to me please please

Thank you.

Reply8

Hi. Could you please tell me how to connect an arduino nano, ir sensor and servo motor to an automatic hand sanitizer project with coding? I really need this to my project.

1 reply
Reply9

What’s the meaning of:
myservo.write(180),
myservo.write(117),
myservo.write(93),
myservo.write(68),
myservo.write(3)?
how you find these numbers ???