Servomotor control with limits and variable speed


#1

Enhanced Sweep

A common introduction to microcontroller servo motor control uses the “Sweep” program. It is a staple among the Arduino programming examples included when you download the Arduino IDE. The program code demonstrates the functionality of the Arduino servo library commands and documents the steps necessary to make a servo arm sweep across its full range of positions.

With a few modifications and some external devices, the sweep servo can become a more useful device capable of moving between adjustable travel limits at an adjustable speed. There are many applications for hobby related projects such as crossing arms on a model train layout or precise movement of doors and landing gear on model aircraft.

The steps used to determine the limits and speed can be accomplished in many ways depending on your programming and sensor experience. For this example, three potentiometers will be added to the standard sweep circuit along with a few refinements of the Arduino code. At the heart of this demonstration is the Arduino Mini V5 which uses the Atmega328P microcontroller(uC), Figure 1. Not all of the uC inputs and outputs will be used allowing for the inclusion of additional peripherals down the road.
image

Setting things up

The Enhanced Sweep project begins with the uC mounted on a breadboard with all the necessary power and input/output connections including three external potentiometer inputs, an ON/OFF tactile switch input, LED center position and limit indicators and an output that drives a single servo.
The breadboard wiring layout is provided in Figure 2.

Note: Standard wire jumper kits such as those found on the Digi-Key prototyping web page are color coded according to length so wire color may change along a wire route. The schematic provided shows single color routing which is possible using custom cut wires from bulk spools.

Parts needed:

Servo: 180 degree (1528-1083-ND), Tactile Switch (450-1650-ND), Potentiometers (3310P-125-103L-ND), Arduino Mini V5 (1050-1044-ND). Diode - Green (160-1130-ND). Diode – Red (160-1127-ND), Capacitor (445-8571-ND), USB to serial converter for Arduino programming (1050-1021-ND). Resistor 10k (CF18JT10K0CT-ND).

Layout Considerations

Typical servos operate at 4.8 – 6.5VDC and can draw as much as 3 amps under load or during direction changes. Although a servo receives its signal directly from the uC, the Arduino Mini on-board voltage regulator cannot provide the level of current the servo requires. Note that the servo power leads are connected directly to the 6V power supply. As the uC has an onboard regulator, it too can run on that same 6V supply. If the servo and uC are running on separate supplies, they must share a common ground in order for the servo to recognize the signal coming from the uC. For this example, a tactile switch is used to start/stop the servo via the uC.

Code (Heavily commented for instructional purposes)

/* 	The potentiometers are used to provide adjustable input on analog pins A0, A1, A2
The analog inputs are used for left and right limits in servo travel as well as servo travel speed.
When deactivated, the servo returns to the nearest limit position at maximum speed.
An LED on digital pin 8 indicates each crossing of the center servo position. An additional LED on pin 9 indicates the servo has reached the maximum limit. The tactile switch is pulled HIGH via a 10k resistor when not pressed. Pressing the button changes its state to LOW (direct connection to ground).	*/
/********************************* Configuration ************************************/

#include <Servo.h>					//Include the Arduino servo library
Servo wiperServo;					//Create a name for the servo

/*************************** Declare and initialize variables ****************************/

int pos; int lastPos; 					//Current position variables
int leftServo; int rightServo; 				//Desired position variables
int servoSpeed = 2;					//Set the initial servo speed

/* startAngle variable - The left and right limit potentiometers can be adjusted to add/subtract an adjustable number of degrees between zero and 90 minus the startAngle variable. With the startAngle variable set at 70, the servo will travel 70 degrees from center before the pot adjustment has any effect. If the pot is set to zero, the servo will travel 70 degrees and then reverse direction. If the pot is set to its maximum, the servo will travel the full 90 degrees and then reverse direction. If the startAngle variable is set to 50 as another example, the limit potentiometers will have 40 degrees of adjustment. The higher the variable value, the finer the potentiometer control. 
*/
int startAngle = 70;

//Designate pin 2 as an interrupt for ON/OFF control using the tactile switch
const byte interruptPin = 2;

//Set the system status flag. Also used for ON/OFF control
volatile byte flag = LOW;   		


/**************************** Setup area - Runs one time ****************************/

void setup() {
pinMode(8, OUTPUT); pinMode(9, OUTPUT);	//Set digital pins 8 and 9 as outputs

//Flash red LED 3 times to indicate uC has entered setup mode.
for (int flash = 0; flash < 3; flash++)
{
digitalWrite(8, HIGH); delay(200); digitalWrite(8, LOW); delay(200);
}

//Identify the servo signal digital pin
wiperServo.attach(10);       
                                           		
//Identify interrupt pin, function to run on interrupt, HIGH to LOW detection
attachInterrupt(digitalPinToInterrupt(interruptPin), onOff, FALLING);   

//Read the analog level of uC pin A0, convert the 12bit value to a degree range (0-90),   
rightServo = map(analogRead(A0), 0, 1023, 90, 0);

//Confine the range of servo travel to the right of center to 0-90.
rightServo = constrain(rightServo, 0, 90);

//Run the servo. The delay gives the servo time to reach its destination.
wiperServo.write(rightServo); delay(2000);

//Read the analog level of uC pin A1, convert the 12bit value to a degree range (90-180),
leftServo = map(analogRead(A1), 0, 1023, 90, 180);

//Confine the range of servo travel to the left of center to 90-180.
leftServo = constrain(leftServo, 90, 180);

//Run the servo. The delay gives the servo time to reach its destination.
wiperServo.write(leftServo); delay(2000);

//Flash an LED 3 times to indicate end of servo test.
for (int flash = 0; flash < 3; flash++)
{
digitalWrite(8, HIGH); delay(200); digitalWrite(8, LOW); delay(200);
}
//Remember the servo position as a starting point in the main program
lastPos = leftServo;

} //End of setup 

/************************* Main program - Loops continuously ***********************/

void loop() {

//Get 12bit value from speed potentiometer and map to desired output range
//Lower values of this variable cause higher servo speeds. 
servoSpeed = map(analogRead(A2), 0, 1023, 0, 20);

//Set range limits for speed - less than 2 will cause servo chatter         
servoSpeed = constrain(servoSpeed, 2, 18);               

//Turn on the green LED if leftServo value is at the center position.
if (analogRead(A1) < 25)
{
leftServo = 90;
digitalWrite(8, HIGH);
 		delay(25);
  		}
  else
 		{
    		leftServo = map(analogRead(A1), 26, 1023, 90 + startAngle, 180);
    		leftServo = constrain(leftServo, 90, 180);

//Turn on the Red LED if leftServo is approaching the maximum position.
    		if (leftServo >= 175) digitalWrite(9, HIGH);
    		else digitalWrite(9, LOW);
 		}

 	//Turn on the green LED if rightServo value is at the center position.
 if (analogRead(A0) < 25)
 		{
    		rightServo = 90;
    		digitalWrite(8, HIGH);
    		delay(25);
  		}
else
 		{
    		rightServo = map(analogRead(A0), 26, 1023, 90 - startAngle, 0);
   		rightServo = constrain(rightServo, 0, 90);

//Turn on the Red LED if rightServo is approaching the maximum position.
   		if (rightServo <= 5) digitalWrite(9, HIGH);
    		else digitalWrite(9, LOW);
  		}

//The following responds to a tactile button press (High to Low transition).
if (flag)                                                  
{
    		if (lastPos > rightServo)

			//Continuous motion loop - from right limit to left limit
    			for (pos = lastPos; pos >= rightServo; pos--)
{

//If the flag changes state (turned OFF), jump out of the loop    
if (!flag) break;                                    
   
//Run the servo motion and LED indication function
 runServo(servoSpeed);                                
 			}

if (lastPos < leftServo)
 
//Continuous motion loop - from left limit to right limit
for (pos = lastPos; pos <= leftServo; pos++) 
      			{
       			 if (!flag) break;                                    
        			runServo(servoSpeed);                                
 			 }
}

//If the flag, set by the switch, is false (OFF) enter this loop
if (!flag)                                             
{
    		if (lastPos >= 90)
    		{
//If the last position is left of center, go to the left limit
wiperServo.write(leftServo);

//Remember the current position.                        
pos = leftServo;
 		}
 else
 		{
//If the last position is right of center, go to the right limit
wiperServo.write(rightServo);
                            
//Remember the current position
 		pos = rightServo;
}
      	 }
} //End of main program

Custom Functions

/*********************************** Custom Functions ******************************/
//The following function runs when a falling edge (HIGH to LOW) interrupt occurs (switch press is detected)
void onOff()                                            
{
//Toggle the flag status
flag = !flag;                                           
}

//The following function runs when servo motion is required
void runServo(int rate)                                   
{
//Send the position value to the servo
  	wiperServo.write(pos);
  	
//Remember the last position for determining which way to go home when flag is false
lastPos = pos;        
  	
//Turn ON the green LED when at center
if (pos == 90) digitalWrite(8, HIGH);	

//Turn OFF the servo center indicator when at any angle other then 90
 	if (pos != 90) digitalWrite(8, LOW);

//Stop for a moment when the servo arm reaches a limit before reversing direction.
 	if (pos == rightServo) delay(100); 
if (pos == leftServo) delay(100); 
  
//Set the speed by delaying the time between position values
delay(rate);
}

 //End of custom functions

Summary

Servo motors are powerful and easy to implement devices that convert signals into motion. The above code demonstrates how to manipulate two simple variables, rightServo and leftServo, in order to make the servo motor do many things over and above a simple sweep function. The code can be easily modified to yield even more possibilities using these same two variables. The tactile switch can be replaced with any kind of trigger device that work on a HIGH to LOW signal configuration such as a magnetic switch or other sensor with a logic output. The applications are limitless.