Vous êtes sur la page 1sur 9

Search...

AboutMe

Photography

Projects

DIY Servo With Arduino, DC Motor, And


Potentiometer
By Church

on July 24th, 2008

15

Posted in Projects | Robots

Not being happy with the ability to only move a minimum of 1 degree and only at a single speed for my time-lapse automaton project, decided to
hack up my pan servo and get a little more control over it via the arduino directly.
The basic idea is to remove the controller from a servo entirely, modify the gears for continuous movement by removing any stop pins, and attach
the potentiometer directly to the arduino. If we then attach the motor to a motor controller (in my case, an L298 Compact Motor Controller) we
can now control direction and speed. By using analogRead() with the wiper of the potentiometer, well have 1024 discreet positions we can read in a
single rotation. Or, about 0.35 degrees as a minimum movement.
Its possible to use this same code and wiring to turn any old DC motor and a potentiometer into a servo by attaching the nal drive-gear of your
motor or project to the shaft of the potentiometer in such a way that it rotates perfectly with the nal drive gear.
This code uses a basic method to avoid the jitter youll get with the analog potentiometer of averaging and canceling out repetition. A number of
readings are averaged, and if the same average occurs in either of the previous two readings, then the difference in position is not recorded.
Otherwise, we use the average reading to determine our current position, and the difference to determine how far we have moved since our last
valid reading.
For mine, I modied an HS-645MG motor that came with the SPG645S kit. I used the potentiometer with the kit, as it mounts in the shaft with a slipcollar to prevent damaging the pot if you attempt to go past its stop. If you were to use a continuous-rotation pot, you could, perceivably, rotate
innitely. For my purposes, one rotation is enough, and the point is to be able to rotate very slowly while still being aware of our position. I nd that
I am able to move exactly one reading value (1024 steps) between each delay in movement, and delay up to nine hours. Your mileage may vary
based on your hardware and other code running.
The code and a wiring diagram are behind the cut:
Heres a diagram of how everything is wired for this sketch. Youll need to click on it to see the entire image.

Edit: 7/25/08 It seems that WordPress had completely eaten my source code when posted. After some searching, I found a new way of doing it.
While it isnt perfect, code portions are no longer eaten up. For best results click on view plain below.
Addendum: there were two bugs preventing full READ_AVG resolution, one was in the division of the total ( tot / READ_AVG shouldve been tot /
READ_AVG + 1), and the other was when re-setting step counter upon reaching READ_AVG, it had to be re-set to -1 to account for the postincrement operation at the end of read_pot().
1
2
3
4
5

/*

DIYServo1.0

ControllerMotorSpeedandMovementwithAbsolutePositioning

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

AsketchforturningastandardDCgearmotorintoaservo
usingapotentiometer.Canalsobeusedtoaddfinergrained
controloverexistingservos.

Controlovermotor:
Speed(PWM)
Direction
Numberofdegreestomove(upto1024)permove
Howtolongtowaitbetweenmoves

Togetfinercontrol(1024degrees,insteadof360)ofanexisting
servo,removethecontrollerandanystoppins,disconnectthe
potentiometerwires.Connectpowerlinesfromservomotortoa
DCmotorcontroller,potentiometerwipertoaninputpinonthearduino,
andtheousidepotentiometerpinsto5v/GND.

Serialmonitor:

use'l'totelltheservotomoveleft
use'r'totelltheservotomoveright
use's'tostoptheservo
use'g'togo(runtheservo)

(c)2008C.A.Churchwww.dronecolony.com

7/24/08initialversion

*/

//USING_CONTROLLERsayswhetherwehavetobringan
//enablepinhigh(suchasfortheCompactL298controller)
//beforesendingPWMdowntheLT/RTpins
//setthisto0ifyoudon'tneedanenablepin

#defineUSING_CONTROLLER1

//enablepin
#defineMOTOR_EN_PIN8
//rightdirectionpin
#defineMOTOR_RT_PIN6
//leftdirectionpin
#defineMOTOR_LT_PIN9

//READ_AVGishowmanyreadingstoaverage
//setittoonelessthantheactual#
//e.g.:10readings=setto9
//
//themoreyouaverage,themoreaccurateyourreadingislikelyto
//betoomanythough,andyou'llstartmissingchangesifthemotor
//ismovingquickly

#defineREAD_AVG9

//motorspeedisfrom0255,testwithlowvalues
//asnotallwillmoveconsistentlyforyou

intmotor_speed=75;

//howmanymstopausebetweenallowedmovements

intmotor_pause_tm=1000;

//howmany'degrees'(absolutedifferencesbetween
//potentiometerreadings)tomovebeforepausing

intmotor_move_deg=5;

//acounterforhowmanydegreeswehavemoved

intmove_deg_cnt=0;

//settingtoadefaultvalue

intmotor_cur_pin=MOTOR_RT_PIN;

//controlindicators
boolmotor_started=false;
boolmotor_paused=false;
boolfirst_run=true;
boolmotor_run=false;

longpaused_tm;

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

//ourcurrentandpreviouspotentiometerreadings

intcur_reading=0;
intpre_reading=0;

intsteps=0;

//ourcurrentreadingsarray,andourpreviousaveragereadingsarray

intvals[READ_AVG+1];
intprev_posts[2]={0,0};

voidsetup(){

Serial.begin(19200);
Serial.println("Ready");

memset((char*)vals,0,sizeof(int)*(READ_AVG+1));

//setmotorcontrolpins

if(USING_CONTROLLER)
digitalWrite(MOTOR_EN_PIN,HIGH);

digitalWrite(MOTOR_LT_PIN,LOW);
digitalWrite(MOTOR_RT_PIN,LOW);

voidloop(){

//seeifanyinputhascomeintheserialport

check_input();

//figureouthowmanydegreeswe'vemoved(ifatall)

move_deg_cnt=move_deg_cnt+read_pot();

if(motor_run==true){
//ifthemotorissupposedtoberunning

//thefollowingcheckistopreventattemptingtorotateallthe
//wayaroundonapotentiometerthathasastop.Ifyoursdoesn't
//haveastopinit,oucanremovethischeck

if((motor_cur_pin==MOTOR_RT_PIN&&prev_posts[1]>=1020)||
(motor_cur_pin==MOTOR_LT_PIN&&prev_posts[1]<=3)){

//we'vereachedourmaximumpoint(don'twanttoharmour
//potentiometer

motor_run=false;
motor_started=false;
motor_paused=false;

//bringpinlow

digitalWrite(motor_cur_pin,LOW);

//printstatus

Serial.println("HaveReachedEdgeofMovement");
}
elseif(motor_started==false){
//themotorissupposedtoberunning,butwehaven'tstarted
//ityet

//PWMoutput

analogWrite(motor_cur_pin,motor_speed);

//setstatusvalues

motor_started=true;
motor_paused=false;
first_run=true;
}
elseif(move_deg_cnt>=motor_move_deg&&motor_paused==false&&first_run==false){

//we'vegoneourspecific#ofdegrees,pausebystoppingthe
//motor

Serial.println("Pausing");

digitalWrite(motor_cur_pin,LOW);

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

motor_paused=true;

//recordwhenwestartedourpause(soweknowwhentostop)

paused_tm=millis();
}
elseif(motor_paused==true&&(millis()paused_tm)>motor_pause_tm){

//ifenoughtimehaspassedtostoppausing

Serial.println("Unpausing");
motor_paused=false;
paused_tm=millis();

//setmove_deg_cnttozerowhenrestartingtoavoidany
//jitterwhilepaused

move_deg_cnt=0;

//generatePWM
analogWrite(motor_cur_pin,motor_speed);

}
}

intread_pot(){

//readthevoltageonthepotentiometer:
cur_reading=analogRead(0);
intdiff=0;

//we'regoingtoaveragethelastREAD_AVGreads
//putinavalueforourcurrentstep

vals[steps]=cur_reading;

//ifwe'vesavedenoughvaluestogoaheadandperformanaverage...

if(steps==READ_AVG){

//resetourreadcounter

steps=1;

//determinetheaveragevalueread
//thisismostlytodealwithbigjitter

inttot=0;
intavg=0;

//sumuptotals

for(inti=0;i<=READ_AVG;i++)
tot+=vals[i];

avg=tot/READ_AVG+1;

//ignorecurrentreadingifitwaseitherofourlasttworeadings
//avoidbouncingbackandforthbetweentworeadings(slightvoltage
//variationinthesamerange)

if(avg==prev_posts[0]||avg==prev_posts[1]){
return(0);
}

//determinetheabsolutedifferencebetweenthecurrentaverage
//andthepreviousaverage

diff=avg>prev_posts[1]?avgprev_posts[1]:prev_posts[1]avg;

//ifthere'sadifferencebetweentheaverages

if(diff>0){

//printournewreading

Serial.println(avg,DEC);

//moveourlastreadingback,andputourcurrentreadingin
//ourarraytotrackthelasttwopositions

prev_posts[0]=prev_posts[1];
prev_posts[1]=avg;

//updatethissothepausecheckknowsthatwehavechangedaposition

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

//(otherwise,startinginapositionoherthan0willmessupour
//pausecheck)

first_run=false;
}

//incrementoursavedvalue#forthenextloop

steps++;

//returnthedifferencerecorded
return(diff);

voidcheck_input(){
if(Serial.available()){
charch=Serial.read();

switch(ch){
case'g':
Serial.println("GoRunningMotor");
motor_run=true;
digitalWrite(13,HIGH);
break;
case's':
Serial.println("StoppingMotor");
motor_run=false;
motor_started=false;
analogWrite(motor_cur_pin,0);
digitalWrite(13,LOW);
break;
case'l':
motor_cur_pin=MOTOR_LT_PIN;
Serial.println("Direction=LEFT");
break;
case'r':
motor_cur_pin=MOTOR_RT_PIN;
Serial.println("Direction=RIGHT");
break;
}
}
}

Marcel - July 28, 2008

What happens to the pot when you go round the clock?


[m]

Reply

Marcel - July 28, 2008

Im kinda hoping that my project will also allow time lapse work with 8 microsteps, I get .8 resolution on the
stepper which is then geared to my drive wheel (not sure of ratio).
Im thinking of also doing multipass video shots and mixing them. We have a massive 10m tidal range (low to
high water). Id really like to shoot a moco shot of the tide coming in and out.

Reply

c.a. church - July 28, 2008

Marcel
A couple of points that may help you a good deal (Ill be posting this info up in much more detail later this
week, with photos and instructions when my new gears come in the mail.)
If you hook up your potentiometer to your nal drive gear, youll divide your measurable motion points by
1024 so, at 360 degrees / 1024 == 0.35 deg minimum movement. However, I can tell you with quite a bit of
authority (ran the test this weekend) @ 17mm F/L 0.35 deg is WAY too big of a movement to be smooth. In fact,
its quite jumpy.
However, if you gear down (slow down) the nal output gear relative to the motors shaft or input gear, you can

increase your resolution by measuring from the source instead.


Say, for example, using my SPG645S kit from ServoCity, its geared at 5:1 so, if I can measure the rotation of
the servos output, instead of the nal drive gear, I get: 360 / 1024 / 5 == 0.07 degrees, a lot ner-grained. Ill be
testing later this week as to how functional that is, but I have plans to move to 15:1 gearing, giving me 0.02
degrees measurable. The reason that works is that in 5:1 gearing, it would take ve rotations of the source to
equal 1 rotation on the output. So, every individual measurement of the source rotation is 1/5th the distance (or
degrees) moved on the nal drive.
You need to know the nal gear ratio, otherwise you wont have anywhere near accurate measurement of
distance, just count the teeth on all gears to determine gear ratio. (Or, if you know theyre the same pitch, you
can get close by measuring the diameter of each. 48P == 48 teeth for each inch of diameter.)
Its important, for the highest resolution, to measure movement off the point in the drive with the highest
number of rotations. If Im running 5:1, I read off the source, if its 1:5, I read off the nal drive gear.
As to the pot going round the clock, if your pot has a stop, you dont want to do that, no sir =) Youll likely
damage the pot. Thats why in the code above it has a hard stop on each end of movement (see around line
136). Obviously, this is a problem if were measuring off the source gear wed reduce our nal movement to
1/5th of a rotation in 5:1 (we increased resolution, but decreased movement). Youll need to replace the pot w/
a continuous rotation version, or cut the stop tab out. Ill be posting an example of doing that with the
HS645MG and its built-in pot in a few days. Presuming I dont destroy the pot in the process! No worries if I do
though, Ill just run one external using a horn in addition to the gear.
If you have a continuous rotation pot, you can just count the times youve gone around to determine total
distance. E.g. :
1
2
3
4
5
6

if(cur_value<last_value){
//calculatetheabsolutedifferenceinvaluesread
diff=(1024last_value)+cur_value;
//updatecountoftotalrotations
rotations++;
}

Does that help? I think my thoughts were a little rambling there, but it starts to make a lot of sense after a while.
By measuring the rotation with potentiometers, you get out of the realm of continuous motion, and into a
specied amount of motion between shots.
!c

Reply

Marcel - July 30, 2008

Does the round-the-clock count work out accurate (precise) enough? Why not use a stepper or an encoder of
some sort? Cost?
Trying to get my buttons and LCD into a case of some description. Im using a VGA cable to communicate back
to the Arduino on the dolly. That way i can unplug once its programmed if needed or use a longer VGA cable if
needed. Ive been stripping a scanner to get some wires to hook up the LCD what fun!
[m]

Reply

c.a. church - July 30, 2008

Marcel, yes, it was all about cost and utilizing the equipment I had on-hand. Unfortunately, the tiny pots they
use inside servos cannot effectively be modied (without serious effort, at least!) due to the wire-arms they
use as wipers they end up shorting the +5v to GND when one goes all the way around.
There would denitely be some lack of accuracy in the 10-20 degrees between the left and right contacts, in a
best case scenario youd have a oating input leading to random reads, which could easily be compensated for
in software, if need be. (You would simply know Im in this region, heading for 0 or 1024 depending on
direction. )
Its funny that you should mention steppers, I found some attached to some cool pumps I picked up recently at
the electronics store, and I might use them for pan/tilt. Picked up a more powerful one as well for $9 used.
Went ahead and ordered that stepper controller you mentioned. Essentially, Ill be replacing all of the servos
and DC motors w/ steppers, as the rest of this stuff is too much work, and my September deadline is
approaching =) So, w/ 1.8 deg stepper, 8 microstepping and 3-1 gearing (what Ill have available for this setup),
that gets me 1.8 / 8 / 3 = 0.075 degrees/movement. Which should be acceptable. With a few more bucks and a
little more fabricating, I can go to 9:1 gearing, giving 0.025 deg/move.
The VGA cable is a good idea I have a second arduino coming in, therell be one driving the motors / camera,
and one driving the UI. Im having the UI part in a separate hand-held case, and was considering using a USB
cable to connect the two. I dont need many wires between, as I intend to use i2c to communicate between the

arduinos. But the UI needs to be connected as stop/start is handled by it, and status (position of each motor)
will be displayed on the UI.
The motor controllers, all power distribution, etc. will be housed inside of the case holding the truck motor on
one end of the track.
Its funny you mention cables, as I spent some time making a few last night for the LCD, keypad, and power on
the
UI
board.
I
used
the
straight,
friction-lock
male
PCB
ends
like
these
:
http://www.jameco.com/webapp/wcs/stores/servlet/ProductDisplay?
langId=-1&storeId=10001&catalogId=10001&productId=687885
Just soldered the wires onto the PCB end, and then used some shrink-wrap to tidy them up. They look very
good and only cost me a few cents a piece. The connectors stay in pretty tight as well, as the friction-lock
plates press on the arduinos female headers. For the arduino side, I just got 1 six pins, 2 8 pins, and 2 2 pins (for
vin/gnd and 5v/gnd).
!c

Reply

Marcel - July 31, 2008

Im glad things are moving forward for you c. (is that Chris, Cecil, Cuthbert??)
I spent a while yesterday rewiring my LCD. I had it all sorted originally but when I came to think about casing
my display and controls I found I was about an inch short. Damn! It took me ages to unsolder and re-solder it
all. Im not very fast. Next job is to put the pair in a case of some sort. Its going to be rough!
You may nd that with the steppers you will be able to run them fast during your setup so you can preview
your start and end points which could be handy, then ip it over to your super slow mode at runtime.
This is the rst time Ive used steppers and Ive been blown away by them. The EasyDriver board (glad thats
been of use to you) makes it SO easy(!) to use them.
I had a couple of old Lexmark Optra C laser printers a little while back. Printers are a great source of steppers! I
just dont have a lot of options for swapping gears in and out the motor has a cog xed to the shaft.
Ive not got much of a budget for this so like you Im trying to use whats to hand!
All the best.
May all your solder joints be good ones!
[m]

Reply

Maria - February 24, 2010

hi how can i attach the POT to the output shaft please?

Reply
c.a. church - March 4, 2010

Maria, sorry for the slow response, your comment got locked up in my spam folder (dunno why thats a
rst!).
Anyhow, you have numerous options, you could have a supported shaft, with one end going beyond the
support, and then either couple your potentiometer to the shaft, or use a hollow shaft with an ID larger
than the OD of your POT shaft and use a small amount of rubber material to create a tight t.
Alternatively, you can use a shaft with one end drilled and tapped, and then match it to a POT with a
threaded shaft, using a little low-strength thread-sealer to prevent it from working loose.
Of course, theres yet another option of the shaft being the actual POT shaft, if it can handle the load.
!c

Reply

c_gel - May 16, 2011

hello,
does your self made servo motor have brake function? I mean when certain angle is achieved, the shaft stops
and freezes at that particular angle in rm way; which cannot be affected if someone tries to push using nger?

sorry for my english.. tq

Reply

Jesse Guardiani - June 18, 2013

Hey, check out my Open Source DIY Servo project: http://www.diyservo.com


We use Arduino for the brain, Pololu 30A for the motor controller, a Magnetic Rotary Encoder (MRE) from the
Mad Scientist Hut, and 3d Printed plastics! We plan to make kits so everyone can use DIYServo with a motor of
their choice, or one of our pre selected motors. Come join the fun!

Reply

Nooman Mufti - September 1, 2013

Hi guys,
My apologies, I know this is an old post. I am working on a project in which I turn small mirrors to face the sun
using sensors. I was using servos, but I needed more than the usual voltage to drive them to move the heavy
mirrors, so I burnt out the internal control chip.
I used an L298N chip to drive the motor + gear box directly and then use the external solar sensors to tell the
motor to go clockwise/anti-clockwise, stop, etc. What I cant make it do is resist a change in the desired
position of the motor shaft once the position has been attained (like a normal Servo does if you try to force it by
hand to change position once an angle is set). This is important for me, since the mirrors try to fall down
because of their own weight and I need the modied Servo to stay and resist this change unless I give it a
command to change position.
Thanks so much,
Kind regards,
Nooman

Reply
Church - September 2, 2013

Nooman,
Are you using the braking mode of the L298N when stopped? If not, try enabling it nearly all H-Bridge
drivers, AFAIK, have a braking mode. If your payload is still too heavy to prevent movement when braking,
you can always do like a normal servo controller, and constantly check the position of the motor via a PID
loop (its possible to not use a PID loop, but you may end up with jitter and lots of back-and-forth
movement). There is a PID library for Arduino here: Arduino PID Library

Reply
Nooman Mufti - September 2, 2013

Dear Church,
Thanks so much for your super prompt advise. I really appreciate it. The brake function is what I
initially thought would work,essentially I set the Enable pin as high and both inputs to the motor as
High, but it doesnt freeze the motor, which essentially just freely moves. I see some other people on
the net have had issues with L293D as well, so it may be a similar problem. Some people suggest
using L6202 (MOSFET based) which denitely does a brake to address this issue
(http://www.avrfreaks.net/index.php?name=PNphpBB2&le=printview&t=22646&start=0)
I was thinking I could just get away with the motor doing an innite oscillation (clock, anticlockwise
motion) once it reaches the desired position (using the solar angle sensors I have). But I think at the
entire setups tipping point, the thing would fall then come back and so forthlots of big jitters, unless
of course, I can do a full brake somehow.maybe I can keep a very low tolerance (super small error)
in my solar angle sensors, so that it can do these innite clockwise/anticlocks at the desired
position/angle (sounds terrible, I know)..maybe I need to try another motor controller chip ?
I will try your other suggestion about the PID as well. I have also ordered another continuous rotation
servo, but from past experience, I need more torque than they can provide at 7 V, so I would need to
overvolt them, thereby burning the control chip againso an external motor control chip with a heat
sink is perhaps the only way to go.
Thanks so much again,
Kind regards,
Nooman

Reply

Leave A Reply
Name (required)
Mail (will not be published) (required)
Website

SubmitComment

Vous aimerez peut-être aussi