Arduino control servo without library: Find out an easy way to do it
here; the key is combining a non-blocking delay with a precise microsecond timer!
"Arduino control servo without library" can it be done? The short
answer is yes!..but...it's not the ideal way to do it.
You definitely won't get as many working servos compared to library
code, but using this method does work.
You are probably here because all your timers are doing something
else and can not be used to drive a servo. Note: Servos use Timer1 but
there is a library that uses Timer2.
If can you should really consider using the built-in servo library that comes with the Arduino code and is very good at driving 12 servos - all interrupt driven and working in the background.
The key to
doing it without a library is to understand how a servo motor is driven. i.e. what exact
signal is required to operate a
servo.
You can see that a servo only has three wires and two of them provide
power and ground. In fact the third is a serial signal that is actually
a PWM signal.
What is the Servo control signal?:Arduino control servo without library
The servo motor is controlled by sending it a pulse-width modulated
(PWM) signal on its control line. The signal consists of a series of
electrical pulses that vary in width to convey positional information to
the servo.
Specifically, the PWM signal is a train of rectangular voltage pulses.
The pulse duration, or width, determines the servo's angular position.
Typically each pulse is 20 milliseconds long, and the interval between
pulses is also 20 ms, giving a pulse repetition rate of 50 Hz.
Image Source: luxorparts datasheet.
Within each 20 ms pulse, there is a high-voltage section between 1-2 ms
wide, representing the commanded position. A 1.5 ms pulse would tell the
servo to go to its center position. The for S3003 servo above, longer pulses rotate the servo
shaft anti-clockwise from center, shorter pulses rotate it clockwise.
The high-voltage section of the pulse is typically 5 volts. The
remainder of the 20 ms pulse is 0V.
This results in a 5V square wave with a precision-controlled ON duration
that is modulated between 1-2 ms to encode position data. The rest of
each 20 ms period is OFF at 0V.
The servo has circuitry to decode this PWM width into a proportional
voltage that powers its DC motor. As the pulse width changes, the
voltage applied to the motor changes, causing it to rotate its shaft to
the new angular position demanded by the master control circuit. With
milliseconds-precision pulses, the servo can achieve very fine
rotational resolution.
So in summary, the technical details of the servo control signal are a
50 Hz PWM voltage waveform varying in ON pulse width from 1-2 ms out of a
20 ms total period, with 5V high and 0V low levels. This conveys
sub-millisecond positional commands to the servo motor.
Important servo signals: Arduino control servo without library
The most important point in creating and using an Arduino control servo
without library is that the high part of the signal must be accurate.
On the other hand the low period (or repeat rate) is not as significant.
this does however, depend on the servo i.e. how far you can increase
the repeat period time before the servo stops working correctly is
entirely dependent on the servo you are using.
TIP: Try increasing the repetition period for your servo this allows more processor time but it depends on the servo
circuit. For an SG90s it allows 100ms (not under load) -seemingly with no bad
effect! but slowed an S3003. However check the holding force.
Using digitalWrite to control a servo
The simplest case:
You can make a servo driver using two delay functions delay() and delayMicroseconds(). This is the type of code you could use:
#define SERVO_PIN 9#define PULSE_HIGH_TIME 1500 // Adjust for your specific servo#define REFRESH_RATE 20 // Set the output refresh rate in millisecondsvoidsetup(){
pinMode(SERVO_PIN, OUTPUT);
}
voidloop(){
digitalWrite(SERVO_PIN, HIGH);
delayMicroseconds(PULSE_HIGH_TIME);
The problem with this code is that it uses blocking delays so your
processor can do nothing else except drive the servo. For this code the
delayMicroseconds() function provides an accurate high pulse while the
less accurate delay function produces a delay to the nearest
millisecond.
This is not ideal so...
...see the next section:
A more interesting case:
This technique uses a combination of the non-blocking millis() timer delay, and the blocking-delay delayMicroseconds() function.
The millis() timer is used to provide an approximate 20ms repeat rate
while the microsecond delay function is use to provide the accurate
pulse high signal. During the pulse high output the processor can do no
other work. This is the sacrifice your code makes to allow accurate
servo positioning.
The advantage of this method is that you also use the non blocking delay
timer millis(). So for the rest of the time the processor is free to do
other stuff - just not during the time that the signal is high.
Also, you have to ensure that the processor activity is done before the
low period of the servo output so the max time is ~20ms-2.5ms = 17.5ms.
Here 2.5ms is the maximum as this is what an SG90 and S3003 needed for
180 Degree rotation.
TIP: Increasing the repeat time
(REPEAT_RATE_MS) allows more processor time but it depends on the servo
circuit. For an SG90s (not under load) it allows 100ms -seemingly with no bad
effect! but slowed an S3003. However check the holding force.
Single servo Sketch: Arduino control servo without library
// Single Servo : Arduino control servo without library // Copyright John Main: TronicsBench.com
// Free for use in non- commercial projects.
#define servoPin 9
#define minPulseWidth 500
#define maxPulseWidth 2500
#define REPEAT_RATE_MS 20
unsignedlongangleChangeTime=millis();
unsignedlongpt20ms=millis();
intangle=0;
voidsetup(){
// Serial.begin(115200);
pinMode(servoPin,OUTPUT);
}
voidloop(){
staticint8_tangle_dir=1;
staticintpulseTime=500;
unsignedlongcurrentTime=millis();
if(currentTime-pt20ms>=REPEAT_RATE_MS){
digitalWrite(servoPin,HIGH);
delayMicroseconds(pulseTime);
digitalWrite(servoPin,LOW);
pt20ms=currentTime;
}
// Move to next position after x seconds
if(millis()-angleChangeTime>=1000){
// Serial.println(angle);
// Serial.println(pulseTime);
angle+=90*angle_dir;
if(angle>=180){angle_dir=-1;}
if(angle<=0){angle_dir=1;}
pulseTime=map(angle,0,180,minPulseWidth,maxPulseWidth);
angleChangeTime=currentTime;
}
servo-no-lib.ino
What else can you do with this code?
One thing you may want to do is to drive another servo. The way you
would do that is to add another servoPin and delayMicroseconds() section
in the main loop.
This does however reduce the processing time by up to another 2.5ms.
Two servo Sketch: Arduino control servo without library
// Two Servos : Arduino control servo without library
// Copyright John Main: TronicsBench.com // Free for use in non- commercial projects.
#define servoPin 9
#define servoPin2 10
#define minPulseWidth 500
#define maxPulseWidth 2500
#define REPEAT_RATE_MS 20
unsignedlongangleChangeTime=millis();
unsignedlongpt20ms=millis();
intangle=0;
voidsetup(){
// Serial.begin(115200);
pinMode(servoPin,OUTPUT);
pinMode(servoPin2,OUTPUT);
}
voidloop(){
staticint8_tangle_dir=1;
staticintpulseTime=500;
staticintpulseTime2=500;
unsignedlongcurrentTime=millis();
if(currentTime-pt20ms>=REPEAT_RATE_MS){
digitalWrite(servoPin,HIGH);
delayMicroseconds(pulseTime);
digitalWrite(servoPin,LOW);
digitalWrite(servoPin2,HIGH);// 2nd servo
delayMicroseconds(pulseTime2);
digitalWrite(servoPin2,LOW);
pt20ms=currentTime;
}
// Move to next position after x seconds
if(millis()-angleChangeTime>=1000){
// Serial.println(angle);
// Serial.println(pulseTime);
angle+=90*angle_dir;
if(angle>=180){angle_dir=-1;}
if(angle<=0){angle_dir=1;}
pulseTime=map(angle,0,180,minPulseWidth,maxPulseWidth);
pulseTime2=map(angle/2,0,180,minPulseWidth,maxPulseWidth);// half the movement of the other.
angleChangeTime=currentTime;
}
}
servo-no-lib-2servos.ino
Written by John Main who has a degree in Electronic Engineering.
Note: Parts of this page were written using claude-instant as a research assistant.
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.